さくらの VPS にウェブサービスを集約する

運用していたウェブサービスが別々のサービス上に点在していたのでさくらの VPS に集約することにしました。 とはいえ一番の動機は VPN サーバーをホストさせていた IDCF クラウドが個人向けサービスを終了させるということでした。集約したいサービスは次のようなものです。

新しくドメインを取得してさくらの VPS に紐付けます。今回取得したのは tk ドメインatlasseed.tk というものです。

Tasking Manager も Softether も Docker で動かしてホスト環境はあまりいじらないことを目標にしています。契約した VPS はメモリ1GB、SSD 30GBのもので、1年間で約1万円です。ロリポップと ConoHa と IDCF クラウドで1年あたり1.3万円くらいしてたので若干安くなる計算です。

以下 VPS の設定メモが無限に続きます。

カスタム OS で debian 9 を入れました。

$ cat /etc/debian_version
debian 9.8

基本設定

システムを更新して ufwSSH の設定を行います。 SSH のポートは 10022 を使うことにしました。 鍵認証が成功したら22番ポートを閉じます。

$ sudo apt update && \
  sudo apt upgrade -y && \
  sudo apt install -y ufw vim

$ sudo ufw allow 22/tcp && \
  sudo ufw allow 10022/tcp
$ sudo ufw enable
$ ufw status
-- snip --

$ sudo vi /etc/ssh/sshd_config
- #Port 22
+ Port 10022

$ sudo reboot

$ mkdir ~/.ssh
$ chmod 700 ~/.ssh
$ touch ~/.ssh/authorized_keys
$ vi ~/.ssh/authorized_keys
// 公開鍵を貼り付け
-- snip --

$ sudo vi /etc/ssh/sshd_config
- #PasswordAuthentication yes
+ PasswordAuthentication no
$ sduo systemctl restart sshd.service

$ sudo ufw status numbered
$ sudo ufw delete 3 // allow 22/tcp (v6)
$ sudo ufw delete 1 // allow 22/tcp

なお、今回作成した鍵は ed25519 のものです。ssh-keygen -t ed25519 -f ~/.ssh/sakuravpsとかでできます。

Docker のインストール

ドキュメントを見ながら Dcoker をインストールしました。Docker に続いて docker-compose もインストールします。

$ sudo apt install -y \
  apt-transport-https \
  ca-certificates \
  curl \
  gnupg2 \
  software-properties-common
  
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository \
  "deb [arch=amd64] https://download.docker.com/linux/debian \
  $(lsb_release -cs) \
  stable"

$ sudo apt update
$ sudo apt install -y docker-ce docker-ce-cli containerd.io

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose

$ sudo usermod -aG docker zoar

// 再ログイン
$ exit

$ docker info
-- snip --
$ docker-compose --version
docker-compose version 1.23.2, build 1110ad01

静的ウェブサイト設置

git をインストールして普通のウェブサイトを設置&SSLの設定をします。 普通のウェブサイトには www.atlasseed.tk でアクセスします。

$ sudo apt install -y git nginx etckeeper
$ git config --global user.name zoar
$ git config --global user.email zoar@k-side.net

$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp

$ cd /var/www/html
$ sudo tar xf ~/backup.tar.gz

$ mkdir -p ~/src/certbot && cd $_
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
$ sudo ./certbot-auto certonly --webroot -w /var/www/html -d www.atlasseed.tk

$ sudo openssl dhparam -out /etc/ssl/dhparam.pem 2048

$ sudo vi /etc/nginx/sites-available/default
server {
    listen 80;
    listen [::]:80;
    return 301 https://$host$request_uri;
}
server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;

    ssl_certificate /etc/letsencrypt/live/ww.atlasseed.tk/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ww.atlasseed.tk/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/ww.atlasseed.tk/fullchain.pem; 

    ssl_dhparam /etc/ssl/dhparam.pem;

    ssl_stapling on;
    ssl_stapling_verify on;

    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:50m;

    ssl_ciphers ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!EXPORT:!DES:!3DES:!MD5:!DSS;
    ssl_prefer_server_ciphers  on;

    add_header Strict-Transport-Security max-age=15768000;
    
