def get_kmsauth_token(creds, config, username, cache): cache_key = 'kmsauth-{}'.format(config['awsregion']) kmsauth_cache = cache.get(cache_key) if kmsauth_cache: expiration = time.strptime(kmsauth_cache['Expiration'], '%Y%m%dT%H%M%SZ') if expiration > time.gmtime() and kmsauth_cache['token'] is not None: logging.debug('Using cached kmsauth token, good until {}'.format( kmsauth_cache['Expiration'])) return kmsauth_cache['token'] config['context'].update({'from': username}) try: token = kmsauth.KMSTokenGenerator( config['kmskey'], config['context'], config['awsregion'], aws_creds=creds, token_lifetime=60).get_token().decode('US-ASCII') except kmsauth.ServiceConnectionError: logging.debug("Network failure for kmsauth") raise LambdaInvocationException( 'Connection error getting kmsauth token.') # We have to manually calculate expiration the same way kmsauth does lifetime = 60 - (kmsauth.TOKEN_SKEW * 2) if lifetime > 0: expiration = datetime.datetime.utcnow() + datetime.timedelta( minutes=lifetime) kmsauth_cache = { 'token': token, 'Expiration': expiration.strftime('%Y%m%dT%H%M%SZ') } cache.set(cache_key, kmsauth_cache) cache.save() return token
def _get_generator(self, action, resource): generator = kmsauth.KMSTokenGenerator( # KMS key to use for authentication to the lambda self.kmsauth_key, # Encryption context to use { # We're authenticating to this service 'to': self.to_context, # It's from this IAM role 'from': self.from_context, # This token is for a service 'user_type': self.user_type_context, # This is an association action 'action': action, 'resource': resource }, # Find the KMS key in this region 'us-east-1') return generator
def generate_user_token(self, user): ''' Question: what kinda access control is it if I have direct access to the authorization key, generate it myself? You'd probably get something handed to you by https://lyft.github.io/confidant/ :param user: :return: ''' if not KMS_AUTH: raise ValueError("kmsauth is not installed") ## prompt for session mfa_creds = self.get_mfa_session(self.kmsauth_mfa_serial) # user to service authentication generator = kmsauth.KMSTokenGenerator( # KMS key to use for authentication self.kmsauth_autogen_key, # Encryption context to use { # We're authenticating to this service 'to': self.kmsauth_autogen_service, # It's from this user 'from': user, # This token is for a user 'user_type': 'user' }, # Find the KMS key in this region self.region, aws_creds=mfa_creds) username = generator.get_username() token = generator.get_token() return username, token
def __init__(self, url=None, auth_key=None, auth_context=None, token_lifetime=None, token_version=None, token_cache_file=None, assume_role=None, mfa_pin=None, region=None, retries=None, backoff=None, config_files=None, profile=None, verify=None): """Create a ConfidantClient object. Args: url: URL of confidant server. Default: None auth_key: The KMS key ARN or alias to use for authentication. Default: None auth_context: The KMS encryption context to use for authentication. Default: None token_lifetime: Lifetime of the authentication token generated. Default: 10 token_version: The version of the authentication token. Default: 2 token_cache_file: The location to use for caching the auth token. If set to empty string, no cache will be used. Default: /dev/shm/confidant/confidant_token assume_role: IAM role to assume for getting KMS auth token. Default: None mfa_pin: pin to use when assuming a role or getting an MFA session. Default: None region: AWS region to connect to. Default: None. retries: Number of retries to use on failed requests. Default: 0 backoff: Backoff factor for retries. See urllib3's Retry helper. Default: 1 config_files: A list of config files to attempt to load configuration from. First file found will be used. Default: ['~/.confidant', '/etc/confidant/config'] profile: profile to read config values from. verify: Whether we verify the servers TLS certificate. """ # Set defaults self.config = { 'url': None, 'auth_key': None, 'auth_context': {}, 'token_lifetime': 10, 'token_version': 2, 'token_cache_file': '/dev/shm/confidant/confidant_token', 'assume_role': None, 'region': None, 'retries': 0, 'backoff': 1, 'verify': True } if config_files is None: config_files = ['~/.confidant', '/etc/confidant/config'] if profile is None: profile = 'default' # Override defaults from config file self.config.update(self._load_config(config_files, profile)) # Override config from passed-in args args_config = { 'url': url, 'auth_key': auth_key, 'auth_context': auth_context, 'token_lifetime': token_lifetime, 'token_version': token_version, 'token_cache_file': token_cache_file, 'region': region, 'backoff': backoff, 'assume_role': assume_role, 'verify': verify } for key, val in args_config.items(): if val is not None: self.config[key] = val # Use session to re-try failed requests. self.request_session = requests.Session() self.request_session.verify = self.config['verify'] for proto in ['http://', 'https://']: self.request_session.mount( proto, HTTPAdapter( max_retries=Retry(total=self.config['retries'], status_forcelist=[500, 503], backoff_factor=self.config['backoff']))) self.iam_client = confidant_client.services.get_boto_client( 'iam', region=self.config['region']) self._load_user_auth_context() self._validate_client() self.sts_client = confidant_client.services.get_boto_client( 'sts', region=self.config['region']) self.kms_client = confidant_client.services.get_boto_client( 'kms', region=self.config['region']) if self.config['assume_role']: self.aws_creds = self._get_assume_role_creds( self.config['assume_role'], mfa_pin) elif mfa_pin: self.aws_creds = self._get_mfa_creds(mfa_pin) else: self.aws_creds = None try: self.generator = kmsauth.KMSTokenGenerator( self.config['auth_key'], self.config['auth_context'], self.config['region'], token_version=self.config['token_version'], token_cache_file=self.config['token_cache_file'], token_lifetime=self.config['token_lifetime'], aws_creds=self.aws_creds) except kmsauth.ConfigurationError: raise ClientConfigurationError('Error configuring kmsauth client.')