SSH-RSA廃止後のTerraform Post SSH-RSAのAzure DevOpsパイプラインの更新

マイクロソフトが最近Azure DevOpsのAzure Reposへの接続におけるSSH-RSAの廃止を発表し、安全な暗号化に向けた重要な変更点を表明しました。この変更は、2022.3以降のAzure DevOps Server版で、2024年末頃に実施される予定で、技術全般にわたるセキュリティプロトコルの強化というトレンドに沿ったものです。しかし、この移行は、特にTerraformモジュール管理のための集中型アプローチを活用している組織にとって、ある課題をもたらしています。こらのモジュールは通常、専用のリポジトリに保存され、他のリポジトリで SSH 経由で参照され、サービスやアプリケーションのインフラをプロビジョニングするために不可欠です。SSH-RSAが廃止されたことで、これらのプラクティスを適応させる必要があります。

# 集中モジュール戦略

Terraformに集中モジュール戦略を採用すると、以下のような大きな利点があります：
- 信頼できる唯一の情報源：Terraformのモジュールを1つのリポジトリに集中させることで、一貫性を確保し、重複を最小限に抑え、インフラストラクチャのコード管理を簡素化します。モジュールの更新やバグ修正は、依存する全てのサービスに簡単に伝搬させ、各サービスのリポジトリを個別に更新する必要がなくなります。
- アップデートの容易さ： モジュールの機能強化、バグ修正、セキュリティパッチは、依存するTerraform設定のモジュール参照を更新するだけで、シームレスに実装し展開できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/cad0038e-3122-32a5-825e-cb748b5c4df7.png)

# Terraformモジュール
一般的な方法として、Terraformのモジュール設定をGitHubやBitbucketのようなバージョン管理リポジトリに保存します。TerraformはGitを使ってモジュールにアクセスし、HTTPSやSSH経由でコンテンツをクローンします。プライベートリポジトリの場合、このプロセスには適切な認証が必要です：
- HTTPS：URL内にパーソナル・アクセストークン（PAT）を埋め込む必要があり、一般にアクセス可能なコードベースや共有コードベースで公開された場合、セキュリティ上のリスクが生じます。
```
module "key_vault_key" {
   source = "git::https://<PAT>@dev.azure.com/GTRekter/Training/_git/Terraform-Modules//keyvault-key?ref=main"
}
```
- SSH: 非インタラクティブなため、自動化システムに適しており、プライベート・リポジトリへのシームレスなアクセスを可能にします。
```
module "key_vault_key" {
    source = "git::git@ssh.dev.azure.com:v3/GTRekter/Training/_git/Terraform-Modules//keyvault-key?ref=main"
}
```
'terraform init'の実施後、Terraformは参照した外部リポジトリを`.terraform/modules`ディレクトリにクローンします。このローカルコピーは'plan'や'apply'の段階で利用され、Terraformの設定が再現可能であることを保証します。
```
.
├───.terraform
│   ├───modules
│   │   └───key_vault_key
│   │       ├───.pipelines
│   │       ├───aks-cluster
│   │       ├───aks-node-pool
│   │       └───keyvault-key
│   └───providers
│       └───registry.terraform.io
├───pipelines
└───main.tf
```

# SSHとマイクロソフトの最新アップデートの問題

SSH-RSAの非推奨化に伴い、開発者は一晩で、リポジトリへの予期せぬアクセスの問題に直面するようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/29336aa5-61f3-5c02-d39c-880bbc8e6379.png)

この問題は、エージェントがSSH-RSA鍵に頼らず、HTTPS上でクローン処理を実行するために発生するしています。ここで使われている認証メカニズムはOAuthで、`http.extraheader`としてパイプラインの設定に埋め込まれています。しかし、TerraformモジュールのソースコードをダウンロードするメカニズムとしてSSHを指定すると、処理が失敗します。

# SSH-RSAからRSA-SHA2-512への移行

新しい暗号方式に移行するには、まずクライアントの公開鍵/秘密鍵ペアを生成する必要があります。これを行うには、`ssh-keygen`コマンドを実行し、`-t`オプションを`rsa-sha2-512`に設定して、生成する鍵のタイプを指定します。完了すると、`*.pub`ファイル（公開鍵）と、拡張子のないファイル（秘密鍵）が出来上がります。

