登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux systemd RestrictAddressFamilies 怎样限制服务使用的网络协议族

来源:17golang原创

时间:2026-10-28 13:20:59 117浏览 收藏

Linux 上用 systemd 管理服务时,如果一个进程只需要本地 Unix socket 或普通 TCP,却不应该随意创建原始链路、数据包捕获等地址族,可以在服务单元里使用 RestrictAddressFamilies=。它的核心是给 socket(2) 的地址族做 allow-list 或 deny-list,而不是替代防火墙,也不是把已经传入进程的套接字重新筛一遍。

官方地址:https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#RestrictAddressFamilies=

最稳妥的落地方式是先列出服务实际需要的地址族,通常保留 AF_UNIX,再按需加入 AF_INET 或 AF_INET6;如果服务由 socket activation 接收套接字,还要把“创建套接字”和“接收传入套接字”分开判断。
  • 只限制服务进程通过 socket(2) 创建的地址族,不负责端口放行、路由和流量过滤。
  • 不写该选项时默认不限制;写地址族列表时是允许列表,前缀 ~ 时是拒绝列表。
  • AF_UNIX 经常承担本地日志、服务管理或 IPC,盲目写成 none 可能让服务启动后无法工作。

先把它放在正确的隔离层

RestrictAddressFamilies= 解决的是“这个 unit 里的进程能以哪些地址族调用 socket 创建接口”,典型目标是减少服务接触不必要网络协议的机会。它不等于 nftables 规则:不会替你决定哪个端口可以访问,也不会替你配置入站和出站流量策略。

因此,应用服务通常需要把两个问题拆开:systemd 负责缩小进程能创建的地址族,防火墙或网络策略负责控制地址、端口和流量。如果只想让服务访问本机守护进程,优先从 AF_UNIX 开始;如果确实要访问 IPv4 或 IPv6,再显式增加对应地址族。

systemd 服务与地址族限制、防火墙和本地 AF_UNIX 的边界关系说明图
图1:静态边界说明图,展示服务进程、地址族白名单、本地 AF_UNIX 与防火墙之间的职责关系,不是运行截图。

allow-list 和 deny-list 怎么选

普通列表是 allow-list。例如下面的值允许服务创建本地 Unix socket、IPv4 和 IPv6 socket,其它地址族不在允许范围内:

[Service]
# 只允许本地 IPC、IPv4 和 IPv6,按服务真实依赖删减
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

如果服务确实需要“默认可用,只屏蔽少数地址族”,可以给列表加 ~,形成 deny-list。它适合已有服务依赖较多、暂时只需要排除某个高风险族的迁移阶段,但长期维护时要警惕新地址族被默认放行:

[Service]
# 仅拒绝指定地址族;其它地址族仍可能保持可用
RestrictAddressFamilies=~AF_PACKET

none 表示拒绝所有地址族。它只适合完全不创建 socket 的短任务,不能把它当成“网络服务的默认安全值”。空字符串则用于撤销前面设置过的限制,适合 drop-in 回滚时恢复继承配置。

把配置放进 drop-in,并保留本地通信

生产环境建议用 drop-in 记录加固意图,这样不会直接改发行版提供的原始 unit。下面的示例假设服务名为 report-worker.service,服务需要访问本地日志 socket 和一个 IPv4 上游:

# 创建服务 drop-in 目录;文件名只表达这次隔离意图
sudo mkdir -p /etc/systemd/system/report-worker.service.d

# 写入最小地址族白名单;AF_UNIX 保留本地日志或 IPC 能力
sudo tee /etc/systemd/system/report-worker.service.d/10-network-family.conf 

如果服务还需要 IPv6,把 AF_INET6 加入同一个值;如果服务根本不需要网络,只要保留它依赖的本地通信方式即可。上线前要把日志、监控、DNS 解析、数据库驱动和插件的 socket 需求列在清单里,不能只看主进程的业务代码。

为什么 socket activation 仍然可能工作

该选项限制的是进程调用 socket(2) 创建新套接字的行为。systemd socket unit 预先创建并通过激活机制传给服务的套接字,不会因为服务单元设置了 RestrictAddressFamilies= 就被收回。因此,服务可能无法自行创建新的 IPv6 socket,但仍然能够处理 systemd 已经传入的监听 socket。

这也是最容易误判的边界:看到服务仍能接收激活连接,并不能说明地址族白名单没有生效;它可能只是没有走新的 socket(2) 路径。另一方面,socketpair() 创建的连接以及通过其它机制传入的套接字也不应被当作普通新建 socket 来推断。

systemd socket activation 传入套接字与服务自行创建套接字的边界说明图
图2:静态结构图,区分 socket unit 传入的监听套接字与服务自行调用 socket 创建的路径,不是运行证据。

和其它沙箱选项组合时看清代价

限制地址族只是沙箱的一层。如果目标是减少服务的系统调用面,可以进一步评估官方手册建议的 SystemCallFilter=@service;如果系统支持多种 ABI,还应考虑 SystemCallArchitectures=native,避免服务通过另一种 ABI 绕过预期边界。它们都会增加兼容性成本,应该按服务依赖逐项开启。

常见的回退顺序是:先撤掉 drop-in 中的限制值,确认服务恢复;再逐个加入 AF_UNIX、AF_INET、AF_INET6 等真正需要的族。不要一遇到连接失败就把列表改成全放行,否则失去了定位依赖的机会。

# 临时回退:清空该 drop-in 中的限制,便于确认故障是否由地址族造成
sudo tee /etc/systemd/system/report-worker.service.d/10-network-family.conf 

上线前的判断清单

检查项应回答的问题常见误区
地址族服务自己创建了哪些 AF_* socket?把端口规则当成地址族规则
本地 IPC日志、监控、DNS 或代理是否依赖 AF_UNIX?直接写 none 导致辅助功能失效
激活路径监听 socket 是服务创建还是 socket unit 传入?以仍能接收连接判断限制没生效
兼容性是否需要 IPv4、IPv6 或多 ABI 支持?只按开发机网络环境写死白名单

归根结底,RestrictAddressFamilies= 适合做“服务进程能创建什么类型 socket”的精细边界。先用 allow-list 表达真实依赖,再单独处理 socket activation、防火墙和系统调用过滤,配置才不会因为职责混杂而难以维护。

常见问题

不写 RestrictAddressFamilies 时默认是什么?

默认不做地址族限制,服务进程可以使用系统允许的地址族。要收紧范围,需要在 unit 或 drop-in 中显式设置。

只允许 AF_INET 会影响本地日志吗?

可能会。很多本地日志或 IPC 路径使用 AF_UNIX;如果服务存在这类依赖,应把 AF_UNIX 一并加入允许列表。

RestrictAddressFamilies 能替代防火墙吗?

不能。它限制创建 socket 时可用的地址族,不负责端口、地址、路由或流量策略,仍需配合网络访问控制。

socket activation 传入的 socket 会被过滤吗?

不会按普通新建 socket 的路径重新过滤。服务单元收到的激活 socket 可能继续工作,所以要把监听 socket 的配置和服务进程的新建 socket 分开审查。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>