aiohttp 使用笔记

最近想找一个 [[Wallabag]] 的 Python 客户端,在 GitHub 简单搜寻了一下之后发现了 wallbag_api 这个仓库,看了一下 Python 代码之后发现库中的代码是用 aiohttp 编写的,所以就来学习一下这个 Python 的异步 HTTP 库 aiohttp。

aiohttp 官方简洁明了,Asynchronous HTTP Client/Server for asyncio and Python. aiohttp 是一个基于 asyncio 模块的异步 HTTP 客户端/服务端框架。

可以 看到 aiohttp 不仅是一个 HTTP 客户端,也可以作为服务端。同时支持服务端的 WebSockets 和客户端 WebSockets。

我们知道 Python 下还有一个比较著名的 HTTP Client 库叫做 requests,这是一个同步的 HTTP 调用客户端,使用 requests 发起 HTTP 调用之后需要同步等待返回结果,而在 aiohttp 可以在发起请求之后将将程序的控制权暂时给别人,等待响应返回结果回来了之后再进行处理,这就可以提升系统的性能。

aiohttp 内部使用了 [[python-asyncio]] 来实现,而 Python 下 asyncio 的核心就是 [[Coroutine 协程]]

Coroutine 协程

Coroutine(协程)是一个更通用的 subroutine(子程序)。

async 方法中使用 await 关键字来表示 coroutine。当使用 await 关键字的时候,coroutine 会将当前程序的控制释放给 Event loop。

import asyncio
async def async_func():
    print('start ...')
    await asyncio.sleep(1)
    print('... end!')

async def main():
    async_func()#this will do nothing because coroutine object is created but not awaited
    await async_func()

asyncio.run(main())

如果直接调用 async_func() 不会有任何作用,需要在前面添加 await

协程适合 IO 密集型任务:

  • 网络请求,比如爬虫 [[aiohttp]]
  • 文件读写,[[aiofile]]
  • Web 框架,[[aiohttp]], [[FastAPI]]
  • 数据库查询,[[asyncpg]], [[databases]], [[aiomysql]]

Installation

安装使用:

pip install aiohttp

作为客户端

aiohttp 作为客户端。

这里可以和 requests 做一个简单的对比,在 requests 中发起同步请求:

import requests 
def hello()    
     return requests.get("http://httpbin.org/get")     

print(hello())

程序在 requests.get 方法调用时会同步等待请求返回。

而如果使用 aiohttp:

#!/usr/local/bin/python3.6
import asyncio 
from aiohttp import ClientSession 

async def hello():     
    async with ClientSession() as session:         
        async with session.get("http://httpbin.org/headers") as response:                
            response = await response.read()                         
            print(response) 
  
loop = asyncio.get_event_loop() 
loop.run_until_complete(hello())

在这个异步代码中我们可以看到很多的异步关键字,async 以及 await 关键字定义函数为异步。

这里使用 ClientSession() 获取一个 Session,session 可以在多个请求之间保留 cookie 和相关信息。因为 session 关闭是一个异步操作,所以需要使用 async with

session.get 发起调用,也是一个异步操作,所以也需要使用 async,而 with 语句保证 response 可以正确关闭。

最后为了让这个异步方法可以执行,需要将其放入一个事件循环。

作为服务端

[[用 aiohttp 写服务器]] 不是本文的重点,所以简单了解一下,先略过。

from aiohttp import web

routes = web.RouteTableDef()

@routes.get('/')
async def hello(request):
    return web.Response(text="Hello, world")

app = web.Application()
app.add_routes(routes)
web.run_app(app)

控制并发

使用 Semaphore 控制

Semaphore 是一个线程计数器,核心是 acquire()release() 方法。

执行 acquire() 方法时,先判断内部值是否小于 0,如果大于 0,获得锁,内部值减一,如果小于 0,阻塞,直到 release() 方法释放锁,内部值加一。

async def fetch(url, semaphore):
    async with semaphore:
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                data = await response.text()
                print('data', data + " " + time.ctime())
                return data

async def run():
    url = 'http://127.0.0.1:5000'
    semaphore = asyncio.Semaphore(2)  # 限制并发量为2
    to_get = [fetch(url, semaphore) for _ in range(10)]
    await asyncio.wait(to_get)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())
    loop.close()

使用 TCPConnector 控制

async def fetch(session, url):
    async with session.get(url) as resp:
        if resp.status != 200:
            resp.raise_for_status()
        data = await resp.text()
        print('data', data + " " + time.ctime())
        return data

async def fetch_multi(session, urls):
    tasks = []
    for url in urls:
        task = asyncio.create_task(fetch(session, url))
        tasks.append(task)
    # gather: 搜集所有future对象,并等待返回
    results = await asyncio.gather(*tasks)
    return results

async def main():
    urls = ["http://127.0.0.1:5000/" for _ in range(10)]
    conn = aiohttp.TCPConnector(limit=3)
    async with aiohttp.ClientSession(connector=conn) as session:
        datas = await fetch_multi(session, urls)
        print(datas)

if __name__ == '__main__':
    asyncio.run(main())


2022-05-03 http , aiohttp , python , aio , async

使用 lux 下载哔哩哔哩视频

Lux 是一个使用 Go 语言编写的视频下载命令行工具,支持的平台很多,提供了包括 macOS、Windows、Linux 等等平台的命令行支持,安装和使用非常简单的。Lux 原来的名字是叫做 Annie(安妮),对标的是 macOS 上一款非常著名的视频下载软件叫做 [[Downie]](唐尼)荔枝数码正版授权。但后来改名成了 Lux。

Lux 支持非常多的视频网站:抖音、哔哩哔哩、半次元、pixivision、优酷、YouTube、爱奇艺、芒果 TV、Tumblr、Vimeo、Facebook、斗鱼视频、秒拍、新浪微博、Instagram、Twitter、腾讯视频、网易云音乐、音悦台