-- snip --

$ sudo nginx -t -c /etc/nginx/nginx.conf
$ sudo systemctl reload nginx.service

live ディレクトリをセットしたから今度はちゃんと更新してくれるはず。

Tasking Manager の設定

あらかじめ Tasking Manager を動かせる Dockerfile と docker-compose ファイルを用意しました。 設定を始める前に旧サーバーから pg_dump -d tasking-manager > tm3.sql でデータベースをバックアップして転送してあります。

Tasking Manager には以前から使っていた MyDNS のドメイン osmtm.pgw.jp でアクセスできるようにするため、nginx のリバースプロキシを設定してリクエストをコンテナの5000番ポートに転送します。 また、 Let's Encrypt の証明書を作るにあたって acme-challenge へのアクセスはホスト側の適当なディレクトリに向かわせておかないと証明書を発行してもらえません。

旧サーバー

まずは旧サーバーにアクセスして証明書を失効させます。 なお、失効させずに証明書をコピーして存続させることもできる模様。

oldServer$ cd ~/src/certbot
oldServer$ sudo ./certbot-auto revoke --cert-path=/etc/letsencrypt/live/osmtm.pgw.jp/cert.pem

新サーバー

失効を確認できたら新しいサーバー側を設定していきます。

$ mkdir ~/docker && cd $_
//TM3 で使われる PostgreSQL のデータを保存するディレクトリを作成
$ mkdir postgres
$ git clone https://github.com/KMR-zoar/tm3.git
$ cd tm3

// TM3で使う環境変数を設定
$ export TM_ENV=Prod
$ export TM_CONSUMER_KEY=%Into your app consumer key%
$ export TM_CONSUMER_SECRET=%Into your app consumer secret%
$ export TM_SECRET=%Into your tm3 secret string%
$ export PG_DATA=%Into your path for PostgreSQL data%

$ wget https://raw.githubusercontent.com/hotosm/tasking-manager/develop/server/config.py
$ vi ./config.py
class ProdConfig(EnvironmentConfig):
-     APP_BASE_URL = 'https://tasks.hotosm.org'
+     APP_BASE_URL = 'https://osmtm.pgw.jp'

$ docker build . -t tm3 \
  --build-arg TM_ENV=${TM_ENV} \
  --build-arg TM_CONSUMER_KEY=${TM_CONSUMER_KEY} \
  --build-arg TM_CONSUMER_SECRET=${TM_CONSUMER_SECRET} \
  --build-arg TM_SECRET=${TM_SECRET}
$ docker-compose up -d

// 旧サーバーからバックアップしたデータを復元する
$ docker cp ~/tm3.sql tm3_postgres_1:/tmp/
$ docker exec -it tm3_postgres_1 bash
pgcontainer# psql -U hottm -d tasking-manager < /tmp/tm3.sql
pgcontainer# exit

$ docker exec -it tm3_tm3_1 bash
tm3container# python manage.py db upgrade
tm3container# exit

$ sudo mkdir /var/www/tm3
$ sudo vi /etc/nginx/sites-available/tm3
server {
       listen 80;
       listen [::]:80;

       server_name osmtm.pgw.jp;

       proxy_set_header    Host    $host;
       proxy_set_header    X-Real-IP    $remote_addr;
       proxy_set_header    X-Forwarded-Host       $host;
       proxy_set_header    X-Forwarded-Server    $host;
       proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;

       location / {
          proxy_pass       http://127.0.0.1:5000;
       }

       // Let`s Encrypt が認証に使うパスだけ別のディレクトリへアクセスするように設定
       location ^~ /.well-known/acme-challenge {
          default_type "text/plain";
          root /var/www/pgw;
       }
}

$ sudo nginx -t -c /etc/nginx/nginx.conf
$ sudo systemctl reload nginx.service

$ cd ~/src/certbot
$ sudo ./certbot-auto certonly --webroot -w /var/www/tm3 -d osmtm.pgw.jp

$ sudo vi /etc/nginx/sites-available/tm3
server {
       listen 80;
       listen [::]:80;

       server_name osmtm.pgw.jp;

       return 301 https://$host$request_uri;
}

