OneGateから発行した証明書を使ってAWS ALBをセキュアにする-その3「公開したCRLをALBに自動反映」

アイキャッチ
目次

前回の記事はこちら

前回はALBでmTLS認証する方法を紹介しましたが、クライアント証明書の失効リストを反映させる方法が少し手間でしたので、手順を簡略化、例えばCRL取得、変換、AWSにアップロード、トラストストアに反映が自動化する方法を考えました。

手間がかかりそうなところを自動化してみます。処理する内容は次のとおりです。

  • OneGateからCRLをダウンロードする
  • CRLをDERからPEMに変換する
  • S3バケットにPEMに変換したCRLをアップロードする
  • トラストストアに新しいCRLを反映させる
  • トラストストアから前回反映させたCRLを削除する

このうち、CRLダウンロードはcurlコマンド、CRLのPEM変換はOpenSSLを使うのでAmazon Linux 2023上で実現できます。そのあとのAWSの操作はAWS CLIを使えばできそうです。

そこで、作業用のAmazon Linux 2023を構築して実現してみます。構成は下記のとおりです。AL2023を使ってCRL取得・変換・S3アップロード・トラストストア反映

作業用AL2023の作り方は省略しますが、Webサーバーと同じようにセキュリティグループを作成してインバウンドで社内からのみSSHアクセスを許可するように設定して、NetAttest -1a-subかNetAttest-1c-subのサブネット上に構築してください。

AWS CLIを使えるようにする

AL2023ではAWS CLIがインストール済みですが、これを使えるようにするにはIAMでAWS CLI用のアカウント作成とアクセスキーの生成、並びにAL2023にアクセスキーの反映が必要です。またこのユーザーに対して操作範囲を作成したロードバランサー及びS3バケットに絞るようにポリシーを設定します。

ポリシーの作成

ポリシーで許可対象はARN formartで記載しますので、まずはトラストストアとS3バケットのARNを調べます。

トラストストアのARNはEC2ダッシュボードにて「ロードバランシング」→「トラストストア」にて作成した「NetAttest-ts」を開いて「トラストストアARN」の値を控えます。

S3バケットのARNはAmazon S3の「汎用バケット」で作成した「netattest-s3」を開き、「プロパティ」タブの「バケットの概要」に記載されています。

トラストストア及びS3バケットに対して必要な操作は次の通りとなります。

サービス許可するアクション
elasticloadbalancingRemoveTrustStoreRevocations
AddTrustStoreRevocations
s3PutObject

この情報を元にロールを作成します。

  1. IAMの「アクセス管理」→「ポリシー」を開き「ポリシーの作成」をクリックします。
  2. ポリシーエディターを「ビジュアル」にしてサービスから「elb v2」を選択します。

  3. アクション許可欄で「書き込み」で「AddTrsutRevocations」と「RemoveTrustRevocations」にチェックを入れ、リソース欄で「ARNを追加」をクリックします。

  4. リソースARN欄に控えていたトラストストアのARNを入力すると、リソースのリージョン、Resource trust store name、Resource trust storeの値が自動で入りますので、「ARNの追加」をクリックします。

  5. 「許可をさらに追加」をクリックします。

  6. サービス欄で「S3」を選択します。

  7. 書き込みで「PutObject」にチェックをいれて、リソース欄で「ARNを追加」をクリックします。

  8. Resource bucket nameに「netattest-s3」を入力して、Resouce object nameの「任意のobject name」にチェックをいれて、「ARNを追加」をクリックします。

  9. 「次へ」をクリックします。

  10. ポリシー名に「NetAttest-Policy」を入力して「ポリシーを作成」をクリックします。

AWS CLIユーザーの作成

  1. IAMにて「アクセス管理」→「ユーザー」を開き「ユーザーの作成」をクリックします。
  2. ユーザー名を「NetAttest-crlUploader」にして「次へ」をクリックします。
  3. 「許可のオプション」にて「ポリシーを直接アタッチする」にチェックを入れて、許可ポリシーで「NetAttest-Policy」を選択して「次へ」をクリックします。

  4. 「ユーザーの作成」をクリックします。
  5. 作成したNetAttest-crlUploaderを開きます。
  6. 「セキュリティ認証情報」タブを開き「アクセスキー」項目の「アクセスキーを作成」をクリックします。
  7. 「コマンドラインインターフェイス(CLI)」にチェックを入れ「上記のレコメンデーションを理解し、アクセスキーを作成します。」にチェックを入れて「次へ」をクリックします。

  8. 説明タグを適当に入力して「アクセスキーを作成」をクリックします。

  9. アクセスキーとシークレットアクセスキーを控えて「完了」をクリックします。
    注)「完了」をクリックするとシークレットキーは二度と表示されませんのでご注意ください。控えそこねた場合には、再度アクセスキーを作成し直してください。

