Skip to content

Commit 26fbb5f

Browse files
jchrostek-ddclaude
andcommitted
refactor: consolidate secret resolution, extract get_aws_credentials helper
- Inline delegated auth into resolve_secrets alongside KMS/SM/SSM rather than as a separate early-return path - Extract get_aws_credentials to handle SnapStart credential resolution in one place, shared across all secret backends - Use shared_client (Datadog egress client) for delegated auth and bare client for AWS API calls (KMS, SM, SSM, SnapStart) to avoid routing AWS credential requests through DD_PROXY_HTTPS Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
1 parent cdfac99 commit 26fbb5f

File tree

3 files changed

+49
-92
lines changed

3 files changed

+49
-92
lines changed

bottlecap/src/bin/bottlecap/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,9 @@ async fn extension_loop_active(
303303
let account_id = r.account_id.as_ref().unwrap_or(&"none".to_string()).clone();
304304
let tags_provider = setup_tag_provider(&Arc::clone(&aws_config), config, &account_id);
305305

306+
// Build one shared reqwest::Client for metrics, logs, trace proxy flushing, and calls to
307+
// Datadog APIs (e.g. delegated auth). reqwest::Client is Arc-based internally, so cloning
308+
// just increments a refcount and shares the connection pool.
306309
let (
307310
logs_agent_channel,
308311
logs_flusher,

bottlecap/src/secrets/decrypt.rs

Lines changed: 46 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,19 @@ use sha2::{Digest, Sha256};
1414
use std::io::Error;
1515
use std::sync::Arc;
1616
use tokio::time::Instant;
17-
use tracing::{debug, error, info, warn};
17+
use tracing::{debug, error};
1818

1919
use crate::secrets::delegated_auth;
2020

2121
pub async fn resolve_secrets(
2222
config: Arc<Config>,
2323
aws_config: Arc<AwsConfig>,
24-
client: Client,
24+
shared_client: Client,
2525
) -> Option<String> {
26-
if !config.dd_org_uuid.is_empty()
27-
&& let Some(api_key) = try_delegated_auth(&config, &aws_config, &client).await
28-
{
29-
return Some(api_key);
30-
}
31-
3226
let api_key_candidate = if !config.api_key_secret_arn.is_empty()
3327
|| !config.kms_api_key.is_empty()
3428
|| !config.api_key_ssm_arn.is_empty()
29+
|| !config.dd_org_uuid.is_empty()
3530
{
3631
let before_decrypt = Instant::now();
3732

@@ -51,38 +46,17 @@ pub async fn resolve_secrets(
5146
}
5247
};
5348

54-
let mut aws_credentials = AwsCredentials::from_env();
55-
56-
if aws_credentials.aws_secret_access_key.is_empty()
57-
&& aws_credentials.aws_access_key_id.is_empty()
58-
&& !aws_credentials
59-
.aws_container_credentials_full_uri
60-
.is_empty()
61-
&& !aws_credentials.aws_container_authorization_token.is_empty()
62-
{
63-
// We're in Snap Start
64-
let credentials = match get_snapstart_credentials(&aws_credentials, &client).await {
65-
Ok(credentials) => credentials,
66-
Err(err) => {
67-
error!("Error getting Snap Start credentials: {}", err);
68-
return None;
69-
}
70-
};
71-
aws_credentials.aws_access_key_id = credentials["AccessKeyId"]
72-
.as_str()
73-
.unwrap_or_default()
74-
.to_string();
75-
aws_credentials.aws_secret_access_key = credentials["SecretAccessKey"]
76-
.as_str()
77-
.unwrap_or_default()
78-
.to_string();
79-
aws_credentials.aws_session_token = credentials["Token"]
80-
.as_str()
81-
.unwrap_or_default()
82-
.to_string();
83-
}
49+
let aws_credentials = get_aws_credentials(&client).await?;
8450

85-
let decrypted_key = if !config.kms_api_key.is_empty() {
51+
let decrypted_key = if !config.dd_org_uuid.is_empty() {
52+
delegated_auth::get_delegated_api_key(
53+
&config,
54+
&aws_config,
55+
&shared_client,
56+
&aws_credentials,
57+
)
58+
.await
59+
} else if !config.kms_api_key.is_empty() {
8660
decrypt_aws_kms(
8761
&client,
8862
config.kms_api_key.clone(),
@@ -124,58 +98,6 @@ pub async fn resolve_secrets(
12498
clean_api_key(api_key_candidate)
12599
}
126100

127-
async fn try_delegated_auth(
128-
config: &Arc<Config>,
129-
aws_config: &Arc<AwsConfig>,
130-
client: &Client,
131-
) -> Option<String> {
132-
let mut aws_credentials = AwsCredentials::from_env();
133-
134-
if aws_credentials.aws_secret_access_key.is_empty()
135-
&& aws_credentials.aws_access_key_id.is_empty()
136-
&& !aws_credentials
137-
.aws_container_credentials_full_uri
138-
.is_empty()
139-
&& !aws_credentials.aws_container_authorization_token.is_empty()
140-
{
141-
// We're in Snap Start
142-
let credentials = match get_snapstart_credentials(&aws_credentials, &client).await {
143-
Ok(credentials) => credentials,
144-
Err(err) => {
145-
error!(
146-
"Error getting Snap Start credentials for delegated auth: {}",
147-
err
148-
);
149-
return None;
150-
}
151-
};
152-
aws_credentials.aws_access_key_id = credentials["AccessKeyId"]
153-
.as_str()
154-
.unwrap_or_default()
155-
.to_string();
156-
aws_credentials.aws_secret_access_key = credentials["SecretAccessKey"]
157-
.as_str()
158-
.unwrap_or_default()
159-
.to_string();
160-
aws_credentials.aws_session_token = credentials["Token"]
161-
.as_str()
162-
.unwrap_or_default()
163-
.to_string();
164-
}
165-
166-
match delegated_auth::get_delegated_api_key(config, aws_config, client, &aws_credentials).await
167-
{
168-
Ok(api_key) => {
169-
info!("Delegated auth API key obtained successfully");
170-
clean_api_key(Some(api_key))
171-
}
172-
Err(e) => {
173-
warn!("Delegated auth failed, falling back to other methods: {e}");
174-
None
175-
}
176-
}
177-
}
178-
179101
fn clean_api_key(maybe_key: Option<String>) -> Option<String> {
180102
if let Some(key) = maybe_key {
181103
let clean_key = key.trim_end_matches('\n').replace(' ', "").clone();
@@ -321,6 +243,39 @@ async fn decrypt_aws_ssm(
321243
Err(Error::new(std::io::ErrorKind::InvalidData, v.to_string()).into())
322244
}
323245

246+
async fn get_aws_credentials(client: &Client) -> Option<AwsCredentials> {
247+
let mut aws_credentials = AwsCredentials::from_env();
248+
// We're in SnapStart — fetch short-lived credentials from the container endpoint
249+
if aws_credentials.aws_secret_access_key.is_empty()
250+
&& aws_credentials.aws_access_key_id.is_empty()
251+
&& !aws_credentials
252+
.aws_container_credentials_full_uri
253+
.is_empty()
254+
&& !aws_credentials.aws_container_authorization_token.is_empty()
255+
{
256+
let credentials = match get_snapstart_credentials(&aws_credentials, client).await {
257+
Ok(credentials) => credentials,
258+
Err(err) => {
259+
error!("Error getting SnapStart credentials: {}", err);
260+
return None;
261+
}
262+
};
263+
aws_credentials.aws_access_key_id = credentials["AccessKeyId"]
264+
.as_str()
265+
.unwrap_or_default()
266+
.to_string();
267+
aws_credentials.aws_secret_access_key = credentials["SecretAccessKey"]
268+
.as_str()
269+
.unwrap_or_default()
270+
.to_string();
271+
aws_credentials.aws_session_token = credentials["Token"]
272+
.as_str()
273+
.unwrap_or_default()
274+
.to_string();
275+
}
276+
Some(aws_credentials)
277+
}
278+
324279
async fn get_snapstart_credentials(
325280
aws_credentials: &AwsCredentials,
326281
client: &Client,

bottlecap/src/secrets/delegated_auth/auth_proof.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,5 +288,4 @@ mod tests {
288288
&vec!["my-org-uuid".to_string()]
289289
);
290290
}
291-
292291
}

0 commit comments

Comments
 (0)