oscerd commented on code in PR #26674: URL: https://github.com/apache/camel/pull/26674#discussion_r4062655426
########## components/camel-aws/camel-aws-common/src/main/java/org/apache/camel/component/aws/common/AwsRuntimeCredentialsResolver.java: ########## @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.aws.common; + +import java.io.File; + +import org.apache.camel.util.ObjectHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain; +import software.amazon.awssdk.auth.credentials.ContainerCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider; +import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider; +import software.amazon.awssdk.auth.credentials.SystemPropertyCredentialsProvider; + +/** + * Detects which AWS credentials source applies to the current runtime - JVM system properties, environment variables, + * web identity / IRSA, a shared profile, or ECS / EKS Pod Identity container credentials - selects the matching + * provider, and reports the chosen source at INFO. + * <p> + * This is an opt-in enhancement over the SDK {@link DefaultCredentialsProvider} whose purpose is observability. The SDK + * chain already resolves credentials in the same order used here, so the selected source never differs from the SDK; + * this class simply makes the resolved source visible in the logs and returns it as a targeted provider. When no source + * is recognised it returns {@code null} so the caller falls back to the SDK default chain (which also covers EC2 + * instance metadata). + * </p> + * <p> + * The detected provider is returned as the head of a chain whose tail is the full {@link DefaultCredentialsProvider}, + * so a detected-but-unusable source (for example a profile without resolvable credentials) still falls back to the SDK + * default chain rather than failing. + * </p> + * + * @since 4.23 + */ +public final class AwsRuntimeCredentialsResolver { + + static final String ENV_ACCESS_KEY = "AWS_ACCESS_KEY_ID"; + static final String ENV_SECRET_KEY = "AWS_SECRET_ACCESS_KEY"; + static final String ENV_WEB_IDENTITY_TOKEN_FILE = "AWS_WEB_IDENTITY_TOKEN_FILE"; + static final String ENV_ROLE_ARN = "AWS_ROLE_ARN"; + static final String ENV_CONTAINER_RELATIVE_URI = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; + static final String ENV_CONTAINER_FULL_URI = "AWS_CONTAINER_CREDENTIALS_FULL_URI"; + static final String ENV_PROFILE = "AWS_PROFILE"; + + static final String SYS_ACCESS_KEY = "aws.accessKeyId"; + static final String SYS_SECRET_KEY = "aws.secretAccessKey"; + + private static final Logger LOG = LoggerFactory.getLogger(AwsRuntimeCredentialsResolver.class); + + private AwsRuntimeCredentialsResolver() { + } + + /** + * The credentials source detected for the current runtime. + */ + public enum Source { + SYSTEM_PROPERTY("JVM system properties (aws.accessKeyId/aws.secretAccessKey)"), + ENVIRONMENT("environment variables (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY)"), + WEB_IDENTITY("web identity token / IRSA (AWS_WEB_IDENTITY_TOKEN_FILE)"), + PROFILE("shared profile (AWS_PROFILE or ~/.aws/credentials)"), + CONTAINER("container credentials (ECS task role / EKS Pod Identity)"), + UNKNOWN("no recognised runtime"); + + private final String description; + + Source(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } + } + + /** + * Detect the current runtime and return the matching credentials provider, or {@code null} when no runtime can be + * recognised (so the caller falls back to the SDK default credentials provider chain). + * + * @return the resolved credentials provider, or {@code null} to use the SDK default chain + */ + public static AwsCredentialsProvider resolve() { + return resolve(RuntimeEnvironment.SYSTEM); + } + + static AwsCredentialsProvider resolve(RuntimeEnvironment environment) { + Source source = detect(environment); + + switch (source) { + case UNKNOWN: + LOG.info("AWS credentials auto-detect: {} - using the SDK default credentials provider chain", + source.getDescription()); + return null; + case WEB_IDENTITY: + // Web identity (IRSA) requires software.amazon.awssdk:sts on the classpath to assume the role; + // camel-aws-common does not pull sts, so delegate to the SDK default provider, which performs the + // web-identity exchange when sts is present and degrades gracefully otherwise. + LOG.info("AWS credentials auto-detect: detected {} - delegating to the SDK default credentials" + + " provider chain (web identity requires software.amazon.awssdk:sts on the classpath)", + source.getDescription()); + return DefaultCredentialsProvider.builder().build(); Review Comment: Thanks — I dug into this, and the env-var case can't actually occur here. `detect()` checks `SYSTEM_PROPERTY` and `ENVIRONMENT` **before** `WEB_IDENTITY`, and both require *complete* static credentials (`EnvironmentVariableCredentialsProvider` needs `AWS_ACCESS_KEY_ID` **and** `AWS_SECRET_ACCESS_KEY`): - Both env vars set → `detect()` returns `ENVIRONMENT` and logs "environment variables", never `WEB_IDENTITY`. - Only `AWS_ACCESS_KEY_ID` set (the mounted-secret example) → the env provider is incomplete and can't win anyway; the chain falls through to web identity. So `WEB_IDENTITY` is only reached when env/system-property static creds are absent, which means `DefaultCredentialsProvider` resolves the web-identity token *first* among the remaining providers — it's equivalent to a targeted `of(webIdentity, default)` head. I kept it as-is rather than pulling in `WebIdentityTokenFileCredentialsProvider` (which still needs `sts` and would resolve identically here), and added a comment in d3160e8 documenting this so it doesn't trip up the next reader. The real caveat you're pointing at is genuine though: when `sts` is absent the token can't be assumed and the chain degrades to profile/container/IMDS, so the logged "web identity" source may differ from what ultimately resolves. The INFO line already flags the `sts` requirement — happy to make that more explicit if you think it's worth it. — _Claude Code on behalf of @oscerd_ -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
