2021 年 2 月份的玩耍

Table of Content

随便写写关于 vsftpd 几个可能导致本地用户无法登录的问题

PAM 服务中检测了用户的 shell 的可用性

PAM 服务中与用户可用 shell 的检测相关条目的一般都是下面这种样子

auth    required        pam_shells.so

当添加本地账户为系统账户时(用户的登录 shell 为 /sbin/nologin)时, 这个条目的检测会不通过, 导致用户验证失败, 且此时的报错信息与普通的用户密码不正确别无二致.

PAM 服务中删除了过多的条目, 导致验证不通过 (存疑)

本条总结尚有疑问, 阅读时请仔细思考, 并欢迎提出建议或指出错误.

一个十分简单的 PAM 文件一般如下:

#%PAM-1.0
auth       required     /lib/security/pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
auth       required     /lib/security/pam_unix.so shadow nullok
auth       required     /lib/security/pam_shells.so
account    required     /lib/security/pam_unix.so
session    required     /lib/security/pam_unix.so

而那一次的调错过程中, 为了允许无合法 shell 的用户登录, 将图中第 3 到 5 行全部注释, 可以发现不再存在 account 条目. 且这一次的报错信息与普通的用户密码错误别无二致.

在修改为仅注释掉第 3 行后, 问题解决. 初步推测一个最简单的 PAM 需要至少 auth, account, session 三个条目.

本地用户的家目录不存在或权限错误

当设置了用户的家目录以及 vsftpd 中使用 local_root 来使用户登陆时自动跳转到指定目录时, 要求家目录存在且权限正确.

一次偶然的调错过程的报错如下:

500 OOPS: cannot change directory:/home-of-the-ftp-user

将所有相关的目录与此目录对比, 发现此目录是用户的家目录, 进一步查看用户的家目录得知, 创建用户时未创建家目录或家目录权限不正确.

根据这一次调错过程反推 vsftpd 登录时目录变动的逻辑, 用户在登录过程中, 会先 cd 到家目录, 然后再 cd 到 local_root 指定的目录.

IMAP 删除文件不彻底

IMAP 协议规定, 在删除文件时, 文件只会被标记删除, 真正删除的步骤在 expuge 这一步.

expuge 在不同的邮箱客户端中有不同的名字, ThunderBird 中该功能的名字叫做 compact , 有些邮箱客户端会定期自动执行 expuge , 所以不会有表现给用户的类似功能.

相关参考资料:

碎碎念的起因

在自行搭建邮箱服务的时候, 配置好限额, 尝试删除之前测试的邮件, 看限额使用情况是否有变化, 发现没有变化以后前往用户的邮件目录发现目录确实如同配额使用情况描述的一般大, 后来才发现这是 IMAP 协议规定的行为.

邮件的 dns 记录

与邮件相关的 DNS 记录有 MX 和 MXE 两个, 但实际上后者只是前者的面向用户的配置简化版, 在应用时仍然会被翻译为前者.

MX 记录

MX 记录需要指向一个域名, 实际的邮箱服务应该由被指向的域名来提供, 被指向的域名应该能够最终查询到一个服务器的 IP.

这里有一个稍微复杂一点的例子, 假设我购买了域名 leafee98.com, IP 为 217.69.11.110 的主机为购买的 VPS, 平时伺服一些奇奇怪怪的服务, 现在额外配置一个邮箱服务在该小机上. 为了增加拓展性, 将邮件服务额外配置一个次级域名, 但是现阶段此次级域名通过 CNAME 指向主域名.

record type host name value
A @ 217.69.11.110
CNAME mail leafee98.com
MX @ mail.leafee98.com

假如邮件的收件人是 non-exist-account@leafee98.com , 一个 MTA 在发送该邮件时会查询 leafee98.com 的 MX 记录, 然后根据得到的新的域名 mail.leafee98.com 尝试查询 IP, 发现此域名没有 A 或 AAAA 记录无法获得 IP, 但是由一条 CNAME 记录指向 leafee98.com , 于是重新查询这个域名的 IP, 最终得到 217.69.11.110 并作为 SMTP 的传送目标.

MXE 记录

