AWS IAM Role AnywhereをDocker+OpenSSLのRootCAで試してみる

PKIベースでAWSのIAMロールを使える IAM Role Anywhereが発表されました。早速使えるようだったのでOpenSSLでCA構築して使ってみた手順を残しておきます。

環境汚さないためにDocker使っています。

AWS Identity and Access Management introduces IAM Roles Anywhere for workloads outside of AWS

What is AWS Identity and Access Management Roles Anywhere? - IAM Roles Anywhere

Extend AWS IAM roles to workloads outside of AWS with IAM Roles Anywhere | AWS Security Blog

[HostOS] RootCA用のDockerコンテナを起動

RootCAはコンテナ上で構築したいので、まずはDebianのコンテナを rootca という名前で立ち上げます。

# コンテナ100分間起動します
docker run --name rootca -d --rm debian:bullseye-slim sleep 60000

# 起動したコンテナでbashを起動
docker exec -it rootca /bin/bash

以下の作業はこのコンテナ内で実施します。

[Docker] OSパッケージインストール

CA作成とawscliのインストールで必要なパッケージをインストールします。

apt update && apt install -y openssl curl unzip less

[Docker] OpenSSLでRootCAを構築

以下の記事を参考にOpenSSLでRootCAを構築します。

qiita.com

以下のコマンドをコピペで構築されます。

mkdir -p /opt/pki/RootCA
mkdir /opt/pki/configs
cd /opt/pki/RootCA
mkdir newcerts
echo "01" > serial
echo "00" > crlnumber
touch index.txt

cat <<'EOF' > /opt/pki/configs/openssl_sign.cnf
[ ca ]
default_ca      = CA_default

[ CA_default ]
dir             = ./
certs           = $dir/certs
crl_dir         = $dir/crl
database        = $dir/index.txt
new_certs_dir   = $dir/newcerts
serial          = $dir/serial
crlnumber       = $dir/crlnumber
crl             = $dir/crl.pem
RANDFILE        = $dir/.rand

name_opt        = ca_default
cert_opt        = ca_default

default_days    = 365
default_crl_days= 30
default_bits    = 2048
default_md      = sha256
preserve        = no
policy          = policy_match

[ policy_match ]
countryName             = match
stateOrProvinceName     = match
organizationName        = match
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints=CA:true
keyUsage = cRLSign,keyCertSign

[ v3_client ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
crlDistributionPoints = URI:http://127.0.0.1:8200/v1/pki/crl
EOF


openssl genrsa -out RootCA_key.pem -aes256 -passout pass:rootcaprivkeypass 2048

openssl req -new \
 -subj "/C=JP/ST=Tokyo/O=EXAMPLE/CN=EXAMPLE Root CA" \
 -out RootCA_csr.pem \
 -key RootCA_key.pem \
 -passin pass:rootcaprivkeypass

openssl ca -config ../configs/openssl_sign.cnf  -batch -extensions v3_ca \
 -out RootCA_crt.pem \
 -in RootCA_csr.pem \
 -selfsign \
 -keyfile RootCA_key.pem \
 -passin pass:rootcaprivkeypass


openssl x509 -in RootCA_crt.pem -out RootCA_crt.pem

コマンド完了時は以下のようになります。

次の手順のため証明書のPEMファイルの中身をコピーしておきます。

cat RootCA_crt.pem
-----BEGIN CERTIFICATE-----
xxx
xxx
xxx
-----END CERTIFICATE-----

[AWS] IAM Role Anyware用のIAM Roleを作成

クライアントからAssumeするIAMロールとして、以下のように Test-IAM-Anyware (名前は任意)というロールを作成しました。ポリシーにはテスト用に AmazonS3ReadOnlyAccess を付けてみます。

信頼関係は以下のように設定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "rolesanywhere.amazonaws.com"
            },
            "Action": [
                "sts:AssumeRole",
                "sts:SetSourceIdentity",
                "sts:TagSession"
            ]
        }
    ]
}

こちらに記載ありますように、この時点で Condition 入れることで信頼する証明書のフィルタリングも可能です。

        "Condition": {
          "StringEquals": {
            "aws:PrincipalTag/x509Subject/CN": "Alice"
          }
        }

[AWS] IAM Role Anywareの信頼アンカー設定

IAM Role Anywareの管理画面を開きます。ちなみに、IAM Role Anywhereはリージョン毎の設定になります。

https://ap-northeast-1.console.aws.amazon.com/rolesanywhere

信頼アンカーを作成するを選択して、外部証明書のバンドルとして先ほどのPEMファイルを貼り付け、信頼アンカーを作成します。

]

[AWS] IAM Role Anywareのプロファイル設定

プロファイルを作成するを選択して、先ほど作った Test-IAM-Anyware のロールを指定します。後はそのままで作成します。

ちなみにこちらのブログの例では、ここでIPアドレス制限をかけているので参考になります。

"Condition": {
        "IpAddress": {
            "aws:SourceIp": "xxx.xxx.xxx.xxx/xx"
        }
    }