项目地址:https://github.com/iawia002/lux

Installation

macOS 下:

brew install ffmpeg
brew install lux

或者从 Release 页面下载,手动安装。

curl "https://github.com/iawia002/lux/releases/download/v0.15.0/lux_0.15.0_macOS_64-bit.tar.gz"

Ubuntu 下安装

sudo apt install ffmpeg
wget "https://github.com/iawia002/lux/releases/download/v0.15.0/lux_0.15.0_Linux_64-bit.tar.gz"
tar zxvf lux_0.14.0_Linux_64-bit.tar.gz
sudo mv lux /usr/local/bin
lux --help

使用

直接下载

Lux 的使用很简单,在终端:

lux "https://www.youtube.com/watch?v=QJXPS0gQ6Eg"
lux "https://www.bilibili.com/video/BV1ZJ411h713"

视频链接给的是播放列表里的第二个视频。如果用 youtube-dl 下载会自作主张地下载播放列表的第一个视频。在对付 B 站播放列表方面,Annie 比较乖一点。

Annie 不仅可以下载视频,如果给的是图片的网址,它就下载图片。

给播放列表的链接就下载播放列表:

下载不同分辨率

首先使用 -i 参数查看可供下载的视频分辨率,-i 参数可以一次添加多个视频链接,空格分隔。

❯ lux -i "https://www.bilibili.com/video/BV1ZJ411h713"

 Site:      哔哩哔哩 bilibili.com
 Title:     【中字】金世正 talk演唱会 全场 非常治愈的谈话
 Type:      video
 Streams:   # All available quality
     [64-7]  -------------------
     Quality:         高清 720P avc1.640028
     Size:            122.36 MiB (128300684 Bytes)
     # download with: lux -f 64-7 ...

     [32-12]  -------------------
     Quality:         清晰 480P hev1.1.6.L120.90
     Size:            108.75 MiB (114028043 Bytes)
     # download with: lux -f 32-12 ...

     [64-12]  -------------------
     Quality:         高清 720P hev1.1.6.L120.90
     Size:            108.57 MiB (113847391 Bytes)
     # download with: lux -f 64-12 ...

     [16-7]  -------------------
     Quality:         流畅 360P avc1.64001E
     Size:            86.10 MiB (90287066 Bytes)
     # download with: lux -f 16-7 ...

     [32-7]  -------------------
     Quality:         清晰 480P avc1.64001F
     Size:            67.43 MiB (70708693 Bytes)
     # download with: lux -f 32-7 ...

     [16-12]  -------------------
     Quality:         流畅 360P hev1.1.6.L120.90
     Size:            67.40 MiB (70670783 Bytes)
     # download with: lux -f 16-12 ...

然后使用 -f 来指定要下载的分辨率:

lux -f 64-7 https://www.bilibili.com/video/BV1ZJ411h713

下载列表

使用 -p 来下载视频列表。

如果要指定列表中的位置,可以使用如下几个选项:

  • -start 从视频列表的第几个开始下
  • -end 下载到几个
  • -items 指定要下载哪几个,比如 1,5,6,8-10
  • -eto B 站独有的参数,用于没有标题只有文件名的播放列表

举例:

lux -p -start 1 -end 15 "https://www.bilibili.com/video/BV1no4y1C7oo"

从文件中读取下载链接

可以使用 -F 参数,从包含视频 URL 的文本文件中下载视频:

lux -F /path/to/links.txt

这里也可以用参数 -start-end-items

按住 ctrl+c 可以中断下载,可以续传。

B 站上可以用 av 和 ep 加数字下载视频,比如:

annie -i ep198381 av21877586

还有三个参数也很有用:

指定保存路径

可以使用 -o 参数后面接路径,来指定将视频保存到该路径下:

lux -o ~/Videos/ URL

使用 -O NAME 来指定输出的名字。

输出 JSON

可以使用 -j 输出 JSON 格式结果。

  • 最出名的 [[youtube-dl]] 还有升级版的 [[yt-dlp]]
  • 适配比较多国内的 [[视频下载工具 you-get]]
  • Go 语言写的视频下载命令行工具 [[lux]]
  • C# 编写的 B 站下载 BBDown

2022-05-01 lux , bilibili , annie , video , 视频下载

Tailscale 简单使用

Tailscale 是一种用于构建安全、私有的网络的软件工具,可以在不同设备之间创建虚拟专用网络(VPN),并允许这些设备之间的安全通信。与传统的 VPN 不同,Tailscale 不需要使用 IP 地址或端口转发来实现网络连接。相反,Tailscale 使用每个设备上的唯一身份验证密钥来实现点对点的直接连接,从而提供了更高的安全性和便捷性。Tailscale 使用更高级的 [[WireGuard]] 协议来进行节点和节点之间的通信。

Tailscale 可以在多种操作系统和设备上使用,包括 Windows、macOS、Linux、iOS、Android 和路由器等。它还提供了许多其他功能,如内网穿透、跨设备的文件共享、远程访问和身份验证等,可以帮助用户更轻松地管理和连接其设备和网络。

在接触到 Tailscale 之前,也用过一段时间的 ZeroTierOmniEdge ,ZeroTier 其实到现在未知还一直在用,但是在中国大陆的速度确实不是很快,大部分的时间我只能连个 SSH 查看一下,其他的网络访问都比较慢,并且还丢包。OminiEdge 则是感觉安装的方式还略微有点麻烦,不如现在要介绍的 Tailscale 方便,并且 Tailscale 还有对应的 Ansible Playbook,我配置好之后基本上,如果有新节点的加入只需要跑一下 [[Ansible]] 即可。

Install

Debian/Ubuntu 下安装:

curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/bionic.gpg | sudo apt-key add -
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/bionic.list | sudo tee /etc/apt/sources.list.d/tailscale.list
sudo apt update && sudo apt install tailscale

启动:

sudo tailscale up

