2025/05/11 Updated by

Docker Image を自作する (4)

ubuntu 24.04LTS + 新規ユーザ + sshd + httpd + cert


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

dockae image "ubuntu24_sshd" からコンテナを作成し、apache2 サーバ+ php を作成する。 その上で php ファイルを include してみる。
  1. docker host (um580) 上の ubuntu 24.04LTS の firewall 設定状況
  2. nitta@um580:~/doc/docker/ubuntu24_sshd$ sudo ufw status
    [sudo] nitta のパスワード:
    状態: アクティブ
    
    To                         Action      From
    --                         ------      ----
    80                         ALLOW       Anywhere
    22                         ALLOW       Anywhere
    443                        ALLOW       Anywhere
    3389                       ALLOW       Anywhere
    
  3. container に forwarding するポートを開放する。
  4. nitta@um580:~/doc/docker/ubuntu24_sshd$ sudo ufw allow from 0.0.0.0/0 to any port 10022 proto tcp
    ルールを追加しました
    nitta@um580:~/doc/docker/ubuntu24_sshd$ sudo ufw allow from 0.0.0.0/0 to any port 10080 proto tcp
    ルールを追加しました
    nitta@um580:~/doc/docker/ubuntu24_sshd$ sudo ufw allow from 0.0.0.0/0 to any port 10443 proto tcp
    ルールを追加しました
    nitta@um580:~/doc/docker/ubuntu24_sshd$ sudo ufw status
    状態: アクティブ
    
    To                         Action      From
    --                         ------      ----
    80                         ALLOW       Anywhere
    22                         ALLOW       Anywhere
    443                        ALLOW       Anywhere
    3389                       ALLOW       Anywhere
    10022/tcp                  ALLOW       Anywhere
    10080/tcp                  ALLOW       Anywhere
    10443/tcp                  ALLOW       Anywhere
    
  5. Container を生成して、起動する。
  6. nitta@um580:~/doc/docker/ubuntu24_sshd$ docker run --name ubuntu24-sshd --restart always -p 10022:22 -p 10080:80 -p 10433:433 -e /home/docker/ubuntu24_nitta -h ubuntu24_nitta -e UNAME=nitta -it ubuntu24_sshd
    First run. Setting up ...
    Creating group nitta with GID=3000
    Creating user nitta with UID=3000, GID=3000
    ownership of '/home/nitta' retained as nitta:nitta
    No command provided. Starting bash ...
    
  7. ただちにユーザ nitta のパスワードを変更する。
  8. root@ubuntu24_nitta:/# passwd nitta
    New password:
    Retype new password:
    passwd: password updated successfully
    
  9. apache2 をインストールする
  10. sudo apt update
    sudo apt install apache2
    
  11. エディタをインストールする
  12. sudo apt install -y vim
    export EDITOR=vi
    
  13. systemctl の制御に apache2 を加える。
  14. /etc/supervisor/conf.d/supervisord.conf
  15. apache2 の設定ファイルを編集する
  16. /etc/apache2/conf-available/security.conf
    /etc/apache2/sites-available/000-defualt.conf
    
  17. phpを有効化する
  18. sudo apt -y install libapache2-mod-php
    
    p
  19. apache2 を起動する。
  20. docker restart ubuntu24-sshd
    
  21. image ubuntu24_sshd を使って container ubuntu24-sshd を作成する
  22. 
    

sshd, httpd (https) が自動起動する ubuntu24.04LTS の docker Image を生成する

方針

