IBM Cloud: Client VPN for VPCにおけるAPI証明書管理 （Secrets Manager)

# はじめに
インターネット環境に接続可能なクライアント端末からIBM Cloud VPC環境に接続してクラウドを利用する際は、仮想的な暗号通信を実現するClient VPN for VPC機能を利用します。この機能を利用するにはVPN Serverの他、シークレットと呼ばれる機密情報（各種証明書等）を管理するSecrets Managerを利用します。
Secrets Managerの各種機能は、クラウドコンソールの他、API/CLI/SDKからも制御する事が可能であり、今までv1/v2が併用利用可能であったが、2023年10月31日のv1のサポート終了に伴い早期に[Secrets Manager API v2 へのマイグレーション](https://cloud.ibm.com/docs/secrets-manager?topic=secrets-manager-api-migration-v2&locale=ja)の移行を推奨しております。
本稿では、クライアントVPN環境で利用されるSecrets Manager上の証明書をこれらAPI v2機能を用いて、管理する方法についてご紹介します。

# 本稿で利用する環境
クライアント端末を除く他の全ての機能をIBM Cloud上で構築します。これらの各コンポーネントの設定に関しては、Qiita上に多々存在する為、参考になる記事をご紹介しながら環境を準備します。
+ VPN Server（VPN通信を受信するサーバ）
+ Secrets Manager（シークレット情報の保管管理）
+ Trusted Profile（本機能の利用により、VPC内のサーバから簡易な認証でAPI機能を利用する）
+ Linuxサーバ（API アクセス用として利用）
+ VPNクライアント端末（OpenVPNクライアントソフトを利用

# Client VPN for VPC環境準備
[こちらの記事](https://qiita.com/takason/items/3d04e4e31daae4def1d7)を参考にClient VPN for VPC環境を構築し、お手元の端末にインストールしたOpenVPNクライアントソフトからVPN接続を実施します。
事前に作成したサーバ証明書や鍵ファイルは、Secrets Managerにて登録管理し、VPN Serverと連携します。
同じく作成したクライアント証明書と鍵ファイルは、VPNクライアント端末に保存して証明書ログイン認証に利用します。

# Trusted Profile環境準備
[こちらの記事](https://qiita.com/testnin2/items/860a3b00a2029e0a2594)を参考にTrusted Profile環境を準備します。通常API/CLI利用する際は、ID/PasswordやAPI Key等を用いた認証のやり取りを行いますが、本機能はセキュリティを維持した上で、これらの認証処理を簡略化します。VPC内の特定のサーバ間及びIBM Kuvernetes ServiceやRed Hat Openshift on IBM CloudのService AccountからTrusted Profile経由でアクセスする事で、認証のやり取りをせずにIBM CloudのIAM Tokenを取得します。

# VPNサーバ証明書運用
VPNサーバ証明書は、Secrets Manager上にて管理します。サーバ証明書の有効期限の初期設定はセキュリティ的観点より、段々と短くなってきております（最近は１３ヶ月が多いと思います）。管理対象の証明書が多い場合運用負荷も高くなる為、Secrets Managerで証明書（有効期限）管理を実施し、有効期限が切れる前にAPI等による自動化で有効期限の更新を実施する事で運用負担権限を図る事ができます。
Secrets Managerでは、証明書の期限管理を下記のように実施可能であり、Slack等を利用して期限が近い事を通知する事も可能です。
![スクリーンショット 2023-09-21 18.16.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354579/95dbb90b-b2dd-efbb-e360-d11081b8af2a.png)
VPN Server側でもSecrets Manager側の証明書情報をもとに以下のように期限が近い事を表示します。
![スクリーンショット 2023-09-21 18.08.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354579/d6f1ba6d-52fb-4dca-628d-9bf7223360ea.png)
証明書は、更新期限が近くないと更新不可能な為、証明書作成の際にEasyrsaのvarファイルの設定を変更して有効期限を短くしております。
```:証明書有効期限変更
pki % cat vars | grep EASYRSA_CERT
#set_var EASYRSA_CERT_EXPIRE	825
set_var EASYRSA_CERT_EXPIRE	4
```
```:証明書有効期限が変更されている事を確認
issued % cat vpn-server.crt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            69:76:44:3b:1b:57:d3:1e:bb:96:7e:af:9c:26:da:7c
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=Easy-RSA CA
        Validity
            Not Before: Sep 21 07:23:43 2023 GMT
            Not After : Sep 25 07:23:43 2023 GMT
        Subject: CN=vpn-server
```
この状態でクライアント端末からOpenVPNで接続できる事を確認しておきます。

# サーバ証明書を更新
以下のコマンドでサーバ証明書を更新します。
```:証明書の更新
easyrsa3 % ./easyrsa renew vpn-server nopass
```

サーバ証明書が更新された事を確認します。Serial Numberや有効期限が変更になってます。
```:更新後の証明書
issued % cat vpn-server.crt 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            a6:52:09:c0:bb:e1:f4:f8:f2:8e:c8:6b:73:f9:7c:bb
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=Easy-RSA CA
        Validity
            Not Before: Sep 21 09:38:22 2023 GMT
            Not After : Sep 25 09:38:22 2023 GMT
        Subject: CN=vpn-server
```

pemファイルを作成します。
```:pem作成
easyrsa3 % openssl x509 -in ./pki/issued/vpn-server.crt -out ./pki/issued/vpn-server.pem
easyrsa3 % ls -l ./pki/issued
total 24
-rw-------  1 masaharu  staff  4572  9 21 18:38 vpn-server.crt
-rw-r--r--  1 masaharu  staff  1237  9 21 18:48 vpn-server.pem
```
このpemファイルをAPIアクセス用のLinuxサーバに転送し、そこからSecrets Manager上のサーバ証明書をTrusted Profileを活用してAPIで更新します。

# Secrets Manager API（v2）によるVPNサーバ証明書更新
Secrets ManagerのAPIについては、[こちらのIBM Cloud Doc](https://cloud.ibm.com/apidocs/secrets-manager/secrets-manager-v2#create-secret-version)をご参照ください。`Create a new secret version`メソッドの中のRequest Bodyとして、`ImportedCertificateVersionPrototype`を利用します。
![スクリーンショット 2023-09-21 20.00.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354579/3b856709-20b8-bbbf-4d5a-76f23c072ca5.png)

以下のコマンドでSecrets Managerにサーバ証明書をアップロードします。
```:証明書のUpload
curl -X POST --location --header "Authorization: Bearer {iam_token}" --header "Accept: application/json" --header "Content-Type: application/json" --data '{"certificate": "{pem}"' "{base_url}/api/v2/secrets/{secret_id}/versions"
```
`{base_url` `{secret_id}`は、各`Secrets`の`Details`の中の`Actions...`で`Show snippet`を選択すると各種情報を確認する事ができます。![スクリーンショット 2023-09-21 20.22.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354579/d549a63b-ef33-669d-676a-8121b94a311a.png)`{iam_token}`に関しては、前述した[Client VPN for VPC環境準備](https://qiita.com/drafts/8bc43fe5b149a3d72e1c/edit#client-vpn-for-vpc%E7%92%B0%E5%A2%83%E6%BA%96%E5%82%99)を参照ください。
`{pem}`に関しては、先ほど証明書からpem形式に変換しましたが、このままでは使えないので改行`/n`が入った一行表示に以下のコマンドで変換します。
```:pem表現変換
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' vpn-server.pem
```
:::note warn
上記`awk`コマンドで変換すると最後の`-----END CERTIFICATE-----\n`に`\n`が残ってしまいます。削除して、POSTしてください。
:::

私の環境では以下のCurlコマンドでリクエストするとこのような結果になります。（iam_tokenはセキュリティ上そのままです）
```:Curl結果
[root@sm-bastion ~]# curl -X POST --location --header "Authorization: Bearer $iam_token" --header "Accept: application/json" --header "Content-Type: application/json" --data '{"certificate": "-----BEGIN CERTIFICATE-----\nMIIDZTCCAk2gAwIBAgIRAKZSCcC74fT48o7Ia3P5fLswDQYJKoZIhvcNAQELBQAw\nFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMjMwOTIxMDkzODIyWhcNMjMwOTI1\nMDkzODIyWjAVMRMwEQYDVQQDDAp2cG4tc2VydmVyMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAugIbHB1ZNcMOy+bmX+BWL1F+b/nRH8NfKxltuf8EwNyL\nWLGDE7bP7AYYpNZ8tcxCneobiDBgMAGkNVi9iDZ5ZSN2nNe+GnuUTsxpRP7DhRj1\nor2phqHH097jOQHLhqKy8BHGu6JXlSr21NBoC8xje5iFTvPFCDHwQRXHb22egeFc\nef86L7H3+IExg20fVgFmYe7Pao7mLVgjkn/L+so9TSBWksuDyaoTrBH25gsSTFvh\nrSDWFgQ2xDxwUSNTQ+ty6nKX7XvQVI2EX4SzmWGMlVHFuvqlWFT+4tIJYEYNvWxS\nD+TR9eEUSd+nSjdTh5yhIsW5FRfl0skTzBhbxQGILwIDAQABo4GuMIGrMAkGA1Ud\nEwQCMAAwHQYDVR0OBBYEFHno0h7ps25FiXWX2UgLDjbqmafUMEYGA1UdIwQ/MD2A\nFDz/1CKn4b4Cz+dviYLz4xoK2zw1oRqkGDAWMRQwEgYDVQQDDAtFYXN5LVJTQSBD\nQYIJAM5q2IrTnek9MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDAV\nBgNVHREEDjAMggp2cG4tc2VydmVyMA0GCSqGSIb3DQEBCwUAA4IBAQB75wRy7ino\nXKN7UblEhN8o5DvGgTd35IQhTr1i2LZ9troZQWK3KrsNEd4InrzJKqIHRpMKDsTz\nsqwIyS/IJcPODR/iU7mHl7+J+4n2oHGBldKcxe7HCjS+EkEoM4X/Wfgl180iZ2oQ\nvcXkdUUCOVv4UwWW39PM5HArDhyvd1ox+90fPtTDOGFYBU/d6E9ljjdUfWrtq7US\nRoEki5pmFRB84WFGTt2mKBAHOXJ9vt3HOJXGfDYRNn9nCD3FXL4VjJynph4Cuxhe\nMZ8EoJMDC73o0Ed8cb3f38nlTklz1LiNaal7/d0wrDEEYRiOmum2S2M2m5jpLeEs\n2Y35ouMuNx5/\n-----END CERTIFICATE-----"}' "https://3fb12463-4d6b-4029-8335-727bf02d0c8b.jp-tok.secrets-manager.appdomain.cloud/api/v2/secrets/5b9681aa-95cf-d24e-6634-6164d7a0f09b/versions" |jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  3156    0  1881  100  1275   1293    877  0:00:01  0:00:01 --:--:--  1294
{
  "alias": "current",
  "certificate": "-----BEGIN CERTIFICATE-----\nMIIDZTCCAk2gAwIBAgIRAKZSCcC74fT48o7Ia3P5fLswDQYJKoZIhvcNAQELBQAw\nFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMjMwOTIxMDkzODIyWhcNMjMwOTI1\nMDkzODIyWjAVMRMwEQYDVQQDDAp2cG4tc2VydmVyMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAugIbHB1ZNcMOy+bmX+BWL1F+b/nRH8NfKxltuf8EwNyL\nWLGDE7bP7AYYpNZ8tcxCneobiDBgMAGkNVi9iDZ5ZSN2nNe+GnuUTsxpRP7DhRj1\nor2phqHH097jOQHLhqKy8BHGu6JXlSr21NBoC8xje5iFTvPFCDHwQRXHb22egeFc\nef86L7H3+IExg20fVgFmYe7Pao7mLVgjkn/L+so9TSBWksuDyaoTrBH25gsSTFvh\nrSDWFgQ2xDxwUSNTQ+ty6nKX7XvQVI2EX4SzmWGMlVHFuvqlWFT+4tIJYEYNvWxS\nD+TR9eEUSd+nSjdTh5yhIsW5FRfl0skTzBhbxQGILwIDAQABo4GuMIGrMAkGA1Ud\nEwQCMAAwHQYDVR0OBBYEFHno0h7ps25FiXWX2UgLDjbqmafUMEYGA1UdIwQ/MD2A\nFDz/1CKn4b4Cz+dviYLz4xoK2zw1oRqkGDAWMRQwEgYDVQQDDAtFYXN5LVJTQSBD\nQYIJAM5q2IrTnek9MBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDAV\nBgNVHREEDjAMggp2cG4tc2VydmVyMA0GCSqGSIb3DQEBCwUAA4IBAQB75wRy7ino\nXKN7UblEhN8o5DvGgTd35IQhTr1i2LZ9troZQWK3KrsNEd4InrzJKqIHRpMKDsTz\nsqwIyS/IJcPODR/iU7mHl7+J+4n2oHGBldKcxe7HCjS+EkEoM4X/Wfgl180iZ2oQ\nvcXkdUUCOVv4UwWW39PM5HArDhyvd1ox+90fPtTDOGFYBU/d6E9ljjdUfWrtq7US\nRoEki5pmFRB84WFGTt2mKBAHOXJ9vt3HOJXGfDYRNn9nCD3FXL4VjJynph4Cuxhe\nMZ8EoJMDC73o0Ed8cb3f38nlTklz1LiNaal7/d0wrDEEYRiOmum2S2M2m5jpLeEs\n2Y35ouMuNx5/\n-----END CERTIFICATE-----",
  "created_at": "2023-09-21T11:54:03Z",
  "created_by": "iam-Profile-ff6fc304-51fc-4cd0-a8d2-8901e5d8e945",
  "expiration_date": "2023-09-25T09:38:22Z",
  "id": "6f63a501-0322-3224-5729-2b53d6337aed",
  "intermediate": "",
  "payload_available": true,
  "private_key": "",
  "secret_group_id": "2c553c09-194d-309f-ad8e-814edd1fa785",
  "secret_id": "5b9681aa-95cf-d24e-6634-6164d7a0f09b",
  "secret_name": "vpn-secret",
  "secret_type": "imported_cert",
  "serial_number": "a6:52:09:c0:bb:e1:f4:f8:f2:8e:c8:6b:73:f9:7c:bb",
  "validity": {
    "not_after": "2023-09-25T09:38:22Z",
    "not_before": "2023-09-21T09:38:22Z"
  },
  "version_custom_metadata": {}
}
```
`serial_number` `validity`の有効期限が先ほど更新したサーバ証明書に置き換わっている事が確認できました。また、サーバ証明書更新後も更新前と遜色なくVPNクライアント端末から接続する事ができました。

# まとめ
Secrets Manager上の証明書をAPIを利用して更新する方法をご紹介しました。ご紹介したAPIを自動化環境に組み込み運用負荷を軽減して頂ければと思います。ご紹介した[IBM Cloud Doc](https://cloud.ibm.com/apidocs/secrets-manager/secrets-manager-v2#create-secret-version)には、他のAPIも紹介されてますので、ご自身の環境にあったものをご利用頂ければと思います。
当初、VPNサーバ関連の一連のAPI運用について記載していたら、原稿が長くなってしまいました。分割した別投稿の[Client VPN for VPCにおけるAPI CRL管理（VPN Server)](https://qiita.com/esakima/items/b3d1acbb6be3a0d6412b)編もご参照頂ければ幸いです。

# 参考サイト
+ [IBM Cloud：Client to Site型VPNでVPC環境に接続する (Secrets Manager)](https://qiita.com/takason/items/3d04e4e31daae4def1d7)
+ [IBM Cloud:：Trusted Profileを使ったVSI for VPCからの安全なAPI/CLI呼び出し方法](https://qiita.com/testnin2/items/860a3b00a2029e0a2594)
+ [IBM Cloud Doc:Secrets Manager API](https://cloud.ibm.com/apidocs/secrets-manager/secrets-manager-v2#create-secret-version)