Sandboxing Nginx
10
4403
13 ноября 2020 9:40
13/11/2020
Visitors have accessed this post 4403 times.
Автор — Юрий Изоркин
В конце 2018 года мне на глаза попался пул-реквест, который позволял запускать сервис nginx от непривилегированного пользователя — https://github.com/NixOS/nixpkgs/pull/51551 — nginx: do not run anything as root. Это повышает защиту web-сервера nginx.
При каждом запуске сервиса nginx происходит проверка конфигурации на стадии preStart от root пользователя. Лог-файлам присваиваются root права, и запуск nginx от обычного пользователя приводит к ошибке, так как нет прав на запись. Вариант решения путем запуска проверки конфигурации с помощью su-exec не подошел, потому что он сильно усложняет скрипт запуска проверки:
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index dee877f1c11..2b50c36377d 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -578,7 +578,7 @@ in
mkdir -p ${cfg.stateDir}/logs
chmod 700 ${cfg.stateDir}
chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
- ${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir} -t
+ ${pkgs.su-exec}/bin/su-exec ${cfg.user}:${cfg.group} /run/wrappers/bin/nginx -c ${configFile} -p ${cfg.stateDir} -t
'';
serviceConfig = {
ExecStart = "${cfg.package}/bin/nginx -c ${configFile} -p ${cfg.stateDir}";
@@ -606,6 +606,14 @@ in
listToAttrs acmePairs
);
+ security.wrappers.nginx = {
+ source = "${cfg.package}/bin/nginx";
+ capabilities = "cap_net_bind_service+eip";
+ owner = cfg.user;
+ group = cfg.group;
+ permissions = "u+rx,g+x";
+ };
+
users.extraUsers = optionalAttrs (cfg.user == "nginx") (singleton
{ name = "nginx";
group = cfg.group;
Нам потребовалось бы добавить в скрипт запуска ${pkgs.su-exec}/bin/su-exec ${cfg.user}:${cfg.group}, а также для nginx добавить разрешение на открытие 80 порта от непривилегированного пользователя — capabilities = «cap_net_bind_service+eip»;.В последних версиях подсистемы инициализации и управления службами systemd опция PermissionsStartOnly= отмечена как устаревшая. Ее использовали для предварительной подготовки старта службы — создания папок, изменения прав и т.п. Теперь для корректной работы служб стоит использовать утилиту systemd-tmpfiles, она автоматически создает временные директории и присваивает им необходимые права.
Решение нашей задачи
А теперь переходим, собственно, к нашей задаче — запустить сервис nginx от непривилегированного пользователя.
Для этого я сделал несколько пул-реквестов с некоторыми изменениями:
diff --git a/default.nix b/default.nix
index eb90dae..ada7a25 100644
--- a/default.nix
+++ b/default.nix
@@ -47,7 +47,7 @@ let
''));
configFile = pkgs.writers.writeNginxConfig "nginx.conf" ''
- user ${cfg.user} ${cfg.group};
+ pid /run/nginx/nginx.pid;
error_log ${cfg.logError};
daemon off;
@@ -366,12 +366,7 @@ in
preStart = mkOption {
type = types.lines;
- default = ''
- test -d ${cfg.stateDir}/logs || mkdir -m 750 -p ${cfg.stateDir}/logs
- test stat -c %a ${cfg.stateDir}
= "750" || chmod 750 ${cfg.stateDir}
- test stat -c %a ${cfg.stateDir}/logs
= "750" || chmod 750 ${cfg.stateDir}/logs
- chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir}
- '';
+ default = "";
description = "
Shell commands executed before the service's nginx is started.
";
@@ -673,23 +668,36 @@ in
}
];
+ systemd.tmpfiles.rules = [
+ "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -"
+ "d '${cfg.stateDir}/logs' 0750 ${cfg.user} ${cfg.group} - -"
+ "Z '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
+ ];
+
systemd.services.nginx = {
description = "Nginx Web Server";
wantedBy = [ "multi-user.target" ];
wants = concatLists (map (vhostConfig: ["acme-${vhostConfig.serverName}.service" "acme-selfsigned-${vhostConfig.serverName}.service"]) acmeEnabledVhosts);
after = [ "network.target" ] ++ map (vhostConfig: "acme-selfsigned-${vhostConfig.serverName}.service") acmeEnabledVhosts;
stopIfChanged = false;
- preStart =
- ''
+ preStart = ''
${cfg.preStart}
- ${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir} -t
- '';
+ ${cfg.package}/bin/nginx -c '${configPath}' -p '${cfg.stateDir}' -t
+ '';
serviceConfig = {
- ExecStart = "${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir}";
+ ExecStart = "${cfg.package}/bin/nginx -c '${configPath}' -p '${cfg.stateDir}'";
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
Restart = "always";
RestartSec = "10s";
StartLimitInterval = "1min";
+ # User and group
+ User = cfg.user;
+ Group = cfg.group;
+ # Runtime directory and mode
+ RuntimeDirectory = "nginx";
+ RuntimeDirectoryMode = "0750";
+ # Capabilities
+ AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
};
};
Собственно, что изменил:
- Очистил preStart секцию.
- Добавил systemd-tmpfiles правила для создания папок. Где:
- d ‘${cfg.stateDir}’ 0750 ${cfg.user} ${cfg.group} — — и d ‘${cfg.stateDir}/logs’ 0750 ${cfg.user} ${cfg.group} — — создают директории с правами 0750 и владельцем nginx по умолчанию.
- Z ‘${cfg.stateDir}’ — ${cfg.user} ${cfg.group} — — при каждом перезапуске сервиса принудительно меняет пользователя на nginx, если по каким-либо причинам он изменился.
- Добавил секции User и Group для запуска службы nginx от обычного пользователя nginx по умолчанию.
- Добавил директорию /run/nginx для размещения pid файла.
- Добавил привилегию CAP_NET_BIND_SERVICE для сервиса, чтобы можно было запускать сервис nginx от обычного пользователя на 80 порту.
- Еще я добавил привилегию CAP_SYS_RESOURCE, чтобы в определенной конфигурации сервис nginx мог менять лимиты. Если привилегии нет, вы получите вот такую ошибку:
nginx[13580]: 2019/05/30 21:24:22 [alert] 13582#13582: setrlimit(RLIMIT_NOFILE, 102400) failed (1: Operation not permitted)
- В конфигурации nginx убрал опцию user ${cfg.user} ${cfg.group};
В одном из следующих пул-реквестов также добавим опцию CapabilityBoundingSet:
# Capabilities
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
+ CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
А сейчас вернемся немного назад и посмотрим, какие привилегии были у процесса nginx до изменений:
sudo cat /proc/28599/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
sudo cat /proc/28603/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
Примечание: у nginx есть один главный и несколько рабочих процессов. Основная задача главного процесса — чтение и проверка конфигурации и управление рабочими процессами. Рабочие процессы выполняют фактическую обработку запросов.Процессы nginx имеют следующие привилегии:
capsh --decode=0000003fffffffff
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
capsh --decode=0000000000000000
0x0000000000000000=
После изменений:
sudo cat /proc/27703/status | grep Cap
CapInh: 0000000001000400
CapPrm: 0000000001000400
CapEff: 0000000001000400
CapBnd: 0000000001000400
CapAmb: 0000000001000400
sudo cat /proc/27704/status | grep Cap
CapInh: 0000000001000400
CapPrm: 0000000001000400
CapEff: 0000000001000400
CapBnd: 0000000001000400
CapAmb: 0000000001000400
В итоге, master и worker процессам nginx мы ограничили привилегии до минимальных и лишили их root прав:
capsh --decode=0000000001000400
0x0000000001000400=cap_net_bind_service,cap_sys_resource
Так выглядят изменения в конфигурации systemd юнита nginx.service:
diff --git a/nginx.service b/nginx.service
index 17b1727..042a40a 100644
--- a/nginx.service
+++ b/nginx.service
@@ -9,11 +9,16 @@ Environment="TZDIR=/nix/store/8w6g0i881wj5zcqgs235hb6iqzhx8m4n-tzdata-2019c/shar
X-StopIfChanged=false
+AmbientCapabilities=CAP_NET_BIND_SERVICE
+AmbientCapabilities=CAP_SYS_RESOURCE
CacheDirectory=nginx
CacheDirectoryMode=0750
+CapabilityBoundingSet=CAP_NET_BIND_SERVICE
+CapabilityBoundingSet=CAP_SYS_RESOURCE
ExecReload=/nix/store/vssdbs9s059qm5rqpw5q25z3c2d065f7-coreutils-8.31/bin/kill -HUP $MAINPID
-ExecStart=/nix/store/f355cri35wli9ndps5rjnrrc188hj2hc-nginx-1.18.0/bin/nginx -c '/nix/store/zas66gwkjvp48jr4cx3d5jf2cxwhbg9q-nginx.conf'
-ExecStartPre=/nix/store/1qymswvfmji4dwj9q2y4cp6jdyjazl2q-unit-script-nginx-pre-start/bin/nginx-pre-start
+ExecStart=/nix/store/f355cri35wli9ndps5rjnrrc188hj2hc-nginx-1.18.0/bin/nginx -c '/nix/store/1s8jnvkia47ghyg91dli5rpqy90294rb-nginx.conf'
+ExecStartPre=/nix/store/h0daa9zlkvpy1xm61bwmqfy5y61jf76d-unit-script-nginx-pre-start/bin/nginx-pre-start
+Group=nginx
LogsDirectory=nginx
LogsDirectoryMode=0750
NoNewPrivileges=true
@@ -22,4 +27,5 @@ RestartSec=10s
RuntimeDirectory=nginx
RuntimeDirectoryMode=0750
StartLimitInterval=1min
+User=nginx
Systemd также позволяет запускать сервисы в режиме изоляции. В составе systemd есть утилита, при помощи которой можно проанализировать безопасность сервисов. Проверим на сервисе nginx:
sudo systemd-analyze security | grep nginx
nginx.service 9.1 UNSAFE 😨
Больше подробностей можно увидеть командой:
sudo systemd-analyze security nginx
Добавим необходимые параметры изоляции для службы nginx:
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 1e9cda7e47858..16c56dc745f9e 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -710,6 +710,26 @@ in
LogsDirectoryMode = "0750";
# Capabilities
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
+ CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
+ # Security
+ NoNewPrivileges = true;
+ # Sandboxing
+ ProtectSystem = "strict";
+ ProtectHome = mkDefault true;
+ PrivateTmp = true;
+ PrivateDevices = true;
+ ProtectHostname = true;
+ ProtectKernelTunables = true;
+ ProtectKernelModules = true;
+ ProtectControlGroups = true;
+ RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+ LockPersonality = true;
+ MemoryDenyWriteExecute = mkDefault true;
+ RestrictRealtime = true;
+ RestrictSUIDSGID = true;
+ PrivateMounts = true;
+ # System Call Filtering
+ SystemCallArchitectures = "native";
};
};
Соответствующий пул-реквест — https://github.com/NixOS/nixpkgs/pull/85567 — nixos/nginx: enable sandboxing.Параметры:
- NoNewPrivileges = true; — запрещает получение дополнительных привилегий.
- ProtectSystem = «strict»; — монтирует всю файловую систему в режим чтения, кроме /dev, /proc и /sys.
- ProtectHome = mkDefault true; — запрещает чтение /home директории, в пользовательской конфигурации можно переопределить параметр.
- PrivateTmp = true; — перемонтирует директории /tmp and /var/tmp в отдельные приватные директории.
- PrivateDevices = true; — устанавливает новую точку монтирования /dev и добавляет псевдооборудование /dev/null, /dev/zero или /dev/random. Таким образом блокируется доступ к физическим устройствам.
- ProtectHostname = true; — устанавливает новое пространство имен UTS и запрещает изменение имени хоста и домена.
- ProtectKernelTunables = true; — запрещает изменения параметров ядра.
- ProtectKernelModules = true; — запрещает загрузку и выгрузку модулей ядра.
- ProtectControlGroups = true; — запрещает изменения в Linux Control Groups (cgroups). Доступ только на чтение.
- RestrictAddressFamilies = [ «AF_UNIX» «AF_INET» «AF_INET6» ]; — разрешает доступ только к заданному семейству сокетов.
- LockPersonality = true; — блокирует системный вызов personal.
- MemoryDenyWriteExecute = mkDefault true; — запрещает маппинг в память, одновременно доступный на запись и исполнение. Требуется разрешить для модулей lua, lua-upstream и pagespeed.
- RestrictRealtime = true; — запрещает попытки включить планирование в реальном времени.
- RestrictSUIDSGID = true; — запрещает попытки установить set-user-ID (SUID) и set-group-ID (SGID) биты для файлов и каталогов.
- PrivateMounts = true; — запускает службу в отдельном собственном пространстве имен в файловой системе.
- SystemCallArchitectures = «native»; — ограничить неиспользуемые системные вызовы.
После изменения получим:
sudo systemd-analyze security | grep nginx
nginx.service 4.2 OK 🙂
Полученные изменения в конфигурации systemd юнита nginx.service:
diff --git a/nginx.service b/nginx.service
index 042a40a..c6c7525 100644
--- a/nginx.service
+++ b/nginx.service
@@ -19,13 +19,30 @@ ExecReload=/nix/store/vssdbs9s059qm5rqpw5q25z3c2d065f7-coreutils-8.31/bin/kill -
ExecStart=/nix/store/f355cri35wli9ndps5rjnrrc188hj2hc-nginx-1.18.0/bin/nginx -c '/nix/store/1s8jnvkia47ghyg91dli5rpqy90294rb-nginx.conf'
ExecStartPre=/nix/store/h0daa9zlkvpy1xm61bwmqfy5y61jf76d-unit-script-nginx-pre-start/bin/nginx-pre-start
Group=nginx
+LockPersonality=true
LogsDirectory=nginx
LogsDirectoryMode=0750
+MemoryDenyWriteExecute=true
NoNewPrivileges=true
+PrivateDevices=true
+PrivateMounts=true
+PrivateTmp=true
+ProtectControlGroups=true
+ProtectHome=true
+ProtectHostname=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectSystem=strict
Restart=always
RestartSec=10s
+RestrictAddressFamilies=AF_UNIX
+RestrictAddressFamilies=AF_INET
+RestrictAddressFamilies=AF_INET6
+RestrictRealtime=true
+RestrictSUIDSGID=true
RuntimeDirectory=nginx
RuntimeDirectoryMode=0750
StartLimitInterval=1min
+SystemCallArchitectures=native
User=nginx
Для применения текущих изменений в NixOS надо установить nixos-unstable релиз и прописать в конфигурации enableSandbox = true;:
services.nginx = {
enable = true;
package = pkgs.nginx;
user = "nginx";
group = "nginx";
enableSandbox = true;
...
};
Для применения изменений в других ОС Linux необходимо вручную править конфигурацию systemd юнита nginx.service и /etc/tmpfiles.d/nginx.conf.
Возможные проблемы и их решение
А сейчас кратко остановлюсь на проблемах, которые возникли после изменений, и способах их решения:
Проблема 1
https://github.com/NixOS/nixpkgs/issues/84391 — если попытаться вернуть старый способ работы сервиса nginx от пользователя root, от имени root заработают все процессы, в том числе и дочерние, а в правах директорий будет прописываться пользователь root.
Решение — https://github.com/NixOS/nixpkgs/pull/84960 — в конфигурации прописать:
services.nginx.appendConfig = let cfg = config.services.nginx; in ''user ${cfg.user} ${cfg.group};'';
systemd.services.nginx.serviceConfig.User = lib.mkForce "root";
Проблема 2
https://github.com/NixOS/nixpkgs/issues/85752 — если разместить кеш nginx в директории stateDir — proxy_cache_path /var/spool/nginx/proxycache … сервис nginx выдает ошибку:
Apr 22 07:41:46 oi-web01 s86gqdy00743baj5rrx1f85b6hpx9d3c-unit-script-nginx-pre-start[1083]: nginx: the configuration file /nix/store/w5jj77nr901gpv3rd22jv8zjdsicw6rx-nginx.conf syntax is ok
Apr 22 07:41:46 oi-web01 s86gqdy00743baj5rrx1f85b6hpx9d3c-unit-script-nginx-pre-start[1083]: 2020/04/22 07:41:46 [emerg] 1084#1084: mkdir() "/var/spool/nginx/proxycache" failed (2: No such file or directory)
Apr 22 07:41:46 oi-web01 s86gqdy00743baj5rrx1f85b6hpx9d3c-unit-script-nginx-pre-start[1083]: nginx: configuration file /nix/store/w5jj77nr901gpv3rd22jv8zjdsicw6rx-nginx.conf test failed
Apr 22 07:41:46 oi-web01 systemd[1]: nginx.service: Control process exited, code=exited, status=1/FAILURE
Apr 22 07:41:46 oi-web01 systemd[1]: nginx.service: Failed with result 'exit-code'.
Apr 22 07:41:46 oi-web01 systemd[1]: Failed to start Nginx Web Server.
Решение — https://github.com/NixOS/nixpkgs/pull/85862a) При сборке пакета nginx нужно вручную указать в параметрах сборки пути к log файлам и cache директориям:
diff --git a/pkgs/servers/http/nginx/generic.nix b/pkgs/servers/http/nginx/generic.nix
index 67a914b6a9884..80bc1458ad7ab 100644
--- a/pkgs/servers/http/nginx/generic.nix
+++ b/pkgs/servers/http/nginx/generic.nix
@@ -68,6 +68,14 @@ stdenv.mkDerivation {
"--with-http_stub_status_module"
"--with-threads"
"--with-pcre-jit"
+ "--http-log-path=/var/log/nginx/access.log"
+ "--error-log-path=/var/log/nginx/error.log"
+ "--pid-path=/var/log/nginx/nginx.pid"
+ "--http-client-body-temp-path=/var/cache/nginx/client_body"
+ "--http-proxy-temp-path=/var/cache/nginx/proxy"
+ "--http-fastcgi-temp-path=/var/cache/nginx/fastcgi"
+ "--http-uwsgi-temp-path=/var/cache/nginx/uwsgi"
+ "--http-scgi-temp-path=/var/cache/nginx/scgi"
] ++ optionals withDebug [
"--with-debug"
] ++ optionals withStream [
b) Добавим патч, который при сборке пакета nginx убирает проверку возможности создания log файлов. Иначе, если указать фиксированный путь, сборка пакета прервется с ошибкой:
builder for '/nix/store/0916gy7pga9xnmrcp8fgy1gqgbyyvb55-nginx-1.18.0.drv' failed with exit code 2; last 10 log lines:
'/nix/store/m5b6ckpq2bz11zsv3sqsprsw7fykp6kr-nginx-1.18.0/conf/scgi_params.default'
test -f '/nix/store/m5b6ckpq2bz11zsv3sqsprsw7fykp6kr-nginx-1.18.0/conf/nginx.conf' \
|| cp conf/nginx.conf '/nix/store/m5b6ckpq2bz11zsv3sqsprsw7fykp6kr-nginx-1.18.0/conf/nginx.conf'
cp conf/nginx.conf '/nix/store/m5b6ckpq2bz11zsv3sqsprsw7fykp6kr-nginx-1.18.0/conf/nginx.conf.default'
test -d '/var/log/nginx' \
|| mkdir -p '/var/log/nginx'
mkdir: cannot create directory '/var': Permission denied
make[1]: *** [objs/Makefile:2107: install] Error 1
make[1]: Leaving directory '/build/nginx-1.18.0'
make: *** [Makefile:11: install] Error 2
[0 built (1 failed), 0.0 MiB DL]
error: build of '/nix/store/0916gy7pga9xnmrcp8fgy1gqgbyyvb55-nginx-1.18.0.drv' failed
{{EJS24}}
diff --git a/auto/install b/auto/install
index d884487..dccc411 100644
--- a/auto/install
+++ b/auto/install
@@ -148,12 +148,6 @@ install: build $NGX_INSTALL_PERL_MODULES
|| cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PATH'
cp conf/nginx.conf '\$(DESTDIR)$NGX_CONF_PREFIX/nginx.conf.default'
- test -d '\$(DESTDIR)
"$NGX_PID_PATH"' \\
- || mkdir -p '\$(DESTDIR)
"$NGX_PID_PATH"'
-
- test -d '\$(DESTDIR)
"$NGX_HTTP_LOG_PATH"' \\
- || mkdir -p '\$(DESTDIR)
"$NGX_HTTP_LOG_PATH"'
-
test -d '\$(DESTDIR)$NGX_PREFIX/html' \\
|| cp -R $NGX_HTML '\$(DESTDIR)$NGX_PREFIX'
END
@@ -161,9 +155,6 @@ END
if test -n "$NGX_ERROR_LOG_PATH"; then
cat << END >> $NGX_MAKEFILE
-
- test -d '\$(DESTDIR)
"$NGX_ERROR_LOG_PATH"' \\
- || mkdir -p '\$(DESTDIR)
"$NGX_ERROR_LOG_PATH"'
END
fi
с) Убираем правила systemd.tmpfiles.rules, которые предварительно создавали необходимые для работы nginx директории:
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 8d49dc66eb1ab..1e9cda7e47858 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -680,12 +680,6 @@ in
}
];
- systemd.tmpfiles.rules = [
- "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -"
- "d '${cfg.stateDir}/logs' 0750 ${cfg.user} ${cfg.group} - -"
- "Z '${cfg.stateDir}' - ${cfg.user} ${cfg.group} - -"
- ];
-
systemd.services.nginx = {
description = "Nginx Web Server";
wantedBy = [ "multi-user.target" ];
d) Убираем настройку stateDir, которая запускала сервис nginx с опцией -p ${cfg.stateDir}. Параметр -p указывает рабочую директорию для nginx.
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 8d49dc66eb1ab..1e9cda7e47858 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -187,7 +187,7 @@ let
then "/etc/nginx/nginx.conf"
else configFile;
- execCommand = "${cfg.package}/bin/nginx -c '${configPath}' -p '${cfg.stateDir}'";
+ execCommand = "${cfg.package}/bin/nginx -c '${configPath}'";
vhosts = concatStringsSep "\n" (mapAttrsToList (vhostName: vhost:
let
@@ -463,13 +463,6 @@ in
'';
};
- stateDir = mkOption {
- default = "/var/spool/nginx";
- description = "
- Directory holding all state for nginx to run.
- ";
- };
-
user = mkOption {
type = types.str;
default = "nginx";
e) В настройках systemd прописываем cache и log директории:
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 8d49dc66eb1ab..1e9cda7e47858 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -708,6 +702,12 @@ in
# Runtime directory and mode
RuntimeDirectory = "nginx";
RuntimeDirectoryMode = "0750";
+ # Cache directory and mode
+ CacheDirectory = "nginx";
+ CacheDirectoryMode = "0750";
+ # Logs directory and mode
+ LogsDirectory = "nginx";
+ LogsDirectoryMode = "0750";
# Capabilities
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ];
};
В итоге, у нас сервис nginx по умолчанию пишет log файлы в директорию /var/log/nginx а cache файлы в /var/cache/nginx.
Проблема 3
https://github.com/NixOS/nixpkgs/issues/87698 — ошибка доступа к временным файлам. В некоторых случаях при обновлении конфигурации системы прописываются неверные права директориям:
sudo ls -l /var/spool/nginx/
total 28
drwx------ 2 nobody nginx 4096 May 12 15:47 client_body_temp
drwx------ 2 nobody nginx 4096 May 12 15:47 fastcgi_temp
drwx------ 4 nginx nginx 4096 May 12 19:30 html
drwxr-x--- 2 nginx nginx 4096 May 12 15:47 logs
drwx------ 12 nobody nginx 4096 May 12 19:30 proxy_temp
drwx------ 2 nobody nginx 4096 May 12 15:47 scgi_temp
drwx------ 2 nobody nginx 4096 May 12 15:47 uwsgi_temp
Решение — https://github.com/NixOS/nixpkgs/pull/95264 — из сервиса nginx-config-reload нужно убрать проверку конфигурации и перенести ее в модуль ExecReload nginx:
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 4c4b7f39e6bb6..461888c4cc4f0 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -704,7 +704,10 @@ in
'';
serviceConfig = {
ExecStart = execCommand;
- ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ ExecReload = [
+ "${execCommand} -t"
+ "${pkgs.coreutils}/bin/kill -HUP $MAINPID"
+ ];
Restart = "always";
RestartSec = "10s";
StartLimitInterval = "1min";
@@ -761,8 +764,7 @@ in
serviceConfig.TimeoutSec = 60;
script = ''
if /run/current-system/systemd/bin/systemctl -q is-active nginx.service ; then
- ${execCommand} -t && \
- /run/current-system/systemd/bin/systemctl reload nginx.service
+ /run/current-system/systemd/bin/systemctl reload nginx.service
fi
'';
serviceConfig.RemainAfterExit = true;
Ну что же, задача решена — мы повысили защиту сервиса nginx и убрали root привилегии.
Если у вас остались вопросы по применению этой функциональности, задавайте их в комментариях.
От редакции
Если вам интересно посещать бесплатные онлайн-мероприятия по DevOps, Kubernetes, Docker, GitlabCI и др. и задавать вопросы в режиме реального времени, подключайтесь к каналу DevOps by REBRAIN.
*Анонсы мероприятий каждую неделю
Практикумы для специалистов по инфраструктуре и разработчиков — https://rebrainme.com.
Наш Youtube-канал — https://www.youtube.com/channel/UC6uIx64IFKMVmj12gKtSgBQ.
Агентство Fevlake, проектируем и поддерживаем IT-инфраструктуры с 2012 года — https://fevlake.com.