DTLS-SRTP

DTLS-SRTP

DTLS-SRTP 开发工作主要基于两点考虑:(1)后续和 webRTC 对接时,无论是 Chrome 还是 Firefox,都要求采用 DTLS-SRTP 传输媒体;(2)在企业通信领域,如果用户不部署「SIP over TLS」, SRTP 加密用的 key 和 salt 在 SIP 消息中明文传输,有可能被有心人士拦截,从而破解 SRTP 流。

与 webRTC 对接还有很多繁琐的细节需要处理,以后再另外讨论。

对于企业通信中可能存在的拦截破解问题,目前通用的解决方法就是部署 DTLS-SRTP。DTLS-SRTP 不再通过 SIP 会话消息传递 key 和 salt,而是在 SRTP 建立之时双方 RTP 端点先进行 DTLS 协商,双方交换证书和密钥,进而加密、解密出 key 和 salt。具体步骤如下图所示:

DTLS-SRTP 流程
DTLS-SRTP 流程

SIP 消息中有两处需要注意:

(1)fingerprint 参数,指示了各自证书的 fingerprint。双方在 DTLS 协商时以此验证证书的有效性。篡改 fingerprint 或者拦截证书,都会导致 DTLS 协商失败。

(2)setup 参数,指示 RTP 端点在 DTLS 协商过程中的角色。DTLS 协商明确要求一方是 client(即协商的发起方),另一方是 server,具体细节请参考 RFC5763

在 DTLS 协商过程中随机生成 key 和 salt,完成协商后 RTP 端点从结果中提取出双方的 key 和 salt,对 RTP 流加密和解密,也就是后续 SRTP 流的处理。

2025 读书列表

2025 读书列表

2025 确实没看多少书,大部分还是网文,真是一言难尽……

程序员修炼之道
代码大全
C语言接口与设计
富兰克林自传
鞋狗
原始码:成为比尔·盖兹
段永平投资问答录
投资的逻辑
文明的逻辑

克拉克森的农场3
第一性原理
地球编年史
兼职无常后我红了
仙妻攻略
凡人修仙传
我师兄太稳健了
暗处袭来一道掌风
谁让他修仙的

2025 投资总结

2025 投资总结

利益声明

本文是个人的经验总结,涉及的股票是持有、或者曾经持有,不构成任何投资建议,请勿以此操作。股市有风险,投资需谨慎。

投资结果

今年收益率为 -0.53%,沪深300收益率为 17.66%。连续两年跑输指数,大牛之年不仅跑输指数,甚至负收益率收官,确实令人失望。

今年除了香港账户的资金投资,同时也用港股通投资了港股,计算港股收益率时有点混淆,不过资金量不大,不会大幅影响投资结果。明年(2026年)还是回到以前的操作,分开两地的资金独立进行投资和计算。

A 股

重仓的是以下个股:(1)珀莱雅;(2)万华化学;(3)新和成;(3)比亚迪;(5)中证A500ETF;(6)恒生红利低波ETF;(7)长江电力;(8)伊利股份;(9)海天味业;以及(10)中国海油。

相比去年,最大的改变是清仓了两只曾经重仓的股票:万科和京东方A。

在四月份的时候思虑再三,最终割肉万科,亏损超过50%,同时仓位过重,这也最终导致今年的收益率为负。2024年总结时万科也是拖累,当时思考了两个问题:(1)房地产行业还会存在吗?(2)万科还会存在吗?现在看来可能还在,但肯定回不去了,房地产不会是以前的房地产,万科也不可能是以前的万科,「管理层红利」显然也是笑话,谁知道还有几位管理层成员要进去踩缝纫机?把地毯掀开,里面全是蟑螂。教训是惨痛的,以后大概率不会在一支股票上投入过重仓位。

对京东方一直有很高的期待,但这么多年看下来,京东方还是强周期属性,技术上依然维持「好、但不足够好」的地位,各地明里暗里推动的内卷永无宁日。万科没有「管理红利」,京东方没有「技术红利」,庆幸的是清仓京东方颇有盈利。

另外清仓的股票是格力电器,然后将资金调仓或者增仓了珀莱雅、伊利股份和海天味业,基本都是老登股票,也就是今年被锤的消费股。虽然收益不佳,不过我觉得不错,期待明年消费有起色。

