适用场景
NTP 时间同步加固适用于以下场景:服务器集群需要统一时间以保证日志时间戳可追溯;TLS 证书有效期校验、Kerberos 认证、双因素动态口令(TOTP)、数据库主从复制依赖准确时钟;企业需要通过等保测评中关于「时间同步与时钟源安全」的检查项;同时服务器被外部探测出开放 UDP 123 端口,存在被用作 NTP 放大攻击反射源的风险。
很多运维人员只关注 NTP 同步是否成功,却忽略了 NTP 服务默认对全网开放 这一事实。当主机运行旧版 ntpd 且未禁用私有模式查询命令时,攻击者只需伪造一个源 IP 发送几十字节的 monlist 请求,就能让服务器向其目标返回数千字节的响应数据,放大倍数可达数百倍。本文给出纯客户端与内网时间服务器两种场景下的完整加固配置。
前置条件
- 主机具备 root 或 sudo 权限,发行版为 CentOS 7+/RHEL 8+/Debian 10+/Ubuntu 20.04+。
- 可访问上游时间源,或已有一台内网标准时间服务器(推荐企业内部统一时钟源)。
- 防火墙使用 nftables、iptables 或 firewalld,并且清楚当前放行的 UDP 端口范围。
- 业务侧已确认没有依赖旧版
ntpd私有命令(ntpdc、ntpq的 monlist / getconfig / config)的程序。
原理说明
NTP 基于 UDP 123 端口通信,属于无连接协议,源 IP 可以随意伪造。放大攻击的成因有三个:一是响应报文远大于请求报文,二是服务端不校验源地址真实性,三是旧版实现开放了私有模式命令。
旧版 ntpd 的 MODE_PRIVATE(模式 7)命令可以直接查询服务器状态。其中 monlist 会返回最近 600 个与该服务器交互过的客户端地址,getconfig 会返回服务器配置。这类响应的体积是请求的数十到数百倍,因此成为经典的 UDP 放大向量。此外 readvar(模式 6)也可被滥用。
chrony 默认不实现模式 6 与模式 7 的私有命令,因此从 ntpd 迁移到 chrony 是最直接的一步加固。但仅仅迁移还不够,仍需要处理三件事:关闭对外提供服务(若无需对外授时)、用 restrict 指令收敛访问权限、在防火墙上对 UDP 123 做速率限制与源网段限制。
restrict 指令的权限修饰符含义如下:ignore 丢弃所有报文;noquery 禁止状态查询但不影响时间同步;nomodify 禁止通过 NTP 修改服务器状态;nopeer 禁止建立对等关联;notrap 禁止模式 6 陷阱服务;limited 对超速客户端做限速;kod 超速时返回 Kiss-o’-Death 报文要求客户端降速。
还要注意:restrict 是「白名单式收紧」,默认行为是允许。因此推荐先写 restrict default ignore 把默认策略改为丢弃,再逐条放开本机与内网网段。如果只写了几条 restrict 而漏写默认项,对外暴露面并没有收敛。
操作步骤
步骤 1:迁移到 chrony 并停用旧服务
# RHEL / CentOS / AlmaLinux
systemctl stop ntpd 2>/dev/null
systemctl disable ntpd 2>/dev/null
yum install -y chrony
systemctl enable --now chronyd
# Debian / Ubuntu
systemctl stop ntp 2>/dev/null
systemctl disable ntp 2>/dev/null
apt-get install -y chrony
systemctl enable --now chronyd
# 如使用 systemd-timesyncd,需先停用,避免与 chronyd 抢占时钟
systemctl disable --now systemd-timesyncd 2>/dev/null
timedatectl set-ntp true
确认 UDP 123 的监听进程已切换为 chronyd:
ss -lunp | grep ':123'
# 期望输出中包含 chronyd,而不是 ntpd
步骤 2:场景 A —— 服务器仅作为 NTP 客户端(推荐)
绝大多数业务服务器只需要向外取时间,不需要对外授时。此时应让 chronyd 完全忽略入站的 NTP 请求。
# /etc/chrony.conf
# ---- 时间源 ----
server ntp.aliyun.com iburst minpoll 4 maxpoll 6
server ntp1.aliyun.com iburst minpoll 4 maxpoll 6
pool cn.pool.ntp.org iburst maxsources 2
# ---- 时钟行为 ----
driftfile /var/lib/chrony/drift
makestep 1.0 3 # 前 3 次校正允许直接跳变,之后仅做缓慢调整
rtcsync # 定期把系统时钟同步到硬件时钟
# ---- 日志 ----
logdir /var/log/chrony
log measurements statistics tracking
# ---- 访问控制:默认丢弃一切入站请求 ----
restrict default ignore
restrict -6 default ignore
restrict 127.0.0.1
restrict ::1
要点说明:restrict default ignore 是本配置的核心,它让服务器不再充当任何人的反射放大器;minpoll 4 / maxpoll 6 将轮询间隔限制在 16 秒到 64 秒,既能快速收敛偏移,又不会给上游造成压力;makestep 1.0 3 解决虚机首次启动时钟偏差过大导致证书校验失败的问题。
步骤 3:场景 B —— 内网时间服务器对外授时
如果这台机器承担内网统一时钟源职责,则需要对指定网段开放服务,同时对超速客户端限速。
# /etc/chrony.conf(在场景 A 基础上追加)
server ntp.aliyun.com iburst
server ntp1.aliyun.com iburst
driftfile /var/lib/chrony/drift
rtcsync
logdir /var/log/chrony
log measurements statistics tracking
restrict default ignore
restrict -6 default ignore
restrict 127.0.0.1
restrict ::1
# 仅允许内网办公与生产网段;允许查询与同步,禁止修改与对等
restrict 10.10.0.0/16 nomodify notrap nopeer limited kod
restrict 172.16.20.0/24 nomodify notrap nopeer limited kod
# 关键:必须显式 allow,chrony 才真正对内网授时
allow 10.10.0.0/16
allow 172.16.20.0/24
注意 allow 与 restrict 是两个独立指令。只写 restrict 不写 allow,chrony 不会为客户端提供时间;只写 allow 不写 restrict,则所有权限都被放开。两者必须成对出现。此处 noquery 被刻意省略,因为内网客户端的 chronyc sources、ntpdate -q 都需要查询权限;limited kod 则会自动惩罚请求过于频繁的客户端。
步骤 4:防火墙层限制 UDP 123
以 nftables 为例,先放行合法网段并对新建连接限速,其余全部丢弃:
# /etc/nftables.conf 片段
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established,related accept
ct state invalid drop
tcp dport { 22, 80, 443 } ct state new accept
# 内网时间服务器:仅允许指定网段,按源 IP 限速
ip saddr 10.10.0.0/16 udp dport 123 ct state new \
meter ntp_per_src { ip saddr limit rate 8/second } accept
ip saddr 172.16.20.0/24 udp dport 123 ct state new \
meter ntp_per_src { ip saddr limit rate 8/second } accept
# 其余来源的 NTP 请求一律丢弃
udp dport 123 drop
icmp type { echo-request } limit rate 5/second accept
}
}
# 应用配置
nft -f /etc/nftables.conf
systemctl enable --now nftables
若仍在使用 iptables,等价规则如下:
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p udp --dport 123 -s 10.10.0.0/16 -m state --state NEW \
-m hashlimit --hashlimit-name ntp --hashlimit 8/sec --hashlimit-burst 16 \
--hashlimit-mode srcip --hashlimit-htable-expire 30000 -j ACCEPT
iptables -A INPUT -p udp --dport 123 -j DROP
iptables-save > /etc/sysconfig/iptables
纯客户端场景可以直接把入站规则简化为一条:iptables -A INPUT -p udp --dport 123 -j DROP,由本机主动发起的高位端口出站连接不受影响。
步骤 5:收紧配置文件与启用时间源认证
# 配置文件仅 root 可写,避免被篡改后重新开放服务
chown root:root /etc/chrony.conf
chmod 644 /etc/chrony.conf
chmod 750 /var/log/chrony
# 关闭命令行管理入口(若有),仅在需要远程 chronyc 时才配置 cmdallow
grep -n 'cmdallow' /etc/chrony.conf
# chrony 4.0+ 支持 NTS(RFC 8915)时间源认证,可防止上游被冒充
# 仅在上游明确支持 NTS 时启用
# server nts.example.net iburst nts
启用 NTS 后,客户端会通过 TLS 建立密钥交换,再用认证的 NTP 报文同步时间,从而抵御中间人伪造时间源。若上游不支持 NTS,退而求其次应选择国内可信的公共时间源或自建两级时间架构,避免直接指向来源不明的境外地址。
配置验证
# 1. 查看服务与监听状态
systemctl status chronyd --no-pager
ss -lunp | grep ':123'
# 2. 查看同步状态,重点看 Leap status 与 System time 偏移
chronyc tracking
# 期望:Leap status: Normal
# System time: 0.0000xxxx seconds fast/slow(毫秒级)
# Reference ID 指向上游时间源
# 3. 查看时间源,^* 表示当前选中源
chronyc sources -v
# 4. 查看服务端统计(内网时间服务器场景)
chronyc serverstats
chronyc clients # 需在配置中启用 clientlog
# 5. 系统层时间状态
timedatectl status
# 6. 日志确认无异常
journalctl -u chronyd --since '1 hour ago' --no-pager | tail -30
tail -n 30 /var/log/chrony/measurements.log
再用外部视角验证是否仍对外响应 NTP,重点确认不再出现 monlist 类响应:
# 从外部主机执行,--script ntp-monlist 用于探测旧版 ntpd 的 monlist 漏洞
nmap -sU -p 123 --script ntp-monlist 203.0.113.10
# 纯客户端加固后,预期结果为 123/udp open|filtered 且无脚本输出
# 若仍返回大量客户端列表,说明遗留 ntpd 未停用,需复查步骤 1
# 旧版 ntpd 环境下的直接验证命令(chrony 不支持模式 7,会返回错误)
ntpdc -n -c monlist 203.0.113.10 2>&1 | head
正常加固后的判定标准:chronyc tracking 显示 Leap status 为 Normal 且偏移在 50 毫秒以内;chronyc sources -v 中至少有一个 ^* 标记的源;本机 ss -lunp 仅显示 chronyd;外部扫描无法获取 monlist 数据。
常见问题
FAQ 1:配置 restrict default ignore 之后,本机 chronyc 显示 Not synchronised,客户端也无法同步,是什么原因?
绝大多数情况是访问控制写得不完整,分为两类。第一类是本机被误伤:restrict 127.0.0.1 与 restrict ::1 必须显式写出,否则本机的 chronyc 查询会被默认的 ignore 丢掉,表现为 chronyc tracking 报 506 Cannot talk to daemon 或状态一直停留在 Not synchronised。第二类是内网客户端被误伤:只写了 restrict 却漏写 allow,或给客户端网段加了 noquery,导致客户端能收到 NTP 报文却无法完成查询。修正方式是检查 restrict 与 allow 是否成对,删掉客户端网段上的 noquery,然后 systemctl restart chronyd。排查顺序建议固定为:先确认本机 chronyc 通、再确认同网段另一台机器 chronyc sources 能看到该服务器。
FAQ 2:chronyd 与虚拟化平台时钟冲突,出现时间反复跳变怎么办?
KVM、VMware 默认会把宿主机时间通过时钟设备传给虚机,如果虚机内 chronyd 同时在调整,就会出现两侧互相拉拽的振荡,日志中可见 System clock wrong by ... 反复出现。推荐做法是二选一:要么在虚机中禁用宿主同步并保留 chronyd,要么保留宿主同步并把 chronyd 降级为「缓慢校正」模式,把 makestep 行改为后续不再跳变。同时确认虚机时钟源为 kvm-clock 或 tsc,可通过 cat /sys/devices/system/clocksource/clocksource0/current_clocksource 查看。若时间跳变曾导致 TLS 证书报 certificate is not yet valid 或 TOTP 校验失败,应在恢复同步后检查应用日志中该时间窗口内的失败记录。
FAQ 3:服务器确实需要对外提供 NTP 服务,如何安全地做?
公开授时服务的暴露面极大,不建议直接对全网开放。可行方案是分层:最外层只允许必要来源访问,例如仅在防火墙放行合作方网段;服务端启用 limited kod 对超速客户端限速,同时对单源做速率限制(见步骤 4 的 meter 规则)。另外可在流量侧叠加检测:把 UDP 123 出站异常大包速率作为独立告警项,一旦出现「出站流量远大于入站」的形态,即可判定被用作反射源,需立即收敛白名单。
总结
NTP 加固的核心是三件事:把旧版 ntpd 换成不实现私有命令的 chrony;用 restrict default ignore 配合逐条放开的白名单收敛访问面;在防火墙层对 UDP 123 做源网段限制与速率限制。纯客户端服务器只需「默认丢弃入站 + 只保留本机查询」,内网时间服务器则需成对编写 allow 与 restrict,并对客户端启用 limited kod。完成配置后,用 chronyc tracking 校验同步精度,用外部 nmap --script ntp-monlist 校验暴露面,两项都通过才算真正加固到位。
时间源本身也是信任链的一环。对外只保留一到两个可信上游,条件允许时启用 NTS 认证;对内统一由一台内网时间服务器授时,避免每台机器各自直连公网造成的管理混乱与安全边界失控。