在 pod 中运行 Nginx,无需提权

Jur*_*rgy 8 nginx security-context docker kubernetes

我试图让 Nginx 以最小的权限运行,同时能够充当端口 80 上的代理。换句话说,这是我正在使用的 securityContext:

            securityContext:
              allowPrivilegeEscalation: false
              readOnlyRootFilesystem: true
              runAsNonRoot: true
              runAsUser: 12321
              runAsGroup: 12321
              privileged: false
              capabilities:
                drop:
                - all
                add: ["NET_BIND_SERVICE"]
Run Code Online (Sandbox Code Playgroud)

据我所知,围绕这个主题设置的大多数 stackoverflow 问题都设置allowPrivilegeEscalationtrue. 除此之外,我发现Bridcrew 的博客将其设置为false,但我无法在我这边重现他们的示例。

我也尝试使用nginxinc/nginx-unprivileged基础图像,但也没有成功。


这是我的 Dockerfile。它由很多chownchmod组成,但我还安装了 libcap2,这样我就可以设置NET_BIND_SERVICE功能并为自定义入口点移动一些文件。

FROM nginx

ENV NGINX_USER="proxy-user" \
    NGINXR_UID="12321" \
    NGINX_GROUP="proxy-group" \
    NGINX_GID="12321"  

RUN set -ex; \
  groupadd -r --gid "$NGINX_GID" "$NGINX_GROUP"; \
  useradd -r --uid "$NGINXR_UID" --gid "$NGINX_GID" "$NGINX_USER" 


# Create empty pid file and assign it to the proxy user
RUN touch /var/run/nginx.pid && \
    chown -R proxy-user:proxy-group /var/run/nginx.pid

# Change permissions of other nginx files
RUN chmod +x /usr/sbin/nginx && \
    chown -R proxy-user:proxy-group /usr/share/nginx/html && \
    chown -R proxy-user:proxy-group /var/cache/nginx && \
    chown -R proxy-user:proxy-group /var/log/nginx && \
    chown -R proxy-user:proxy-group /etc/nginx/conf.d

# Install libcap and set the NET_BIND_SERVICE capability to the Nginx binary
RUN apt-get -qq update && \
    apt-get -qq install --no-install-recommends libcap2-bin -y && \
    setcap CAP_NET_BIND_SERVICE=ep /usr/sbin/nginx

COPY nginx.conf /etc/nginx/nginx.conf
COPY nginx.conf.template /etc/nginx/templates/

# Move all nginx files to another directory. We will later mount a writable directory under /etc/nginx/ and copy these files back  before launching Nginx
RUN mv /etc/nginx /etc/nginx-defaults

# This file does the copying and launches nginx (via docker-entrypoint.sh)
COPY entrypoint.sh .
RUN chmod +x ./entrypoint.sh
USER proxy-user

ENTRYPOINT ./entrypoint.sh
Run Code Online (Sandbox Code Playgroud)

正如评论中提到的,我们将挂载 Nginx 可以使用的几个可写的空目录:

FROM nginx

ENV NGINX_USER="proxy-user" \
    NGINXR_UID="12321" \
    NGINX_GROUP="proxy-group" \
    NGINX_GID="12321"  

RUN set -ex; \
  groupadd -r --gid "$NGINX_GID" "$NGINX_GROUP"; \
  useradd -r --uid "$NGINXR_UID" --gid "$NGINX_GID" "$NGINX_USER" 


# Create empty pid file and assign it to the proxy user
RUN touch /var/run/nginx.pid && \
    chown -R proxy-user:proxy-group /var/run/nginx.pid

# Change permissions of other nginx files
RUN chmod +x /usr/sbin/nginx && \
    chown -R proxy-user:proxy-group /usr/share/nginx/html && \
    chown -R proxy-user:proxy-group /var/cache/nginx && \
    chown -R proxy-user:proxy-group /var/log/nginx && \
    chown -R proxy-user:proxy-group /etc/nginx/conf.d

# Install libcap and set the NET_BIND_SERVICE capability to the Nginx binary
RUN apt-get -qq update && \
    apt-get -qq install --no-install-recommends libcap2-bin -y && \
    setcap CAP_NET_BIND_SERVICE=ep /usr/sbin/nginx

COPY nginx.conf /etc/nginx/nginx.conf
COPY nginx.conf.template /etc/nginx/templates/

# Move all nginx files to another directory. We will later mount a writable directory under /etc/nginx/ and copy these files back  before launching Nginx
RUN mv /etc/nginx /etc/nginx-defaults

# This file does the copying and launches nginx (via docker-entrypoint.sh)
COPY entrypoint.sh .
RUN chmod +x ./entrypoint.sh
USER proxy-user

ENTRYPOINT ./entrypoint.sh
Run Code Online (Sandbox Code Playgroud)

在 nginx.conf 中,有几个路径指向这些目录,以防它们还不是默认值:

http {
    proxy_temp_path /tmp/nginx/proxy_temp;
    client_body_temp_path /tmp/nginx/client_temp;
    fastcgi_temp_path /tmp/nginx/fastcgi_temp;
    uwsgi_temp_path /tmp/nginx/uwsgi_temp;
    scgi_temp_path /tmp/nginx/scgi_temp;

    ...
}
Run Code Online (Sandbox Code Playgroud)

这是自定义入口点

            volumeMounts:
              - name: nginx-etc
                mountPath: /etc/nginx
              - name: nginx-cache
                mountPath: /var/cache/nginx
              - name: nginx-tmp
                mountPath: /tmp/nginx
              - name: nginx-pid
                mountPath: /var/run
Run Code Online (Sandbox Code Playgroud)

我收到的错误是由于权限被拒绝,它无法绑定到端口 80:

2023/03/31 12:31:03 [emerg] 11#11: bind() to 0.0.0.0:80 failed (13: Permission denied)
nginx: [emerg] bind() to 0.0.0.0:80 failed (13: Permission denied)
Run Code Online (Sandbox Code Playgroud)

设置allowPrivilegeEscalationtrue修复该问题。以及绑定到非特权端口(>1024),但是有没有办法解决特权升级问题,而不必绑定到更高的端口?


根据请求编辑要使用的整个 pod 的 yaml。此示例使用未修改的 nginx 映像,该映像也尝试绑定到端口 80 并显示绑定错误。

http {
    proxy_temp_path /tmp/nginx/proxy_temp;
    client_body_temp_path /tmp/nginx/client_temp;
    fastcgi_temp_path /tmp/nginx/fastcgi_temp;
    uwsgi_temp_path /tmp/nginx/uwsgi_temp;
    scgi_temp_path /tmp/nginx/scgi_temp;

    ...
}
Run Code Online (Sandbox Code Playgroud)