去年换车比亚迪的宋Pro,一年体验下来感觉不错,对这种技术导向型企业天然有好感,因此逐步加仓了比亚迪。如果明年有机会的话,可能会增加在比亚迪的仓位。

考虑到明年大概率人民币升值、美元贬值,因此清掉港股通的中国海洋石油,换成A股的中国海油。港股的中国海油石油仓位没有变化。

港股

港股的持仓仍然比较满意,目前持有:(1)四大行;(2)安踏体育;(3)中国海洋石油;(4)盈富基金;以及(5)中国飞鹤。

但是今年在港股的操作让我想掐死自己。曾经持有网易,盈利30%后抛了,然后它今年涨了近60%;曾经持有上美股份,盈利25%后抛了,然后它今年涨了112%。曾经持有农夫山泉,小有盈利后换到「低估」的中国飞鹤,喜迎-21%。

其他操作都显得微不足道了。

展望

今年是令人失望的一年,股市和实业都和年初的预期大相径庭。期待明年实业和消费都有提振,投资方面维持目前的持仓,对以比亚迪为代表的科技行业有更多期待,会重点关顾。

生料带

生料带

家里一个水龙头坏了,换了一个新水龙头。拧上去后水管接口处有细微的漏水,缠上生料带后好了点,还有一点点漏水。问了一下小区的维护水电工,据说生料带要缠 26 圈!我大概缠了 10 圈,先这么用着试试吧,要是有问题再拧下来重新缠生料带。

UTC

UTC

LinodeDigital Ocean 是两家我们非常喜欢、一直向客户推荐的云服务器运营商。它们的系统都很棒,我们自己的网站、云通信系统也是选择这两家的服务,十几年来一直非常稳定、省心省力。选择任何一家都是正确的,但就我个人而言,更偏爱 Linode。

技术方面我们一直都选择部署、运营 Debian 系统,Linode 安装的 Debian 节点默认配置更贴心一点,比如时区的配置。

Linode 的 Debian 节点默认采用 UTC 时区,而 DO 的 Debian 节点默认采用数据中心所在的时区。多数情况下选择哪个时区不是问题,完全是个人喜好。我们有些客户就喜欢设置为本地时区,时间管理上很方便。而我们的系统跨越多个时区,比如云通信系统节点分布在法兰克福、达拉斯等不同地点,统一采用 UTC 时区就更有利于管理,比如定时任务、系统时间同步、以及执行各类与时间相关的脚本等。

如果是在 DO 部署节点,采用以下命令修改为 UTC 时区即可(无需重启系统或者时间服务):

sudo timedatectl set-timezone UTC

修改完成后,再使用 timedatectl 命令即可检查当前系统的时区配置:

               Local time: Tue 2025-10-28 01:09:37 UTC
Universal time: Tue 2025-10-28 01:09:37 UTC
RTC time: Tue 2025-10-28 01:09:37
Time zone: UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
计算 IV

计算 IV

在 SRTP 对负荷进行加密(或者解密)时需要先计算 IV (Initialization vector, 初始化向量),RFC3711定义了该值的计算方法:

IV = (k_s * 2^16) XOR (SSRC * 2^64) XOR (i * 2^16)

看起来似乎很普通、也很容易理解,无非就是三个参数的异或计算:(1)k_s,(2)SSRC 以及(3)i。

IV 是 16 字节的数据流;k_s 是 session salt(大小14字节),要求左移2字节;SSRC 是 RTP 中的源标识参数,要求左移8字节。

i 参数有点特殊,定义为「包索引」,它是 48 比特的值,规范中定义如下:

i = 2^16 * ROC + SEQ.

ROC 是 rollover counter, RTP 包中没有这个参数,需要根据 SEQ 的值来判断是否需要设置 ROC。这里提一下, RTP 协议非常山炮地用 2 字节定义 SEQ 值,可想而知它完全不够用。因此应用层不得不自己计算ROC值,组合 ROC 值和 SEQ 值 才能完整判断 RTP 包的真实序列号。

组合出三个 16 字节的数据流后,异或计算得出 IV 值,请参考以下图表示意:

IV 异或计算