これでAWS側の設定は完了です。

[Docker] クライアント証明書を作成

今回は /opt/pki/RootCA でそのまま作業してしまいます。

以下の手順でクライアント証明書を発行します。

pwd
# -> 作業ディレクトリ確認 /opt/pki/RootCA

# 鍵を生成
openssl genrsa -out my-key.pem 2048

# CSRを作成
openssl req -new \
 -subj "/C=JP/ST=Tokyo/O=EXAMPLE/CN=EXAMPLE Client" \
 -out my-csr.pem  \
 -key my-key.pem

# クライアント証明書を発行
openssl ca -config ../configs/openssl_sign.cnf  \
 -batch -extensions v3_client \
 -out my-crt.pem \
 -in my-csr.pem \
 -cert RootCA_crt.pem \
 -keyfile RootCA_key.pem \
 -passin pass:rootcaprivkeypass

# PEMだけに変換
openssl x509 -in my-crt.pem -out my-crt.pem
# この時点で以下の3ファイルが出力されています
ls my-*
# -> my-crt.pem  my-csr.pem  my-key.pem

# CAの証明書で発行したクライアント証明書をVerifyしてみて確認します
openssl verify -CAfile RootCA_crt.pem my-crt.pem
# -> my-cert.pem: OK

この証明書を使ってAWSにアクセスしていきます。

[Docker] helperツールを使って証明書からIAM一時アクセス情報を取得

証明書を使ってIAMロールの認証情報を取得するには以下のhelperツールを利用します。

docs.aws.amazon.com

helperツールをインストールします。

mkdir -p /opt/aws/
curl --output /opt/aws/aws_signing_helper https://s3.amazonaws.com/roles-anywhere-credential-helper/CredentialHelper/latest/linux_amd64/aws_signing_helper
chmod +x /opt/aws/aws_signing_helper

AWSの画面から信頼アンカーのARN(trust-anchor-arn)とプロファイルのARN(profile-arn)、 Test-IAM-Anyware のIAMロールのARNを取得しておき、ツールにパラメータを渡して実行してみます。

※変数に入れている値はダミー値です

ANCHOR_ARN=arn:aws:rolesanywhere:us-west-1:123456789012:trust-anchor/xxxxxx
PROFILE_ARN=arn:aws:rolesanywhere:us-west-1:123456789012:profile/xxxxx
ROLE_ARN=arn:aws:iam::123456789012:role/Test-IAM-Anyware
/opt/aws/aws_signing_helper credential-process \
      -certificate my-crt.pem \
      -private-key my-key.pem \
      -trust-anchor-arn ${ANCHOR_ARN} \
      -profile-arn ${PROFILE_ARN} \
      -role-arn ${ROLE_ARN} 

以下のようにIAMアクセスに必要なトークン情報などが取れます。

{
  "Version": 1,
  "AccessKeyId": "xxxxx",
  "SecretAccessKey": "xxxxx",
  "SessionToken": "xxxx",
  "Expiration": "2022-07-08T01:20:52Z"
}

次はこの情報をawscliから利用してみます。

[Docker] AWS CLIから利用

何も入っていないOSなのでawscliのインストールから。

curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
./aws/install

以下のバージョンがインストールできました。

aws --version
# -> aws-cli/2.7.13 Python/3.9.11 Linux/5.13.0-52-generic exe/x86_64.debian.11 prompt/off

以下のように先ほどのhelperツールのコマンドをawcliのconfigに設定することで、awscliから認証情報が利用できるので、以下の用に設定してみます。

mkdir ~/.aws
cat <<EOF > ~/.aws/config
[profile anywhere]
    credential_process = /opt/aws/aws_signing_helper credential-process -certificate /opt/pki/RootCA/my-crt.pem -private-key /opt/pki/RootCA/my-key.pem -trust-anchor-arn ${ANCHOR_ARN} -profile-arn ${PROFILE_ARN} -role-arn ${ROLE_ARN} 
EOF

これでawscliからも使えるはずです。

まずは、以下のコマンドを実行してみて、Test-IAM-Anyware にAssumeできていることを確認できました。

aws sts get-caller-identity --profile anywhere
{
    "UserId": "Axxxxxxxxx:xx",
    "Account": "123456789012",
    "Arn": "arn:aws:sts::123456789012:assumed-role/Test-IAM-Anyware/xx"
}

IAMロールがAssumeできているので、IAMロールの権限に沿ってS3の読み込みも可能になっています。

aws s3 ls --profile anywhere

[AWS] Pythonから利用

以下のようにPython+boto3からも利用可能です。

# Python3インストール
apt install -y python3 python3-pip

# boto3インストール
pip3 install boto3

# PythonワンライナーでS3バケット一覧取得
python3 -c 'import boto3;import pprint;s=boto3.Session(profile_name="anywhere");c=s.client("s3");pprint.pprint(c.list_buckets())'

[ホストOS] RootCA用のDockerを停止

置いておいても100分で停止しますが念のため、コンテナ片付けます。

docker rm -f rootca