```
$ ssh-keygen -t rsa-sha2-512
Generating public/private rsa-sha2-512 key pair.
Enter file in which to save the key (~/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in ~\.ssh\id_rsa
Your public key has been saved in ~\.ssh\id_rsa.pub
The key fingerprint is:
SHA256:zWr3tUHtDLjICoxb6+GjacGmHpYgdVcV9SGVEu2htos gtrekter@Sample
The key's randomart image is:
+---[RSA 3072]----+
|        ..oo++o. |
|       .    .o+. |
|  . . .      +.. |
| . . .   o  o... |
|o  .    S o...o .|
|.. .+o   o ..o + |
|  +o..= o +...o o|
| ....=.= oE... o |
| ...+o+..   . .  |
+----[SHA256]-----+
```

エージェントが Terraform モジュールをダウンロードするために使用する新しい鍵に関連付けられたサービスアカウントを設定することで、この鍵を Azure DevOps と統合します。設定方法は以下の通りです：
·	Azure DevOpsのOrganization Settingsに移動する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/6937efdc-597e-a154-76e0-ecf45284f0b4.png)

·	ユーザーでユーザーの追加を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/4c5a82a6-651e-a824-1cfa-f142bcc8e256.png)

·	ユーザーの電子メールを入力し、プロジェクトに割り当て、必要な権限を得るためにプロジェクトコントリビューターグループに追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/63b2f8ec-9e26-5ed9-7191-d69d0e3eb767.png)

** 注: セキュリティプロトコルの関係上、SSH 公開キーは Azure DevOps UI 経由でしか生成できないため、新しいアカウントにはメールボックスが必要です。** 

- アカウントが設定されたら、新しいユーザー認証情報でログインし、アバターの横にあるアイコンをクリックしてSSH公開キーを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/1a942c71-7bcb-2650-cc47-49413f055e92.png)

- 新しいキーをクリックし、公開鍵を公開キーデータフィールドに貼り付け、追加をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/c95f2e5e-ecdb-92d5-c0ba-1b34f34014c2.png)

Azure認証用のRSA-SHA2-512 SSHキーの準備ができたら、SSH設定に欠かせないもう1つの重要なコンポーネント、`known_hosts`ファイルを設定する必要があります。このファイルは、SSHで接続するサーバーの公開キーを記録し、各サーバーの公開キーとそのURLをペアにして、接続先のサーバーの身元を確認することでSSH接続の整合性を確保し、中間者攻撃（MitM）から保護します。このファイルは以下のコマンドで生成できます：

```
$ ssh-keyscan ssh.dev.azure.com >> ~/.ssh/known_hosts
# ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10
# ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10
# ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10
# ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10
# ssh.dev.azure.com:22 SSH-2.0-SSHBlackbox.10
```

## (オプション）キーをAzure Key Vaultに追加

Azureを利用する場合、SSHキーのような機密データをAzure Key Vaultで保護するのがスマートな方法です。秘密管理を一元化するだけでなく、プロジェクトのセキュリティ体制を強化することができます。プライベートSSHキーと`known_hosts`ファイルを保存するKeyVaultを作成または選択したら、それぞれに新しいシークレットを追加します。このガイドでは、SSH秘密鍵には`ado-ssh-key`、known_hostsファイルには`ado-ssh-host-key`と名付けます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/ea2ca408-e744-fda4-7de4-e2dd304ccdeb.png)

**注意: SSH 鍵は複数行に渡るため、鍵が破損する可能性のある書式の問題を防ぐために、新しい行を必ず \n で置き換えてください。**

Azure DevOpsエージェントが実行時にこれらのシークレットにアクセスできるようにするには、Azureへのサービス接続が必要です。そのために：

- Azure DevOpsプロジェクトで、プロジェクト設定に移動し、サービスコネクションを選択し、新しいサービスコネクションをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/28b3a2ac-be6f-7c54-8dbe-d6d298a2591f.png)

- Azure Resource Managerを選択し、プロンプトに従って購読契約を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/86e7bcb6-83f0-3237-316b-531058597048.png)

Azure DevOpsは自動的にAzureのサービスプリンシパルを作成し、構成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/89feccbc-a560-6d2c-123e-8db89eac7b63.png)

