mirror of
https://github.com/NixOS/nix.git
synced 2025-12-22 17:01:08 +01:00
libstore: add AWS SSO support for S3 authentication
This enables seamless AWS SSO authentication for S3 binary caches without requiring users to manually export credentials. This adds SSO support by calling aws_credentials_provider_new_sso() from the C library directly. It builds a custom credential chain: Env → SSO → Profile → IMDS The SSO provider requires a TLS context for HTTPS connections to SSO endpoints, which is created once and shared across all providers.
This commit is contained in:
parent
a6eb2e91b7
commit
ec91479076
2 changed files with 92 additions and 7 deletions
|
|
@ -13,6 +13,9 @@
|
||||||
# include <aws/crt/auth/Credentials.h>
|
# include <aws/crt/auth/Credentials.h>
|
||||||
# include <aws/crt/io/Bootstrap.h>
|
# include <aws/crt/io/Bootstrap.h>
|
||||||
|
|
||||||
|
// C library headers for SSO provider support
|
||||||
|
# include <aws/auth/credentials.h>
|
||||||
|
|
||||||
# include <boost/unordered/concurrent_flat_map.hpp>
|
# include <boost/unordered/concurrent_flat_map.hpp>
|
||||||
|
|
||||||
# include <chrono>
|
# include <chrono>
|
||||||
|
|
@ -30,6 +33,44 @@ AwsAuthError::AwsAuthError(int errorCode)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to wrap a C credentials provider in the C++ interface.
|
||||||
|
* This replicates the static s_CreateWrappedProvider from aws-crt-cpp.
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> createWrappedProvider(
|
||||||
|
aws_credentials_provider * rawProvider, Aws::Crt::Allocator * allocator = Aws::Crt::ApiAllocator())
|
||||||
|
{
|
||||||
|
if (rawProvider == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto provider = Aws::Crt::MakeShared<Aws::Crt::Auth::CredentialsProvider>(allocator, rawProvider, allocator);
|
||||||
|
return std::static_pointer_cast<Aws::Crt::Auth::ICredentialsProvider>(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an SSO credentials provider using the C library directly.
|
||||||
|
* The C++ wrapper doesn't expose SSO, so we call the C library and wrap the result.
|
||||||
|
* Returns nullptr if SSO provider creation fails (e.g., profile doesn't have SSO config).
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> createSSOProvider(
|
||||||
|
const std::string & profileName,
|
||||||
|
Aws::Crt::Io::ClientBootstrap * bootstrap,
|
||||||
|
Aws::Crt::Io::TlsContext * tlsContext,
|
||||||
|
Aws::Crt::Allocator * allocator = Aws::Crt::ApiAllocator())
|
||||||
|
{
|
||||||
|
aws_credentials_provider_sso_options options;
|
||||||
|
AWS_ZERO_STRUCT(options);
|
||||||
|
|
||||||
|
options.bootstrap = bootstrap->GetUnderlyingHandle();
|
||||||
|
options.tls_ctx = tlsContext ? tlsContext->GetUnderlyingHandle() : nullptr;
|
||||||
|
options.profile_name_override = aws_byte_cursor_from_c_str(profileName.c_str());
|
||||||
|
|
||||||
|
// Create the SSO provider - will return nullptr if SSO isn't configured for this profile
|
||||||
|
// createWrappedProvider handles nullptr gracefully
|
||||||
|
return createWrappedProvider(aws_credentials_provider_new_sso(allocator, &options), allocator);
|
||||||
|
}
|
||||||
|
|
||||||
static AwsCredentials getCredentialsFromProvider(std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> provider)
|
static AwsCredentials getCredentialsFromProvider(std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> provider)
|
||||||
{
|
{
|
||||||
if (!provider || !provider->IsValid()) {
|
if (!provider || !provider->IsValid()) {
|
||||||
|
|
@ -91,6 +132,12 @@ public:
|
||||||
logLevel = Aws::Crt::LogLevel::Warn;
|
logLevel = Aws::Crt::LogLevel::Warn;
|
||||||
}
|
}
|
||||||
apiHandle.InitializeLogging(logLevel, stderr);
|
apiHandle.InitializeLogging(logLevel, stderr);
|
||||||
|
|
||||||
|
// Create a shared TLS context for SSO (required for HTTPS connections)
|
||||||
|
auto allocator = Aws::Crt::ApiAllocator();
|
||||||
|
auto tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(allocator);
|
||||||
|
tlsContext =
|
||||||
|
std::make_shared<Aws::Crt::Io::TlsContext>(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
AwsCredentials getCredentialsRaw(const std::string & profile);
|
AwsCredentials getCredentialsRaw(const std::string & profile);
|
||||||
|
|
@ -111,6 +158,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Aws::Crt::ApiHandle apiHandle;
|
Aws::Crt::ApiHandle apiHandle;
|
||||||
|
std::shared_ptr<Aws::Crt::Io::TlsContext> tlsContext;
|
||||||
boost::concurrent_flat_map<std::string, std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider>>
|
boost::concurrent_flat_map<std::string, std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider>>
|
||||||
credentialProviderCache;
|
credentialProviderCache;
|
||||||
};
|
};
|
||||||
|
|
@ -123,18 +171,53 @@ AwsCredentialProviderImpl::createProviderForProfile(const std::string & profile)
|
||||||
getpid(),
|
getpid(),
|
||||||
profile.empty() ? "(default)" : profile.c_str());
|
profile.empty() ? "(default)" : profile.c_str());
|
||||||
|
|
||||||
|
auto bootstrap = Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap();
|
||||||
|
|
||||||
|
// If no profile specified, use the default chain
|
||||||
if (profile.empty()) {
|
if (profile.empty()) {
|
||||||
Aws::Crt::Auth::CredentialsProviderChainDefaultConfig config;
|
Aws::Crt::Auth::CredentialsProviderChainDefaultConfig config;
|
||||||
config.Bootstrap = Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap();
|
config.Bootstrap = bootstrap;
|
||||||
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderChainDefault(config);
|
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderChainDefault(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
Aws::Crt::Auth::CredentialsProviderProfileConfig config;
|
// For named profiles, build a custom credential chain: Environment → SSO → Profile → IMDS
|
||||||
config.Bootstrap = Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap();
|
// The SSO provider will gracefully fail if SSO isn't configured for this profile,
|
||||||
// This is safe because the underlying C library will copy this string
|
// and the chain will move on to the next provider.
|
||||||
// c.f. https://github.com/awslabs/aws-c-auth/blob/main/source/credentials_provider_profile.c#L220
|
Aws::Crt::Auth::CredentialsProviderChainConfig chainConfig;
|
||||||
config.ProfileNameOverride = Aws::Crt::ByteCursorFromCString(profile.c_str());
|
auto allocator = Aws::Crt::ApiAllocator();
|
||||||
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderProfile(config);
|
|
||||||
|
// 1. Environment variables (highest priority)
|
||||||
|
auto envProvider = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderEnvironment(allocator);
|
||||||
|
if (envProvider) {
|
||||||
|
chainConfig.Providers.push_back(envProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. SSO provider (try it, will fail gracefully if not configured)
|
||||||
|
auto ssoProvider = createSSOProvider(profile, bootstrap, tlsContext.get(), allocator);
|
||||||
|
if (ssoProvider) {
|
||||||
|
debug("[pid=%d] added SSO provider to credential chain for profile '%s'", getpid(), profile.c_str());
|
||||||
|
chainConfig.Providers.push_back(ssoProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Profile provider (for static credentials)
|
||||||
|
Aws::Crt::Auth::CredentialsProviderProfileConfig profileConfig;
|
||||||
|
profileConfig.Bootstrap = bootstrap;
|
||||||
|
profileConfig.ProfileNameOverride = Aws::Crt::ByteCursorFromCString(profile.c_str());
|
||||||
|
auto profileProvider =
|
||||||
|
Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderProfile(profileConfig, allocator);
|
||||||
|
if (profileProvider) {
|
||||||
|
chainConfig.Providers.push_back(profileProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. IMDS provider (for EC2 instances, lowest priority)
|
||||||
|
Aws::Crt::Auth::CredentialsProviderImdsConfig imdsConfig;
|
||||||
|
imdsConfig.Bootstrap = bootstrap;
|
||||||
|
auto imdsProvider = Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderImds(imdsConfig, allocator);
|
||||||
|
if (imdsProvider) {
|
||||||
|
chainConfig.Providers.push_back(imdsProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderChain(chainConfig, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
AwsCredentials AwsCredentialProviderImpl::getCredentialsRaw(const std::string & profile)
|
AwsCredentials AwsCredentialProviderImpl::getCredentialsRaw(const std::string & profile)
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,8 @@ if s3_aws_auth.enabled()
|
||||||
deps_other += aws_crt_cpp
|
deps_other += aws_crt_cpp
|
||||||
aws_c_common = cxx.find_library('aws-c-common', required : true)
|
aws_c_common = cxx.find_library('aws-c-common', required : true)
|
||||||
deps_other += aws_c_common
|
deps_other += aws_c_common
|
||||||
|
aws_c_auth = cxx.find_library('aws-c-auth', required : true)
|
||||||
|
deps_other += aws_c_auth
|
||||||
endif
|
endif
|
||||||
|
|
||||||
configdata_pub.set('NIX_WITH_AWS_AUTH', s3_aws_auth.enabled().to_int())
|
configdata_pub.set('NIX_WITH_AWS_AUTH', s3_aws_auth.enabled().to_int())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue