在不重启容器的情况下重新加载容器中的 sshd 配置

Reload sshd configuration in container without restarting the container

我想将 ssh 堡垒跳线部署为 Kubernetes 集群中的部署。这应该通过 ConfigMap 或 Secret 接收其 sshd_config 以及 authorized_keys。这些当然会随着时间的推移而改变,因此有必要重新加载 sshd 服务。

我怎样才能使这个过程自动化?更新配置或 authorized_keys 文件时不应终止现有的 ssh 连接。

我的 dockerfile 是:

FROM docker.io/alpine:latest
RUN apk add --no-cache openssh-server

EXPOSE 22/tcp
CMD ["/usr/sbin/sshd", "-D", "-e"]

我的部署如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sshd-server
  namespace: sshd
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: sshd-server
    spec:
      containers:
      - name: my-sshd-server
        image: my-sshd-server-image:latest
        imagePullPolicy: Always
        ports:
          - containerPort: 22
        volumeMounts:
        - mountPath: /etc/ssh/sshd_config
          name: sshd_config
        - mountPath: /user/.ssh/authorized_keys
          name: authorized_keys
...

如果您将 ConfigMap 安装为目录,则目录内容将在您更新 ConfigMap 时更新(可能会在短暂的延迟之后)。

这意味着如果您只是关心您的 authorized_keys 文件,您可以这样做:

创建以下 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: ssh-config
data:
  authorized_keys: |
    ssh-rsa ...
    ssh-rsa ...
  sshd_config: |
    StrictModes no
    AuthorizedKeysFile  /config/authorized_keys

并使用如下方式部署您的 ssh pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sshtest
spec:
  replicas: 1
  template:
    spec:
      containers:
        - image: quay.io/larsks/alpine-sshd:5
          imagePullPolicy: Always
          name: sshtest
          ports:
            - containerPort: 22
              name: ssh
          volumeMounts:
            - name: ssh-config
              mountPath: /config
            - name: ssh-config
              mountPath: /etc/ssh/sshd_config
              subPath: sshd_config
            - name: ssh-data
              mountPath: /etc/ssh

      volumes:
        - name: ssh-config
          configMap:
            name: ssh-config
            defaultMode: 0440
        - name: ssh-data
          emptyDir: {}

其中 quay.io/larsks/alpine-sshd:5 就是 alpine + sshd + an ENTRYPOINT 即 运行 和 ssh-keygen -A。你应该建立自己的 而不是随机一些随机人的图像 :).

这将直接在 Kubernetes 上运行,但 不会 运行 在 OpenShift 上 无需额外工作。

使用此配置(以及适当的 Service)您可以 ssh 使用对应于 的 authorized_keys 部分中包含的 public 键之一 ssh-config ConfigMap.

当您更新 ConfigMap 时,容器最终会看到 更新值,无需重新启动。


如果你真的想响应 sshd_config 中的变化,那 变得有点复杂。 sshd本身没有 built-in 响应配置文件变化的工具, 所以你需要添加一个 sidecar 容器来监视配置文件 更新,然后将适当的信号 (SIGHUP) 发送到 sshd。 像这样:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sshtest
spec:
  replicas: 1
  template:
    spec:
      shareProcessNamespace: true
      containers:
        - image: docker.io/alpine:latest
          name: reloader
          volumeMounts:
            - name: ssh-config
              mountPath: /config
            - name: ssh-data
              mountPath: /etc/ssh
          command:
            - /bin/sh
            - -c
            - |
              while true; do
                if [ -f /etc/ssh/sshd_config ] && [ -f /etc/ssh/sshd.pid ]; then
                  if ! diff -q /config/sshd_config /etc/ssh/sshd_config; then
                    cp /config/sshd_config /etc/ssh/sshd_config
                    kill -HUP $(cat /etc/ssh/sshd.pid)
                  fi
                fi

                sleep 10
              done
        - image: quay.io/larsks/alpine-sshd:6
          imagePullPolicy: Always
          name: sshd
          ports:
            - containerPort: 22
              name: ssh
          volumeMounts:
            - name: ssh-config
              mountPath: /config
            - name: ssh-data
              mountPath: /etc/ssh
      volumes:
        - name: ssh-config
          configMap:
            name: ssh-config
            defaultMode: 0600
        - name: ssh-data
          emptyDir: {}

这需要一个稍微修改过的容器镜像,其中包括 以下 ENTRYPOINT 脚本:

#!/bin/sh

if [ -f /config/sshd_config ]; then
    cp /config/sshd_config /etc/ssh/sshd_config
fi

ssh-keygen -A
exec "$@"

使用此配置,reloader 容器会监视 ConfigMap 提供的配置文件中的更改。当它检测到更改时,它会将更新的文件复制到正确的位置,然后将 SIGHUP 发送到 sshd,从而重新加载其配置。

这不会中断现有的 ssh 连接。