前提
Wasmでブラウザ上にて動作するアプリケーションを作りたいと思い、現在制作に取り掛かっています。
このアプリケーションのバックエンドの永続化の手段としてAWSのRDSを選択することに決めました。
使っている言語はRustで、バックエンドの構成としては大体こちら
をほぼ引用するという形をとり、RDS Proxyを経由してRDSへと接続しようと考えています。
実現したいこと
curlやreqwestクレート等を用いてAPI Gatewayを通じてAWS Lambdaをキックし、RDS ProxyさらにはRDSへと接続するバックエンドを作成したいです。
当面の目標としては、RDS Proxyの画面にアクセスして確認できるQueryRequests
のグラフにアクセス履歴が表示されていない状態ですので、これを表示されるようにすることです。
なお、RDSはMySQLを利用しようと思っていて、Rust側としてはsqlxクレートを使おうと考えています。
発生している問題・エラーメッセージ
エラーメッセージではなくログになりますが、下記の該当ソースコードをAWSにアップロードしブラウザ上でテストを実行したときに出力されるログを掲載させていただきます。
補足として
AWS Secrets Managerに作成したシークレットの名前は"SecretsManager"です。
Lambda関数の名前は"Test_Function_02"です。
リージョンは"ap-northeast-3"です。
一部情報は念のため"x"で伏せています。
改行がないため少し見づらいですが、そのままを掲載する方が良いと思ったのでご容赦をお願いいたします。
START RequestId: 41e749a7-dc70-4118-9610-13fde7eaxxxx Version: $LATEST 2022-10-17T08:49:28.071756Z INFO lambda_function_01: region="ap-northeast-3" 2022-10-17T08:49:28.271553Z INFO lambda_function_01: get_secret_value_string="GetSecretValue { handle: Handle { client: Client { connector: DynConnector, middleware: DynMiddleware, retry_policy: Standard { config: Config { initial_retry_tokens: 500, retry_cost: 5, no_retry_increment: 1, timeout_retry_cost: 10, max_attempts: 3, initial_backoff: 1s, max_backoff: 20s, base: 0x5566f7xxxx }, shared_state: CrossRequestRetryState { quota_available: Mutex { data: 500, poisoned: false, .. } } }, timeout_config: Config { api: Api { call: Unset, call_attempt: Unset }, http: Http { connect: Unset, write: Unset, read: Unset, tls_negotiation: Unset }, tcp: Tcp { connect: Unset, write: Unset, read: Unset } }, sleep_impl: Some(TokioSleep) }, conf: Config }, inner: Builder { secret_id: None, version_id: None, version_stage: None } }" 2022-10-17T08:49:28.271607Z INFO lambda_function_01: secret_id_string="GetSecretValue { handle: Handle { client: Client { connector: DynConnector, middleware: DynMiddleware, retry_policy: Standard { config: Config { initial_retry_tokens: 500, retry_cost: 5, no_retry_increment: 1, timeout_retry_cost: 10, max_attempts: 3, initial_backoff: 1s, max_backoff: 20s, base: 0x5566f7xxxx }, shared_state: CrossRequestRetryState { quota_available: Mutex { data: 500, poisoned: false, .. } } }, timeout_config: Config { api: Api { call: Unset, call_attempt: Unset }, http: Http { connect: Unset, write: Unset, read: Unset, tls_negotiation: Unset }, tcp: Tcp { connect: Unset, write: Unset, read: Unset } }, sleep_impl: Some(TokioSleep) }, conf: Config }, inner: Builder { secret_id: Some(\"SecretsManager\"), version_id: None, version_stage: None } }" 2022-10-17T08:49:28.365589Z INFO lambda_function_01: sent_string="Err(ServiceError { err: GetSecretValueError { kind: Unhandled(Error { code: Some(\"AccessDeniedException\"), message: Some(\"User: arn:aws:sts::67913522xxxx:assumed-role/Test_Function_02-role-lip4xxxx/Test_Function_02 is not authorized to perform: secretsmanager:GetSecretValue on resource: SecretsManager because no identity-based policy allows the secretsmanager:GetSecretValue action\"), request_id: Some(\"bb4d7bc5-f651-49ec-9b20-7d6eb371xxxx\"), extras: {} }), meta: Error { code: Some(\"AccessDeniedException\"), message: Some(\"User: arn:aws:sts::67913522xxxx:assumed-role/Test_Function_02-role-lip4xxxx/Test_Function_02 is not authorized to perform: secretsmanager:GetSecretValue on resource: SecretsManager because no identity-based policy allows the secretsmanager:GetSecretValue action\"), request_id: Some(\"bb4d7bc5-f651-49ec-9b20-7d6eb371xxxx\"), extras: {} } }, raw: Response { inner: Response { status: 400, version: HTTP/1.1, headers: {\"x-amzn-requestid\": \"bb4d7bc5-f651-49ec-9b20-7d6eb371xxxx\", \"content-type\": \"application/x-amz-json-1.1\", \"content-length\": \"308\", \"date\": \"Mon, 17 Oct 2022 08:49:28 GMT\", \"connection\": \"close\"}, body: SdkBody { inner: Once(Some(b\"{\\\"__type\\\":\\\"AccessDeniedException\\\",\\\"Message\\\":\\\"User: arn:aws:sts::67913522xxxx:assumed-role/Test_Function_02-role-lip4xxxx/Test_Function_02 is not authorized to perform: secretsmanager:GetSecretValue on resource: SecretsManager because no identity-based policy allows the secretsmanager:GetSecretValue action\\\"}\")), retryable: true } }, properties: SharedPropertyBag(Mutex { data: PropertyBag, poisoned: false, .. }) } })" 2022-10-17T08:49:28.365643Z INFO lambda_function_01: value_string="\"No value!\"" END RequestId: 41e749a7-dc70-4118-9610-13fde7eaxxxx REPORT RequestId: 41e749a7-dc70-4118-9610-13fde7eaxxxx Duration: 599.81 ms Billed Duration: 635 ms Memory Size: 128 MB Max Memory Used: 25 MB Init Duration: 35.15 ms
該当のソースコード
toml
1[package] 2name = "lambda_function_01" 3version = "0.1.0" 4edition = "2021" 5 6[dependencies] 7aws-config = "0.49.0" 8aws-sdk-secretsmanager = "0.19.0" 9lambda_runtime = "0.7.0" 10serde_json = "1.0.86" 11sqlx = { version = "0.6.2", features = ["runtime-tokio-rustls", "mysql"] } 12tokio = { version = "1.21.2", features = ["full"] } 13tracing = { version = "0.1", features = ["log"] } 14tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
rust
1use aws_config::meta::region::RegionProviderChain; 2use aws_sdk_secretsmanager::{output::GetSecretValueOutput, Client}; 3use lambda_runtime::{service_fn, Error, LambdaEvent}; 4use serde_json::{json, Value}; 5use tracing::info; 6use tracing_subscriber; 7 8#[tokio::main] 9async fn main() -> Result<(), Error> { 10 let func = service_fn(func); 11 lambda_runtime::run(func).await?; 12 Ok(()) 13} 14 15async fn func(_event: LambdaEvent<Value>) -> Result<Value, Error> { 16 tracing_subscriber::fmt::init(); 17 18 let region_provider = RegionProviderChain::default_provider().or_else("ap-northeast-3"); 19 20 let region = region_provider.region().await.unwrap().as_ref().to_string(); 21 info!(region); 22 23 let shared_config = aws_config::from_env().region(region_provider).load().await; 24 let client = Client::new(&shared_config); 25 26 let get_secret_value = client.get_secret_value(); 27 let get_secret_value_string = format!("{:?}", get_secret_value); 28 info!(get_secret_value_string); 29 30 let secret_id = get_secret_value.secret_id("SecretsManager"); 31 let secret_id_string = format!("{:?}", secret_id); 32 info!(secret_id_string); 33 34 let sent = secret_id.send().await; 35 let sent_string = format!("{:?}", sent); 36 info!(sent_string); 37 38 let resp = sent.unwrap_or(GetSecretValueOutput::builder().build()); 39 40 let value = resp.secret_string().unwrap_or("No value!").to_string(); 41 let value_string = format!("{:?}", value); 42 info!(value_string); 43 44 // ...上記でSecrets情報ができるはずなのでこれを使いRDS Proxyを通してRDSに接続したい... 45 // しかしエラーが発生してしまうので秘密情報を取得できない 46 47 let host: &str = &secret_info["host_proxy"].as_str().unwrap(); 48 let username: &str = &secret_info["username"].as_str().unwrap(); 49 let password: &str = &secret_info["password"].as_str().unwrap(); 50 let database: &str = &secret_info["dbname"].as_str().unwrap(); 51 52 let url = format!("mysql://{}:{}@{}/{}", username, password, host, database); 53 54 let pool = sqlx::mysql::MySqlPoolOptions::new() 55 .max_connections(5) 56 .connect(&url) 57 .await; 58 let pool_string = format!("{:?}", pool); 59 info!(pool_string); 60 61 Ok(json!({ 62 "statusCode": 200, 63 "headers": { "content-type": "application/json" }, 64 "body": "Hello world!", 65 })) 66}
出力結果
なお、テストの実行結果として以下のように動作をしています。
{ "body": "Hello world!", "headers": { "content-type": "application/json" }, "statusCode": 200 }
実施手順としては
% cargo lambda build --release --arm64 --output-format zip
で生成されたbootstrap.zip
を直接ブラウザからアップロードし、ブラウザ上にあるテストタブに切り替えてテスト
ボタンを押下して実施しています。
補足
ちなみにベタがきの部分は冒頭の引用サイトの内容を参考にして実行を試みました。
伏せておいた情報としては、パスワードにはSecretsManagerのパスワードを使い、ホストにはRDS Proxyのエンドポイントを使い接続を試しています。
さいごに
どうぞ、お分かりの方がいらっしゃいましたらご教示のほどよろしくお願いいたします。

回答1件
あなたの回答
tips
プレビュー
下記のような回答は推奨されていません。
このような回答には修正を依頼しましょう。
また依頼した内容が修正された場合は、修正依頼を取り消すようにしましょう。
2022/10/18 21:33 編集
2022/10/19 03:30
2022/10/19 21:24