Key Vaultとサービス接続の準備ができたので、最後のステップとして、`terraform init`のようなタスクを開始する前にSSH鍵を取得するようにパイプラインを更新します。パイプラインの適切な位置に以下のタスクを挿入します：

```
- task: AzureKeyVault@1
  displayName: 'Get Azure DevOps SSH key and Host Key from KeyVault'
  inputs:
    azureSubscription: VSES Training
    KeyVaultName: kv-training-ssh-dev-krc
    SecretsFilter: 'ado-ssh-key, ado-ssh-host-key'
```

# エージェント SSHキーのセットアップ

RSA-SHA2-512 SSHを使用して生成された新しいSSHキーと、Azure DevOpsユーザーにリンクされた公開キーを使用して、これらのコンポーネントをパイプラインに統合する時が来ました。Azure Key Vaultを使用しないことを選択した場合、Azure DevOps変数グループは、SSHキーとknown_hostsファイルを含む秘密の値を保存するための代替手段を提供します。必要なときにアクセスできるように、パイプラインでこれらを参照することを忘れないでください。

`terraform init`のようなタスクを実行する前に、エージェントのSSH環境を新しいキーで設定する必要があります：
- SSHディレクトリを作成し、正しい権限で`.ssh`ディレクトリを設定します：
```
echo "Create SSH directory and set permissions"
mkdir -p ~/.ssh/
chmod 700 ~/.ssh/
```
- プライベートキーファイルを生成し、名前（例：`ado_id_rsa`）を付けて作成します。これにより、他のサービスのキーとの競合を回避します。
```
echo "Set SSH keys"
echo "$(ado-ssh-key)" > ~/.ssh/ado_id_rsa
chmod 600 ~/.ssh/ado_id_rsa
```
- Azure KeyVaultまたはAzure DevOps変数グループからのknown_hostsコンテンツを追加して、エージェントが信頼されたホストを認識するようにします。
```
echo "Set known_hosts entries"
echo "$(ado-ssh-host-key)" >> ~/.ssh/known_hosts
```
- SSH設定ファイルを作成し、ターゲットが特定のホストの場合、特定のキー（ここではado_id_rsa）を使用するようSSHに指示する。
```
echo "Create the SSH config file"
echo -e "Host ssh.dev.azure.com\n  HostName ssh.dev.azure.com\n  User git\n  IdentityFile ~/.ssh/ado_id_rsa\n  IdentitiesOnly yes\n\nHost bitbucket.org\n  HostName bitbucket.org\n  User git\n  IdentityFile ~/.ssh/bitbucket_id_rsa\n  IdentitiesOnly yes" > ~/.ssh/config
chmod 600 ~/.ssh/config
```
- SSHセットアップが正常に機能していることを確認します。
```
ssh -Tv git@ssh.dev.azure.com || true
echo "SSH connection test completed"
```

最後のタスクは次のようになります：
```
- task: CmdLine@2
  displayName: Setup SSH key
  inputs:
    script: |
      echo "Create SSH directory and set permissions"
      mkdir -p ~/.ssh/
      chmod 700 ~/.ssh/

      echo "Generate and set SSH keys"
      echo "$(ado-ssh-key)" > ~/.ssh/ado_id_rsa
      chmod 600 ~/.ssh/ado_id_rsa

      echo "Update known_hosts entries"
      echo "$(ado-ssh-host-key)" >> ~/.ssh/known_hosts

      echo "Create the SSH config file"
      echo -e "Host ssh.dev.azure.com\n  HostName ssh.dev.azure.com\n  User git\n  IdentityFile ~/.ssh/ado_id_rsa\n  IdentitiesOnly yes" > ~/.ssh/config
      chmod 600 ~/.ssh/config
    
      ssh -Tv git@ssh.dev.azure.com || true
      echo "SSH connection test completed"
```

これらの設定を行うことで、Azure DevOps エージェントは更新された SSH 鍵を使用して Terraform インフラストラクチャに安全に接続し、デプロイすることができるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477222/49fd19de-b69d-8b7b-527a-99bbb9909dc0.png)

# 参照
- Microsoft SSH-RSA deprecation announcement:
[https://devblogs.microsoft.com/devops/ssh-rsa-deprecation/](https://devblogs.microsoft.com/devops/ssh-rsa-deprecation/)