aws Fargate × golangでIAM Database認証をする
仕事で必要になったのでサンプルを書きました。FargateでIAM Database認証をしているサンプルをネット上で見つけることができなかったことと、awsに掲載されているコードのコピペでは動かなかったので2日くらいハマっていました。誰かの助けになればと思います 😉
やること一覧
- Aurora (postgres) 作成
- IAM Database認証ユーザ設定
- go application 作成
- Fargate taskRoleArnを作成
くっそ長いので注意 🙃
Databaseを用意
今回はAurora (postgreSQL) を使用します。IAM Database認証を有効にしています。
master passwordは仮値を入れて作成後、consoleなどから更新をかけます。
resource "aws_rds_cluster" "this" { cluster_identifier = "aurora-test-cluster" engine = "aurora-postgresql" engine_version = "11.8" master_username = "postgres" master_password = "VeryStrongPassword!" # 仮パスワード # 細かい設定値は省略... # IAM Database認証を有効にする iam_database_authentication_enabled = true lifecycle { # master passwordはaws cli / console 等で変更する ignore_changes = [master_password] } }
IAM Database認証ユーザの作成
Security groupでEC2からAuroraへのアクセスを許可したあとで、EC2上で作業を行います。IAM認証ユーザと適当なTableを作成しています。ユーザにはReadOnlyのRoleをつけます。
IAM Database認証を行うので、それ用のIAM Policy Documentも設定します。これは後述のFargateのtaskRoleArnにつけるものと同じなのでそちらを参照してください。
# postgres install sudo rpm -ivh --nodeps https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm sudo sed -i "s/\$releasever/7/g" "/etc/yum.repos.d/pgdg-redhat-all.repo" sudo yum install -y postgresql11 # master user login psql -h aurora-test-db.cluster-xxxxxx.ap-northeast-1.rds.amazonaws.com -U postgres # Create IAM User # https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.DBAccounts.html CREATE USER testuser WITH LOGIN; GRANT rds_iam TO testuser; # create database CREATE DATABASE testdb; # switch database \c testdb # create schema CREATE SCHEMA testschema; # create table CREATE TABLE testschema.sample( > id char(4) not null, > label text not null, > primary key(id) > ); # insert a record INSERT INTO testschema.sample (id, label) VALUES ('1', 'ラベル1'); # https://gist.github.com/oinopion/4a207726edba8b99fd0be31cb28124d0 # set readOnly role # Create a group CREATE ROLE readaccess; # Grant access to existing tables GRANT USAGE ON SCHEMA testschema TO readaccess; GRANT SELECT ON ALL TABLES IN SCHEMA testschema TO readaccess; # Grant access to future tables ALTER DEFAULT PRIVILEGES IN SCHEMA testschema GRANT SELECT ON TABLES TO readaccess; GRANT readaccess TO testuser; # logout \q # IAM Login Test # https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.Connecting.AWSCLI.PostgreSQL.html export RDSHOST="aurora-test-db.cluster-xxxxxx.ap-northeast-1.rds.amazonaws.com" export PG_USER="testuser" export PGPASSWORD="$(aws rds generate-db-auth-token --hostname $RDSHOST --port 5432 --region ap-northeast-1 --username $PG_USER)" # verify-fullの場合、root証明書が必要 wget https://s3.amazonaws.com/rds-downloads/rds-ca-2019-root.pem psql "host=$RDSHOST port=5432 sslmode=verify-full sslrootcert=/home/ec2-user/rds-ca-2019-root.pem dbname=testdb user=$PG_USER password=$PGPASSWORD" # postgresはdefault requireなので、root証明書なしでもLoginできる psql "host=$RDSHOST port=5432 dbname=testdb user=$PG_USER password=$PGPASSWORD" # table access # select -> ok, insert -> ng select * from testschema.sample; INSERT INTO testschema.sample (id, label) VALUES ('2', 'ラベル2'); ERROR: permission denied for table sample # logout exit
go application
golangでIAM Database認証を使って接続します。私が試したとき (2020/12) では、aws公式に紹介されているコードそのままでは動作しませんでした 😔 なので修正を加えています。
// 証明書を利用しない方法で接続しています func Connect() *sql.DB { dbName := os.Getenv("dbname") dbUser := os.Getenv("user") dbHost := os.Getenv("host") dbPort := os.Getenv("port") region := os.Getenv("region") dbEndpoint := fmt.Sprintf("%s:%s", dbHost, dbPort) // https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.Connecting.Go.html // aws公式ではNewEnvCredentials() で 認証情報を取得しているが、この方法だとtaskRoleArnのACCESS_KEY_IDを利用できずにエラーになる // creds := credentials.NewEnvCredentials() // sessionからCredentialsを取得した場合、taskRoleArnのACCESS_KEY_ID 他をいい感じに利用してくれる conf := aws.NewConfig().WithRegion(region) sess := session.Must(session.NewSession(conf)) authToken, err := rdsutils.BuildAuthToken(dbEndpoint, region, dbUser, sess.Config.Credentials) if err != nil { panic(err) } dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s", dbHost, dbPort, dbUser, authToken, dbName, ) db, err := sql.Open("postgres", dsn) if err != nil { panic(err) } err = db.Ping() if err != nil { panic(err) } return db }
goでは、ACCESS_KEY_IDを以下の順序で解決します。Fargateでは通常3を利用すると思います。
- 環境変数
- Credential File
- IAM Role
Fargate taskはtaskRoleArnに設定したIAM RoleのACCESS_KEY_ID等を AWS_CONTAINER_CREDENTIALS_RELATIVE_URI から取得します。
タスク用の IAM ロール - Amazon Elastic Container Service
ところが、AWSで紹介されている credentials.NewEnvCredentials()
そのままだとIAM Roleを使ってくれません。環境変数から値を取得しようとしてエラーになります。
IAM Roleを利用するためには、sessionから取得する必要があります。
また、ECSの場合、IAM Roleから値を取得するのにもちょっとした罠があり、PID 1でApplicationが動作していないと、そのままでは取得できません。
詳細はAWS公式を参照ください。
Amazon ECS で "アクセス拒否" エラーを発生させないように IAM タスクロールを設定する
Fargate Security Group
go applicationを動かすFargateのSecurity GroupのIAM Policy Documentは以下のように設定します。EC2に設定したものと同じです。
# https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.IAMPolicy.html data "aws_iam_policy_document" "ecs_task" { statement { effect = "Allow" actions = ["rds-db:connect"] resources = [ "arn:aws:rds-db:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:dbuser:${aws_rds_cluster.this.cluster_resource_id}/testuser", "arn:aws:rds-db:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:dbuser:${aws_rds_cluster_instance.this.dbi_resource_id}/testuser", # ざっくり * 指定する場合 # "arn:aws:rds-db:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:dbuser:*/testuser", ] } }
AWS公式の説明通り dbi_resource_id を指定しただけの場合、Accessを拒否されました 🤮 (2020/12 現在)
一応、cluster_resource_idを追加で指定することでアクセスを確認できました。
これをtaskRoleArnに設定します。 (ExecutionRoleではないので注意)
追記
auroraの場合、cluster_resource_idだけを指定すれば良いようです。
IAM データベースアクセス用の IAM ポリシーの作成と使用 - Amazon Aurora
あとはLaunchしてアクセスを確認して終了です 💃🏽
まとめ
細かいところで色々はまったりして疲れましたが、代わりにAWSに少し詳しくなれたような気がしました。
Spring Boot Versionも試したい 😌
参考
IAM 認証を使用したデータベースアカウントの作成 - Amazon Relational Database Service
コマンドラインから IAM 認証を使用して DB インスタンスに接続する: AWS CLI および psql クライアント - Amazon Relational Database Service
IAM 認証および AWS SDK for Go を使用した DB クラスターへの接続 - Amazon Aurora
タスク用の IAM ロール - Amazon Elastic Container Service
IAM データベースアクセス用の IAM ポリシーの作成と使用 - Amazon Relational Database Service
Amazon ECS で "アクセス拒否" エラーを発生させないように IAM タスクロールを設定する
How to create read only user in PostgreSQL · GitHub
クレデンシャルの適切な扱い方 ー AWS SDK for Goの場合 | Developers.IO