本記事のゴール
AWS 上で WordPress サイトを構築する等、プライベートで AWS 環境を構築している際にどうしてもネックになるのが、WordPress サーバーやステップサーバー目的での EC2 に、どうやって安全に SSH 接続をするかが問題になります。
上図の赤線の箇所ですね。職場の環境だと、職場ネットワークには固定 IP が付与されているので IP 制限できる、であるとか、お金に物を言わせたソリューションで解決できるのですが、プライベート目的だとこの点なかなか痛い出費になります。
本記事では、インターネット越しに、プライベートで管理している Amazon EC2 に安全に、かつ低価格で SSH 接続する方法をご紹介したいと思います。
どこからでも SSH 接続できることの恐怖
具体的な解決策の解決に入る前に、なぜ安全な SSH 接続の経路が必要か、どこからでも SSH 接続できるようにしておくことのリスクをご説明したいと思います。
こちらの IPA の 2019 年下半期 (2019 年 7 月~ 12 月) の報告にて、アクセス制限されていない SSH サービスに対する執拗な攻撃がまとめれているとおりで、インターネットにフリーに公開している SSH はブルートフォースアタックの対象となり、攻撃の踏み台にされる恐れがあります。
そもそも SSH 接続ってその特性上コンピュータプログラムでのアクセスもサポートされている前提なので (IaC とかでもそうですが)、悪意のあるコンピュータプログラムの攻撃の対象になりやすいんですよね・・。
安全な SSH 接続
接続の安全性を高めるために、多要素認証 (多段階ではない!) を取り入れます。多要素認証とは、「知識要素」「所持要素」「固有要素」「場所」の 4 つの要素のうち、認証の際に異なる 2 つ以上の要素 (1 つ目の階層レベルのもの) を用いることです。多要素認証については、別記事にて詳細を解説します。
私も誤解していたのですが、SSH 秘密鍵は所持要素にあたりません (所持要素は厳密には、その要素について「自分が持っていない=他者が持っている」が成立することが条件だそうです、なので秘密鍵はどちらかというと知識要素に分類されることになります) ので、認証の際追加で所持要素、固有要素、場所のどれかが必要になることがわかります。
ここで、SSH 接続を多要素認証する以下の 3 つのアイディアについて、その方法とコストを考えていきたいと思います。
- AWS Client VPN の利用
- 固定のグローバル IP の取得
- SSH 接続時の認証を多要素化
AWS Client VPN の利用
AWS Client VPN とは、AWS VPC 内のサブネットにお手軽に VPN 接続を実現するサービスです。VPN 接続で手元のローカル PC からサブネットに直接接続できますので、そこから EC2 に SSH 接続するのはセキュアです。
実は、こちらのソリューションは単なる問題のすり替えでしかなく、SSH 接続はセキュアになったもののベースの考え方としては VPN 接続自体を多要素認証する必要があると思います。その意味では、こちらにあるとおり AWS Client VPN では SAML 2.0 経由のフェデレーション認証を使って多要素認証を実現できるようなので、それで実現して良いのかもしれません。もしくは、VPN 接続はパスワード認証、SSH 接続は公開鍵認証を用いることで 2 要素にみなしていいかもですが、そもそもサブネット内に 1 要素での認証で接続させてよいかの問題が出ます。
また、AWS Client VPN はエンドポイントをアタッチしている分で課金があり、これが 1 時間あたり $0.15 となっております。エンドポイントのアタッチ、デタッチをこまめに切り換える作業は考えられずずっと張りっぱなしだと思いますので、そうすると年間の料金が
$0.15 × 24 (時間) × 365 (日) × 110 (ドル円) = 144,540 円
となってしまいます。またこれに加え VPN 接続した時間分の料金も $0.05/時間でかかります。
固定グローバル IP の取得
安全な場所として、VPN 接続サービスを利用し、固定 IP アドレスを得てそこから SSH 接続する手段があります。
固定 IP アドレス取得のサービスは、インターネット検索をすればすぐに見つかると思います。相場としては大体月額 1,000 円強ぐらいから、ですかね。割とリーズナブルに使えます。
ただし、プロトコルが PPTP や L2TP/IPSec といった少し古い物が使われているケースが多く、また認証も単なるパスワード認証によるもののみと盤石でないものが多いように見えます。幸い、これらのサービスのネットワークに侵入されても、所有している AWS のネットワークとは別物なので、リスクは低減されていそうに見えますが。
ただ、こちらの方法は何よりお手軽なんですよね。固定 IP 取得サービスの手順にきっちり従えば、セットアップもそこまで難しくないように見えます。
SSH 接続時の認証を多要素化
そもそも SSH の認証自体を多要素にしてしまえば、有料のサービスを使わなくてすみます。ただ、SSH 接続はさきほども申し上げたとおり攻撃を受けやすいプロトコルなので、併せて万全の対策もお伝えします。
まずは多要素認証から。Google Authenticator を使って、SSH 接続を多要素認証にしてしまいます。
まずは Google Authenticator のインストールから。スーパーユーザーになっていただいている前提で、EPEL レポジトリを有効にし、
# amazon-linux-extras install epel -yGoogle Authenticator (PAM モジュール付き、PAM は後ほど軽く解説) をインストールします。
# yum install -y google-authenticatorQR コード関連のライブラリのインストール。
# yum install -y qrencode-libsいったん SSH 接続するユーザに戻り、ホームディレクトリに戻ります。
# exit
$ cd ~Google Authenticator を実行し、認証トークンを発行します。
$ google-authenticatorすると、時刻により変動するトークンか聞かれますが、当然セキュリティ上時刻変動必要ですので、y
Do you want authentication tokens to be time-based (y/n) yこの後、画面いっぱいに QR コードが出てくるので、専用のアプリ (スマホの Google Authenticator 等) で読み取ります。
同時に、~/.google_authenticator をアップデートするか、と問われますが、 y と回答してください。こちらは同期したスマホを紛失したりしてワンタイムパスワードが入手できなくなった際の復旧に必要なので、控えておいてください。
Do you want me to update your "/home/${ユーザ名}/.google_authenticator" file? (y/n) y次に以下のメッセージが出ます。これは 30 秒に 1 度更新されるワンタイムパスワードで複数回ログイン出来ないようにするものですので、セキュリティを強化する意味では y と回答してください。
Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y次に、以下のような長い質問が飛んできます。
By default, a new token is generated every 30 seconds by the mobile app.
In order to compensate for possible time-skew between the client and the server,
we allow an extra token before and after the current time. This allows for a
time skew of up to 30 seconds between authentication server and client. If you
experience problems with poor time synchronization, you can increase the window
from its default size of 3 permitted codes (one previous code, the current
code, the next code) to 17 permitted codes (the 8 previous codes, the current
code, and the 8 next codes). This will permit for a time skew of up to 4 minutes
between client and server.
Do you want to do so? (y/n) n内容としては、クライアントとサーバーとの間の時間のズレによってログインできない問題が発生することがあるため、このズレに対応するための余分なワンタイムパスワードを発行してよいか聞かれています。セキュリティ面を強化するのであれば、n と回答するのが無難でしょう。もしお手持ちのスマホ等のクライアントでズレが頻繁に発生してログインできなくなるようなことがあれば、先程の ~/.google_authenticator を使って復旧しつつ、改めて認証トークンを更新する際にこちらの質問には y と回答するようにしてください (Amazon EC2 は NTP で時刻を更新しており、スマホであれば NTP で時刻を更新しているので、時間のズレは起きにくいとは思いますが)。
最後に、ログインの試行回数を 30 秒に 3 回以下に制限するか聞かれますが、ここではいったんセキュリティ重視で y と回答しています。
If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting? (y/n) y次に、SSH の認証を 2 段階にしていきます。スーパーユーザーになります。
$ sudo su -PAM の設定を変更していきます。PAM とは、日本語にすると「プラグ可能な認証モジュール」のことだそうで、Linux の認証に多く使われているものになります。
まずは Google Authenticator を使った、2 要素認証の PAM モジュールを組みます。
# vi /etc/pam.d/google-auth#%PAM-1.0
auth required pam_env.so
auth required pam_unix.so try_first_pass nullok # パスワード認証
auth sufficient pam_google_authenticator.so try_first_pass # Google OTP
auth requisite pam_succeed_if.so uid >= 500 quiet
auth required pam_deny.so上記のモジュールでは、ユーザーのパスワードを入力した後に、Google Authenticator のワンタイムパスワードを入力するようになっています。もしユーザーパスワードの入力は不要だということであれば、2 行目を削除すると、Google Authenticator のワンタイムパスワードだけで、認証されるようになります。
次に、PAM での SSH 周りの認証方式について書き換えを行います。
# vi /etc/pam.d/sshd#%PAM-1.0
auth required pam_sepermit.so
#auth substack password-auth # コメントアウト
auth substack google-auth # 追加
auth include postlogin
...最後に、/etc/ssh/sshd_config も多要素認証の設定に変更します。
# vi /etc/ssh/sshd_config...
# Change to no to disable s/key passwords
ChallengeResponseAuthentication yes # アンコメント
#ChallengeResponseAuthentication no # コメントアウト
AuthenticationMethods publickey,keyboard-interactive # 追加
...この後 sshd を再起動すると、2 要素認証が反映されます。
# systemctl restart sshdさらに SSH 接続を安全に
SSH 接続できるユーザを限定する
/etc/ssh/sshd_config を修正することで可能です。
# vi /etc/ssh/sshd_config...
# DenyUsers ec2-user # コメントアウト
AllowUsers ${ユーザ名} # 追加
...sshd を再起動すれば、設定が反映されます。
# systemctl restart sshdSSH のポート番号を変更
そもそも SSH のデフォルトのポート番号 22 はトロイの木馬等の攻撃対象ともなるので、変更した方が安全です。安全なポート番号はご自身で調べていただいた上で、
まずは使いたいポート番号がサーバーで使われているか確認します。
# lsof -i:${ポート番号}上記コマンドを入力して何も表示されなければ OK です。
ポートを変更する前に、AWS のセキュリティグループのインバウンドルールで、対象のポートをオープンにしておきます。
こちらでも、/etc/ssh/sshd_config を修正することになります。
# vi /etc/ssh/sshd_config...
#Port 22
Port ${ポート番号}
...修正したら、sshd を再起動して設定反映できます。
# systemctl restart sshd最後に、忘れず元の SSH のポート番号 22 をセキュリティグループのインバウンドルールから除外しておきます。
おまけ: sudo も Google Authenticator で 2 要素認証に
PAM の設定を転用することで、sudo も多要素認証にすることが可能です。
# vi /etc/pam.d/sudo#%PAM-1.0
#auth include system-auth # コメントアウト
auth include google-auth # 追加
account include system-auth
password include system-auth
session optional pam_keyinit.so revoke
session include system-authsudo の実行は踏み台にされたときに危険ですので、こちらも併せて Google Authenticator で保護することをおすすめします。
なお、/etc/pam.d/google-auth の設定内容によっては (本記事の設定からパスワード認証を削除する設定にしていた場合)、パスワード認証から Google Authenticator による認証に変わるだけで多要素認証にはならないので、ご注意ください。
まとめ
以上、3 とおりの SSH 接続のセキュリティ強化の方法をご紹介しましたが、コスト面から考えると 3 つ目の SSH 接続に多要素認証を導入するのが今回は最もおすすめです。SSH 接続に多要素認証を導入しつつ、「さらに SSH 接続を安全に」で紹介した方法でセキュリティを強化しておくのがおすすめです。