从图中也可以看出,IV 虽然有 16 个字节,但是实际上最后两个字节根本没用,因此默认设置为 0 即可。

roc + seq 的设置可以取巧一下。虽然完整定义是 48-bits,但用32-bits也足够了(考虑到 VoIP 对话中一般 20ms 一个包,序列号要溢出32bits的值需要超过990天)。而简化成 32-bits 的最大好处就是直接调用 C 函数(htonl)进行网络字节序转换即可,只需要填充第10个字节到第13个字节。

SSRC 是 32-bits 整数,因此也需要转换成网络字节序;而 session salt 本身是 14 字节的数据流,无需转换字节序。

Apache、PHP-FPM 的一点小修改

Apache、PHP-FPM 的一点小修改

好事多磨,上次修改 HTTP/2 并采用 PHP-FPM 后,运转良好,但是 php-fpm 有一些告警信息,提示 apache 送过来的请求是一些不存在的 php 文件。

如果是不存在的 php 文件,apache 直接返回 404 就可以,确实没必要送给 php-fpm 处理。默认配置是 apache 没有判断文件是否存在,增加判断即可。按照下图修改 /etc/apache2/conf-available/php8.2-fpm.conf 文件:

修改 php-fpm 默认配置

两个部分的处理差别只在于多了一个 If “-f %{REQUEST_FILENAME}” 判断而已。我觉得这个判断作为默认配置更合理,不知道 apache/php-fpm 团队为什么默认不判断文件是否存在。

HTTP/2

HTTP/2

系统环境: Debian 12 + Apache 2.4.65 + PHP 8.2,都是 Debian 12 默认的配置。

启动 HTTP/2 非常简单,直接使能 http2 模块、然后重启 apache2 即可:

sudo a2enmod http2

但是在处理 MPM(Multi-Processing Module) 时出现以下问题:

The mpm module (prefork.c) is not supported by mod_http2

Apache 默认采用 mpm_prefork,需要改用 mpm_event 才能使能 http2。工作模式改变后,PHP 的工作方式也需要更改,不再作为 apache 的一个模块运行,而是独立运行,因此需要安装 php-fpm (FastCGI Process Manager):

sudo apt install php-fpm

Debian 12 默认采用 php 8.2 版本,因此实际安装、配置的是 php8.2-fpm。

其他配置如下所示:

sudo a2dismod php8.2
sudo a2dismod mpm_prefork

sudo a2enconf php8.2-fpm
sudo a2enmod proxy_fcgi setenvif
sudo a2enmod mpm_event
sudo a2enmod http2

最后重启 apache2 即可:

sudo systemctl restart apache2

然后可以用 curl 测试服务器是否采用了HTTP/2:

curl -v --http2 -I https://yourdomain.com

php-fpm 作为系统的 services 独立启动和运行,检查 /lib/systemd/system 目录可以找到相关的配置(php8.2-fpm.service),也可以使用以下命令检查 php-fpm 的状态:

sudo systemctl status php8.2-fpm

php8.2-fpm 默认的参数有点偏小,需要调整(比如调整为默认值的 5 倍)。修改 /etc/php/8.2/fpm/pool.d/www.conf 中参数,如下所示:

pm.max_children = 25
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
Postfix / Dovecot 配置的一些修改

Postfix / Dovecot 配置的一些修改

email 大概是自互联网洪荒时代就留存下来的技术,有着厚重的历史沉淀,回味且让人困惑。互联网技术往往粗糙、简陋,像有点漏水的瓶,能用,但就是哪哪总有点不对。而 email 更像是漏水的镶金大瓦罐,更漏水、更实用、更异域风情。相比其他互联网服务,例如web,配置 email 总是让人战战兢兢,需要理解极为繁琐的概念、在不同的配置文件左右横跳,然而过了一段时间后,你会发现,原来配置还没完,还有地方能优化,超有精雕细琢的样子。

最近我就雕琢了一下。

