#263 SMTP 认证
SMTP Email 2018-05-18最常见的三种 SMTP 认证方法:
- PLAIN
- LOGIN
- CRAM-MD5
coding in a complicated world
最常见的三种 SMTP 认证方法:
Return-Path 头就是收信方记录 SMTP 会话 MAIL FROM 地址用的,这个地址不会显示在邮件中,只是在邮件需要回退的时候才会起到作用。
python2 -m smtpd -h
# An RFC 2821 smtp proxy.
# Usage: /usr/lib/python2.7/smtpd.py [options] [localhost:localport [remotehost:remoteport]]
# --nosetuid, -n
# --version, -V
# --class classname, -c classname
# --debug, -d
# --help, -h
python -m smtpd -h
# An RFC 5321 smtp proxy with optional RFC 1870 and RFC 6531 extensions.
# Usage: /usr/lib/python3.9/smtpd.py [options] [localhost:localport [remotehost:remoteport]]
# --nosetuid, -n 默认会设置用户为 nobody,如果不是 root 会因权限不足失败
# --version, -V
# --class classname, -c classname 默认: PureProxy
# --size limit, -s limit 消息大小限制(RFC 1870 SIZE extension),默认是 33554432 字节,即 32MB
# --smtputf8, -u 启用 SMTPUTF8 扩展(RFC 6531)
# --debug, -d
# --help, -h
# 如果不指定主机,就使用 localhost
# 如果主机是 localhost,端口使用 8025
# 如果是其他主机,端口使用 25
python3 -m smtpd -n
# 默认的 PureProxy 会给转信出去,正常情况会被服务器拒绝
python3 -m smtpd -n -c smtpd.DebuggingServer
Python 3.9 的 PureProxy 有 BUG,会报 process_message() got an unexpected keyword argument 'mail_options'。
blackhole.py
import smtpd
import time
class BlackHoleServer(smtpd.SMTPServer):
def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
print('%s %s %s -> %s' % (time.strftime('%Y-%m-%d %H:%M:%S'), peer, mailfrom, rcpttos))
setup.py
import setuptools
setuptools.setup(name="blackhole", py_modules=["blackhole"])
附件下载:blackhole.zip
python setup.py install --user
python -m smtpd -n -c blackhole.BlackHoleServer
import smtplib
smtp = smtplib.SMTP('localhost', 8025)
from_addr = 'admin@markjour.com'
to_addr = 'you@markjour.com'
smtp.sendmail(from_addr, to_addr,
f"""From: {from_addr}\nTo: {to_addr}\nSubject: just4fun\n\nhello, world!""")
FTP 需要 pyftpdlib 包的支持。
在当前目录起 HTTP 服务,可以用于测试和临时性的文件下载服务。
# Default bind to 0.0.0.0:8000
python -m SimpleHTTPServer
# Maybe you want to use port 8080
python -m SimpleHTTPServer 8080
除了可以指定端口,还可以指定绑定地址、工作目录。
# Also bind to 0.0.0.0:8000
python -m http.server
python -m http.server -h
# usage: server.py [-h] [--cgi] [--bind ADDRESS] [--directory DIRECTORY] [port]
#
# positional arguments:
# port Specify alternate port [default: 8000]
#
# optional arguments:
# -h, --help show this help message and exit
# --cgi Run as CGI Server
# --bind ADDRESS, -b ADDRESS
# Specify alternate bind address [default: all interfaces]
# --directory DIRECTORY, -d DIRECTORY
# Specify alternative directory [default:current directory]
python -m http.server 9999
python -m http.server --bind=127.0.0.1
python -m http.server --bind=127.0.0.1 9999
python -m http.server -d ~/Pictures
如果可以的话,使用 redis-cli monitor 命令来输出所有 Redis 命令也很方便。
有时,条件不允许,或者 Redis 需要处理其他的连接,我希望将自己代码调用的 Redis 命令输出到日志中,方便调试。
相关的文章:
邮件是由纯文本组成,其详细的格式有很多 RFC 规范需要遵守。我这里只能对我所了解的,也是基础的 —— 或者说最核心的 —— 格式做一个说明。
最核心的部分是 1982 年的 RFC 822 (STANDARD FOR THE FORMAT OF ARPA INTERNET TEXT MESSAGES),之后又做过一些更新, 比如 RFC 2822 和 RFC 5322 (Internet Message Format) 和一堆补丁更新。本文要讲的基本格式,从开始到现在并没有什么明显变化。
邮件是一种纯文本格式,最开始只包含 ASCII 字符,后来引入了 MIME 之后,可以制定别的编码,比如 UTF-8 等。
换行符是 \r\n,也就是 CR + LF。
整体来说,一封邮件由邮件头(Headers)和邮件体(Payload)组成。
邮件头包含若干个头字段
邮件头和邮件体之间用一个空行隔开
RFC2882 和 RFC5322 都规定了电子邮件每一行的长度,排除行末 CRLF,不可以超过 998 个字符,建议不超过 78 个字符。
There are two limits that this specification places on the number of
characters in a line. Each line of characters MUST be no more than
998 characters, and SHOULD be no more than 78 characters, excluding
the CRLF.
如果太长,应该拆分成多行,下一行行首加上至少一个空格或者制表符,表示是上一行的延续。
From: Bob <bob@markjour.com>
To: Mark <mark@markjour.com>
Subject: Hello
Hello, Bob,
Would you like to join me for dinner?
--
Mark
; ( Octal, Decimal.)
# 字符
CHAR = <any ASCII character> ; ( 0-177, 0.-127.)
# 字母
ALPHA = <any ASCII alphabetic character>
; (101-132, 65.- 90.)
; (141-172, 97.-122.)
# 数字
DIGIT = <any ASCII decimal digit> ; ( 60- 71, 48.- 57.)
# 控制字符
CTL = <any ASCII control ; ( 0- 37, 0.- 31.)
character and DEL> ; ( 177, 127.)
# 回车
CR = <ASCII CR, carriage return> ; ( 15, 13.)
# 换行
LF = <ASCII LF, linefeed> ; ( 12, 10.)
# 空格
SPACE = <ASCII SP, space> ; ( 40, 32.)
# 制表符
HTAB = <ASCII HT, horizontal-tab> ; ( 11, 9.)
# 引号
<"> = <ASCII quote mark> ; ( 42, 34.)
# 回车换行
CRLF = CR LF
# 空白
LWSP-char = SPACE / HTAB ; semantics = SPACE
# 连贯空白, 折行空白
linear-white-space = 1*([CRLF] LWSP-char) ; semantics = SPACE
; CRLF => folding
# 特殊字符
specials = "(" / ")" / "<" / ">" / "@" ; Must be in quoted-
/ "," / ";" / ":" / "\" / <"> ; string, to use
/ "." / "[" / "]" ; within a word.
# 分隔符
delimiters = specials / linear-white-space / comment
# 文本
text = <any CHAR, including bare ; => atoms, specials,
CR & bare LF, but NOT ; comments and
including CRLF> ; quoted-strings are
; NOT recognized.
# 原子字符
atom = 1*<any CHAR except specials, SPACE and CTLs>
quoted-string = <"> *(qtext/quoted-pair) <">; Regular qtext or
; quoted chars.
qtext = <any CHAR excepting <">, ; => may be folded
"\" & CR, and including
linear-white-space>
domain-literal = "[" *(dtext / quoted-pair) "]"
dtext = <any CHAR excluding "[", ; => may be folded
"]", "\" & CR, & including
linear-white-space>
# 注释
comment = "(" *(ctext / quoted-pair / comment) ")"
ctext = <any CHAR excluding "(", ; => may be folded
")", "\" & CR, & including
linear-white-space>
quoted-pair = "\" CHAR ; may quote any char
phrase = 1*word ; Sequence of words
word = atom / quoted-string
对应上 ASCII:
0 - 31 控制字符, 其中包括常用的:
- HT ( 9) 水平制表符
- LF (10) 换行
- CR (13) 回车
32 空格
33 - 47 符号 !"#$%&'()*+,-./
48 - 57 数字
58 - 64 符号 :;<=>?@
65 - 90 大写字母
91 - 96 符号 [\]^_`
97 - 122 小写字母
123 - 126 符号 {|}~
127 控制字符(DEL)
CHAR 0-127
CTL 0-37 + 127
符号中:
()<>[]@,;:\".
13 个被视作特殊字符,需要转义
!#$%&'*+-/=?^_`
15 个就是普通符号
atom = 数字 + 字母 + 普通符号
field = field-name ":" [ field-body ] CRLF
field-name = 1*<any CHAR, excluding CTLs, SPACE, and ":">
field-body = field-body-contents
[CRLF LWSP-char field-body]
field-body-contents =
<the ASCII characters making up the field-body, as
defined in the following sections, and consisting
of combinations of atom, quoted-string, and
specials tokens, or else consisting of texts>
字段名称允许使用的字符范围非常宽泛,而且大小写不敏感,
但是一般实践中:
Received MTA 轨迹(传输过程中的相关信息)Date 发信时间,格式:Fri, 21 Nov 1997 09:55:06 -0600Sender Mail From 地址From 发件人Subject 邮件标题To 收件人Cc 抄送Bcc 密送Reply-To 回复地址Message-ID 邮件标识References 回复邮件标识,逗号隔开In-Reply-To 回复邮件标识(会话发起的第一封)Return-Path 发信任地址(2020/07/31,邮件的 Return-Path 头是什么)Comments 说明Keywords 关键字一般采用 X- 开头的字段名称表示自定义字段,或者叫拓展字段:
常见的拓展字段:
X-Mailer# cat /etc/logrotate.conf
weekly
su root adm
rotate 4
create
#dateext
#compress
include /etc/logrotate.d
以 Nginx 配置为例:
# cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 按日切割
missingok # 如果文件不存在,则不创建
rotate 14 # 最多保留 14 个日志文件
compress # 压缩
delaycompress # 延迟压缩
notifempty # 如果文件为空,则不创建
create 0640 www-data adm
# 可能一次切割多个日志,
# 但是后面遇到的每个脚本都只执行一次,
# 在所有日志切割之前或之后
sharedscripts
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi \
endscript
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}
其他常用选项:
dateext 部分日志需要添加日期后缀lastaction/endscript 最后执行的指令,很有用,比如最后将日志备份到某些地方比如:
rsync -au *.gz 192.168.64.234:/backup/nginx-logs/`hostname -s`/
从阿帕网(ARPANET)时代一直到互联网的早期,网络节点比较少,都是通过本地 hosts 文件来实现主机名到 IP 地址的映射。
根据维基百科的信息,斯坦福研究所(SRI-NIC,Stanford Research Institute Network Information Center)负责维护一个公共 HOSTS.TXT 文件(RFC 606、RFC 608),各个网络节点定期通过 FTP 下载最新版本进行同步。
PS:这个时候如果有主机名重复了谁来管?打电话过去让他们改名?
这套机制一直运行了十几年。随着互联网规模扩大,公共 HOSTS.TXT 文件变得越来越大,变化也越来越频繁。每当有新的主机加入网络、IP 地址发生变化,都需要重新同步整个文件。
这种模式存在几个明显问题:
后来人们开始设计域名和域名相关的公共设施。RFC 819《The Domain Naming Convention for Internet User Applications》和 RFC 881《The Domain Names Plan and Schedule》提出了域名系统的早期设计思路。
最终在 1983 年形成了下面两个 RFC 文档:
几年后(1987),正式的 DNS 标准 RFC 1034 和 RFC 1035 推出。我们现在常见的 A / MX / NS / CNAME / PTR / SOA 等记录类型都是在这个标准中定义的。
这套标准一直运行到现在。虽然几十年来不断增加新的记录类型、支持国际化域名(IDN)、引入 DNSSEC、安全加密传输等扩展能力,但其核心设计思想基本没有改变:
DNS 可以说是互联网历史上寿命最长、最成功的基础设施之一。并且,在可以预见的未来,DNS 仍然会是互联网最重要的基础设施之一。
最初 DNS 主要运行于 UDP 53 端口。RFC 1035 同时规定了 TCP 支持,最早 TCP 主要用于:
今天绝大多数 DNS 服务仍同时支持 UDP 和 TCP。
互联网本是美国国防部高级研究计划局(DARPA)的产物,早期域名分配几乎由南加州大学教授乔恩·波斯特尔一人代管。1998年,克林顿政府为避免让联合国下属的国际电信联盟(ITU)——一个由各国政府和官营电信主导的机构——接管互联网,刻意设计了一条"私有化"路线:成立非营利组织 ICANN,负责全球域名系统(DNS)和顶级域名分配,与美国商务部签合同但通过"私人身份"运作,以此保持互联网开放、不受单一政府直接操控。然而 ICANN 总部在加州、受美国法律管辖,全球13台根服务器多数也在美国手里,等于事实上的互联网域名最高仲裁者仍是美国。这让包括中国、俄罗斯在内的大量国家长期不满,伊拉克战争期间".iq"域名被暂停解析一事更被视为美国"悬剑在手"的象征。
2013年斯诺登曝光美国大规模监听后,国际社会对"美国垄断互联网命门"的反弹到达顶点,联合国层面甚至出现推动另建政府主导治理体系的呼声。为消解这一合法性危机、顺应互联网去中心化叙事,奥巴马政府于2014年宣布启动移交程序——将美国商务部对 ICANN 的"合同监管权"解除。2016年3月,ICANN 正式提交脱离美国政府联系的过渡计划。
真正的问题不在"美国是否善良",而在于:这根命门本来就不该由任何一个国家独握。移交的本质是把制度合法性从"美国庇护下的开放"转向"多方利益攸关体(multi‑stakeholder)自我维持的透明与问责"。但争议也正在于此——保守派担心脱离美国后,ICANN 会被威权国家通过政府咨询渠道逐步蚕食;支持者则认为根服务器的分布式结构和自下而上的共识机制本身就是防火墙。美国让出的不是善意,而是一张越来越护不住的垄断椅子。
| 年份 | 技术/标准 | RFC | 主要创新 |
|---|---|---|---|
| 1983 | DNS 概念与实现 | RFC 882, RFC 883 | 首次提出 DNS,替代 HOSTS.TXT |
| 1987 | DNS 正式标准 | RFC 1034, RFC 1035 | 建立现代 DNS 架构,至今仍是核心基础 |
| 1997 | 动态更新(DDNS) | RFC 2136 | DNS UPDATE 接口,允许客户端动态修改 DNS 记录 |
| 1998 | NOTIFY | RFC 1996 | 主 DNS 主动通知从 DNS 更新,不用定时轮询 |
| 1999 | IXFR(增量同步) | RFC 1995 | 只同步变更部分,降低 Zone Transfer 成本 |
| 1999 | EDNS0 | RFC 2671 -> 6891 | 打破 UDP 512 字节限制(拓展到 KB 级别) |
| 2000 | SRV 记录 | RFC 2782 | DNS 服务发现机制 |
| 2003 | 国际化域名(IDNA) | RFC 3490 系列 | 支持中文、日文等 Unicode 域名 |
| 2005 | DNSSEC | RFC 4033/4034/4035 | DNS 数据签名与验证 |
| 2008 | Source Port Randomization | RFC 5452 | 提高抗缓存投毒能力 |
| 2010 | NSEC3 | RFC 5155 | 防止 DNSSEC Zone Walking(枚举所有域名) |
| 2011 | CAA 记录 | RFC 6844 -> 8659 | 允许域名所有者声明:仅承认某些 CA 给该域名颁发的证书 |
| 2013 | DNS Cookies | RFC 7873 | 防御 DNS 放大攻击 |
| 2016 | DNS over TLS (DoT) | RFC 7858 | DNS 加密传输 |
| 2018 | QNAME Minimization | RFC 7816 | 降低 DNS 查询隐私泄露 |
| 2018 | DNS over HTTPS (DoH) | RFC 8484 | DNS 走 HTTPS 通道 |
| 2020 | SVCB / HTTPS RR | RFC 9460 | 服务发现和 HTTPS 配置优化 |
| 2021 | Oblivious DoH (ODoH) | RFC 9230 | 隐藏客户端 IP |
| 2022 | DNS over QUIC (DoQ) | RFC 9250 | 基于 QUIC 的 DNS |
| 2023 | DDR | RFC 9462 | 自动发现 DoH/DoT 服务 |
RFC 2136: Dynamic Updates in the Domain Name System (DNS UPDATE)
复用 DNS 报文格式,但把 Opcode 设为 5(UPDATE),直接在 UDP/TCP 53 端口上向权威 DNS 服务器发送增删改指令(四段式结构):
| 段 | 作用 |
|---|---|
| Zone | 声明你要改哪个 zone(如 home.arpa.或 example.com.) |
| Prerequisite | 前置条件——"只有当 host A == 1.2.3.4时才更新"(原子 compare-and-swap,防竞态) |
| Update | 实际操作——ADD / DELETE 某条 RR |
| Additional | 认证载体——通常是 TSIG(RFC 2845)或 GSS-TSIG(RFC 3645, Kerberos) |
注意:DDNS 本身可以说是一个需求,就是动态调整 DNS,虽然 RFC 2136 经常被称之为 DDNS,但实质是 DDNS 的一种技术方案。
有一些脚本或者路由器插件轮询 IP 然后通过云服务商 API 来动态修改 DNS 记录,在很多场景下也被称之为 DDNS,不要混淆。
云服务商 DNS 都不对外提供 UPDATE 操作,它们通过 HTTP API(IAM/Key 鉴权)来提供 DNS 记录更新。
RFC 2136 DDNS 的典型阵地是你自有/私有部署的受控权威服务器(BIND、PowerDNS、Windows AD DNS、Infoblox 等),其中最常见生产场景是:
客户端发起 DHCP 请求时会带上 hostname(Option 12),DHCP 服务器分配地址后,通过 TSIG/GSS-TSIG 向可写 zone 发 UPDATE,自动注册内部名 A 记录及对应 PTR(反向区也要可被管理)。
DNSSEC
安全
DNSCrypt
2011 年提出。
DNSCrypt 在 DNS 请求和解析服务器之间建立加密通信,并验证服务器身份。
它并非 IETF 标准,但曾经被 OpenDNS 等服务广泛采用。
DNS over TLS(DoT)
2016 年成为标准。RFC 7858 Specification for DNS over Transport Layer Security (TLS)
DNS 全程加密,默认使用 TCP 853 端口。
DNS over HTTPS(DoH)
2018 年成为标准。RFC 8484 DNS Queries over HTTPS (DoH)
特点:
可以复用现有 Web 基础设施。
DNS over QUIC(DoQ)
RFC 9250。
基于 QUIC,兼具 UDP 的低延迟和 TLS 的安全性。
DNS over Tor
通过 Tor 网络转发 DNS 查询,其主要目标是隐藏客户端来源地址,提高匿名性。
它不是 DNS 标准协议,而是一种部署方案。
Oblivious DNS-over-HTTPS(ODoH)
ODoH 在 DoH 基础上增加代理节点,确保代理服务器看不到查询内容,DNS 服务器看不到客户端 IP,从而进一步提升隐私保护能力。
sudo apt install -y nodejs npm
# 已经不需要这句了:
# sudo ln -s `which nodejs` /usr/bin/node
# node -v
# npm -v
npm config set registry=https://registry.npm.taobao.org
sudo npm upgrade -g npm
# sudo npm install -g yarn --registry=https://registry.npm.taobao.org
curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
yarn config set registry https://registry.npm.taobao.org
echo registry=https://registry.npm.taobao.org > ~/.npmrc
npm config get registry
https://registry.npmjs.org/
npm config set registry=https://registry.npm.taobao.org
yarn config get registry
https://registry.yarnpkg.com
yarn config set registry https://registry.npm.taobao.org