Braindump

systemd と sshd を有効にした Docker コンテナに一般ユーザーでSSH接続する方法

April 30, 2023
docker

はじめに #

Ansible の Playbook 開発時、Docker コンテナで動作検証するときがある。 場合によっては systemd を有効にしたコンテナに SSH 接続したいときがある。 更に、その接続を一般ユーザーで行いたいときもある。

sshd を有効にしただけのコンテナには一般ユーザーで SSH 接続できる。

しかし、systemd と sshd を同時に有効化するとコンテナに一般ユーザーで SSH 接続できなくなる。

本記事では上記の問題とその対処法について説明する。

⚠️ 注意 #

systemd を有効化するにはコンテナ起動時に --privileged オプションが必要となる。 --privileged オプションを付けて実行すると、コンテナはホストマシンのリソースへの制限がほとんど無くなるため、特別な理由が無い限り systemd の有効化は非推奨。

一般ユーザーで SSH 接続する際に起こる問題 #

Docker コンテナにおいて CentOS や RHEL のベースイメージを使用している場合、systemd と sshd を有効にした状態で一般ユーザーで SSH 接続を試みると以下のエラーが発生する。

$ ssh USER@hostname
System is booting up. Unprivileged users are not permitted to log in yet. Please come back later. For technical details, see pam_nologin(8).

原因 #

この原因は pam_nologin モジュールが有効になっており、ログインが制限されているところにある。

pam_nologin モジュールは UNIX および Linux システムで使用される認証フレームワークである。 システム管理者がシステムへのログインを制限するために使用され、 /run/nologin ファイルが存在する場合に一般ユーザーがシステムにログインできないように制限する。

なぜ systemd と sshd を同時に有効化した場合に限り pam_nologin が有効になるのか #

systemd は、システムの初期化プロセスとしてサービスの起動順序を制御し、サービスの依存関係を解決するために使用される。

sshd と systemd を有効にした場合、systemd はシステムの初期化時に /run/nologin ファイルが存在するかどうかをチェックし、存在する場合は pam_nologin モジュールが有効になる。 これにより、一般ユーザーは SSH 接続できなくなる。

sshd のみを有効にした場合は systemd がシステムの初期化プロセスとして機能していないため、 pam_nologin モジュールは有効にならず、一般ユーザーが SSH で接続できる。

systemd と sshd を有効化した Docker コンテナに一般ユーザーで SSH 接続する方法 #

Dockerfile #

sshd と systemd を有効化し、一般ユーザーによる SSH 接続を可能にする Dockerfile は下記のとおり。

FROM redhat/ubi8

# 必要なパッケージのインストール
ARG PACKAGES="openssh-server openssh-clients sudo systemd systemd-udev"
RUN set -x \
    && dnf clean all \
    && dnf -y install --disableplugin=subscription-manager ${PACKAGES}

# システムディレクトリのマスク解除
# systemd を有効化するために必要
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
    rm -f /lib/systemd/system/multi-user.target.wants/*;\
    rm -f /etc/systemd/system/*.wants/*;\
    rm -f /lib/systemd/system/local-fs.target.wants/*; \
    rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
    rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
    rm -f /lib/systemd/system/basic.target.wants/*;\
    rm -f /lib/systemd/system/anaconda.target.wants/*;

# ユーザーとパスワード
ARG user="foo"
ARG password="bar"

# グループとユーザーの作成
RUN groupadd -r usergroup \
    && useradd -r -g usergroup -m -s /bin/bash ${user}

# SSH用の設定
RUN mkdir /var/run/sshd
RUN systemctl enable sshd.service
RUN ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -t rsa
RUN ssh-keygen -f /etc/ssh/ssh_host_ed25519_key -N '' -t ed25519
EXPOSE 22

# パスワード認証を許可
RUN sed -i 's/#PermitRootLogin yes/PermitRootLogin yes/' /etc/ssh/sshd_config \
  && sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config

# pam_nologin モジュールを無効にするためのファイルを編集する
RUN sed -i '/account.*required.*pam_nologin.so/d' /etc/pam.d/sshd

# ユーザーのパスワード設定
RUN echo "${user}:${password}" | chpasswd

# ユーザーにsudo権限を付与
RUN echo "${user} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/${user}

# SSHポートを開放
EXPOSE 22

# コンテナ内でsystemdを起動
CMD ["/sbin/init"]

前述したように、一般ユーザーでログインするためには pam_nologin モジュールを無効化する必要がある。 そのための設定行は以下の部分

# pam_nologin モジュールを無効にするためのファイルを編集する
RUN sed -i '/account.*required.*pam_nologin.so/d' /etc/pam.d/sshd

コンテナの起動 #

コンテナのビルドは通常通りだが、起動時にいくつかのオプションが必要。

$ docker build -t systemd-ssh-image .
$ docker run -d \
  --privileged \
  -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
  -p 2222:22 \
  --name systemd-ssh-container \
  systemd-ssh-image

systemd を有効化するために必要なオプションは下記二つ

  • --privileged オプションを使用してコンテナに特権アクセスを与える
  • -v /sys/fs/cgroup:/sys/fs/cgroup:ro オプションでホストの cgroup ファイルシステムをコンテナにマウントする

--privileged オプションを付けて実行すると、コンテナはホストマシンのリソースへの制限がほとんど無くなるため、特別な理由が無い限り systemd の有効化は非推奨。

ここまでくれば、起動したコンテナに一般ユーザー (foo) で SSH 接続が可能となる。

$ ssh foo@localhost -p 2222