2025/12/14 Updated by

Docker Image を自作する (2b)

ubuntu 24.04LTS + 新規ユーザ + sshd + syslogd + fail2ban


[Up] Japanese English
このページ内での表記:
「ホストOSの対話環境」は背景色を黄色(lightyellow)で表す。
「Conainer 内の root 権限の対話環境」は背景色を水色(azure)であらわす。
「Conainer 内の一般ユーザ権限の対話環境」は背景色を赤色(#ffeeee)であらわす。
「他のPCの対話環境」は紫色(#eeeeff)で表す。

攻撃対策済みの sshd が自動起動する ubuntu24.04LTS の docker Image を生成する

方針

作成手順

  1. 作業用フォルダを作成する
  2.   $ mkdir -p ~/doc/docker/ubuntu24_sshd_fail2ban
      $ cd ~/doc/docker/ubuntu24_ssd_fail2ban
    
  3. 作業用フォルダの中に dockerfile を作成する。
  4. Dockerfile
    # ゲストOS: Ubuntu 24.04 LTS
    
    FROM ubuntu:24.04
    
    
    # Change Your Own UNAME, UID, GID, PASS
    
    ENV UNAME=guest
    ENV UID=3000
    ENV GID=3000
    ENV PASS=password
    
    ENV SSHD_PORT=22
    
    # 必要なパッケージのインストール
    
    RUN apt-get update && \
        DEBIAN_FRONTEND=noninteractive apt-get install -y \
        sudo \
        bash \
        openssh-server \
        supervisor \
        net-tools iputils-tracepath traceroute iputils-ping curl iproute2 \
        ufw \
        locales \
        tzdata rsyslog fail2ban \
        && rm -rf /var/lib/apt/lists/*
    
    
    # SSH 設定: パスワード認証を有効化
    
    RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \
        sed -i "s/^#Port.*/Port ${SSHD_PORT}/" /etc/ssh/sshd_config && \
        mkdir /var/run/sshd
    
    
    # supervisord の設定ファイルを設置する (Daemon 起動用)
    
    RUN mkdir -p /var/log/supervisor/conf.d
    COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
    
    
    # locale 設定
    RUN locale-gen ja_JP.UTF-8 && \
        update-locale LANG=ja_JP.UTF-8
    ENV LANG=ja_JP.UTF-8
    ENV LC_ALL=ja_JP.UTF-8
    
    
    # timezone 設定
    RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
    RUN echo 'Asia/Tokyo' > /etc/timezone
    
    
    # fail2bin
    COPY jail.local /etc/fail2ban
    
    
    # ポート開放
    
    EXPOSE 22
    
    
    # Copy Shell Script "entrypoint.sh"
    
    COPY entrypoint.sh /entrypoint.sh
    RUN chmod +x /entrypoint.sh
    
    ENTRYPOINT ["/entrypoint.sh"]
    
    CMD []
    
  5. 作業用フォルダの中に entrypoint.sh を作成する
  6. entrypoint.sh
    #!/bin/bash
    set -e
    
    if [ ! -f /var/app/.initialized ]; then
        ######## First Time ########
    
        echo "First run. Setting up ..."
        mkdir -p /var/app
        touch /var/app/.initialized
    
        # ユーザーが存在しない場合のみ作成する
        if id "${UNAME}" &>/dev/null; then
            echo "User ${UNAME} already exists. Skipping creation."
        else
            # 同名グループが無ければ作成する
            if ! getent group "${UNAME}" &>/dev/null; then
                echo "Creating group ${UNAME} with GID=${GID}"
                groupadd -g ${GID} ${UNAME}
            else
                echo "Group ${UNAME} already exists. Skipping group creation."
            fi
        
            echo "Creating user ${UNAME} with UID=${UID}, GID=${GID}"
            useradd -m -u ${UID} -g ${GID} -s /bin/bash ${UNAME}
            echo "${UNAME}:${PASS}" | chpasswd
            usermod -aG sudo ${UNAME}
        fi
    
        # ホームディレクトリの Owner が root:root になることがあるので明示的に変更する。
        chown -v ${UNAME}:${UNAME}  /home/${UNAME}
    
        # SSHD のポート番号を変更する
        sed -i "s/^Port.*/Port ${SSHD_PORT}/" /etc/ssh/sshd_config
        
    else
        ######## Second Time or Later ########
        
        echo "Starting for the second time or later ..."
    
    fi
    
    # firewall
    /usr/sbin/ufw enable
    /usr/sbin/ufw allow from 0.0.0.0/0 to any port ${SSHD_PORT} proto tcp
    /usr/sbin/ufw default deny incoming
    
    # supervisord start (background)
    /usr/bin/supervisord -c /etc/supervisor/supervisord.conf &
    
    # Execute Commands in CMD
    
    if [ "$#" -gt 0 ]; then
        exec "$@"
    else
        echo "No command provided. Starting bash ..."
        exec bash
    fi
    
  7. 作業用フォルダの中に supervisord.conf を作成する
  8. supervisord.conf
    # supervisord の追加`設定ファイル
    # /etc/supervisor/conf.d/supervisord.conf
    
    [supervisord]
    nodaemon=true
    
    
    [program:rsyslog]
    command=/usr/sbin/rsyslogd -n
    autostart=true
    autorestart=true
    priority=5
    
    
    [program:sshd]
    # syslog に送る。
    command=/bin/sh -c "/usr/sbin/sshd -D -e 2>&1 | logger -t sshd"
    autostart=true
    autorestart=true
    
    
    [program:fail2ban]
    command=/usr/bin/fail2ban-server -xf start
    autorestart=true
    stderr_logfile=/var/log/fail2ban.err.log
    stdout_logfile=/var/log/fail2ban.out.log
    priority=10
    
  9. 作業用フォルダの中に jail.local を作成する。
  10. jail.local
    # fail2ban settings
    # /etc/supervisor/conf.d/jail.local
    
    [sshd]
    enabled = true
    port    = ssh
    filter  = sshd
    backend = polling
    logpath = /var/log/syslog
    maxretry = 5
    findtime = 10m
    bantime = 1h
    
  11. image を build する。
  12.   $ docker build -t ubuntu24_sshd_fail2ban .
      ...
    成功
    
  13. 生成した image を確認する
  14. $ docker image ls
    repository            tag       image id       created         size
    ubuntu24_sshd_fail2ban   latest    534506433e64   7 minutes ago   316MB
    ...
    
  15. (注意) 上では、用意した supervisod.conf をContainer の /etc/supervisor/conf.d/supervisord.conf にコピーしている。 supervisord は起動時に /etc/supervisor/supervisord.conf を読み込むが、このファイルの中で /etc/supervisor/conf.d/*.conf をインクルードするように指定されている。

Container 用の永続的なファイルシステムを作成する

コンテナに永続的なファイルシステムを提供するために、1777 のパーミッションでフォルダを作っておく。 skicky bit が on (1777) のフォルダには、 「誰でもファイルを作成できるが、作成した本人だけがファイルを変更したり消したりできる」 という特徴がある。

$ sudo mkdir -p /home/docker         ← ディレクトリを作成する
$ sudo chmod 1777 /home/docker       ← 誰でもファイルを作成できるが、作成した本人にしか消去できないモードに設定する
$ ls -ld /home/docker                ← ディレクトリのsticky bit が on になっていることを確認する。
drwxrwxrwt 3 root root 4096  4月 26 15:47 /home/docker

docker network を生成する

「アクセスしてきたクライアントの正しいipアドレスが container 上の httpdサーバ (apache2) に伝わる」ように macvlan を使って「Container に物理 NIC と同じネットワークの独立した IP アドレスを与える」方法を採る。 これは、現時点(2025/12/10)では docker ホスト OS が Linux の場合にだけ使える手法である。

前提条件と環境

  1. ホストOSの接続しているネットワークを調べる。
  2. $ netstat -nr
    カーネルIP経路テーブル
    受信先サイト    ゲートウェイ    ネットマスク   フラグ   MSS Window  irtt インタフェース
    0.0.0.0         192.168.12.1    0.0.0.0         UG        0 0          0 eno1
    172.17.0.0      0.0.0.0         255.255.0.0     U         0 0          0 docker0
    192.168.12.0    0.0.0.0         255.255.255.0   U         0 0          0 eno1
    
  3. ホストOSの IP アドレスを調べる。
  4. $ ifconfig 
    	...
    eno1: flags=4163  mtu 1500
            inet 192.168.12.3  netmask 255.255.255.0  broadcast 192.168.12.255
            inet6 2409:11:3920:6400:5a47:caff:fe71:3b7d  prefixlen 64  scopeid 0x0
            inet6 2409:11:3920:6400:4c1a:ff8c:eab1:41c9  prefixlen 64  scopeid 0x0
            inet6 fe80::5a47:caff:fe71:3b7d  prefixlen 64  scopeid 0x20
            ether 58:47:ca:71:3b:7d  txqueuelen 1000  (イーサネット)
    ...
    
  5. macvlan ネットワークを作成する。
  6. $ docker network create -d macvlan \
        --subnet=192.168.12.0/24 \
        --gateway=192.168.12.1 \
        -o parent=eno1 \
        --ip-range=192.168.12.192/28 \
        macvlan_net
    
    
  7. docker のネットワーク一覧を表示する。macvlan_net が作成されていることがわかる。
  8. $ docker network ls
    NETWORK ID     NAME          DRIVER    SCOPE
    ebbd998b2086   bridge        bridge    local
    17420af9f271   host          host      local
    557533733a33   macvlan_net   macvlan   local
    a3538f29efc6   none          null      local
    
    $ docker network ls --no-trunc
    NETWORK ID                                                         NAME          DRIVER    SCOPE
    ebbd998b20868aea17c1e9f3cd85edb3c7c0ce9181edfe9b308284604275e136   bridge        bridge    local
    17420af9f27199ef5081c56e85910d913c73ae8f3885e48e4ed0b09a06d48016   host          host      local
    557533733a33e84ca9eb665736f6a273414778f30a3e744411596008728b0b2b   macvlan_net   macvlan   local
    a3538f29efc60166d3f639d42894eff9f902649036619190011c3a66b262a06a   none          null      local
    

Docker Contaner を生成する

Image ubuntu24_sshd2 を用いて、 macvlan_net ネットワークに接続する、 新しい Container ubuntu24-sshd-fail2ban を生成する。

  1. image から container を生成して起動する。 下の実行例はユーザ名を www とした場合である。
  2.   $ docker run --name ubuntu24-sshd-fail2ban --restart always \
        --hostname sshd_server \
        -e UNAME=www -e UID=2000 -e GID=2000 \
        --network macvlan_net \
        --ip 192.168.12.195 \
        --cap-add=NET_ADMIN \
        -v /home/docker/sshd195:/mnt/hostos \
        -it ubuntu24_sshd_fail2ban
    
    起動オプション
  3. Container を起動した対話環境が、そのままContainer 内で動作する bash との対話環境になる。root権限でloginした状態である。
  4. First run. Setting up ...            ← 生成された Container 内で entrypoint.sh が実行される
    Creating group www with GID=3000
    Creating user www with UID=3000, GID=3000
    info: Adding user `www' to group `sudo' ...
    ownership of '/home/www' retained as www:www
    No command provided. Starting bash ...
    root@sshd_server:/# 2025-12-14 12:33:31,857 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
    2025-12-14 12:33:31,857 INFO Included extra file "/etc/supervisor/conf.d/supervisord.conf" during parsing
    2025-12-14 12:33:31,860 INFO RPC interface 'supervisor' initialized
    2025-12-14 12:33:31,860 CRIT Server 'unix_http_server' running without any HTTP authentication checking
    2025-12-14 12:33:31,860 INFO supervisord started with pid 38
    2025-12-14 12:33:32,864 INFO spawned: 'rsyslog' with pid 41
    2025-12-14 12:33:32,866 INFO spawned: 'fail2ban' with pid 42
    2025-12-14 12:33:32,867 INFO spawned: 'sshd' with pid 43
    2025-12-14 12:33:34,000 INFO success: rsyslog entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    2025-12-14 12:33:34,000 INFO success: fail2ban entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    2025-12-14 12:33:34,000 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    #                ← Container 内の対話環境 (root 権限の bash) が動く
    
  5. (Container 内の root 権限で) ユーザ名を調べる。
  6. # whoami
    root
    
  7. (Container 内の root 権限で) 直ちに ユーザ www のパスワードを変更する。
  8. # passwd www
    New password: 新しいパスワード        ← 新しいパスワードを入力する(エコーバックされない)
    Retype new password: 新しいパスワード ← もう一度新しいパスワードを入力する(エコーバックされない)
    
  9. (Container 内の root 権限で) ufw を有効化する
  10. # ufw enable
    Status: active
    
    # ufw allow from 0.0.0.0/0 to any port 22 proto tcp
    Rule added
    
    外部からのアクセスはデフォルトで禁止にする。
    # ufw default deny incoming
    
    ファイアウォールの設定状況を見る
    # ufw status
    Status: active
    To                         Action      From
    --                         ------      ----
    22/tcp                     ALLOW       Anywhere
    
  11. (Container 内の root 権限で) localhost の一般ユーザに ssh でアクセスできることを確認する。
  12. # ssh www@localhostwww ユーザにログインする Password: ← パスワードを入力する。エコーバックされない。
    Welcome to Ubuntu 24.04.2 LTS (GNU/Linux 6.11.0-25-generic x86_64) ... To run a command as administrator (user "root"), use "sudo ". See "man sudo_root" for details. www $
  13. Container 内の www ユーザ権限の対話環境を終了 Container 内の root 権限の対話環境に戻る。
  14. $ exit logout
    $
  15. キーボード操作 ('Control-P' と 'Control-Q' を連続してタイプする)により、ホストOSの対話環境に抜ける。 Container の対話環境は動作したままであるが、現在の端末からは切り離される。
  16. # ^p ^q ← Container内の対話環境で Control-P + Control-Q
    $ ← ホストOSの対話環境に戻る

アクセス遮断の確認

192.168.12.2 から、存在しないユーザに対する ssh アクセスを 5回失敗するとファイアウォールが閉じられることを確認した。
$ sudo fail2ban-client status sshd
Status for the jail: sshd
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     6
|  `- File list:        /var/log/syslog
`- Actions
   |- Currently banned: 1
   |- Total banned:     1
   `- Banned IP list:   192.168.12.2