VPSを使ってSSL対応のホームページを公開してみよう(HTTPS-PORTAL編)

**この投稿は、2024年JINSのアドベントカレンダー19日目の記事です。**

どうも、JINS生活4年目の内田と申します。
眉毛を剃るのに失敗してしまったことがございまして、その時にインタビューが来たものだから大変なことになってしまいました。

https://techplay.jp/column/1893

## 概要

今回は、VPS上にDockerとDocker Composeをインストールして、自分で用意したHTMLを設置し、オリジナルのドメインでSSL対応のホームページをインターネットに公開するところまでをやります。

↓今回の手順で作成したホームページ↓

https://www.harapeko-pekorincho.info/

### 用意するもの

- VPS
    - ConoHa, XServer, さくらのVPS, EC2 等、何でも良いです
    - 仮想化方式にOpenVZが用いられているVPSではDockerが利用できないことがあります(WebArenaの一部インスタンス等)
    - Ubuntu 24.04 LTSを推奨します
- ドメイン
    - 好きなところで取得してください

### この記事の手順でやること

- SSHポートの変更
- Docker, Docker Composeのインストール
- HTTPS-PORTALの設定

### この記事の手順でやらないこと

- VPSの契約、インスタンスの立ち上げ
- SSH接続の方法や鍵ペアの設定
- ドメインの契約
- HTMLのコーディング

## やってみよう

### 1. SSHポートの変更

これは基本です。
サービスを公開するしないにかかわらず、インターネットから接続できるサーバーを用意したら必ず行いましょう。
(代替手段が思いつくレベルの人なら不要です)

:::note
VPSサービスでは、AWSやGCP等のサービスと違い、細かなFWの設定ができないケースがあります。
その場合、SSH接続で利用するポートを初期設定のTCP22番にしておくと、世界中の悪い人たちが接続試行をしてきます。
(最悪の場合、サーバーが乗っ取られて犯罪に利用されてしまうことも！)
そのリスクを減らすために、SSHで利用するポート番号を変更します。

今回は、22番から42200番へ変更します。
:::

:::note warn
**1-3. 接続確認** が終わるまで、**ポート変更前**から開いているSSH接続は閉じないでください！
設定に不備があった場合、外部から接続できなくなり、
**コンソール接続が不可能なVPSの場合、インスタンスの作り直しになってしまい、大泣きすることになってしまいます。**
:::

参考記事:

https://qiita.com/11ppm/items/a17e585e05cc3903f5c9


#### 1-1. `ssh.service`の設定変更

`sshd_config`をテキストエディタで開く

```bash
sudo nano /etc/ssh/sshd_config
```

