From e71d71065df17cbcd335f0e8f1df3a56070d3675 Mon Sep 17 00:00:00 2001 From: Carlo Piovesan Date: Thu, 16 Jul 2026 08:49:11 +0200 Subject: [PATCH 1/2] Bump duckdb to v1.5.4 --- .github/workflows/MainDistributionPipeline.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/MainDistributionPipeline.yml b/.github/workflows/MainDistributionPipeline.yml index 90248c5..a98ddab 100644 --- a/.github/workflows/MainDistributionPipeline.yml +++ b/.github/workflows/MainDistributionPipeline.yml @@ -17,7 +17,7 @@ jobs: uses: duckdb/extension-ci-tools/.github/workflows/_extension_distribution.yml@v1.5-variegata with: extension_name: aws - duckdb_version: v1.5.3 + duckdb_version: v1.5.4 ci_tools_version: v1.5-variegata exclude_archs: 'wasm_mvp;wasm_eh;wasm_threads;windows_amd64_rtools;windows_amd64_mingw' # Doesn't work anyway: env local file or env access possible @@ -28,7 +28,7 @@ jobs: secrets: inherit with: extension_name: aws - duckdb_version: v1.5.3 + duckdb_version: v1.5.4 ci_tools_version: v1.5-variegata exclude_archs: 'wasm_mvp;wasm_eh;wasm_threads;windows_amd64_rtools;windows_amd64_mingw' # Doesn't work anyway: env local file or env access possible deploy_latest: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main' }} From 3ed66c770e896c695a8150925bff399471a832a6 Mon Sep 17 00:00:00 2001 From: Carlo Piovesan Date: Thu, 16 Jul 2026 04:24:06 +0200 Subject: [PATCH 2/2] Identify DuckDB in AWS API User-Agent via ClientConfiguration appId Every AWS API call the extension makes (STS AssumeRole, SSO portal, RDS) previously went out with the bare aws-sdk-cpp default User-Agent, with nothing identifying DuckDB. Stamp ClientConfiguration::appId with "duckdb/" in BuildClientConfigWithCa(), and route the SSO client config through the same helper, so the SDK appends " app/duckdb/" to the User-Agent of every client we build. The token matches DuckDB's own User-Agent product token (duckdb/, GetDefaultUserAgent) that httpfs sends on regular HTTP/S3 calls. A user-configured app id (AWS_SDK_UA_APP_ID env var or sdk_ua_app_id profile key, which the ClientConfiguration constructor loads into appId) still wins. This is a purely programmatic per-client approach: no process-global env mutation, and no custom HttpClientFactory. It covers the clients this extension constructs and hands a config (STS AssumeRole, SSO, RDS). The credential-discovery providers that build their own default-config client internally (IMDS/instance-profile, STS web-identity, the default provider chain) are not covered, as the SDK exposes no per-config app id for them short of the env var or an HTTP-layer interceptor. --- src/aws_secret.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/aws_secret.cpp b/src/aws_secret.cpp index c7d3b7a..678de21 100644 --- a/src/aws_secret.cpp +++ b/src/aws_secret.cpp @@ -2,6 +2,7 @@ #include "duckdb/common/case_insensitive_map.hpp" #include "duckdb/common/exception.hpp" +#include "duckdb/main/database.hpp" #include "duckdb/main/extension/extension_loader.hpp" #include @@ -43,6 +44,12 @@ static struct { const string default_ = "exists"; } CreateAwsSecretValidation; +//! Note: this is a partial duplication of duckdb's GetDefaultUserAgent() +//! It needs to be reimplemented since the format is not compatible +static string DuckDBAwsUserAgentAppId() { + return "duckdb/" + string(DuckDB::LibraryVersion()); +} + //! Build a ClientConfiguration with the detected CA file path applied (if any). //! This is required because libcurl is statically linked from a RHEL-based //! manylinux image, so its default CA path is /etc/pki/tls/certs/ca-bundle.crt, @@ -55,6 +62,12 @@ static Aws::Client::ClientConfiguration BuildClientConfigWithCa() { if (!SELECTED_CURL_CERT_PATH.empty()) { cfg.caFile = SELECTED_CURL_CERT_PATH; } + // Identify DuckDB in the User-Agent, unless the user has already configured an app + // id (via AWS_SDK_UA_APP_ID or the sdk_ua_app_id profile key, which the + // ClientConfiguration constructor loads into cfg.appId): their value wins. + if (cfg.appId.empty()) { + cfg.appId = DuckDBAwsUserAgentAppId(); + } return cfg; } @@ -137,12 +150,10 @@ class DuckDBCustomAWSCredentialsProviderChain : public Aws::Auth::AWSCredentials aws_profile.SetExternalId(external_id); AddSTSProvider(aws_profile); } else if (item == "sso") { - // Pass an explicit ClientConfiguration so the SSO portal HTTPS call - // uses the detected CA path. See BuildClientConfigWithCa() comment. - auto sso_config = Aws::MakeShared("DuckDBAwsSSO"); - if (!SELECTED_CURL_CERT_PATH.empty()) { - sso_config->caFile = SELECTED_CURL_CERT_PATH; - } + // Pass an explicit ClientConfiguration so the SSO portal HTTPS call uses + // the detected CA path and the DuckDB app id. See BuildClientConfigWithCa(). + auto sso_config = Aws::MakeShared("DuckDBAwsSSO", + BuildClientConfigWithCa()); if (profile.empty()) { AddProvider(std::make_shared(Aws::String(), sso_config)); } else {