事情其实和我们的 email 本身没有关系,和 IPv6 的地址范围有关。数据中心服务商给我们提供了一个比较大范围的 IPv6 地址群,然而不幸的是其他一些用户也在这个地址群内。我们通过自己的 IPv6 地址发送 email 时,由于反垃圾机构已经将这个地址群拉黑了,导致我们的 email 无法送达到客户。这是典型的「殃及池鱼」,我们进行了申诉(拉黑一个大范围的 IPv6 地址群显然过于粗暴、完全不合理),并向数据中心反馈情况(希望能查查是谁在发垃圾邮件)。反垃圾邮件机构不理会我们(有了SPF、DKIM等技术都不能让它们更精确、更理智点),而数据中心只是表示可以将我们划到另一个小点的 IPv6 地址群……

互联网就是这么荡气回肠,到处漏水,到处打补丁,到处都是草台班子。

我们回头重新思考,将 email 系统限制在 IPv4 地址显然是个简单有效的解决方案。IPv4 的主要缺点是地址太少,但在这个问题上反而是优点,估计没有哪家反垃圾邮件机构会疯到拉黑一大段 IPv4 地址。

方案有了,改起来倒是不太复杂。第一步修改 DNS 的 MX 记录绑定 IPv4 地址。进 DNS 服务商的面板里改改就可以。

第二步修改 Postfix 以及 Dovecot 的配置。Postfix 管理邮件的发送和接收,而 Dovecot 管理个人的邮箱,大致功能就是这样,它们共同组成整个 email 系统。

(1)修改 Dovecot

可以直接修改 /etc/dovecot/dovecot.conf 文件,但这样不太好, 如果将来系统升级,可能还要注意保留配置不被覆盖,因此建议将自己的配置存放在 /etc/dovecot/conf.d/ 目录下。例如,在该目录下创建 99-myvoipapp.conf 文件,并设置以下内容绑定 IPv4 地址(本机的实际地址,这里假设是 1.1.1.1):

listen = 1.1.1.1

(2)修改 Postfix

Postfix 不像 Dovecot 那样提供自定义配置的存放目录,因此只能修改 /etc/postfix/main.cf 文件,修改 inet_interfaces 项绑定 IPv4 地址(本机的实际地址,这里假设是 1.1.1.1):

inet_interfaces = 1.1.1.1

(3)启用 SMTPS

完成上述两步操作后,email 系统绑定了 IPv4 地址,重启 Postfix 以及 Dovecot 就可以了。Debian 系统默认配置没有启动 SMTPS,只启动了SMTP。建立 TCP 连接后,客户端收到 STARTTLS 命令才启用 TLS 加密传输过程,这样可以兼容化石级的 email 客户端,但目前主流的 email 客户端(包括我们正在使用的客户端)都支持 SMTPS,完全可以一开始就直接建立 TLS 连接。

修改 /etc/postfix/master.cf 文件,将以下内容的注释去掉即可启动 SMTPS:

smtps     inet  n       -       -       -       -       smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=$mua_client_restrictions
-o smtpd_helo_restrictions=$mua_helo_restrictions
-o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_recipient_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o milter_macro_daemon_name=ORIGINATING

(4)相关命令

完成配置后,可以使用以下命令检查是否正确:

sudo postfix check
sudo dovecot -n

使用以下命令重启 Postfix 和 Dovecot:

sudo systemctl restart postfix
sudo systemctl restart dovecot

使用以下命令检查 Postfix 和 Dovecot 的端口占用情况:

sudo netstat -tlnp | grep dovecot
sudo netstat -tlnp | grep tcp | grep :25
sudo netstat -tlnp | grep tcp | grep :465

SMTP 默认采用 TCP 25 端口,而 SMTPS 默认采用 TCP 465 端口。

dpkg: 无法恢复的致命错误,中止

dpkg: 无法恢复的致命错误,中止

在一台测试设备上升级 Debian,更新、安装软件时出现以下错误:

dpkg: 无法恢复的致命错误,中止:
软件包 libpipewire-0.3-common 的文件列表缺少最后结尾的换行符
错误: Sub-process /usr/bin/dpkg returned an error code (2)

在网上搜索了一下,解决方法简单:删除掉“/var/lib/dpkg/info”目录下软件包对应的 .list 文件即可。比如对上述错误提示,删除以下文件:

sudo rm /var/lib/dpkg/info/libpipewire-0.3-common.list

然后重新 update & upgrade 。如果没有安装该软件包,重新安装即可。