MXE 就是 MX Easy 记录, 实际上就是 MX 和 A 记录的合并版, 允许用户以更简单的方式配置邮箱服务器的地址, 在用户的角度来看, MXE 允许直接指定提供邮箱服务的主机 IP 而无需首先指向一个新的域名再建立该域名的 A 或 AAAA 记录, 但是在实际的应用中, MXE 记录会被翻译为一条 MX 记录再加上一条 A 记录, 所以实际上并没有减少解析次数, 却降低了灵活性.

参考资料

邮件验证机制

DKIM (DomainKeys Identified Mail)

即使用域名公钥验证邮件, 为邮件添加 DKIM 字段可以是发件服务器或中转服务器的责任, 表示此发件服务器或中转服务器为此邮件背书, 它在 DNS 记录中的形式为 selector._domain.example.org 中的 TXT 记录, selector 需要替换为相应的字符串, 通过对该域名进行TXT 记录查询可以获得一条 DomainKey, 其值大概如下.

v=DKIM1; h=sha256; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0bIFzUHNaltP1PrqtpZ4JRd0UnOC0ReCQf3hDwzz88YN9pVx/FUVlpy5oH9l9zHWN6Zr74JrXRiajUEBtUWXhPjEOj9js+Fv46Wb8TymI32k35yCUs0V5RaeWCrzf3pUxPgI5UR5YiBuhzQqi55VTyXh9XG1tR0aNNSe58uCrMgcUB

其中 v 以及其值表示这条 TXT 记录描述 DKIM 的规则, p 以及其值描述公钥的实际的值.

当邮件服务器通过 SMTP 接收到一封邮件以后, 如果邮件已经被 DKIM 服务所签名, 则其邮件头部会有如下的条目.

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=leafee98.com;
    s=202102; t=1613816450;
    bh=lHBjIu06sa9wcv8cn2/vD8BdLca6aupWWdnb/QBy92U=;
    h=To:From:Subject:Date:From;
    b=qnHha89GzY9P7nO0sT1eXWsj0TNDV7eHOeiyr1NfI4h1PVLQvKN3cPdQmXfBOu73t
     4yiE8KDk28NDiHr4bUhP5ZknCgoUJYJyNVbLSdPkZQKKofHUUErLGOLBD7ARq9LwcX
     /Z6KCTnHGTycV1Jot9ww4Ih+jSRmaqZ77dnv7f1ziB7RrG6/akUq3PkVq9Iq82DecB
     s9BDsUHUxcQgVxsolTl1PklgTh8EP5fT8O4u+QH//xSTNIufy9penw7rNHsbOUjXlO
     6+4IFCLdzfLGwV2rpQ/0MH+4K0uCei5g0Os2kII3Ge5svpo3JwuoySUrxfsPP8zRBH
     3iF1euw7C7vcw==

生成此 b= 条目要先对指明的部分 To:From:Subject:Date:From 进行 hash, 然后使用私钥进行加密, 邮件服务器接受到邮件以后, 从 202102._domainkey.leafee98.com 的 TXT 记录获取到公钥, 然后将密文进行解密, 然后再移除签名部分, 重新进行一次 hash, 比对两个结果是否相同, 相同则验证通过.

DKIM 解决的不是邮件的加密问题, 而是保证邮件在传送过程中没有被修改.

仅 DKIM 不能保证收到的邮件一定来自可信的发件服务器, 因为 DKIM 的公钥在 d=leafee98.com 字段, 邮件在中转过程中可以有中间人移除原来的 DKIM-Signature 头部, 然后重新添加自己的头部并重新指向自己设定的域名如 d=example.com , 或者直接自行发送新的邮件但是署名发送方为 leafee98.com 并仍然自行添加 DKIM-Signature 字段, 这样接收方服务器就会使用 example.com 来查询并获取公钥, 之后再进行进一步的验证, 结果自然是成功的, 这样就受到了第三方的攻击.

SPF (Sender Policy Framework)

通过一条存在于 DNS 中的 TXT 记录声明域内的哪些主机允许发送邮件, 其形式一般如以下第一条查询结果.

