Visitors have accessed this post 374 times.

Sandboxing Nginx

2
0
374
13 ноября 2020 9:40
Автор: Rebrain Me
DevOps

Visitors have accessed this post 374 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 до изменений:

  • У master процесса:
sudo cat /proc/28599/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
  • У worker процесса:
sudo cat /proc/28603/status | grep Cap
CapInh: 0000000000000000
CapPrm: 0000000000000000
CapEff: 0000000000000000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

Примечание: у nginx есть один главный и несколько рабочих процессов. Основная задача главного процесса — чтение и проверка конфигурации и управление рабочими процессами. Рабочие процессы выполняют фактическую обработку запросов.Процессы nginx имеют следующие привилегии:

  • У master процесса:
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
  • У worker процесса:
capsh --decode=0000000000000000
0x0000000000000000=

После изменений:

  • У master процесса:
sudo cat /proc/27703/status | grep Cap
CapInh: 0000000001000400
CapPrm: 0000000001000400
CapEff: 0000000001000400
CapBnd: 0000000001000400
CapAmb: 0000000001000400
  • У worker процесса:
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.

Комментарии (3)
Введено символов из возможных
Не отвечать

Вам также может понравится

Зачем нужны системы оркестрации?
array(1) { [0]=> object(WP_Term)#11136 (16) { ["term_id"]=> int(10) ["name"]=> string(10) "Kubernetes" ["slug"]=> string(10) "kubernetes" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(10) ["taxonomy"]=> string(8) "category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(3) ["filter"]=> string(3) "raw" ["cat_ID"]=> int(10) ["category_count"]=> int(3) ["category_description"]=> string(0) "" ["cat_name"]=> string(10) "Kubernetes" ["category_nicename"]=> string(10) "kubernetes" ["category_parent"]=> int(0) } } Kubernetes

Автор - Андрей Трошин
Понятие оркестрации
Оркестрация или оркестровка - если обратиться к wiki - это автоматическое размещение, координация и управление сложными компьютерными системами и службами. Давайте попробуем упростить и провести аналогию с обычным музыкальным оркестром. Оркестр - это такая штука, которая позволяет сыграть грандиозное...

4
0
16 октября 2020
Протокол SSH
array(1) { [0]=> object(WP_Term)#924 (16) { ["term_id"]=> int(6) ["name"]=> string(5) "Linux" ["slug"]=> string(5) "linux" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(6) ["taxonomy"]=> string(8) "category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(23) ["filter"]=> string(3) "raw" ["cat_ID"]=> int(6) ["category_count"]=> int(23) ["category_description"]=> string(0) "" ["cat_name"]=> string(5) "Linux" ["category_nicename"]=> string(5) "linux" ["category_parent"]=> int(0) } } Linux

Протокол SSH (Secure SHell) - один из основных и очень важных инструментов работы с Linux (тут надо отметить, что он может использоваться и для других платформ - OpenBSD, Windows, macOS). SSH применяется для зашифрованного соединения сервера и клиента путем создания защищенного соединения на удаленном компьютере. Используется прежде всего для...

1
0
25 мая 2020
Nmap — 3 часть
array(1) { [0]=> object(WP_Term)#11135 (16) { ["term_id"]=> int(6) ["name"]=> string(5) "Linux" ["slug"]=> string(5) "linux" ["term_group"]=> int(0) ["term_taxonomy_id"]=> int(6) ["taxonomy"]=> string(8) "category" ["description"]=> string(0) "" ["parent"]=> int(0) ["count"]=> int(23) ["filter"]=> string(3) "raw" ["cat_ID"]=> int(6) ["category_count"]=> int(23) ["category_description"]=> string(0) "" ["cat_name"]=> string(5) "Linux" ["category_nicename"]=> string(5) "linux" ["category_parent"]=> int(0) } } Linux

Часть 3
Автор — Сергей Попов

1 часть статьи
2 часть статьи
Обход IDS/Firewall and spoofing
«А может, тебе еще ключ от квартиры, где деньги лежат?»
Не существует такой магической опции, которая позволяла бы обнаруживать и обходить брандмауэры и IDS (intrusion detection systems). Для этого необходимы навыки и опыт. Ниже только некоторые...

3
0
18 сентября 2020