作業マシンでAWS CLIを使えるように設定

作業用マシンのAWS CLIをNetAttest-crlUploaderユーザーでAWSを操作するように設定します。
現在の作業マシンのAWS CLIの状態は次の通りです。

$ aws --version
aws-cli/2.23.11 Python/3.9.21 Linux/6.1.130-139.222.amzn2023.x86_64 source/x86_64.amzn.2023
$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key                <not set>             None    None
secret_key                <not set>             None    None
    region           ap-northeast-1             imds

「aws configure」を実行すると対話形式でAWS Secret Access Keyを求められますので、NetAttest-crlUploaderユーザーのアクセスキーのシークレット値を入力します。

$ aws configure
AWS Access Key ID [None]: <アクセスキー名>
AWS Secret Access Key [None]: <アクセスキーのシークレット>
Default region name [None]:
Default output format [None]:

$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key     ****************GMSO shared-credentials-file
secret_key     ****************BitW shared-credentials-file
    region           ap-northeast-1             imds

これで作業マシンでAWS CLIを実行するとAWSをNetAttest-crlUploaderユーザーとして操作されます。

NetAttest-crlUploaderユーザーでS3バケットを操作できるようにする

既にIAMのポリシー設定でS3バケットを操作できる様になっていますが、S3バケット側でもNetAttest-crlUploaderユーザーでの操作を許可するポリシーを作成する必要があります。

  1. S3のポリシーの設定にはユーザーのARNがが必要になりますので、IAMの「アクセス管理」→「ユーザー」からNetAttest-crlUploaderを開いて「概要」欄の「ARN」の値を控えます。
  2. Amaozn S3の「汎用バケット」の「netattest-s3」を開き、「プロパティ」タブの「Amazon リソースネーム(ARN)」の値も控えます。
  3. 「アクセス許可」タブのバケットポリシー項目の「編集」をクリックします。

  4. 「ポリシージェネレーター」をクリックします。

  5. 下記を設定して「Add Statement」をクリックします。

    項目
    Select Type of PolicyS3 Bucket Policy
    EffectAllow
    PrincipalNetAttest-crlUploaderユーザーのARN値
    ActionGetObject
    ObjectOwnerOverrideToBucketOwner
    PubObject
    Amazon Resource Name (ARN)「netattest-s3のARN値」/*
  6. 「Generate Policy」をクリックします。

  7. 生成されたポリシーをコピーして「Closed」をクリックします。

  8. 「バケットポリシーを編集」に戻って、AWS Policy Genenaterで生成した値で上書きして「変更を保存」をクリックします。

コマンドでCRL取得からトラストストアへの反映

AWS CLI及びS3バケットへのアクセス権の付与ができましたので、実際にコマンドを使ってCRL取得からトラストストアへの反映までを行っていきます。

curlコマンドを使ってOneGateからCRLを取得する

AL2023にて「curl -O <CRLのURL>」を実行します。

$ curl -O http://<OneGateのテナントURL>/certs/<数字>/certs.crl
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 16792  100 16792    0     0   372k      0 --:--:-- --:--:-- --:--:--  381k
$ ls
certs.crl

CRLをDER形式からPEMに変換する

既に紹介済みですが、OpenSSLコマンドをで変換します。

$ openssl crl -inform DER -outform PEM -in certs.crl -out certs_pem.crl
$ ls
certs.crl  certs_pem.crl

S3バケットにCRLをアップロードする

AWS CLIを使ってS3にCRLをアップロードします。アップロードするにはS3 URIで指定します。
S3 URIはAmazon S3にて「汎用バケット」の「netattest-s3」の「certs_pem.crl」を開いてS3 URLの値を控えます。

S3にアップロードするには「aws s3 cp <アップロードするファイル> <アップロード先URL>」を実行します。

$ aws s3 cp certs_pem.crl s3://netattest-s3/certs_pem.crl
upload: ./certs_pem.crl to s3://netattest-s3/certs_pem.crl

トラストストアにCRLを反映させる

トラストストアにCRLを反映するにはトラストストアのARN、CRLが保存されているS3バケット名、S3バケットにアップロードしたcerts_pem.crlのキー名が必要です。

  • トラストストアのARNの調べ方
    EC2の「ロードバランシング」→「トラストストア」の「NetAttest-ts」を開いて「トラストストアARN」の値を控えます。
  • S3バケット名
    CRLがアップロードされているS3のバケット名です。今回は「netattest-s3」となります。
  • キー名
    Amazon S3の「汎用バケット」「netattest-s3」でアップロードしたcerts_pem.crlを開いて、「オブジェクトの概要」のキーの値を控えます。

この情報を元に「aws elbv2 add-trust-store-revocations --trust-store-arn <トラストストアのARN> --revocation-contents S3Bucket=<S3バケット名>,S3Key=<キー名>,RevocationType=CRL」を実行します。

$ aws elbv2 add-trust-store-revocations --trust-store-arn arn:aws:elasticloadbalancing:ap-northeast-xxxxxxxx:truststore/NetAttest-ts/xxxxxxxxx --revocation-contents S3Bucket=netattest-s3,S3Key=certs_pem.crl,RevocationType=CRL
{
    "TrustStoreRevocations": [
        {
      "TrustStoreArn": "arn:aws:elasticloadbalancing:ap-northeast-xxxxxxxx:truststore/NetAttest-ts/xxxxxxxxx",
            "RevocationId": 6,
            "RevocationType": "CRL",
            "NumberOfRevokedEntries": 333
        }
    ]
}

これでトラストストアにCRLが反映しました。

ただGUIでトラストストアを確認してみると前のCRLの情報も残っているので、これを削除します。

前のCRL情報を削除する

aws elbv2 add-trust-store-revocationsコマンドのレスポンスに「"RevocationId" :  <ID番号>」が含まれており、トラストスアに反映したCRLのID番号がわかります。
この番号の1つ前が先に反映させたCRLなので、を「aws elbv2 remove-trust-store-revocations --trust-store-arn <トラストストアのARN>  --revocation-ids <ID番号から1を引いた数字>」を実行します。

$ aws elbv2 remove-trust-store-revocations --trust-store-arn arn:aws:elasticloadbalancing:ap-northeast-xxxxxxxx:truststore/NetAttest-ts/xxxxxxxxx?--revocation-ids 5

CRLの取得からAWSへの反映をシェルスクリプト化

いままでの流れをシェルスクリプトにしてみました。トラストストアARNなどが長いので一部を変数化してありますので、変数を自分の環境の値に置き換えてご利用ください。

#!/bin/bash

# 【変数】ご自身の環境に合わせて書き換えてご利用ください

# OneGateのCRL配布ポイントのURL
cdp="http://<SOGのテナントURL>/certs/<数字>/certs.crl"

# CRLファイルをアップロードするS3バケットのURL
S3URL="s3://netattest-s3"

# CRLがアップロードされているS3バケット名
Bucket="netattest-s3"

# S3バケットのCRLファイルのキー名
CRLKey="certs_pem.crl"

# トラストストアのARN
TrustStoreARN="arn:aws:elasticloadbalancing:ap-northeast-xxxxxxxx:truststore/NetAttest-ts/xxxxxxx"

# 【シェル本編】
# 前回のシェルスクリプトを実行したときのローカルから消しそこねたたCRLファイル本体があれば削除
if [ -f ./certs.crl ]; then
        rm ./certs.crl
fi
if [ -f ./certs_pem.crl ]; then
        rm ./certs_pem.crl
fi

# OneGateからCRLファイルをダウンロード
curl -O $cdp

# ダウンロードしたCRLをDER形式からPEM形式に変換
openssl crl -inform DER -outform PEM -in certs.crl -out certs_pem.crl

# S3バゲットにアップロード
aws s3 cp certs_pem.crl $S3URL

# EC2トラストストアに反映、コマンドの結果ををcrl_no.tmpに格納
aws elbv2 add-trust-store-revocations --trust-store-arn $TrustStoreARN --revocation-contents S3Bucket=$Bucket,S3Key=$CRLKey,RevocationType=CRL > crl_no.tmp

# JSONから値を抜き出すjqコマンドを使ってcrl_no.tmpからRevocationIdの値をCRL_No変数に代入
CRL_No=$(cat crl_no.tmp | jq '.TrustStoreRevocations[].RevocationId')

# CRL_Noの値から1を引いて、前回反映させた失効情報をトラストストアから削除
aws elbv2 remove-trust-store-revocations --trust-store-arn $TrustStoreARN --revocation-ids $((--CRL_No))

# 生成されたファイルたちを削除
rm -f ./certs.crl
rm -f ./certs_pem.crl
rm -f ./crl_no.tmp

こちらの内容を記載したファイルをcrl_upload.shとして保存して、実行権限をつけて実行してみます。

$ chmod 755 crl_upload.sh
$ ./crl_upload.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 16792  100 16792    0     0   613k      0 --:--:-- --:--:-- --:--:--  630k
upload: ./certs_pem.crl to s3://netattest-s3/certs_pem.crl

エラーが表示されなければスクリプトの完成です。

こちらを利用すれば、クライアント証明書がインポートした端末が紛失等にあってアクセスをブロックしたい場合に下記の流れで実施することで、証明書失効反映が比較的簡単になります。

  1. 利用者より管理者宛に端末紛失の連絡が入る
  2. 管理者はOneGateの証明書管理から該当端末の証明書を失効させてCRLを更新
  3. crl_upload.shを実行

crl_upload.shの定期自動実行

crl_upload.shを定期的に自動実行できれば、端末紛失時などはOneGateで失効・CRL更新だけ行えばAWSに反映させることも可能です。

Linuxで定期実行するのはcronを使うのが一般的でしたが、Amazon Linux2023ではsystemdのtimerを使用して定期実行するようになりましたので、systemdで10分おきにcrl_upload.shを実行する方法を紹介します。

/etc/systemd/system/crlupload.serviceファイルの作成

serviceファイルは実行内容を記載ファイルになります。今回は/home/ec2-user/crl_upload.shファイルを実行する内容にします。

$ sudo vi /etc/systemd/system/crlupload.service

[Unit]
Description=execute crl_upload.sh
[Service]
ExecStart=/usr/bin/sh /home/ec2-user/crl_upload.sh
User=ec2-user
WorkingDirectory=/home/ec2-user
  • Unit
    • Descriptionはこのサービスの説明です。crl_upload.shを実行する物である内容がわかればよいと思います。
  • Service
    • ExecStartは実行する内容です。今回は/home/ec2-user/crl_upload.shをシェル(/usr/bin/sh)で実行する内容を記載しました。
    • UserはExecStartの内容を実行するユーザーを指定します。
    • WorkingDirectoryはExecStartの内容を実行する場所を指定します。

/etc/systemd/system/crlupload.timerファイルの作成

timerファイルはserviceファイルを実行する間隔を指定します。今回は10分おきに実行させます。

$ sudo vi /etc/systemd/system/crlupload.timer

[Unit]
Description=execute crl_upload.sh
[Timer]
OnCalendar=*-*-* *:00/10:00
[Install]
WantedBy=timers.target
  • Unit
    • Descriptionはこのtimerの目的を記載します。serviceファイルと同じ内容で問題ないと思います。
  • Timer
    • OnCalendarは実行タイミングです。フォーマットはYYYY-MM-DD hh:mm:ssです。*を指定すれば毎日、毎分などを表現できます。また/は繰り返しを表しており、mmの00/10は00分から10分おきを表現しています。フォーマットがあっているか、意図したタイミングで実行できかについてはsystemd-analyze calendarコマンドでチェックできます。
      $ systemd-analyze calendar "*-*-* *:00/10:00"
      Normalized form: *-*-* *:00/10:00
          Next elapse: Tue 2025-04-01 05:10:00 UTC
             From now: 21s left
  • Install
    • WantedBy=timers.targetと記載することでcronのように時間になったら実行されます。

サービスの自動起動有効とサービスの起動

作成したcrlupload.timerを自動起動有効にして起動します。

$ sudo systemctl enable crlupload.timer
Created symlink /etc/systemd/system/timers.target.wants/crlupload.timer → /etc/systemd/system/crlupload.timer
$ sudo systemctl is-enabled crlupload.timer
enabled
$ sudo systemctl start crlupload.timer
$ systemctl status crlupload.timer
● crlupload.timer - execute crl_upload.sh
     Loaded: loaded (/etc/systemd/system/crlupload.timer; enabled; preset: disabled)
     Active: active (waiting) since Tue 2025-04-01 05:35:58 UTC; 35s ago
    Trigger: Tue 2025-04-01 05:40:00 UTC; 3min 25s left
   Triggers: ● crlupload.service


Apr 01 05:35:58 ip-192-168-2-116.ap-northeast-1.compute.internal systemd[1]: Started crlupload.timer - execute crl_upload.sh.

※設定ファイルの記載を変更した場合などは「sudo systemctl daemon-reload」を実行してから「sudo systemctl restart crlupload.timer」を実行してください。

これで10分おきにCRLを取得してトラストストアに反映させることができます。

動作を確認するにはAWSのトラストストアで「失効ID」が増えているか、もしくは「journalctl -u crlupload.*」を実行して動作ログをご確認ください。

記事を書いた人

ソリトンシステムズ・テクニカルチーム