$ host -t txt leafee98.com
leafee98.com descriptive text "v=spf1 mx -all"
$ host -t mx leafee98.com
leafee98.com mail is handled by 50 mail.leafee98.com.
$ host -t a mail.leafee98.com
mail.leafee98.com is an alias for leafee98.com.
leafee98.com has address 217.69.11.110
$ host -t ptr 110.11.69.217.in-addr.arpa
110.11.69.217.in-addr.arpa domain name pointer mail.leafee98.com.

当收到邮件时, 收件方会根据邮件地址中的域名查询其 SPF, 如上第一条查询结果则表示允许该域名下所有的 MX 记录所指向的主机均允许发送邮件. 收件方继续查询 MX 记录找到 mail.leafee98.com , 再一次查询其 IP 最终得到 217.69.11.110 , 收件方将此 IP 与实际和自己建立连接的邮件服务器的 IP 进行比对, 发现匹配, 则 SPF 验证通过.

此外, 为了避免注册多个域名并使用单一主机来降低成本进行 spam 行为, 有些收件方仍然会查询与自己实际建立连接的 IP 的反向 DNS 记录, 即 PTR 记录, 这个记录将 IP 映射到域名, 且一个 IP 只能映射到一个域名, 所以想要申请多个域名却仅使用少于域名数量的主机, 那么为每一个域名都配置好 PTR 记录是不可能的, 所以 PTR 记录不匹配时, 有些邮件收件方会将从这些 PTR 记录不匹配的邮件服务器中收取的邮件标记为垃圾邮件.

SPF 同样不保护邮件内容, 它通过直接指明有权限发送/中转该域名邮件的邮箱服务器, 来应对假冒域名发送邮件.

比如一个主机名被配置为 mail2.example.com, 并且作为 example.com 中的一个租赁给客户的主机其 A 记录和 PTR 记录都正常, 收件服务器收到署名为 leafee98.com 的邮件时会发现其 IP 与 mail2.example.com 的 PTR 记录匹配, 也无法标记为垃圾邮件, 邮件有可能被正常接收.

但是在有配置 SPF 的情况下, 收件服务器会查询 leafee98.com 的 TXT 记录并从中找到 SPF 的限制进而得出此域名允许发送邮件的邮件服务器的 IP, 再与和自己实际建立连接的服务器的 IP 进行匹配, 匹配不通过, 确认此邮件服务器没有发件权限, 进而标记为垃圾邮件甚至拒收此邮件.

DMARC (Domain-based Message Authentication Reporting and Conformance)

DMARC 表现在 DNS 中只是一个 TXT 记录, 它实际上声明了此域名所使用的 DKIM 和 SPF 政策, 以及验证出错以后的汇报地址等.

$ host -t txt _dmarc.leafee98.com
_dmarc.leafee98.com descriptive text "v=DMARC1; p=quarantine; rua=mailto:dmarc-report@leafee98.com; pct=100; adkim=r; aspf=r"

DMARC 需要附加在主机名为 _dmarc 的域名下的 TXT 记录, 如上则表示整个 leafee98.com 所使用的 DMARC 政策, v= 条目表示协议名称以及版本号, p= 条目表示验证出错时的处理办法, 上面例子中则表示标记为垃圾邮件, rua= 表示汇报的地址, adkim=aspf= 表示 DKIM 和 SPF 政策的严格程度.

DMARC 本身不提供任何验证机制, 它只是指明 DKIM 和 SPF 政策, 邮件接收方应当根据 DMARC 指明的政策来验证邮件的 DKIM 和 SPF.

由于 DKIM 和 SPF 政策都是可选的, 所以有些邮箱服务器并没有实现这两种验证方式, 所以邮件收件方可能不知道是否应当对邮件进行这两种验证, 在没有 DMARC 时, 邮件接收方如果对所有的邮件都进行验证则会导致大量的验证不通过, 同时也不知道应当如何处理这些不通过验证的邮件, 如果对所有邮件都不进行验证则会使 DMARC 和 SPF 形同虚设, 玄学随机抽取邮件进行验证又是十分荒谬的. 而 DMARC 就是解决的这一问题.

一些参考资料

说是参考资料, 其实只是在配置过程中以及撰写博客的过程中打开的网页罢了

Leave a Reply

Your email address will not be published. Required fields are marked *




Enter Captcha Here :