查看 Tailscale IP 地址:

ip addr show tailscale0

或者

tailscale ip -4

需要注意的是目前是没有方法自定义 Tailscale 分配的 IP 地址。如果想要改变被分配的 IP 地址,可以通过如下方式强制分配新的:

ACL

Tailscale ACL(Access Control List),访问控制列表,可以严格限制特定用户或设备在 Tailscale 网络上访问的内容。

Tailscale/Headscale 的默认访问规则是 default deny,也就是黑名单模式,只有在访问规则明确允许的情况下设备之间才能通信。所以 Tailscale/Headscale 默认会使用 allowall 访问策略进行初始化,该策略允许加入到 Tailscale 网络的所有设备之间可以相互访问。

Tailscale/Headscale 通过使用 group 这种概念,可以只用非常少的规则就能表达大部分安全策略。除了 group 之外,还可以为设备打 tag 来进一步扩展访问策略。结合 group 和 tag 就可以构建出强大的基于角色的访问控制(RBAC)策略。

Tailscale 的访问控制权限借鉴了 [[RBAC]] 基于角色的访问控制。

Headscale 的 ACL 策略主要包含以下几个部分:

  • acls:ACL 策略定义。
  • groups:用户的集合。Tailscale 官方控制器的“用户”指的是登录名,必须是邮箱格式。而  Headscale 的用户就是 namesapce
  • hosts:定义 IP 地址或者 CIDR 的别名。
  • tagOwners:指定哪些用户有权限给设备打 tag。
  • autoApprovers:允许哪些用户不需要控制端确认就可以宣告 Subnet 路由和 Exit Node。

ACL 规则

acls 部分是 ACL 规则主体,每个规则都是一个 HuJSON 对象,它授予从一组访问来源到一组访问目标的访问权限。

所有的 ACL 规则最终表示的都是允许从特定源 IP 地址到特定目标 IP 地址和端口的流量。虽然可以直接使用 IP 地址来编写 ACL 规则,但为了可读性以及方便维护,建议使用用户、Group 以及 tag 来编写规则,Tailscale 最终会将其转换为具体的 IP 地址和端口。

每一个 ACL 访问规则长这个样子:

  - action: accept
    src:
      - xxx
      - xxx
      - ...
    dst:
      - xxx
      - xxx
      - ...
    proto: protocol # 可选参数

Tailscale/Headscale 的默认访问规则是 default deny,也就是黑名单模式,只有在访问规则明确允许的情况下设备之间才能通信。所以 ACL 规则中的 action 值一般都写 accept,毕竟默认是 deny 嘛。

src 字段表示访问来源列表,该字段可以填的值都在这个表格里:

高级功能

  • route 功能,通过虚拟路由实现通过一个节点访问到节点所在局域网内的所有内网设备
  • [[Tailscale Exit Nodes]] 出口节点功能实现设备的异地出口访问,让设备通过指定的节点作为出口

reference

  • [[2018-06-14-zerotier]]
  • [[2021-11-29-omniedge-usage]]

2022-04-28 tailscale , vpn , virtual-networks , linux , ubuntu

图片压缩工具 Squoosh 离线版

Squoosh 是 Google 推出的一个图片压缩工具。之前整理macOS 初始化设置 的时候就说过,基本上算是必不可少的一个压缩工具了。

如果要大批量压缩图片,之前也介绍过 jpegoptim 和 optipng

如果要实现更加高阶的图片操作,比如 resize, blur, crop, despeckle, dither, drow on, flip, join, re-sample 等等,也可以了解一下 [[ImageMagick]]。

但使用 Squoosh 的过程中有一点不方便的就是 Google 只提供了在线版,之前也在 issue 中提过,官方回复是本来这个工具就是命令行的一个在线化,没有必要再搞一个离线的版本。

但最近发现 GitHub 上有人做出了一个离线版本的 Squoosh。

离线版本的 Squoosh 基于 Electron,并使用了 GoogleChromeLabs 之前开源的在线版。

如果是在 macOS 下

macOS 有一个内置的图片压缩命令

