Creating and Using an AWS Virtual MFA Device with the AWS SDK for Python
Some scenarios, like on-premise servers that need access to AWS resources, require AWS IAM Users to be created. These “service users” are given Access Keys, which serve as long term credentials and needed to be protected. An additional measure of protection could be to enable programmatic multifactor authentication for these users.
This Python code creates a virtual MFA device with Boto 3 (AWS SDK for Python). The create_virtual_mfa_device
response includes a shared secret (saved in string_seed
) and the bytes of a PNG image of a QR code that can be used with standard Authenticator apps.
session = boto3.session.Session(profile_name='kjh-SuperDuperUser')
iam_client = session.client('iam')
response = iam_client.create_virtual_mfa_device(
Path='/service-user/',
VirtualMFADeviceName='kjh-SuperDuperUser'
)
string_seed = response['VirtualMFADevice']['Base32StringSeed']
qrbytes = response['VirtualMFADevice']['QRCodePNG']
with open('kjh-SuperDuperUser.png', mode='wb') as f:
f.write(qrbytes)
f.close()
Using GitHub - pyotp/pyotp: Python One-Time Password Library, two consecutive codes are generated from the string_seed
to enable the virtual mfa device for the user.
totp = pyotp.TOTP(string_seed)
# the code from 30 seconds ago
code_1 = totp.generate_otp(totp.timecode(datetime.datetime.now() - datetime.timedelta(seconds=30)))
# the current code
code_2 = totp.generate_otp(totp.timecode(datetime.datetime.now()))
response = iam_client.enable_mfa_device(
UserName='kjh-SuperDuperUser',
SerialNumber='arn:aws:iam::123456789012:mfa/service-user/kjh-SuperDuperUser',
AuthenticationCode1=code_1,
AuthenticationCode2=code_2
)
Afterwards, looking at the AWS Console indicates kjh-SuperDuperUser
now has an assigned MFA device. Additionally, kjh-DuperRole
has this Trust Policy, which requires MFA to be present for sts:AssumeRole
.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/service-user/kjh-SuperDuperUser"
},
"Action": "sts:AssumeRole",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}
To use the Virtual MFA Device in an assume_role
API call, a TOTP code needs to be generated and passed as a parameter.
sts_client = session.client('sts')
totp = pyotp.TOTP(string_seed)
token_code = totp.now()
response = sts_client.assume_role(
RoleArn='arn:aws:iam::123456789012:role/kjh-DuperRole',
RoleSessionName='my-python-session',
SerialNumber='arn:aws:iam::123456789012:mfa/service-user/kjh-SuperDuperUser',
TokenCode=token_code
)
Note: This IAM Policy needed to be added to kjh-SuperDuperUser
in order to allow the MFA related actions.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:CreateVirtualMFADevice",
"iam:DeleteVirtualMFADevice"
],
"Resource": [
"arn:aws:iam::123456789012:mfa/service-user/${aws:username}"
]
},
{
"Effect": "Allow",
"Action": [
"iam:EnableMFADevice",
"iam:DeactivateMFADevice",
"iam:ResyncMFADevice"
],
"Resource": [
"arn:aws:iam::123456789012:user/service-user/${aws:username}"
]
},
{
"Effect": "Allow",
"Action": [
"iam:ListVirtualMFADevices"
],
"Resource": [
"arn:aws:iam::123456789012:mfa/*"
]
}
]
}
References: