Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/britive/federation_providers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .aws import AwsFederationProvider
from .aws_sts_jwt import AwsStsJwtFederationProvider
from .azure_system_assigned_managed_identity import AzureSystemAssignedManagedIdentityFederationProvider
from .azure_user_assigned_managed_identity import AzureUserAssignedManagedIdentityFederationProvider
from .bitbucket import BitbucketFederationProvider
Expand All @@ -12,6 +13,7 @@
class FederationProviders:
def __init__(self, britive) -> None:
self.aws = AwsFederationProvider(britive)
self.aws_sts_jwt = AwsStsJwtFederationProvider(britive)
self.azure_system_assigned_managed_identity = AzureSystemAssignedManagedIdentityFederationProvider(britive)
self.azure_user_assigned_managed_identity = AzureUserAssignedManagedIdentityFederationProvider(britive)
self.bitbucket = BitbucketFederationProvider(britive)
Expand Down
40 changes: 40 additions & 0 deletions src/britive/federation_providers/aws_sts_jwt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from .federation_provider import FederationProvider


class AwsStsJwtFederationProvider(FederationProvider):
def __init__(
self,
profile: str = None,
audience: str = None,
duration_seconds: int = 300,
signing_algorithm: str = 'ES384',
) -> None:
self.profile = profile
self.audience = audience or 'britive'
self.duration_seconds = max(60, min(3600, duration_seconds))
self.signing_algorithm = signing_algorithm
super().__init__()

def get_token(self) -> str:
try:
import boto3
import botocore.exceptions as botoexceptions
except ImportError as e:
raise Exception(
'boto3 required - please install boto3 package to use the aws-sts-jwt federation provider'
) from e

try:
session = boto3.Session(profile_name=self.profile)
except botoexceptions.ProfileNotFound as e:
raise Exception(f'Error: {e!s}') from e

sts_client = session.client('sts')

response = sts_client.get_web_identity_token(
Audience=[self.audience],
DurationSeconds=self.duration_seconds,
SigningAlgorithm=self.signing_algorithm,
)

return f'OIDC::{response["WebIdentityToken"]}'
25 changes: 22 additions & 3 deletions src/britive/helpers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from britive.exceptions.unauthorized import InvalidTenantError, unauthorized_code_map
from britive.federation_providers import (
AwsFederationProvider,
AwsStsJwtFederationProvider,
AzureSystemAssignedManagedIdentityFederationProvider,
AzureUserAssignedManagedIdentityFederationProvider,
BitbucketFederationProvider,
Expand Down Expand Up @@ -102,9 +103,10 @@ def source_federation_token(provider: str, tenant: Optional[str] = None, duratio
sourced outside of this SDK and provided as input via the standard token presentation
options.

Six federation providers are currently supported by this method.
The following federation providers are currently supported by this method.

* AWS IAM/STS, with optional profile specified - (aws)
* AWS STS JWT via GetWebIdentityToken - (awsstsjwt)
* Azure System Assigned Managed Identities (azuresmi)
* Azure User Assigned Managed Identities (azureumi)
* Bitbucket Pipelines (bitbucket)
Expand All @@ -116,14 +118,21 @@ def source_federation_token(provider: str, tenant: Optional[str] = None, duratio
Any other OIDC federation provider can be used and tokens can be provided to this class for authentication
to a Britive tenant. Details of how to construct these tokens can be found at https://docs.britive.com.

:param provider: The name of the federation provider. Valid options are `aws`, `azuresmi`, `azureumi`, `bitbucket`,
`gcp`, `github`, `gitlab`, and `spacelift`.
:param provider: The name of the federation provider. Valid options are `aws`, `awsstsjwt`, `azuresmi`, `azureumi`,
`bitbucket`, `gcp`, `github`, `gitlab`, and `spacelift`.

For the AWS provider it is possible to provide a profile via value `aws-profile`. If no profile is provided
then the boto3 `Session.get_credentials()` method will be used to obtain AWS credentials, which follows
the order provided here:
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/credentials.html#configuring-credentials

For the AWS STS JWT provider (awsstsjwt) it is possible to provide optional parameters via pipe-delimited
values: `awsstsjwt-<profile>|<audience>|<signing_algorithm>|<duration_seconds>`. All parameters are optional.
Use an empty string for profile to skip it (e.g. `awsstsjwt-|myaudience`). Defaults: audience=`britive`,
signing_algorithm=`ES384`, duration_seconds=`300`. Valid signing algorithms are `ES384` and `RS256`.
Duration must be between 60 and 3600 seconds. This provider requires the AWS account to have IAM outbound
identity federation enabled.

For Azure User Assigned Managed Identities (azureumi) a client id is required. It must be
provided in the form `azureumi-<client-id>`. From the Azure documentation...a user-assigned identity's
client ID or, when using Pod Identity, the client ID of an Azure AD app registration. This argument
Expand Down Expand Up @@ -168,6 +177,16 @@ def source_federation_token(provider: str, tenant: Optional[str] = None, duratio
if provider_name in federation_providers:
return federation_providers[provider_name]()

if provider_name == 'awsstsjwt':
parts = helper[1].split('|') if len(helper) > 1 else []
profile = safe_list_get(parts, 0) or None
audience = safe_list_get(parts, 1) or None
signing_algorithm = safe_list_get(parts, 2) or 'ES384'
duration = int(safe_list_get(parts, 3) or 300)
return AwsStsJwtFederationProvider(
profile=profile, audience=audience, duration_seconds=duration, signing_algorithm=signing_algorithm
).get_token()

if provider_name == 'azuresmi':
return AzureSystemAssignedManagedIdentityFederationProvider(audience=safe_list_get(helper, 1)).get_token()

Expand Down