Let's Encrypt で SSL化しているドメインに、後からサブドメインを追加し、かつ SSL化する手順のメモを公開する。

まえおき不要の場合はここからジャンプ
 → 手順のみ読みたい場合
 → certbot の 手順から読みたい場合

はじめに余談 (読み飛ばしてOK)

こんな状態なので、一般的な手法で追加サブドメインのSSL化が可能なのか、やや不安だった(結果: 以下手順でも無事成功)。

個人的には、以下を目的に実行した。

まえおき おわり。

以下、実際の手順。

1.前提条件の確認

以下を前提条件に話を進める。

  • HTMLの保存ディレクトリ
    /usr/share/nginx/html/
  • 新たに追加するサブドメインのディレクトリ
    /usr/share/nginx/html/hoge
  • 最初にSSL化したドメインのディレクトリ
    /usr/share/nginx/html/freesoft
  • nginx.conf の場所
    /etc/nginx
  • Let's Encrypt のプライベートキー保存場所
    /etc/letsencrypt
  • certbot は既にインストール済み

ディレクトリは一般的なものにしてあるが、各自が環境に合わせて読み替えること。

2.新サブドメイン用のディレクトリを作成する

まず、サーバー上に新サブドメイン用のディレクトリを作成する

  • 例)ディレクトリ「hoge」作成場所
    /usr/share/nginx/html/hoge/ 
  • ディレクトリのプロパティを設定する。
    • グループ nginx [993] など
      所有者 nginx  [995] など
      パーミッション  755、707、705 など
      この辺の設定が無難。
    • 「所有者 root」や「パーミッション  777」は、サーバーアプリ や php などが動かなかったりする。注意。

ディレクトリ作成ここまで。
続いて、nginx の設定ファイルでサブドメインの設定を行う。

3.nginx.conf の server ディレクティブに以下を追加。

まず 非SSL の 80番ポートでアクセスが可能になる準備をしておく。
(重要: これがないと certbot による認証ができない)
(どうせ SSL化するのだから、ポート80はシンプルで良い。)

  • 一般的なサーバーの nginx.conf の場所
    /etc/nginx
  • nginx.conf に以下を追記(server ディレクティブ内)
    • # -------------------------------------------------------
          server {
              listen       80;
              listen       [::]:80;
              server_name  hoge.tvbok.com  ;
              root         /usr/share/nginx/html/hoge;
              index  index.html index.htm ;

      #     SSL化が済んだら ココ有効にする
      #     return 301 https://hoge.tvbok.com$request_uri;  

          }
      # -------------------------------------------------------
  • nginx.conf の追記が終わったら
    • nginx -t  (文法チェック)
      nginx -s reload
  • 無事 nginx の再起動が終わったら、この時点ではOK。
    (ドメイン設定がまだなので、まだブラウザでアクセスしない)

以上で、サブドメイン追加&SSL化「前」の準備は終わり。
※ 赤字「return 301 https://hoge.tvbok.com$request_uri; 」の部分は、最終的にコメントアウトを外すので、覚えておくこと。

4.nginx.conf で SSL 設定 を行う

