Dynamic Secrets and Just-In-Time Access
Dynamic Secrets and Just-In-Time Access
Dynamic secrets revolutionize IaC secret management by generating credentials on-demand with automatic expiration. Instead of long-lived passwords, systems receive temporary credentials valid only for specific durations. This approach significantly reduces risk from compromised credentials while simplifying rotation procedures.
Database dynamic secrets exemplify this pattern's power. Rather than sharing root database passwords, secret management platforms generate temporary user accounts with appropriate permissions. These accounts automatically expire, ensuring compromised credentials have limited windows of usefulness. IaC tools request credentials during execution, receiving fresh secrets for each deployment.
# Dynamic secret management implementation
import hvac
import time
from contextlib import contextmanager
from typing import Dict, Any
import logging
class DynamicSecretManager:
def __init__(self, vault_addr: str, auth_method: str = 'approle'):
self.vault_addr = vault_addr
self.auth_method = auth_method
self.client = None
self.logger = logging.getLogger(__name__)
def authenticate(self, role_id: str, secret_id: str):
"""Authenticate with Vault using AppRole."""
self.client = hvac.Client(url=self.vault_addr)
response = self.client.auth.approle.login(
role_id=role_id,
secret_id=secret_id
)
self.client.token = response['auth']['client_token']
self.lease_duration = response['auth']['lease_duration']
self.logger.info("Successfully authenticated with Vault")
@contextmanager
def get_database_credentials(self, role: str) -> Dict[str, str]:
"""Get temporary database credentials."""
try:
# Request dynamic credentials
response = self.client.read(f'database/creds/{role}')
credentials = {
'username': response['data']['username'],
'password': response['data']['password'],
'lease_id': response['lease_id'],
'lease_duration': response['lease_duration']
}
self.logger.info(f"Generated dynamic credentials for role {role}")
yield credentials
finally:
# Revoke credentials on exit
if 'lease_id' in locals():
self.revoke_lease(credentials['lease_id'])
def revoke_lease(self, lease_id: str):
"""Explicitly revoke a lease."""
try:
self.client.sys.revoke_lease(lease_id)
self.logger.info(f"Revoked lease {lease_id}")
except Exception as e:
self.logger.error(f"Failed to revoke lease: {e}")
def setup_aws_dynamic_secrets(self, aws_access_key: str,
aws_secret_key: str):
"""Configure AWS secret engine for dynamic credentials."""
# Enable AWS secret engine
self.client.sys.enable_secrets_engine(
backend_type='aws',
path='aws'
)
# Configure root credentials
self.client.write(
'aws/config/root',
access_key=aws_access_key,
secret_key=aws_secret_key
)
# Create roles with specific policies
policies = {
'deploy-role': '''
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:*",
"iam:*",
"s3:*"
],
"Resource": "*"
}
]
}
''',
'read-only-role': '''
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"s3:Get*",
"s3:List*"
],
"Resource": "*"
}
]
}
'''
}
for role_name, policy in policies.items():
self.client.write(
f'aws/roles/{role_name}',
credential_type='iam_user',
policy_document=policy,
default_sts_ttl='15m',
max_sts_ttl='1h'
)
Just-in-time (JIT) access extends dynamic secrets to human operators. Rather than maintaining standing privileges, administrators request temporary elevated access when needed. IaC deployments can trigger JIT access workflows, granting necessary permissions for deployment duration then automatically revoking them.