sshログインパスワードを固定にしている方へ

## はじめに
私が所属する[aslead DevOps](https://aslead.nri.co.jp/solution/aslead_devops.html)チームでは、日々変化するユーザの開発サーバ構成に対して、セキュリティを保ちつつ開発業務の効率化・自動化ができないかを検討しています。

この記事では、開発サーバなどのOSにsshでログインする際の方法についてご紹介します。そもそもセキュリティの観点では、OSに対してsshでログインせず操作できる状態が理想ではあります。しかしながら、テスト環境での利便性やレガシーシステムへの接続など、様々な事情でsshのログインを残さざるを得ないケースもあるかと思います。

そこで、Hashicorp社が提供しているVaultを利用し、sshログインするパスワードがランダムに払い出されることでセキュリティを向上させるアプローチを考えてみます。使い捨てのコードを用意する、ワンタイムパスワード(OTP)とは若干異なりますのでご注意下さい。
 
## Vaultとは


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/acb29790-db2d-5752-73e1-0bfa11fc2d36.png)
(画像は　https://medium.com/hashicorp-engineering/vault-enterprise%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6-%EF%BC%91-e5579c2fb8d5 より引用)

[公式サイト](https://www.vaultproject.io/)より、Vaultは主に以下のような機能を持っています。

* Secret Management：様々な環境のパスワードのライフサイクルを管理する
* Data Encryption：データの暗号化/複合化を行う（データ自体は保持せず、変換だけ）
* Identity-based Access：同一人物の様々な環境上のアカウントを一つに取りまとめる

本記事ではSecret Managementに分類される機能を取り扱います。

## 想定するパスワード運用
以下のようなセキュリティに懸念がある運用ケースを想定し、解消を試みてみます。

- パスワードでsshログインしている
- 構築したOSに共通のパスワードを設定している
- パスワード一覧表をファイルサーバなどに置いて運用している
- 定期的にパスワード変更する運用があるが、規則性のあるパスワードで変更される
- 管理者権限ユーザのパスワードは作業で必要になったメンバに徐々に渡している

## Vaultによる解決策
上記の課題に対し、今回は[公式チュートリアル](https://learn.hashicorp.com/tutorials/vault/ssh-otp)を参考にログインするたびにランダムにパスワードが変更されるアプローチをとってみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/62c04ca9-f59a-d1c8-5d31-37f8b35dbddc.png)

これにより、以下のようなメリットがもたらされます。

* パスワードの文字列を考える必要がなく、構築したどのOSも異なるパスワードになる
* パスワード一覧はVaultが保持し、Vault自体のログインはLDAPなど既存の認証の仕組みが利用できる
* 管理者権限を渡す必要があっても、一度しか使えないためチームごとの権限分割は保たれる

以下では具体的にVaultを構築し、ランダムなパスワードが発行される仕組みについて実装していきます。

## 実装
###実装する環境
今回は以下の環境で実装を行います。
AWSを利用していますが、ローカルでも確認可能な内容になっています。

* Vault：ver1.5.4
* OS：Amazon Linux 2
* IaaS：AWS EC2サービス

###OS部分の構築

本記事の主な解説対象ではないため、割愛します。
[Acrovision社の記事](https://www.acrovision.jp/service/aws/?p=1829)などが参考になるかと思います。

以降は、Vaultが稼働するOSを`vault`、ログイン対象OSを`target`というホスト名で作成し、
お互いに任意の通信が可能な状態で構築された前提で進みます。

###Vaultのインストール

2020/07/24より、[Linuxレポジトリからの取得が可能に](https://www.hashicorp.com/blog/announcing-the-hashicorp-linux-repository/)なりましたので、こちらを利用して`vault`に対してインストールしていきます。

```bash
$ sudo yum install -y yum-utils
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
amzn2-core                                                       | 3.7 kB  00:00:00
Package yum-utils-1.1.31-46.amzn2.0.1.noarch already installed and latest version
Nothing to do

$ sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
Loaded plugins: extras_suggestions, langpacks, priorities, update-motd
adding repo from: https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo
grabbing file https://rpm.releases.hashicorp.com/AmazonLinux/hashicorp.repo to /etc/yum.repos.d/hashicorp.repo
repo saved to /etc/yum.repos.d/hashicorp.repo

$ sudo yum install -y vault
～中略～
Complete!
```

説明の簡便のため、起動は開発者モードで行います。
`-dev-listen-address`は`vault`のIPアドレスを含めて下さい。EC2の場合は下記のようにメタデータから取得することができます。

```bash
$ vault server -dev -dev-root-token-id="root" -dev-listen-address="$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4):8200"
==> Vault server configuration:

             Api Address: http://172.31.39.141:8200
                     Cgo: disabled
         Cluster Address: https://172.31.39.141:8201
              Go Version: go1.14.7
              Listener 1: tcp (addr: "172.31.39.141:8200", cluster address: "172.31.39.141:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
               Log Level: info
                   Mlock: supported: true, enabled: false
           Recovery Mode: false
                 Storage: inmem
                 Version: Vault v1.5.4
             Version Sha: 1a730771ec70149293efe91e1d283b10d255c6d1
～中略～
==> Vault server started! Log data will stream in below:
～省略～
```

ブラウザから`Api Address`に記載されたアドレス（上記では`http://172.31.39.141:8200`）にアクセスし、下記のようにログイン画面が表示されればインストールと起動は完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/fd9e3a08-37cb-358d-a3b3-ecc5971936ee.png)

### パスワードのランダム化

#### `vault`側の設定
パスワードのランダム化には[SSH Secret Engine](https://learn.hashicorp.com/vault/secrets-management/sm-ssh-otp)という機能を利用します。
利用の仕方については後述するとして、本節では設定を行います。
先ほど開いたものとは別に`vault`のターミナルを開きます。

```bash
$ export VAULT_ADDR="http://$(curl -s http://169.254.169.25
4/latest/meta-data/local-ipv4):8200"

$ vault secrets enable ssh
Success! Enabled the ssh secrets engine at: ssh/

$ vault write ssh/roles/otp_key_role key_type=otp \
>     default_user=ec2-user \
>     cidr_list=0.0.0.0/0
Success! Data written to: ssh/roles/otp_key_role
```

ブラウザから`vault`に8200ポートでアクセスし、Tokenに`root`を入力してログイン、`Secrets>ssh`にアクセスし、`otp_key_role`が表示されれば設定は成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/792e90ce-1913-bae5-0338-b7d5eb8b0d79.png)

#### `target`側の設定
Vaultにパスワードを照合する`Vault SSH Helper`を配置します。
`target`がインターネットにアクセスできない場合は、アクセス可能な端末で取得し転送して下さい。

```bash
$ wget https://releases.hashicorp.com/vault-ssh-helper/0.1.6/vault-ssh-helper_0.1.6_linux_amd64.zip
$ sudo unzip -q vault-ssh-helper_0.1.6_linux_amd64.zip -d /usr/local/bin
$ sudo chmod 0755 /usr/local/bin/vault-ssh-helper
$ sudo chown root:root /usr/local/bin/vault-ssh-helper
```

`Vault SSH Helper`の設定ファイルを作成します。
`vault_addr`のIPアドレスは`vault`のものを使用します。
`tls_skip_verify`でTLS証明書の検証はスキップしていますが、本格利用ではスキップしないことを推奨します。

```bash
$ sudo mkdir /etc/vault-ssh-helper.d/
$ sudo tee /etc/vault-ssh-helper.d/config.hcl <<EOF
vault_addr = "http://172.31.39.141:8200"
tls_skip_verify = false
ssh_mount_point = "ssh"
allowed_roles = "*"
EOF
```

Linuxの認証モジュールであるPAMの設定ファイルを修正し、`Vault SSH Helper`を利用した認証を定義します。


```bash
$ sudo cp /etc/pam.d/sshd /etc/pam.d/sshd.orig
$ sudo vi /etc/pam.d/sshd
## 以下をコメントアウト
#auth      required     pam_sepermit.so
#auth       substack     password-auth
#auth       include      postlogin
# Used with polkit to reauthorize users in remote sessions
#-auth      optional     pam_reauthorize.so prepare
## 以下を追記
auth requisite pam_exec.so quiet expose_authtok log=/var/log/vault-ssh.log /usr/local/bin/vault-ssh-helper -dev -config=/etc/vault-ssh-helper.d/config.hcl
auth optional pam_unix.so not_set_pass use_first_pass nodelay
```

sshdの設定ファイルを修正します。
パスワードではなくチャレンジレスポンス形式で、PAMを使って認証するようにします。
EC2を利用する場合には**公式チュートリアルから追加で設定が必要な項目**があることに注意して下さい。

```bash
$ sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
$ sudo vi /etc/ssh/sshd_config
## 以下の設定項目になるように修正
ChallengeResponseAuthentication yes
UsePAM yes
PasswordAuthentication no
## 以下はEC2の場合に設定とコメントアウトが必要
PubkeyAuthentication no
#AuthorizedKeysCommand /opt/aws/bin/eic_run_authorized_keys %u %f
#AuthorizedKeysCommandUser ec2-instance-connect
$ sudo systemctl restart sshd
```

検証コマンドを実行し、以下のような出力がされていれば設定は完了です。

```bash
$ vault-ssh-helper -verify-only -dev -config /etc/vault-ssh-helper.d/config.hcl
==> WARNING: Dev mode is enabled!
[INFO] using SSH mount point: ssh
[INFO] using namespace: us
[INFO] vault-ssh-helper verification successful!
```

### ランダムパスワードでのログイン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/9eaffa82-f1dd-2d89-1a9f-aa5ddb47d4fb.png)
（画像は https://learn.hashicorp.com/vault/secrets-management/sm-ssh-otp より引用）

ブラウザから`vault`に8200ポートでアクセスし、Tokenに`root`を入力してログインします。
ログイン後、 `Secrets>ssh`にアクセスし、`otp_key_role`の右にある三点リーダーをクリックします。
表示されたメニューから、 `Generate Credential`をクリックします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/0d98dbca-a6bf-5196-84e6-36f3ee106037.png)

`Username`にはログインしたいユーザを入力します。ここでは`ec2-user`にしています。
`IP Address`には`target`のIPアドレスを入力します。ここでは`172.31.32.67`にしています。
ユーザとIPアドレスを入力したら、`Generate`ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/6f784498-22d5-79f2-2fdc-1f423ee3c3c5.png)

パスワードが生成されますので控えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108008/5f3a4e9f-ace4-f41f-5291-3121e394d0a9.png)

`target`に対して先ほど控えたパスワードでsshログインするとログインに成功します。

```bash
$ ssh ec2-user@172.31.32.67
Password:
```

先ほどと同じパスワードでもう一度ログインしようとすると、失敗します。

```bash
$ ssh ec2-user@172.31.32.67
Password:
Password:
```

## まとめ
本記事で伝えたかったことは以下の2点です。

* 固定のsshログインパスワードや一覧表などの運用はセキュリティに懸念がある
* Hashicorp Vaultによりログインごとにパスワードを払い出すことができる