sips -s formatOptions high path/*.jpg

2022-04-28 macos , linux , application , squoosh , google , electron , google-chrome , image , image-compress

大法官说了算 美国司法观察笔记 读书笔记

这是 [[20220417-21 天计划]] 的第二本书,看过 [[何帆]] 翻译的很多本作品,这一本《大法官说了算:美国司法观察笔记》是何帆多年的司法观察记录。作者以[[苏特大法官]] 的辞职开始说起,先后介绍了大法官是如何产生的,最高法院内部是怎么运行的,网络时代的法庭,法院对言论自由的态度,对死刑的态度,等等角度对大法官展开了详细的描述,最后一章对现在美国的最高法院的每一位大法官做了单独的介绍。

个人感觉这是一本非常适合想要了解大法官,以及最高法院的读者,在书中可以对整个最高法院的历史,以及大法官为何一言九鼎有一个非常直观的认识。

如果读完钦佩于大法官的人格魅力,那么可以继续看[[九人-美国最高法院风云]],如果读完更关心最高法院是如何界定言论自由的,那么继续阅读 [[言论的边界 美国宪法第一修正案简史]] ,如果读完更醉心于案件的错综复杂以及大法官们抽丝剥茧梳理给出判决,那么可以再读[[美国宪政历程 影响美国的 25 个司法大案]]

当然如果不想读这些大部头作品,那么也有牛津通识读本中的 [[美国最高法院通识读本]],[[批评官员的尺度]] 等等作品可以继续。

司法天平的倾斜

或许看过美剧、韩剧律政剧的人,对那个蒙着眼睛、一手持剑,一手天平的正义女神不再陌生,我也曾在这些剧集中一次次被法官「正义」的判决所感染。然而稍微了解一下美国的司法史就可以看到在不同的历史时期司法的天平都不是绝对的公平。

美国的历史上存在《惩治煽动性言论法》,曾一度赋予了总统以无上的言论审查权;最高法院也会允许雇主不受任何限制的和劳工签订超长工作时间的合同,大法官们认为雇主有权利和劳工签订不受时间限制的合同;更甚至[[坦尼大法官]]在 1857 年的 [[斯科特案]] 判决中判定黑人不是美国公民,直接导致了南北战争。[[202011041258-最高法院历史上的污点]] 不是一两句话就可以说清楚的,但是正是因为司法只是三权分立中的一个分支,更甚至是一个在最初没有一点地位的分支,司法天平的倾斜甚至倾倒,可以用行政的立法的权力予以纠正。

林肯当上总统之后,用宪法第十四修正案推翻了最高法院关于黑人不是美国公民的判决,将倾斜的天平拨了回来。

而司法的存在也正是为了约束行政权力的过大,以及立法分支的肆意立法。大法官们手握[[司法审查权]],可以对立法分支涉嫌违宪的法律进行审查。正是这三个分支的相互制约才使得美国在过去 200 多年的历史中,一直在修修补补中度日,除了南北战争这一次严重的战争之外一直可以不断地进化。

这两天在 Twitter 刚好看到一则推文,提出一个问题,如果这三个分支都不再很好的执行自己的职责,司法分支不再对超越宪法的立法进行审查,而行政权力以更强硬的手段行政,那么该怎么办?最终给出的答案是美国的持枪自由。但是我们都知道非要走到那一步必然会导致更严重的问题,这也不是这一本书所讨论的问题了。

保守和自由派的对决

在最高法院的描述中,一直少不了「自由派」和「保守派」两派大法官的描述。但是,我们要知道,在这里「保守」绝不是一个贬义词,保守派大法官只是始终对宪法保持警惕之心,深怕任意解释宪法会破坏法治框架。而自由派大法官,并非不尊重宪法,只是他们认为一部「活的宪法」才能与时俱进,适应不断变幻的现实,体现制宪先贤的本意。他们会秉持「至善论」(Perfectionism)的观点,不断创制一些新权利,进而将该权利解释为宪法的应有之义。

保守的含义其实颇为多元,可能是对无孔不入的「大政府」的畏惧,可能是对上帝的虔诚信仰,可能是对种族、文化、性的审慎态度,很难以进步或落后加以评判。

美国是一个缺乏改革传统的国家,就像上面提到的那样,越是重大的变革,越是以润物细无声的渐进方式完成。最高法院当然能够推动社会变革,但是「大法官说了算」并不意味着判决是推动变革的主要途径。[[布朗案]]之后依然有百万的黑人儿童无法获得读书的机会。最高法院的判决并非只是权力的宣示,更是一种理想的表达,当权力和理念合二为一时,才能慢慢影响人们的观念,直至形成一种更为先进的文化。这些观念被大多数人接受的时候,就会演变成制度和法律。

无论最高法院如何判决,绝大多数人都会接受判决结果。这种信任建立在过去上百位大法官孜孜努力的基础之上。


2022-04-27 reading , reading-2022 , judge , supreme-court , 司法

每天学习一个命令:umask 命令简单介绍

在使用 LinuxServer 的 Docker 镜像的时候经常会需要设置 umask 值,这个值的具体作用一直不太清楚,正好整理一下。

什么是 umask

在 Linux 或 Unix 操作系统中,所有的新文件都有默认权限的。umask 命令行允许用户设置文件的 creation mask, 这决定了新创建的文件或目录的权限。

预设的文件夹权限是:

0755 drwxr-xr-x

预设的文件权限是:

0644 -rw-r--r--

在 Linux 下创建目录或文件时有一个默认值,这个权限减去 umask 的权限就是新建的目录或文件的实际权限了。

这个配置会被 mkdir, touch, tee 等等一些命令使用。

Linux 权限模型

在深入了解 umask 之前,首先再来解释一下 Linux 的权限模型。在 Linux 中,每一个文件都三个类别用户组:

  • file owner
  • Group members
  • Everyone else

每一个类别都有三个权限:

  • read
  • write
  • execute

这使用用户可以决定哪些用户允许读、写、执行这个文件。

使用 ls -l dirname 来查看:

drwxr-xr-x 12 einverne users 4.0K Apr  8 20:51 dirname
|[-][-][-]    [------] [---]
| |  |  |        |       |       
| |  |  |        |       +-----------> Group
| |  |  |        +-------------------> Owner
| |  |  +----------------------------> Others Permissions
| |  +-------------------------------> Group Permissions
| +----------------------------------> Owner Permissions
+------------------------------------> File Type

第一个字幕表示文件的类型:

  • d 表示目录
  • - 文件
  • l symbolic link

之后的九位,每三位一组:

  • 第一组,是拥有者的权限
  • 第二组是组权限
  • 第三组是所有人的权限

权限有两种表示方法:

  • Symbolic
  • Numeric

Symbolic annotation

字母 rwx 分别表示读(read),写(write),执行(execute),而 - 表示没有权限。

Numeric

权限还可以使用数字来表示,r 可以使用 4 来表示, w 是 2,x 是 1。

比如 rwxr-xr-x 使用数字表示则是 755.

再回到 umask,在 Linux 系统中,默认的创建文件权限是 666,默认给 user, group, others 读和写的权限。目录的权限是 777。

umask 可以用来查看或改变新文件或目录的权限。umask 只会影响当前 shell 环境。在大部分的 Linux 发行版中,默认的系统级别的 umask value 一般在 pam_umask.so 或者 /etc/profile 中。

直接执行 umask 来查看输出,在我本机输出是 002

umask 值包含了新创建的文件或目录不拥有的权限。

上面提到过,默认的新文件的权限是 666,如果 umask 值是 002 则,新创建的文件权限就是 666-002 结果是 664

用户也可以使用 umask -S 来查看 symbolic notation:

❯ umask -S
u=rwx,g=rwx,o=rx

设置 umask

/etc/profile 中可以设置 umask 值。比如不想要其他用户有任何权限,umask 就要设置为 027.

umask 027

reference


2022-04-26 linux , umask , permission

自建邮件服务器的选择和比较

在过去几年的时间里面,一直再寻求各种服务的 Self-hosted ,但唯独邮件服务器自建起来的步骤比较麻烦,但也不是不可能,甚至邮件服务器的 Self-hosted 方案更加全面。

下面这些邮件服务器架设方案是过去几年里面尝试和使用的。

  • [[Poste]] 分为免费版和收费版,可以使用 Docker 部署,所有服务集中在一个镜像中,部署比较简单,适合商用服务,如果出现问题还可以付费进行咨询。Poste 的付费版本提供了更加强大的日志审查,诊断分析工具等等,具体可以参考官网
  • [[Mailu]] 是一个使用 Python 编写的邮件服务器,可以使用 Docker 安装部署,集成了 dovecot, postfix 等等。个人的使用体验就是比较小巧,但是功能齐全。还自带 Webmail。 使用 Mailu 搭建邮件服务器
  • [[mailcow]] 相对于 Mailu 更加强大,但也相对比较消耗资源,mailcow 可以管理多用户,多域名,后台功能非常详细,使用 [[SOGo]] webmail。 使用 Mailcow 搭建邮件服务器.
  • [[postal]] 一个使用 Rust 编写的邮件服务器,可以发送和接收邮件。可以使用 HTTP 接口
  • [[Maddy]] 是一个使用 Go 语言实现的多合一邮件服务器。没有 Web 界面,需要借助客户端

其他一些方案:

  • [[Mail-in-a-Box]]
  • [[iRedMail]]
  • [[modoboa]]
  • [[hMailServer]] 是一款为 Windows Server 编写的邮件服务器。
  • [[Salmon]] Python 实现的邮件服务器
  • docker-mailserver
  • [[zimbra]]

总结

我个人在首先尝试 Mailu ,学习基本的邮件服务器的协议必要的 DNS 配置之后,从 Mailu 切换长期使用 Mailcow。Mailcow 支持多用户,多域名配置,相对 Mailu 要重一些。所以如果是简单的轻度使用推荐 Mailu,Python 编写,Docker 镜像依赖简单,后台简洁。如果是重度使用,那么推荐 Mailcow,虽然镜像搭建稍微复杂,后台管理也稍微复杂一些,但是更强大。

推广

如果你不想自己搭建邮件服务器,那么也可以选购 EV Hosting 推出的域名邮箱托管服务,可以以极低的价格获得无限域名无限邮箱服务。

reference

更多总结: https://medevel.com/list-os-mail-server/


2022-04-25 mail-server , email , self-hosted , self-host , linux , docker , smtp

使用 Mailcow 自建邮件服务器

Mailcow 是一个可以使用 Docker 容器化部署的邮件服务器。

GitHub: https://github.com/mailcow/mailcow-dockerized

Mailcow 的优点

[[Mailcow]] 相较于 [[Mailu]] 整体比较完整,功能相对比较丰富。

  • 支持二步验证,甚至支持 macOS 指纹验证
  • Webmail 使用 SOGo
  • 支持给每个域名增加管理员
  • 有良好的日志查看系统
  • Sync job 功能可以快速、简单地迁移到另外的提供商
  • Web 界面上可以下载 profile,快速在 macOS 和 iOS 中配置使用

缺点

  • 内存占用比较大,只是部署了 Mailcow 就占去了 3G 内存,如果禁用 Solr 和 ClamAV 可以节省内存,日常占用在 1G~2G,但就丧失了搜索和病毒扫描
  • 用的组件比较多,Web 端整体使用体验比 Mailu 慢

安装

Mailcow 整体的搭建过程比较简单,如果之前搭建过 Mailu,实际上,参考官网的教程,并没有太多需要再强调的,甚至如果在同一台机器上,从 Mailu 上迁移过来,只需要修改 [[DKIM]] 记录即可。

提前的 DNS 设置可以参考官网。主要需要配置一些 A 记录,CNAME,[[DKIM]], [[SPF]],[[DMARC]] 等等。

配置 DNS

# Name              Type       Value
mail                IN A       1.2.3.4
autodiscover        IN CNAME   mail.example.org. (your ${MAILCOW_HOSTNAME})
autoconfig          IN CNAME   mail.example.org. (your ${MAILCOW_HOSTNAME})
@                   IN MX 10   mail.example.org. (your ${MAILCOW_HOSTNAME})

SPF 记录配置

# Name              Type       Value
@                   IN TXT     v=spf1 mx a -all

安装:

git clone https://github.com/mailcow/mailcow-dockerized
cd mailcow-dockerized

然后执行:

./generate_config.sh

交互命令中输入自己的 MAILCOW_HOSTNAME,比如 mail.example.com

然后启动:

docker-compose pull
docker-compose up -d

然后使用配置好的域名 mail.example.com 登录,默认的用户名 admin 密码 moohoo

配置 SSL

编辑 mailcow.conf 然后配置 HTTP_BIND=

编辑 data/conf/nginx/redirect.conf:

server {
  root /web;
  listen 80 default_server;
  listen [::]:80 default_server;
  include /etc/nginx/conf.d/server_name.active;
  if ( $request_uri ~* "%0A|%0D" ) { return 403; }
  location ^~ /.well-known/acme-challenge/ {
    allow all;
    default_type "text/plain";
  }
  location / {
    return 301 https://$host$uri$is_args$args;
  }
}

重启:

docker-compose up -d
docker-compose restart nginx-mailcow

使用

进入后台之后,在 Configuration->Domains -> 添加域名。

添加域名之后需要修改域名的对应 DNS 记录。在界面会有显示。

添加完域名之后可以点击页面中的 Mailboxes 来添加域名邮箱来收发邮件。

Mailcow 使用 [[SOGo]] 作为默认的 Webmail 客户端。

客户端设置

几个常用的客户端连接端口:

Service Encryption Port
IMAP STARTTLS 143
IMAPS SSL 993
POP3 STARTTLS 110
POP3S SSL 995
SMTP STARTTLS 587
SMTPS SSL 465

升级

我的 Mailcow 是使用 Docker 安装的,所以升级也比较好升级,直接 git pull origin master 更新最新的代码,然后执行 sudo ./update.sh 即可。

但是我在升级的过程中遇到点问题,我本地的 Mailcow docker 项目存在一些无意中被修改的文件,并且我因为半年的时间没有更新,所以本地好多文件和远程的冲突了,git pull origin master 是无法拉取并合并到本地的,我自己的看了一下冲突的文件(如下),我判断这些文件直接可以覆盖更新。

On branch master
Your branch is behind 'origin/master' by 717 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   update.sh

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   data/conf/postfix/main.cf

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .github/workflows/assets/
        data/Dockerfiles/backup/
        data/web/css/themes/

所以我就直接执行:

git fetch --all
git reset --hard origin/master

直接使用远程的分支重置覆盖本地的修改。

然后再执行 sudo ./update.sh,升级的过程中 update 脚本会处理很多问题。这个地方需要注意 docker-compose 的版本要保持最新,用 v1 版本的 docker-compose 可能会出问题。

sudo apt install docker-compose-plugin

或者自己手动安装

sudo curl -SL https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

然后再执行 sudo ./update.sh,等脚本执行完成即升级完成。

特性梳理

Alias 别名

Mailcow 的 alias 是指可以通过该功能,将收件人的电子邮件地址映射到其他多个电子邮件地址的设置。用户别名将一个或多个电子邮件地址映射到一个用户帐户。例如,如果您有一个名为 [email protected] 的用户,您可以创建一个用户别名 [email protected],将所有发送到 [email protected] 的邮件都转发到 [email protected]

如果要配置 Catch-All,直接在 Alias 中将 @ 符号前的用户名省略,比如要所有发送到 @example.com 的邮件全部都发送给另一个邮箱, 直接配置 @example.com 即可。

Domain aliases 域名别名

在 Mailcow 中,域名别名是指,将一个域名映射到另外一个域名。这在很多情况下都非常有用,比如当公司开设新品牌或更改其主要域名时,或者需要合并多个域名(例如“example.com”和“example.net”)时。

通过在 Mailcow 中设置域名别名,发送到备用域名的电子邮件将会被传递到与主要域名相同的邮箱中。这意味着用户可以继续收到发送到任何一个域名的电子邮件,而不必管理多个电子邮件帐户。

总的来说,Mailcow 域名别名是管理电子邮件域名和确保消息传递到正确收件人的功能。

ObDH

Address rewriting

Address rewriting (地址重写) 的功能就是在用户配置的域名邮箱往外发送邮件的时候,通过 BCC 的方式重写发件人的信息。

这个功能下面有两个部分:

  • BCC maps,用于将发送和接收到的邮件的副本静默转发到后台配置的一个地址。BCC map 下有两种类型
    • Sender map,如果发件人是配置的 Local destination,则增加 BCC 邮箱地址
    • Recipient map,如果收件人是配置的 Local destination,则增加 BCC 邮箱地址
  • Recipient maps,用于在发送邮件之前替换邮件上的目标地址。

O0S2

手工配置

推广

如果你不想自己搭建邮件服务器,那么也可以选购 EV Hosting 推出的域名邮箱托管服务,可以以极低的价格获得无限域名无限邮箱服务。

reference


2022-04-23 mailcow , mail , email , email-server , linux , docker , smtp , spf

法官能为民主做什么 读书笔记

这是 [[20220417-21 天计划]] 中的第一本书,记录一下读书笔记。

这本书的作者是最高法院[[斯蒂芬-布雷耶大法官]],英文的标题是 Making our Democracy Work: A Judge’s View 可以看到这是布雷耶大法官多年的观察记录。

三句话总结书的内容

本书主要分为三个部分:

  • 第一部分通过具体的案例介绍民众是如何建立起对最高法院的信任,并接受他们心中不认同的判决的过程。
    • [[马伯里案]] 中确立了[[司法审查权]]
    • 切诺基印第安人土地纷争中涉及到了「判决的可执行性」,如果一个判决没有人愿意执行怎么办?该案件确立了「同案同判」原则。总统动用权力破坏最高法院的判决执行,损害了最高法院的权威
    • 1857 年,臭名昭著的 [[德雷德·斯科特案]]引发内战
    • [[布朗案]] 终结了种族隔离,总统派兵保护黑人学生上学,司法至上的确立
    • [[布什诉戈尔案]],证明了官员和人民已经逐步接受了最高法院的判决和宪法解释,已经形成一种习惯,哪怕内心不认同,也遵循最高法院的宪法解释
  • 第二部分[[斯蒂芬-布雷耶大法官]]提出了一些实用主义的宪法解释方法。
    • 对国会立法,合理推断理性立法者的立法意图
    • 对于行政行为,考虑相关法律的立法目的,兼顾行政分支的专长
    • 解释与联邦主义相关的法律时,注重辅从原则
    • 处理下级法院职能相关问题,考虑专门分工
    • 处理最高法院与本院先例关系时,强调稳定性
  • 第三部分,在解释与个人基本权利相关的法律是,注重价值判断与比例原则

启发或想法

自上一次美国提名大法官开始,已经陆陆续续看过不少的关于最高法院,美国宪政历程,大法官的相关传记也看了不少,但是这一本由大法官主笔的著作依然让我看到了一些不一样的内容。

虽然那些经典的案例在其他著作中也曾经被一遍一遍的分析,但都是旁观者的视角,[[斯蒂芬-布雷耶大法官]]用全书的三分之一的篇幅列举了一个又一个经典案例来回答了一个问题,最高法院的信任是从何而来的。

可以看到的是最高法院从美国建国以来一步步走来并不顺利,也曾有过多次臭名昭著的判决,但是最高法院拥有[[司法审查权]],只要好好的利用这一个至上的权力,不过分迎合主流,为社会的少数群体发声,维护他们的权利,最高法院就可以一步一步慢慢地获得民众的信任。

而且可以看到的是,民众的权利也只有在最高法院,在宪法的保护下,才不会一步步被庞大的国家机器所侵犯,遇到不公和政府的权力扩张,可以随时通过最高法院去维护。

公民自由的要旨就在于,任何个人受到侵害,都有申请法律保护的权利

第二个与众不同的便是,在其他的著作中我们只能通过最后的判决以及判词来揣摩大法官的思考与想法,而这一部著作的第二部分,作者让我们窥探到了其判案的逻辑,以及内心的思考。

书中的第二部分更像是一个如何做法官的说明书,[[斯蒂芬-布雷耶大法官]]讲述大法官应该怎么做才能赢得并延续公众对大法官的信任。

布雷耶大法官总结了一套实用主义法律解释方法。

  • 不拘泥于法律条文起草的时代,应用到不断变化的现实中
  • 解释先发时,充分考虑其他政府的职能能彼此之间的关系

司法权的存在一方面是为少数族群争取应得的权利,另一方面是防止政府,国会权力的无限扩张,而这两者本身就不矛盾,政府权力的扩张一定会侵害到个体权利。

民众对大法官的信任从何而来?

最高法院通过解释和裁判,尽职履责,促进宪法有效实施,从而赢得并延续公众对它的信任。

过分迎合主流政治势力的判决,会削弱甚至剥夺宪法对人民的保护,尤其是对非主流个人、群体的保护力度。[[德雷德·斯科特案]]

法官能为民主做什么?

布雷耶认为,最高法院能实现制宪者心中切实可行的民主,推动宪法在实践中有效地运行。

如何界定个人自由?

[[比例原则]] 来平衡宪法价值观与实际利益诉求之间的冲突。

谁应该看这本书

对三权分立感兴趣,想要了解最高法院,以及大法官的人。

三个 Quotes

公民自由的要旨就在于,任何个人受到侵害,都有申请法律保护的权利。

公众的信任,并非自动脱胎于成文宪法的存在。它必须经过构建、打造,而且一旦形成,就必须永久维持。

我们的社会,自由是一项基本标准”,未经审判的拘押“即使有例外,也必须给予严格限制”。“值此最具挑战与最不确定的时刻,我国对正当程序的承诺正经受遭受最严峻的考验;也正是在这个时候,我们也必须在自己的家园,守护那些我们在海外为之浴血奋战的原则”

制宪先贤们试图创造的民主政体,能够维护自由,有效运行,并垂范久远。他们也意识到,确有必要由一个机构来守护宪法创制的法律边界。

《积极自由》中指出,如果公众不积极参与美国的政治生活,宪法对创制民主政体的影响将微乎其微。同样,如果公众仅仅因为不喜欢对宪法的某种解释,就恣意置之不理,宪法对实现切实可行的宪政民主之影响,也将乏善可陈。

当本杰明·富兰克林被问及制宪会议创立了什么样的政府时,他给出了那句著名的回答:“一个共和国,夫人,如果你们能好好维系它的话。”由此带来的挑战是,如何令我们继承的民主宪法,始终处于切实可行的状态。

司法独立是一个国家的基本理念。但是,当苏共一名党委书记打电话给法官,告诉他某个案子该如何裁判时,司法独立将荡然无存——因为某些潜在的惩罚是不言自明的,这个法官可能再没有像样的住房,子女也可能不会再得到很好的教育。


2022-04-22 reading , reading-2022 , 司法 , 大法官 , 布雷耶

使用 Docker 安装 Mastodon 实例搭建自己的社交网络

早之前就对 Mastodon 有所耳闻,当时 Google+ 关闭的时候就想着自建一个 Mastodon 实例,但是一直因为没有服务器,也没抽出时间就耽搁了,期间一直在寻找一个比较合适的栖息地,但国内的豆瓣越来越封闭,动不动就删贴,Twitter 是进来使用比较多的社交媒体,但也并没有怎么深入的用,只是在上面关注了一些行业里面的大佬。最近正好 So you Start 服务器中的资源很有剩余,就尝试安装一下 Mastodon。

[[Mastodon]] 是一个开源的、分布式社交网络,他创立的目的就是用来代替 Twitter。和 Twitter 一样,用户可以 Follow 他人,也可以相互关注,用户可以发布消息、图片、视频等等。但是和 Twitter 不同的是,Mastodon 没有一个中心化的存储和内容审查。Mastodon 使用 [[ActivityPub]] 协议进行通信,任何实现了该协议的站点都可以同 Mastodon 实例进行通信。

Mastodon 由无数社区维护的服务器运行,用户在某一台服务器上注册的账号可以和其他网络节点进行通信,也可以跨节点交换数据。

先来看看成果:

欢迎来玩。

Prerequisites

  • Mastodon 需要比较高的服务器配置,推荐至少 2 核 4GB 内存以上,空间最好也稍微大一些,前期可以把资源文件放本地,但扩展起来把媒体资源文件放到云存储上比较好
  • 一个域名
  • 可以发送邮件的 [[SMTP]] 配置,可以使用 Mailcow 自建,或者这些,或者使用邮件服务提供商
  • 系统:Ubuntu/Debian
  • 安装 dockerdocker-compose
  • 可选 Cloudflare

安装

本文使用 Docker + Nginx 的方式配置,使用 Docker 起服务,然后在宿主机上使用 Nginx 反向代理(因为 So you Start 这台机器之前就已经安装了 Nginx,否则的话会使用 Nginx-Proxy 或者 [[Nginx Proxy Manager]] 来直接管理 Docker 容器)。

因为 Docker Compose 的配置比较直观,我就直接放一个链接

首先创建网络:

docker network create external_networks
docker network create internal_network

Database

这里直接选用 [[PostgreSQL]] 作为数据库存储。

Redis

选择 Redis 作为缓存和队列。

Elasticsearch

Elasticsearch 是可选的组件,主要用来对内容做全文检索。

初始化安装

之后就是 tootsuite/mastodon 本体。

第一次安装需要先执行一个初始化的动作,将这部分内容另存为一个 docker-compose.init.yml 文件:

version: "3"
services:
  web:
    image: tootsuite/mastodon:v3.4.4
    restart: always
    environment:
      - "RAILS_ENV=production"
    command: bash -c "rm -f /mastodon/tmp/pids/server.pid; tail -f /etc/hosts"
    networks:
      - mastodon_networks

networks:
  mastodon_networks:
    external: true

然后指定该文件

docker-compose -f docker-compose.init.yml run --rm web bundle exec rake mastodon: setup

在初始化的过程中会要求提供域名,数据库用户名密码,以及 SMTP 等等配置。按照提前准备的内容依次输入即可。然后就可以获得一份初始配置。记得从终端拷贝下来,并保存为 .env.production

然后再使用 docker-compose up -d 启动所有的容器。

Nginx 反向代理

在 Mastodon 的 GitHub 仓库中官方提供了 Nginx 配置文件模板

可以拉一下代码,然后直接拷贝使用。

git clone https://github.com/mastodon/mastodon.git
cd mastodon
sudo cp dist/nginx.conf /etc/nginx/sites-available/mastodon
sudo ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon

然后修改配置文件中的 example.com 为自己的域名,然后记得将网站的 root 修改为自己服务器上的路径。

sudo /etc/init.d/nginx reload

等待 Nginx 加载文件之后,就可以访问域名,进入实例。

实例维护

查看 Mastodon 实例的性能和日志,可以访问:https://domain.com/sidekiq [[Sidekiq]] 是一个 Ruby 的后台异步任务系统。

查看数据库的信息和查询效率,可以访问:https://domain.com/pghero

其他搭建教程

本文因为已经使用了 Nginx,所以直接使用 Nginx 做反向代理,如果你是一个台没有占用 80,443 端口的服务器,那么还可以直接使用 Nginx-Proxy,或者 Nginx Proxy Manager 来直接接管 Docker 容器。

升级实例版本

修改 docker-compose.yml 文件中的版本,然后重启实例。

docker-compose run --rm web rails db:migrate
docker-compose run --rm web rails assets: precompile

其他

单用户节点

修改 .env.production 中增加:

SINGLE_USER_MODE=true

然后重启服务。

SSL/TLS certificate

使用 Certbot

sudo snap install core && sudo snap refresh core
sudo snap install --classic certbot
sudo certbot --nginx

每周一开放注册

00 0 * * 1 root docker exec mastodon_web_1 tootctl settings registrations approved >> /home/mastodon/log/mastodon/all.log 2>&1
00 0 * * 2 root docker exec mastodon_web_1 tootctl settings registrations close >> /home/mastodon/log/mastodon/all.log 2>&1

开启 ES 全文检索

ES_ENABLED=true
ES_HOST=es
ES_PORT=9200

然后停止服务重新启动。然后在 https://domain.com/admin 中就能看到全文搜索已经启用了。

然后执行:

docker-compose run --rm web bin/tootctl search deploy

就可以建立索引。

遇到问题

上传图片 500

网上大致看了一下,然后 docker-compose logs -f 看了一下日志,发现 ERROR:

web_1        | [9c1db729-7032-4c87-a85c-c38dc47380cf] Errno::EACCES (Permission denied @ dir_s_mkdir - /opt/mastodon/public/system/media_attachments):

可以看到权限不足,因为 Docker 部署,挂载到了本地的 ~/mastodon/public 目录下,所以:

sudo chown -R 991:991 ~/mastodon/public

上传图片 422 Error processing thumbnail for uploaded media

同样也是上传文件的报错。但是这个错误发生在 sidekiq

docker-compose logs -f sidekiq

查看错误,发现:

sidekiq_1    | 2022-05-12T03:59:56.171Z pid=6 tid=2fmy WARN: Errno::ENOENT: No such file or directory @ rb_sysopen - /opt/mastodon/public/system/media_attachments/files/108/286/964/537/181/703/original/559172f05be085c3.jpeg

一查配置发现,Sidekiq 挂载的时候没有暴露其中最后一行。

     volumes:
       - /etc/localtime:/etc/localtime:ro
       - /etc/timezone:/etc/timezone:ro
       - ~/mastodon/public/system:/mastodon/public/system

添加最后一行,重启即可。这个只能怪我太粗心了。

reference


2022-04-21 mastodon , linux , docker , social-media , twitter , distributed-system

电子书

本站提供服务

最近文章

  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。
  • 最棒的 Navidrome 音乐客户端 Sonixd(Feishin) Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。 Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。
  • 中心化加密货币交易所 Gate 注册以及认证 Gate.io 是一个中心化的加密货币交易所。Gate 中文通常被称为「芝麻开门」,Gate 创立于 2013 年,前身是比特儿,是一家致力于安全、稳定的数字货币交易所,支持超过 1600 种数字货币的交易,提供超过 2700 个交易对。
  • 不重启的情况下重新加载 rTorrent 配置文件 因为我在 Screen 下使用 rTorrent,最近经常调试修改 rtorrent.rc 配置文件,所以想要找一个方法可以在不重启 rTorrent 的情况重新加载配置文件,网上调查了一下之后发现原来挺简单的。
  • Go 语言编写的网络穿透工具 chisel chisel 是一个在 HTTP 协议上的 TCP/UDP 隧道,使用 Go 语言编写,10.9 K 星星。