golang で AWS Lambda から IAM 認証を利用して RDS に接続するのにめっちゃハマった話
2017年5月ぐらいから IAM 認証で RDS の MySQL or Aurora に接続できるようになっていて、これを利用すれば Lambda から高速にデータベースアクセスができてハッピーになれそうということで試した。
で、aws-sdk-go をつかってアクセスしようとしたけど
Error 1045: Access denied for user 'iam_user'@'ec2-xx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com' (using password: YES)
となって全然繋がらなかった。
コードは以下のような感じ
package main import ( "github.com/aws/aws-lambda-go/lambda" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/service/rds/rdsutils" "fmt" "crypto/x509" "io/ioutil" "crypto/tls" "database/sql" "github.com/go-sql-driver/mysql" ) var certFile = "./rds-combined-ca-bundle.pem" var dbEndpoint = "foo.bar.ap-northeast-1.rds.amazonaws.com" var dbUser = "iam_user" var dbName = "iamtest" var dbPort = "3306" var awsRegion = "ap-northeast-1" func RDSConnect() (string, error) { awsCredentials := credentials.NewEnvCredentials() authToken, err := rdsutils.BuildAuthToken( dbEndpoint, awsRegion, dbUser, awsCredentials, ) if err != nil { panic(err.Error()) } dsnStr := fmt.Sprintf( "%s:%s@tcp(%s:%s)/%s?tls=true&allowCleartextPasswords=true", dbUser, authToken, dbEndpoint, dbPort, dbName, ) db, err := sql.Open("mysql", dsnStr) if err != nil { panic(err.Error()) } defer db.Close() return "ok", nil } func RegisterTLSConfig() { rootCertPool := x509.NewCertPool() pem, err := ioutil.ReadFile(certFile) if err != nil { panic(err.Error()) } if ok := rootCertPool.AppendCertsFromPEM(pem); !ok { panic(err.Error()) } err = mysql.RegisterTLSConfig("custom", &tls.Config{ RootCAs: rootCertPool, }) if err != nil { panic(err.Error()) } } func main() { RegisterTLSConfig() lambda.Start(RDSConnect) }
結論から言うと、rdsutils.BuildAuthToken の引数の dbEndopoint は hostname:port の形式で書かないとダメ。
authToken, err := rdsutils.BuildAuthToken(
fmt.Sprintf("%s:%s", dbEndpoint, dbPort),
awsRegion,
dbUser,
awsCredentials,
)
とすれば動く。
そしていまドキュメントを再読したら
rdsutils - Amazon Web Services - Go SDK
Endpoint consists of the hostname and port, IE hostname:port, of the RDS database.
とバッチリ書いてあった!!
と言うわけでめちゃくちゃハマった話でした。
その他の注意点
- RDS への IAM 認証は 1秒間に新規で 20接続までしかできない
- rds-db-connect という特殊な IAM ポリシーを作って Lambda にアタッチしなければならない
- データベース上で、AWSAuthenticationPlugin を有効にしたユーザーを作らなければならない
- TLS 接続が必須で、rds-combined-ca-bundle.pem を指定しなければならない
などなど色々と大変。
接続数20はハードリミットで変更できないらしく、現状はかなり限定的な要件でしか使えないなーという印象です。
詳しくは以下を参照のこと