すきま風

勉強したことのメモとか

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を利用すると思います。

  1. 環境変数
  2. Credential File
  3. 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

IAM認証によるRDS接続を試してみた | Developers.IO

Amazon Linux 2 にPostgreSQL 11 をインストールする - Qiita