server {
       listen 443 ssl;
       listen [::]:443 ssl;

       server_name osmtm.pgw.jp;

       ssl_certificate /etc/letsencrypt/live/osmtm.pgw.jp/fullchain.pem;
       ssl_certificate_key /etc/letsencrypt/live/osmtm.pgw.jp/privkey.pem;
       ssl_trusted_certificate /etc/letsencrypt/live/osmtm.pgw.jp/fullchain.pem;

       ssl_dhparam /etc/ssl/dhparam.pem;

       ssl_stapling on;
       ssl_stapling_verify on;

       ssl_session_timeout 1d;
       ssl_session_cache shared:SSL:50m;

       ssl_ciphers ECDHE+RSAGCM:ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:!EXPORT:!DES:!3DES:!MD5:!DSS;
       ssl_prefer_server_ciphers  on;

       add_header Strict-Transport-Security max-age=15768000;

       proxy_set_header    Host    $host;
       proxy_set_header    X-Real-IP    $remote_addr;
       proxy_set_header    X-Forwarded-Host       $host;
       proxy_set_header    X-Forwarded-Server    $host;
       proxy_set_header    X-Forwarded-For    $proxy_add_x_forwarded_for;

       location ^~ /.well-known/acme-challenge {
          default_type "text/plain";
          root /var/www/tm3;
       }

       location / {
          proxy_pass       http://127.0.0.1:5000;
       }
}

$ sudo nginx -t -c /etc/nginx/nginx.conf
$ sudo systemctl reload nginx.service

証明書の自動更新を設定する

$ cd ~/src/certbot
$ sudo sh -c "echo '00 03 13 * * root /home/zoar/src/certbot/certbot-auto renew --post-hook \"systemctl reload nginx.service\"' > /etc/cron.d/letsencrypt"
// 更新でエラーが出ないかチェックする
$ sudo ./certbot-atuo renew --dry-run
-- snip --

Congratulations, all renewals succeeded. とか出れば問題はないはず。

VPN サーバー

これは siomiz/softethervpn を使って構築することにしました。今回はその中の siomiz/softethervpn:alpine を使っています。

設定ファイルの作成

一回コンテナを動かすことで設定ファイルを生成します。生成後に設定ファイルをホスト側にコピーして本番用コンテナにバインドして使う感じです。

$ docker run -it -e PSK=%PSK Pre-Shared \
    Key% -e SPW=%VPN Server Password% \
    -e HPW=%Server HUB Password% \
    -e USERS=%User%:%User Password% \
    --name vpnconf siomiz/softethervpn:alpine echo

-e PSKIPSec で接続する時に必要な事前共有キー。
-e SPWVPN サーバーの管理機能にアクセスする時に必要なパスワード。サーバー管理マネージャーで使う
-e HPWVPN サーバー内のハブ管理に使うパスワード。
-e USERSVPN クライアントからの認証に使うユーザー名とパスワードのセット。さらにセミコロンで区切ると複数のユーザー/パスワードを用意できる。

停止しているコンテナからできあがった設定ファイルを取り出します。

$ docker container rm vpnconf
$ mkdir -p ~/docker/vpn && cd $_
$ docker cp vpnconf:/usr/vpnserver/vpn_server.config /home/zoar/docker/vpn/vpn_server.config

VPN のコンテナを起動する時は引数が多いので docker-compose を使うことにしました。

$ touch docker-compose.yml
$ vi docker-compose.yml
vpn:
  image: siomiz/softethervpn:alpine
  cap_add:
    - NET_ADMIN
  ports:
    - "%SoftEther Port%"
    - "%L2TP/IPSec Port%"
    - "%L2TP/IPSec Port%"
    - "%L2TP/IPSec Port%"
  volumes:
    - /home/kmr/docker/vpn/vpn_server.config:/usr/vpnserver/vpn_server.config
  restart: always

コンテナから L2TP/IPSec を晒す設定もしていますが ufw でインターネット側に晒すのは Softether のポートだけにします。

$ sudo ufw allow %Softether Port%
$ docker-compose up -d

適当な VPN クライアントから接続できれば OK。

おしまい

集約されていったサービスは順次削除や解約していきましょうかね。