ついでに SSL用の 443ポートの設定も、この段階で nginx.conf に追加する。
(ここでも ディレクトリ構成は一般的なものに書き換えている)
(覚え書きのため、個人的な設定もここに残しておく
(※少々問題あるのでコピペ非推奨)
 あくまでも、参考程度に。

例えば以下のような設定を nginx.conf に追加する。

  • ※ コピペ非推奨(理由は後述)
    #----------------------------------------------------------------------------
    #------------------------  新サブドメイン hoge  ---------------------
    #----------------------------------------------------------------------------

    server {
     listen 443 ssl http2;
     server_name hoge.tvbok.com ;
     root   /usr/share/nginx/html/hoge;
     index  index.php index.html index.htm ;
     # privkey などは、最初にSSL化したドメインのものと共通↓
     ssl_certificate /etc/letsencrypt/live/freesoft.tvbok.com/fullchain.pem;
     ssl_certificate_key /etc/letsencrypt/live/freesoft.tvbok.com/privkey.pem;
      # SSL設定はお好みで
     ssl_prefer_server_ciphers on;
     ssl_dhparam dhparam.pem;
     ssl_ciphers  ECDHE+AESGCM:DHE+AESGCM:AESGCM:HIGH:!aNULL:!MD5;
     ssl_session_cache shared:SSL:5m;
     ssl_session_timeout 2m;
    #----------------------------------------------------------------------------
    #----------------------------------------------------------------------------

        open_file_cache max=100 inactive=3s;
        open_file_cache_valid    20s;
        open_file_cache_min_uses 1;
        open_file_cache_errors   on;
        proxy_no_cache     1;
        proxy_cache_bypass 1;
        # 0でキャッシュ有効、1で無効

        keepalive_timeout  0;
        types_hash_max_size 2048;

        proxy_buffering       on;
        proxy_buffer_size     8k;
        proxy_buffers         32 8k;
    #----------------------------------------------------------------------------
        error_page 404    /e/404.htm;
        error_page 500 502    /e/500.htm;
        error_page 503    /e/503.htm;
        error_page 403    /e/403.htm;
        location /e/ {
        internal;
        allow all;
        access_log  /var/log/nginx/access.log  main if=$js_404;
        }

    ### リファラによるアクセス拒否設定はこの辺に書く
        if ($http_referer ~* (hoge.com|fuga.jp) ){
            return 403;
        # なぜかNginxデフォの403ページに飛ぶケースがある。
        # 時間が無くて直していない

            break;
        }
    #---------------------------------仮設定ここから----------------------------------
    # 実験用のサブドメインなので、自分以外は閲覧できないようにする
    # 実験用の制限ありディレクトリは、
    # 「location /」の下、 且つ PHP 設定は重複して書かないと「/fuga」の中でphpが動かない。
    #  しかし、最後に書くと deny が効かない。なのでここに書く。

    # root から禁止すると certbot  の自動更新が出来なくなるので注意!

    location /fuga {
          # 閲覧されたくない実験用ディレクトリ
          # 許可する IPアドレスのみをここに記述する

          allow 127.0.0.1 ;
          allow xxx.xx.xxx.xxx ;  # 自宅のIPや、自前の他サーバーなどは許可する
          allow xxx.xx.xxx.xxx ;  # 自宅のIPや、自前の他サーバーなどは許可する
          deny all;

        location ~ .*\.(htxt|zip|exe|css|gz|ico|png|jpe?g|gif)$ {
            access_log      off;
            expires         7d;
              # etag off;
            break;
            }
        location ~ .*\.(htm|js|txt|xml)$ { access_log off; break;}     # ログなし、expiresなし
         rewrite  ^(.*)\.html(.*) $1.html permanent;

    location ~ [^/]\.(php|html)(/|$) {
        etag off;
        fastcgi_split_path_info ^(.+\.html)(/.+)$;
            if (!-f $document_root$fastcgi_script_name) {
            error_page 404 /e/404.htm;
            return 404; break;
            }

        access_log  /var/log/nginx/access.log  main if=$log_404;

        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.html;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_intercept_errors on;
        }
        }
    #---------------------------------仮設定ここまで----------------------------------
    location / {
        location ~ .*\.(htxt|zip|exe|css|gz|ico|png|jpe?g|gif)$ {
            access_log      off;
            expires         7d;
              # etag off;
            break;
            }
        location ~ .*\.(htm|js|txt|xml)$ { access_log off; break;}     # ログなし、expiresなし
         rewrite  ^(.*)\.html(.*) $1.html permanent;

    location ~ [^/]\.(php|html)(/|$) {
        etag off;
        fastcgi_split_path_info ^(.+\.html)(/.+)$;
            if (!-f $document_root$fastcgi_script_name) {
            error_page 404 /e/404.htm;
            return 404; break;
            }

        access_log  /var/log/nginx/access.log  main if=$log_404;

        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.html;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_script_name;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_intercept_errors on;

        }

        } # 「location /」ここまで
        }
    #----------------------------------------------------------------------------
    #------------------------  新サブドメイン hoge  ---------------------
    #----------------------------------------------------------------------------

nginx.conf の追記・保存が終わったら
nginx -t  (文法チェック)
nginx -s reload で nginx を再起動する。
※追記: 念のため、reboot でサーバー全体を再起動するのが良いかも?

以上で、サーバー側のひとまずの準備おわり。

5.サブドメインを追加する

私の場合(さくらインターネット + さくらのVPS の場合)では、さくらインターネットの「ドメインコントロールパネル」にて 目的のドメイン に サブドメインの Aレコードを追加する。(※ドメインコントロールパネルへのアクセス手順はこのページで解説

  • /usr/share/nginx/html/hoge
    に、テキトーに index.html をアップする
  • (さくらインターネット + さくらのVPS の場合)
    「ドメインコントロールパネル」で以下のように追加する。
    さくらインターネットの「ドメインコントロールパネル」
    ▲クリックで拡大
    「hoge    A    160.16.119.113(現在の別館サーバのIPアドレス)」
    上記を入力して「保存」をクリック。
    ここまでの手順で、サブドメインの追加は完了。
  • 私の場合だと、http://hoge.tvbok.com/
    ※ この時点では ↑ https ではない ↑
    で index.html が表示されれば、ここまではOK。

index.html が無事 表示できたら、いよいよ SSL化を行う。

追記: 注意。まだ新ドメインは開かないこと!

  • この時点で新サブドメインに https でアクセスするのは、絶対ににダメ。
  • 443ポートは開放しているが、まだTSL設定が有効になっていない状態なので、https でアクセスすると、「危険なサイト」扱いになってしまう。
    ※ この場合、危険判定は半日程度で解除されるので、失敗してもアタフタしないでOK。

6.追加したサブドメインをSSL化する

ここが、このページの本題。
Let's Encrypt の場合、SSL化は certbot というモジュールで行う。
(「追加」という趣旨上、certbot は既にインストール済みとして扱う)

  • certbot のコマンドは以下。コンソールで実行する。
    まずは、「--dry-run」付きで、エラーが出ないか確認
    • こんな風に書けばOK
      certbot certonly --dry-run --webroot --force-renew --cert-name freesoft.tvbok.com -w /usr/share/nginx/html/freesoft -d freesoft.tvbok.com -w /usr/share/nginx/html/blog -d blog.tvbok.com -w /usr/share/nginx/html/hoge -d hoge.tvbok.com
    • 分かりやすく改行
      certbot
      certonly    # ・・・・ 証明書を取得してローカルに保存する指示
      --dry-run  # ・・・・ テスト実行。エラーが出ないかチェック
      --webroot # ・・・・  nginx用のコマンド
      --force-renew # ・・・・ 強制更新
      --cert-name freesoft.tvbok.com
      #  ・・・・ ↑大元のドメインを指定(最初にSSL化したヤツ)
      -w /usr/share/nginx/html/freesoft -d freesoft.tvbok.com
      #  ・・・・ ↑SSL化ドメインの指定(大元のドメインもここにも書く)
      -w /usr/share/nginx/html/blog     -d     blog.tvbok.com
      #  ・・・・ ↑SSL化ドメインの指定(すでにSSL化済が他にもあれば記述)
      -w /usr/share/nginx/html/hoge     -d     hoge.tvbok.com
      #  ・・・・ ↑SSL化ドメインの指定(今回 追加分はこれ)
    • 補足
      「/usr/share/nginx/html/」は、一般的な nginx サーバのディレクトリ階層を表したもの。各自、自分の環境に合わせて書き換える。
  • 実際に実行した画面がコチラ
    certbot の --dry-run
    ▲クリックで拡大
    実行するとダイアログが出る。「Update」を選択する。
    (小文字の「u」を入力すればOK)
  • 「u」を入力後の画面
    dry-run 成功画面
    ▲クリックで拡大
    「successful」が表示されれば、「--dry-run 」はひとまず成功。
    成功したら、上記コマンドから「--dry-run 」を抜いて、本チャンで実行する。
  • 「--dry-run 」抜き本チャン実行
    certbot certonly 成功画面
    ▲クリックで拡大
    「Congratulations!」でSSL化は成功
    プライベートキーは、最初にSSL化した時のものと共用となる。
    (つまり、cron 等で更新管理していたものは、そのままでOK?)
  • 詳細確認は以下で行う
    /etc/letsencrypt/renewal
    /var/log/letsencrypt
    変なエラーが出ていないか確認する。
    privkey などが nginx.conf に記述したものと同じ階層にあるか、同じ名前か、を確認する(重要)
    ※「Congratulations!」が出ていれば、大抵はOKのはず。

以上で、certbot の SSL化は成功。

7.HTTP > HTTPS のリダイレクトを設定する

ここで「■3.サブドメイン追加前の準備」でコメントアウトした、
443ポートへのリダイレクト部分のコメントアウトを外す。

  • nginx.conf
    「■3.サブドメイン追加前の準備」と同じもの
    • # -------------------------------------------------------
          server {
              listen       80;
              listen       [::]:80;
              server_name  hoge.tvbok.com  ;
              root         /usr/share/nginx/html/hoge;
              index  index.html index.htm ;

      #     以下のコメントアウトを外す
             return 301 https://hoge.tvbok.com$request_uri;  
          }
      # -------------------------------------------------------
  • nginx.conf の変更が終わったら
    • nginx -t  (文法チェック)
      nginx -s reload で nginx を再起動。
      念のため「reboot」などでサーバーを再起動しよう。

ここまでの手順で、新サブドメインの追加とそのSSL化は完了。
最終チェックする。↓

今回の例でいえば、https://hoge.tvbok.com/ をブラウザで開く。
※ (重要) この時点ではじめて ↑ https あり ↑でアクセスする。
これで アップした index.html が HTTPS で表示されていれば、SSL化は成功。
※ その後、HTTP 通信も、無事リダイレクトされるか確認する。
※ php、cgi、サーバーアプリ等が意図通り動くかどうか、もチェックする

以上で、「後からサブドメインを追加して、それを含めて Let's Encrypt でSSL化する」手順の紹介はおわる。

ただし、このページの解説で、抜けている部分はまだある。

「コピペ非推奨」の理由

上記の nginx.conf (443側) を丸々コピペすると、以下の問題が発生する。

  • TSL設定がかなり古い。
    2017年頃に書いたディレクティブである。TSL1.0~1.1 をサポートしている
  • 存在しないディレクトリのルートにアクセスすると、404エラーではなく403エラーになってしまう。
  • 404、403、503、500 エラーのリダイレクトが、チョット怪しい。
    条件によっては、サーバールートで指定(もしくは nginx デフォルト)のエラーページに飛んでしまう。
    (つまり、別ドメインのエラーページを開く危険性がある)
    (直したつもりだが、どこまで直っているか未調査)

これらは、いつか直さなきゃなーと思いつつ、2018年頃から色々と時間が取れなくなってしまい、未だに放置している。そのうち直す。

最大の注意点(?)

今回、一時的にではあるが、SSL化は成功しているのに追加したサブドメインが「危険なサイト」扱いされてしまった。その辺のメモを以下に記す。

上記のコードを丸々コピペし、SSL化直後に 404、403、503 エラーを発生させると、新しく追加したサブドメインが「危険なサイト」もしくは「保護されていない通信」扱いになってしまう場合があるかも?(大事にはならないが、注意が必要)

  • 「危険なサイト」警告は、何もしなくても半日くらいで解除されるので大きなミスにはならないないハズ。 
    • SSL化直後、nginx.conf の「閲覧禁止設定」の箇所を間違えていた?
      メインPC の Chrome でアクセスして発生。
    • Edge、Firefox では発生せず。
      別環境の Chrome (通信環境は同じ) でも発生せず。
    • 5つの環境で試し、さらにもう一個サブドメイン追加&SSL化を行ったが、「危険なサイト」扱いされたのは 初回のみ、メイン PCの Chrome で1回だけ、という結果だった。
    • つまり、再現性がなく、何が原因だったか分からずじまい。(後述の追記で再現できた)
  • 恐らく、「閲覧禁止設定」の箇所を間違えていた(もしくは、存在しないディレクトリのルートを開いてしまった)、その時に、SSL化 していないドメインのエラーページに飛んでしまった・・・・と思うのだが、再現しない。

上にも書いたが、何もしなくても、この警告は半日くらいで解除されるので、大きな問題にはならないと思う。しかし「かなり気持ち悪い&ガッカリ感が半端ない」ので、丸々コピペする人は注意しておこう。。。

 追記: 「危険なサイト」現象の再現できた!

すでにアチコチで追記を入れているのでピンと来る人もいると思うが、「nginx.conf で ポート 443 のTSL 設定を外したり、完全に未設定な状態にする」と、この問題が見事に再現できた。
つまり、ポート 443 の SSL 設定が正しく動作していなかった可能性が非常に大きい。

  • 当初、私は以下の順序で作業を進めていた
    • nginx.conf で 80番開放、index.html
      http で hoge.tvbok.com へ アクセス
      certbot の認証
      nginx.conf で 443番開放& 80>443 リダイレクト有効化
      nginx -s reload
      間髪入れずに https で hoge.tvbok.com へ アクセス
  • この時、「うっかりして nginx -s reload を忘れた」もしくは「nginx -s reload 直後すぎて、nginx が再起動していなかった」のどちらかであると思われる。
  • つまり、nginx  の SSL 設定が有効化されないウチに HTTPS でアクセスしちゃったのだ。
  • 原因として「エラーページのリダイレクト」に目星を付けてしまい、気が付くのが大幅に遅れた。
  • とりあえず、上記で紹介したコードをまるっと使っても、大惨事にはならない感じである。(TSLが古いとか、エラーページのリダイレクトが怪しい等の問題は残るので、そこは注意する。)

         ↓

追記おまけ: reboot を勧めた理由

  • もし「reboot」ではなく「nginx -s reload」を使いたいのであれば、ディレクティブ実行後、しばらく待ってから(1~2分待ってから) 新規にSSL化したドメインにアクセスする事。
    理由: nginx -s reload の再起動は、条件により時間がかかるケースがあるため。
  • nginx の 再起動が完了する前に新サブドメインにアクセスしてしまうと、TSL設定が有効になっておらず ブラウザから「危険なサイト」判定 を受けてしまう。
    ※ この場合、半日程度で解除されるので、失敗してもアタフタしないでOK。

今回 得た教訓

nginx に重要な変更を行った時は、「nginx -s reload」ではなく「reboot」しよう。
「nginx -s reload」するなら、再アクセスは時間が経ってから行おう。既に稼働中のドメインであるならば、絶対に「reboot」などでサーバー全体を再起動させよう。

今後の課題

新サブドメイン追加&SSL化直後にこの記事をアップした。なので、CRON の自動更新が(サブドメイン追加前と同じ設定で)無事に実行されるか、まだ確証が持てない状況である。

  • 今回、SSL自動更新用 CRON は(このままで大丈夫と判断したので)全く弄っていない。1ヶ月後、2026/3/02 に自動更新するかチェック
    • 現状、2017年に設定した CRON を、そのまま動かしている
  • 新しく登録したドメインのディレクトリには「.well-known」フォルダが生成されていない。1ヶ月後、どうなるか?これもチェックする。
    • 前回、どのタイミングで「.well-known」フォルダが生成されたのかメモを残していないので、よくわからない。
  • ディレクトリ「freesoft」の「.well-known」を、WinSCP上で長クリックしてフォルダ名をコピーする等、お行儀の悪い操作を行ってしまった。
  • これらの悪影響が出ないか、03/02 頃 にチェックすること。

このページの更新は以上。