作成手順

  1. 作業用フォルダを作成する
  2.   $ mkdir -p ~/doc/docker/ubuntu24_sshd_https
      $ cd ~/doc/docker/ubuntu24_ssd_https
    
  3. 作業用フォルダの中に Dockerfile を作成する。
  4. Dockerfile
    授業で配布するプリントを参照して下さい。
    
  5. 作業用フォルダの中に entrypoint.sh を作成する
  6. entrypoint.sh
    授業で配布するプリントを参照して下さい。
    
  7. 作業用フォルダの中に supervisord.conf を作成する
  8. supervisord.conf
    授業で配布するプリントを参照して下さい。
    
  9. Image を build する。
  10.   $ docker build -t ubuntu24_sshd_https .
      ...
    成功
    
  11. 生成した Image を確認する
  12. $ docker image ls
    REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
    ubuntu24_sshd_https   latest    d12fc3012451   2 hours ago    329MB
    ...
    
  13. (注意) 上で用意した supervisod.conf は、Container の /etc/supervisor/conf.d/supervisord.conf というパスにコピーしている。 supervisord を起動するときに設定ファイルとして指定しているのは、標準でインストールされる /etc/supervisor/supervisord.conf である。このファイルには /etc/supervisor/conf.d/*.conf をインクルードするように記述されている。
  14. [参考] 標準の /etc/supervisor/supervisord.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 アドレスを与える」ことにする。

前提条件と環境

  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. docker のネットワーク一覧を表示して、macvlan_net が存在するかどうかを調べる。
  6. $ 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
    
  7. (macvlan ネットワークが存在しない場合は ) macvlan ネットワークを作成する。
  8. $ 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
    
    
  9. docker のネットワーク一覧を表示する。macvlan_net が作成されていることがわかる。
  10. $ 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
    
  11. macvlan ネットワークの詳細をみる
  12.   $ docker network inspect macvlan_net
      [
        {
            "Name": "macvlan_net",
            "Id": "557533733a33e84ca9eb665736f6a273414778f30a3e744411596008728b0b2b",
            "Created": "2025-05-18T13:38:38.53151226+09:00",
            "Scope": "local",
            "Driver": "macvlan",
            "EnableIPv4": true,
            "EnableIPv6": false,
            "IPAM": {
                "Driver": "default",
                "Options": {},
                "Config": [
                    {
                        "Subnet": "192.168.12.0/24",
                        "IPRange": "192.168.12.192/28",
                        "Gateway": "192.168.12.1"
                    }
                ]
            },
            "Internal": false,
            "Attachable": false,
            "Ingress": false,
            "ConfigFrom": {
                "Network": ""
            },
            "ConfigOnly": false,
            "Containers": {
                "65cf25b6ca3756fd9babc1b29d9b1a30d493c9ee85a713b530a91e999f9c35de": {
                    "Name": "ubuntu24-sshd-httpd-vlan193",
                    "EndpointID": "f8525f08c56d699552c023cdd7a589c2dd177ed575085d15b44eea960f83df3e",
                    "MacAddress": "a6:a6:4d:50:7a:0d",
                    "IPv4Address": "192.168.12.193/24",
                    "IPv6Address": ""
                }
            },
            "Options": {
                "parent": "eno1"
            },
            "Labels": {}
        }
    ]
    

Docker Contaner を生成する

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

Container を作成+実行する

  1. image から container を生成して起動する。 下の実行例はユーザ名を www とした場合である。
  2.   $ docker run --name ubuntu24-sshd-https-vlan193 --restart always \
        -e UNAME=www -e UID=2000 -e GID=2000 \
        --network macvlan_net \
        --ip 192.168.12.193 \
        --cap-add=NET_ADMIN \
        -v /home/docker/httpd:/mnt/hostos \
        -it ubuntu24_sshd_https
    
    起動オプション
  3. Container を起動した対話環境が、そのままContainer 内で動作する bash との対話環境になる。root権限でloginした状態である。
  4. First run. Setting up ...
    Creating group www with GID=2000
    Creating user www with UID=2000, GID=2000
    info: Adding user `www' to group `sudo' ...
    ownership of '/home/www' retained as www:www
    No command provided. Starting bash ...
    root@74996e43a896:/# 2025-05-25 04:58:18,756 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-05-25 04:58:18,759 INFO RPC interface 'supervisor' initialized
    2025-05-25 04:58:18,759 CRIT Server 'unix_http_server' running without any HTTP authentication checking
    2025-05-25 04:58:18,759 INFO supervisord started with pid 41
    2025-05-25 04:58:19,763 INFO spawned: 'apache2' with pid 44
    2025-05-25 04:58:19,765 INFO spawned: 'cron' with pid 45
    2025-05-25 04:58:19,766 INFO spawned: 'php-fpm' with pid 46
    2025-05-25 04:58:19,768 INFO spawned: 'sshd' with pid 48
    2025-05-25 04:58:20,787 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    2025-05-25 04:58:20,788 INFO success: cron entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    2025-05-25 04:58:20,788 INFO success: php-fpm entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    2025-05-25 04:58:20,788 INFO success: sshd entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    #                ← Container 内の対話環境 (root 権限の bash) が動く
    
  5. 直ちに ユーザ www のパスワードを変更する。
  6. # passwd www
    New password: 新しいパスワード        ← 新しいパスワードを入力する(エコーバックされない)
    Retype new password: 新しいパスワード ← もう一度新しいパスワードを入力する(エコーバックされない)
    
  7. ネットワークコマンドはインストール済みである。もしも、インストールされていない場合は以下の手順でインストールする。
  8. まず、aptのデータベースを更新する。
    #  apt update
    
    ifconfig, netstat, arp をインストールする
    # apt install -y net-tools
    
    traceroute をインストールする
    $ apt install -y iputils-tracepath traceroute
    
    ping をインストールする
    $ sudo apt install -y iputils-ping
    
    curl をインストールする
    $ sudo apt install -y curl
    
    ip をインストールする
    $ sudo apt install -y iproute2
    
  9. netstat コマンドを用いて、ルーティング情報を調べる。 → Container が接続しているネットワークは 192.168.12.0/24で、 デフォルトのルーティング先が 192.168.12.1 であることがわかる
  10. $ netstat -nr
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
    0.0.0.0         192.168.12.1    0.0.0.0         UG        0 0          0 eth0
    192.168.12.0    0.0.0.0         255.255.255.0   U         0 0          0 eth0
    
  11. ifconfig でContainer のIPアドレスを調べる。 → 192.168.12.193 であることがわかる。
  12. $ ifconfig
    eth0: flags=4163  mtu 1500
            inet 192.168.12.193  netmask 255.255.255.0  broadcast 192.168.12.255
            ether 8a:9e:8a:00:aa:16  txqueuelen 0  (Ethernet)
            RX packets 5434  bytes 30400112 (30.4 MB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 4498  bytes 300984 (300.9 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    lo: flags=73  mtu 65536
            inet 127.0.0.1  netmask 255.0.0.0
            inet6 ::1  prefixlen 128  scopeid 0x10
            loop  txqueuelen 1000  (Local Loopback)
            RX packets 8  bytes 1058 (1.0 KB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 8  bytes 1058 (1.0 KB)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
  13. Container では firewall は動作していない。現在の Image 元にした ubuntu:24.04 では ufw や iptavbles がインストールされていないため。
  14. # iptables -L
    bash: iptables: command not found
    # ufw status
    bash: ufw: command not found
    
  15. 手動で firewall を設定する。
  16. ufw をインストールする。
    # apt update
    # apt install -y ufw
    
    ufw を有効化する
    # ufw enable
    Status: active
    
    # ufw allow from 0.0.0.0/0 to any port 22 proto tcp
    Rule added
    # ufw allow from 0.0.0.0/0 to any port 80 proto tcp
    Rule added
    # ufw allow from 0.0.0.0/0 to any port 443 proto tcp
    Rule added
    
    外部からのアクセスはデフォルトで禁止にする。
    # ufw default deny incoming
    
    ファイアウォールの設定状況を見る
    # ufw status
    Status: active
    To                         Action      From
    --                         ------      ----
    22/tcp                     ALLOW       Anywhere
    80/tcp                     ALLOW       Anywhere
    443/tcp                    ALLOW       Anywhere
    
  17. ネットワーク上の他のマシンから、ssh接続できることを確認する。
  18. sshで
    $ ssh www@192.168.12.193
    
    [注意] パスワードを迅速に変更しておくこと。
  19. ネットワーク上の他のマシンから httpd 接続できることを確認する。
  20. ブラウザで url: http://192.168.12.193/

httpsを使えるように設定する

証明書を発行する

  1. ルータを使って外部ネットワークと切り分けている場合は、ルータのポートフォワーディング機能を用いて 80番ポートと 443番ポートへのアクセスが、apache2 サーバに転送されるように設定しておく。
  2. certbot をインストールする ← これは Dockerfile の中でやってあるはず。
  3. # apt -y install certbot 
    
  4. certbot のパラメータ
  5.   --webroot でWebサーバの公開ディレクトリ配下を認証用に一時領域に使う
      -w DocumentRoot
      -d 証明書を取得する対象の FQDN (= Fully Qualified Domain Name)
    
  6. certbot を使って、証明書を発行する。下の例は、自分のWWWサーバに対して example.com というドメインが登録してあり、WWWの管理者が root@example.com の場合である。適切な名前に変更すること。
  7. # certbot certonly --webroot -w /var/www/html -d example.com  
    Saving debug log to /var/log/letsencrypt/letsencrypt.log
    Enter email address (used for urgent renewal and security notices)
     (Enter 'c' to cancel): root@example.com 
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Please read the Terms of Service at
    https://letsencrypt.org/documents/LE-SA-v1.3-September-21-2022.pdf. You must
    agree in order to register with the ACME server. Do you agree?
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: y 
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Would you be willing, once your first certificate is successfully issued, to
    share your email address with the Electronic Frontier Foundation, a founding
    partner of the Let's Encrypt project and the non-profit organization that
    develops Certbot? We'd like to send you email about our work encrypting the web,
    EFF news, campaigns, and ways to support digital freedom.
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    (Y)es/(N)o: n 
    Account registered.
    Requesting a certificate for example.com
    
    Successfully received certificate.
    Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
    Key is saved at:         /etc/letsencrypt/live/example.com/privkey.pem
    This certificate expires on 2023-09-01.
    These files will be updated when the certificate renews.
    Certbot has set up a scheduled task to automatically renew this certificate in the background.
    
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    If you like Certbot, please consider supporting our work by:
     * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
     * Donating to EFF:                    https://eff.org/donate-le
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    
  8. 証明書を自動的に更新する設定を行う。 証明書の更新手順が通常とは異なるので注意すること。
  9. docker container ではsystemd が動作していないので systemctl が使えない。 そのため、cron を使って certbot を定期的に実行する。

  10. cron はインストールされているはず。
  11. # which cron 
    /usr/sbin/cron
    
    # ps aux | grep cron 
    root          44  0.0  0.0   4192  2340 pts/0    S    04:10   0:00 /usr/sbin/cron -f
    root        1223  0.0  0.0   3528  1740 pts/0    S+   04:19   0:00 grep --color=auto cron
    
    
  12. [Optional] Editor をインストールして環境変数を設定する。
  13. # apt install -y vim 
    $ export EDITOR=vi 
    
  14. certbot renew を crontab に登録する
  15. $ crontab -e
  16. 以下を追加する。毎日 2:00 に更新をチェックする。証明書が更新されたときは apache2 を再起動する。
  17. この Container では apache2 を supervisord 経由で起動しているので、 apache2 の再起動に systemctl や apachectl は使えない。 supervisorctl を使う。
    0 2 * * * certbot renew --quiet --deploy-hook "supervisorctl restart apache2"
    

apache2 の設定を変更する

  1. apache2 にsslモジュールを追加する
  2. # a2enmod ssl
    # a2enmod ssl 
    Considering dependency mime for ssl:
    Module mime already enabled
    Considering dependency socache_shmcb for ssl:
    Enabling module socache_shmcb.
    Enabling module ssl.
    See /usr/share/doc/apache2/README.Debian.gz on how to configure SSL and create self-signed certificates.
    To activate the new configuration, you need to run:
      service apache2 restart
    
  3. /etc/apache2/sites-available/default-ssl.conf が生成されるので編集する。
  4. /etc/apache2/sites-available/default-ssl.confの変更点
    授業で配布するプリントを参照して下さい。
    
  5. SSLを有効化する
  6. /etc/apache2/sites-enabled/default-ssl.conf から /etc/apache2/sites-available/default-ssl.conf への シンボリックリンクを張る。
    # a2ensite default-ssl 
    Enabling site default-ssl.
    To activate the new configuration, you need to run:
      service apache2 reload
    
  7. SSLを有効化する
  8. mod_ssl を有効化する。具体的には /etc/apache/mods-enabled/ssl.{load, conf} から/etc/apache/mods-available/ssl.{load,conf} へのシンボリックリンクを張る。
    # a2enmod ssl 
    Considering dependency mime for ssl:
    Module mime already enabled
    Considering dependency socache_shmcb for ssl:
    Module socache_shmcb already enabled
    Module ssl already enabled
    
  9. apache2 を再起動する
  10. supervisorctl を使って再起動する。
    # supervisorctl restart apache2 
    2025-06-06 15:26:04,195 INFO waiting for apache2 to stop
    2025-06-06 15:26:04,204 WARN stopped: apache2 (terminated by SIGTERM)
    apache2: stopped
    2025-06-06 15:26:04,214 INFO spawned: 'apache2' with pid 1418
    2025-06-06 15:26:04,257 WARN exited: apache2 (exit status 0; not expected)
    2025-06-06 15:26:05,261 INFO spawned: 'apache2' with pid 1421
    2025-06-06 15:26:07,227 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
    apache2: started
    
  11. ネットワーク上の他のマシンからブラウザで https 接続してみる。
  12. URL: https://example.com