コメントアウトを解除(頭の#を削除)しポート番号を`22`から`42200`に変更

```config
Port 42200
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632669/c08ed6f4-a642-577b-5a50-b51630ef693d.png)

`ssh.service`を再起動

```bash
sudo systemctl restart ssh.service
```

#### 1-2. `ssh.socket`の設定変更

:::note
このセクションの手順は、Ubuntu 22.04以前では必要ありません
:::

`/lib/systemd/system/ssh.socket`をテキストエディタで開く

```bash
sudo nano /lib/systemd/system/ssh.socket
```

`ListenStream`を`42200`に変更

```config
ListenStream=42200
```

設定再読み込みと、`ssh.socket`の再起動

```bash
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
```

#### 1-3. 接続確認

:::note warn
接続確認が終わるまで、**ポート変更前**から開いているSSH接続は閉じないでください！
設定に不備があった場合、外部から接続できなくなり、
**コンソール接続が不可能なVPSの場合、インスタンスの作り直しになってしまいます。**
:::

ポート番号を指定して接続

```bash
ssh <user>@<ip> -p 42200
```

接続が確認できたら、ポート変更前から開いているSSH接続は閉じても大丈夫です。

### 2. Dockerの設定

とりあえずSSHのポートが変更できたので、22番ポートを執拗に狙ってくる輩への対策はできました！
次はDockerをインストールしてこのサーバー上で利用できるようにしましょう。

#### 2-1. Dockerのインストール

以下コマンドでインストール

```bash
curl https://get.docker.com | sudo sh
```

#### 2-2. sudoコマンドなしでDockerを利用できるようにする

:::note warn
この手順は業務で使うサービスの**本番環境**では行わないでください。
(業務用本番環境ではRootlessモードを設定してください)
:::

```bash
sudo usermod -aG docker $USER
```

一旦ログアウト(切断)をし、再接続後、以下のコマンドを実行

```bash
docker version
```

以下のように出力されれば問題なし

```bash
Client: Docker Engine - Community
 Version:           27.4.1
 API version:       1.47
 Go version:        go1.22.10
 Git commit:        b9d17ea
 Built:             Tue Dec 17 15:46:31 2024
 OS/Arch:           linux/arm64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          27.4.1
  API version:      1.47 (minimum version 1.24)
  Go version:       go1.22.10
  Git commit:       c710b88
  Built:            Tue Dec 17 15:46:31 2024
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.7.24
  GitCommit:        88bf19b2105c8b17560993bee28a01ddc2f97182
 runc:
  Version:          1.2.2
  GitCommit:        v1.2.2-0-g7cb3632
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

### 3. 資材の作成

Dockerをサーバーいれることができました。
後は設定ファイルとホームページのコンテンツとなるHTMLを用意したらサーバーにいれるものは全て用意できたことになります！

:::note
`example.com` は、ご自身で取得したドメインに読み替えてください。
:::

全ファイル配置後のツリー

```bash
.
├── Dockerfile
├── docker-compose.yml
└── vhosts
    └── www.example.com
        └── index.html
```

#### 3-1. HTMLファイルの配置

内容は何でもよいです

```html:vhosts/www.example.com/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>www.example.com</title>
  </head>
  <body>
    <h1>www.example.com</h1>
  </body>
</html>
```

#### 3-2. `Dockerfile`の記述

```dockerfile:Dockerfile
FROM steveltn/https-portal:1

COPY ./vhosts/ /var/www/vhosts/

ENTRYPOINT ["/init"]
```

#### 3-3. `docker-compose.yml`の記述

```yaml:docker-compose.yml
version: '3'
services:
  https-portal:
    build:
      context: .
    restart: always
    volumes:
      - ssl-certs:/var/lib/https-portal
    environment:
      DOMAINS: >
        www.example.com,
        example.com => www.example.com
      # 疎通確認ができるまで、STAGEはlocalにしておくこと
      STAGE: local
    ports:
      - '80:80'
      - '443:443'
    logging:
      options:
        max-size: '200k'
        max-file: '10'

volumes:
  ssl-certs:
```

:::note
ログの最大サイズを指定することで、サーバーのストレージがログで一杯にならないようにしています。

```yaml
    logging:
      options:
        max-size: '200k'
        max-file: '10'
```
:::

:::note warn
**4-2. HTTPS-PORTALを立ち上げ、疎通確認を行う** が終わり、疎通確認ができるまでは`STAGE`を`production`にしないでください。
HTTPS-PORTALを立ち上げた時にSSL証明書を取得する処理が走りますが、その時点でサーバーがインターネットよりアクセスできる状態になっていないと、Let's Encryptのレート制限に当たってしまい、**しばらく同じドメインでの証明書取得ができなく**なってしまいます。

```yaml
      # 疎通確認ができるまで、STAGEはlocalにしておくこと
      STAGE: local
```
:::

### 4. 公開設定をする

もう一歩です！
後は取得したドメインとサーバーを紐づけたら完了です！

:::note
`example.com` は、ご自身で取得したドメインに読み替えてください。
:::

#### 4-1. DNS設定

ドメインを取得したサービスでAレコードの設定を行いましょう。

設定値は
|Host|Type|Value|
|:--|:--|:--|
|example.com|A|VPSのパブリックIPアドレス|
|www.example.com|A|VPSのパブリックIPアドレス|

各サービスによって手順が異なりますが、今回はお名前.comでの手順を紹介します。

管理画面のサイドバーより **ドメイン > ドメイン機能** を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632669/9ec44d12-9dea-1631-5725-69e17ac9ea07.png)

**DNS関連機能の設定** を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632669/f8630ebd-45fc-ccc0-e1cf-2e3e16e62a1f.png)

設定対象のドメインを選択し、次へ進む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632669/16ef20ab-c4ef-e521-cb55-abe04f2b1a78.png)

**DNSレコード設定を利用する** の横にある **設定する** ボタンを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632669/15962315-eb2e-f711-a2f0-a04249392fbb.png)

Aレコードを設定する
**確認画面に進んだ後、完了画面まで行くこと(忘れがちです)**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632669/2fa5ab75-0a47-0aec-70f1-8cf95532afee.png)

#### 4-2. HTTPS-PORTALを立ち上げ、疎通確認を行う

`docker-compose.yml` が存在するディレクトリで下記コマンドを実行

```bash
docker compose up -d --build
```

立ち上がったら、ブラウザで、 `https://example.com` にアクセスしてください。

証明書エラーが出るはずなので、example.comにアクセスする(安全ではありません)をクリックし、**3-1** で設置したHTMLが表示されれば疎通確認は完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3632669/dc266e39-dfe7-25a8-0118-61969a80f2e7.png)


#### 4-3. 環境変数を本番設定に変更し、SSL証明書を取得

**3-3** で設置した`docker-compose.yml`を編集し、`STAGE`を`production`にします。

```yaml:docker-compose.yml
version: '3'
services:
  https-portal:
# ----- 省略 -----
      STAGE: producrion
# ----- 省略 -----
```

:::note warn
**4-2. HTTPS-PORTALを立ち上げ、疎通確認を行う** が終わり、疎通確認ができるまでは`STAGE`を`production`にしないでください。
HTTPS-PORTALを立ち上げた時にSSL証明書を取得する処理が走りますが、その時点でサーバーがインターネットよりアクセスできる状態になっていないと、Let's Encryptのレート制限に当たってしまい、**しばらく同じドメインでの証明書取得ができなく**なってしまいます。
:::

再びアクセスすると、証明書エラーが出なくなっているはずです！
お疲れ様でした！

## 終わりに

実のところ、HTTPS-PORTALの本来の用途は静的コンテンツサーバーではございません。
次回は、本来の使い方であるSSL終端用のリバースプロキシとして設定する方法をご紹介したいと思います。