我之前一直都是使用docker部署服务,一度热衷于使用docker部署各种self-hosted服务。不知哪一天脑子某根筋不对, 总纠结containerd占用了部分内存,索性尝试一下systemd。

之前我是了解过systemd的,所以只要在网上搜一下配置模板,然后配置好要启动的程序再systemctl start xxx.service一下 就可以了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/root/app/nginx/logs/nginx.pid
ExecStartPre=/root/app/nginx/sbin/nginx -t
ExecStart=/root/app/nginx/sbin/nginx
ExecReload=/root/app/nginx/sbin/nginx -s reload
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

上面是nginx的模板非常简单,但也足够使用了,直到我重新部署maddy(一个用go实现的邮件服务)。 经常使用linux小伙伴都知道启动一个服务最好不要用root用户以避免安全问题。对于我来说一直在使用root用户 图方便(反正是自己的vps,没啥重要数据),完全没有安全意识,而maddy官方文档中给出了更安全的配置方法, 借此我才有机会更多的了解了systemd,下面给出maddy systemd的配置步骤(maddy本身的配置不在本次讨论)。

  1. 创建一个新用户
1
useradd -mrU -s /sbin/nologin -d /var/lib/maddy -c "maddy mail server" maddy

参数解释:

  • -m创建home目录
  • -r创建为系统用户
  • -U创建同名的group
  • -s新用户的login shell
  • -dhome目录的路径
  • -c用户的一些信息
  1. 复制maddy release附带的service配置文件到systemd标准目录中
1
cp systemd/*.service /etc/systemd/system
  1. 加载新service配置文件并启动
1
2
systemctl daemon-reload
systemctl start maddy

整个配置流程还算简单,关键在于maddy的service内容。maddy有两个service文件:maddy.servicemaddy@.service,其中带@符号的文件可以视为模板,这两个文件的内容差不多,这里只展示maddy.service

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
[Unit]
Description=maddy mail server
Documentation=man:maddy(1)
Documentation=man:maddy.conf(5)
Documentation=https://maddy.email
After=network-online.target

[Service]
Type=notify
NotifyAccess=main

User=maddy
Group=maddy

# cd to state directory to make sure any relative paths
# in config will be relative to it unless handled specially.
WorkingDirectory=/var/lib/maddy

ConfigurationDirectory=maddy
RuntimeDirectory=maddy
StateDirectory=maddy
LogsDirectory=maddy
ReadOnlyPaths=/usr/lib/maddy
ReadWritePaths=/var/lib/maddy

# Strict sandboxing. You have no reason to trust code written by strangers from GitHub.
PrivateTmp=true
ProtectHome=true
ProtectSystem=strict
ProtectKernelTunables=true
ProtectHostname=true
ProtectClock=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

# Additional sandboxing. You need to disable all of these options
# for privileged helper binaries (for system auth) to work correctly.
NoNewPrivileges=true
PrivateDevices=true
DeviceAllow=/dev/syslog
RestrictSUIDSGID=true
ProtectKernelModules=true
MemoryDenyWriteExecute=true
RestrictNamespaces=true
RestrictRealtime=true
LockPersonality=true

# Graceful shutdown with a reasonable timeout.
TimeoutStopSec=7s
KillMode=mixed
KillSignal=SIGTERM

# Required to bind on ports lower than 1024.
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

# Force all files created by maddy to be only readable by it
# and maddy group.
UMask=0007

# Bump FD limitations. Even idle mail server can have a lot of FDs open (think
# of idle IMAP connections, especially ones abandoned on the other end and
# slowly timing out).
LimitNOFILE=131072

# Limit processes count to something reasonable to
# prevent resources exhausting due to big amounts of helper
# processes launched.
LimitNPROC=512

# Restart server on any problem.
Restart=on-failure
# ... Unless it is a configuration problem.
RestartPreventExitStatus=2

ExecStart=/usr/local/bin/maddy run

ExecReload=/bin/kill -USR1 $MAINPID
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

可以看到配置中明确指定使用maddy用户启动服务,另外需要注意的是Type=notify,该类型要求服务支持systemd notify, 所以如果启动了自己写的程序或者不知道第三方应用是否支持,你可以把该类型改为simple,并且不需要NotifyAccess配置选项。

下面来详细说说配置文件中关于安全的选项(Gemini生成),其他选项可以自行搜索。

  • PrivateTmp=true:为服务创建一个私有的 /tmp 和 /var/tmp 目录,与其他进程隔离。
  • ProtectHome=true:禁止服务访问 /home、/root 和 /run/user 目录。邮件服务器通常不需要访问用户家目录。
  • ProtectSystem=strict:将 /usr、/boot 和 /etc 目录挂载为只读,防止服务修改系统文件和配置。
  • ProtectKernelTunables=true:禁止服务修改内核参数(sysctls)。
  • ProtectHostname=true:禁止服务修改系统主机名。
  • ProtectClock=true:禁止服务修改系统时钟。
  • ProtectControlGroups=true:禁止服务修改 Linux 控制组(cgroups)文件系统,防止其逃避资源限制。
  • RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6:限制服务能使用的网络协议族。这里只允许 Unix 套接字(用于本地通信)、IPv4 和 IPv6,这正是邮件服务器所需要的全部。
  • NoNewPrivileges=true:禁止服务及其子进程通过 execve() 获取新的权限(例如,通过 SUID/SGID 程序)。这是一个非常重要的安全特性。
  • PrivateDevices=true:禁止服务直接访问物理设备(/dev 下的文件),除非明确允许。
  • DeviceAllow=/dev/syslog:作为 PrivateDevices 的例外,允许服务访问 /dev/syslog 以便写入系统日志。
  • RestrictSUIDSGID=true:使文件上的 SUID 和 SGID 位失效。
  • ProtectKernelModules=true:禁止服务加载或卸载内核模块。
  • MemoryDenyWriteExecute=true:强制实施 W^X(Write XOR Execute)策略。内存页要么是可写的,要么是可执行的,但不能同时是两者。这可以防止许多缓冲区溢出类的攻击。
  • RestrictNamespaces=true:禁止服务创建新的命名空间(namespace),防止其进一步隔离自身或创建容器。
  • RestrictRealtime=true:禁止服务获取实时调度策略,防止其通过占用 CPU 资源发动拒绝服务攻击。
  • LockPersonality=true:锁定进程的“个性”(personality),防止其改变执行域(例如,模拟旧版 Linux ABI)。

可以看出maddy的service配置文件相比最简单的模板来说安全了许多,大家可以根据它来自定义修改。

参考