openrolesanywhere
is an open-source client for AWS IAM Roles Anywhere.
Consider this project to be a proof-of-concept. It's unlikely something that you
would actually use in production, even if it technically works. It was more for
my own education.
The official client works, but has a few short-comings IMO. It's
not open source (it is now!) and it requires you to store private keys in plaintext on the
filesystem. This project lets you use private keys stored in an SSH agent.
This is more flexible - and more secure if you use something like Secretive
which stores unexportable keys in the macOS Secure Enclave hardware.
First, we're going to use an AWS KMS asymmetric key as the private key
for our certificate authority[1]. Look at key.yml
for an example
KMS key to create.
Once you have the key ARN, we run the following command. It will create a new
self-signed certificate (using the private key stored in KMS) and register it
in Roles Anywhere as a trust anchor. The certificate will be stored in
~/.config/openrolesanywhere/ca.pem
.
openrolesanywhere admin create-ca \
--name NameOfYourChoiceForTrustAnchorInRolesAnywhere \
--kms-key-id fecbd22d-c110-beef-cafe-7986a49ee7f0 \
--validity-duration 8760h \
--serial-number 12345 \
--common-name whateverYouWant \
[--organization org] \
[--organization-unit unit] \
[--country country] \
[--locality locality] \
[--province province] \
[--street-address addr] \
[--postal-code 1234]
Next, we create a profile. As best I can tell, this is a mapping from trust anchors to IAM roles. Run this:
openrolesanywhere admin create-profile \
--name SomeProfileName \
--session-duration 3600s \
--role-arn arn:aws:iam::012345678912:role/SomeRoleName \
--role-arn arn:aws:iam::012345678912:role/AnotherRoleName
Now we can start requesting certificates. First run ssh-add -l
to get a list
of fingerprints for the keys stored in your SSH agent. Mine looks like this:
256 SHA256:KBsk40KWP/UDoYoiFnpFk+z5JnMInwsrAFONMLrlryc ecdsa-sha2-nistp256 (ECDSA)
256 SHA256:z/A9nNwdk1ZTmwtdrAlF2JQnGS8C7V3ozOPMt5lgqBk ecdsa-sha2-nistp256 (ECDSA)
I want to use that second key, so I'll run the following command:
openrolesanywhere request-certificate \
--ssh-fingerprint SHA256:z/A9nNwdk1ZTmwtdrAlF2JQnGS8C7V3ozOPMt5lgqBk
That prints out the following:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYsYssxy2c9hesBTw0b5fNvoMzr8H
OMuqJ7OzWyLAqdPtcryZ8qHuZWAdd68z4A20u/quPhomJEa7ZGlzfU672A==
-----END PUBLIC KEY-----
Now we can send that to our administrator (which is probably us) and they will run:
openrolesanywhere admin accept-request \
--request-file path/to/above/publickey.pem \
--validity-duration 8760h \
--serial-number 67890 \
--common-name nameOfTheEnduser \
[--organization org] \
[--organization-unit unit] \
[--country country] \
[--locality locality] \
[--province province] \
[--street-address addr] \
[--postal-code 1234]
That will print out a certificate to the terminal, like this one:
-----BEGIN CERTIFICATE-----
MIICoDCCAkWgAwIBAgIDAQkyMAoGCCqGSM49BAMCMHIxCTAHBgNVBAYTADEJMAcG
A1UECBMAMQkwBwYDVQQHEwAxCTAHBgNVBAkTADEJMAcGA1UEERMAMQ4wDAYDVQQK
EwVteW9yZzEJMAcGA1UECxMAMQ4wDAYDVQQDEwVvcmFjYTEOMAwGA1UEBRMFMTIz
NDUwHhcNMjIwNzE0MDQ1NTMzWhcNMjMwNzE0MDQ1NTMzWjAyMQ4wDAYDVQQKEwVt
eW9yZzEQMA4GA1UEAxMHZW5kdXNlcjEOMAwGA1UEBRMFNjc4OTAwWTATBgcqhkjO
PQIBBggqhkjOPQMBBwNCAARixiyzHLZz2F6wFPDRvl82+gzOvwc4y6ons7NbIsCp
0+1yvJnyoe5lYB13rzPgDbS7+q4+GiYkRrtkaXN9TrvYo4IBCDCCAQQwDgYDVR0P
AQH/BAQDAgeAMB8GA1UdIwQYMBaAFOtkd1bCuMx15rQyMunITV9hKFFnMIHQBgNV
HREEgcgwgcWGXmFybjphd3M6cm9sZXNhbnl3aGVyZTphcC1zb3V0aGVhc3QtMjo2
MDc0ODE1ODE1OTY6cHJvZmlsZS9hZmViMTgzZS02OGUyLTQyMWEtYmVlNC01NTVh
MGI0NDc5NDaGY2Fybjphd3M6cm9sZXNhbnl3aGVyZTphcC1zb3V0aGVhc3QtMjo2
MDc0ODE1ODE1OTY6dHJ1c3QtYW5jaG9yL2Y5MjM2ZTVhLWZkOTMtNDU4Ny1iZmJj
LWFhODk2Y2E0ZWM1MjAKBggqhkjOPQQDAgNJADBGAiEA2MHoibJqVMyLUzcpBkY/
OyPX0k/nQ2Tqz7ElU7xSUq4CIQCJBHcVKwU0F2w8g0pecrIG/WhA0cO+xRl1VOz8
TcS7MQ==
-----END CERTIFICATE-----
We tell the end-user (again, probably ourselves) to store that file in
~/.config/openrolesanywhere/mycert.pem
. Once they've done that, they can
now configure the AWS CLI and SDKs to use it to retrieve AWS credentials.
To do that, the end-user adds this to their ~/.aws/config
:
[profile example]
credential_process = openrolesanywhere credential-process --name mycert --role-arn arn:aws:iam::012345678912:role/SomeRoleName
region = ap-southeast-2
Now running aws sts get-caller-identity --profile example
will work! Likewise,
the AWS SDK in most programs should work out of the box by setting an environment
variable like AWS_PROFILE=example
.
In an ideal world, there's all sorts of features you might expect to see in a fully-featured production version of this:
- Support for PKCS#11 to support smart cards
- Support for keys stored in TPMs
- Yubikeys (are probably covered by one of the above?)
- A SigV4 implementation that isn't copy-pasted and edited from the AWS SDK
- Documentation for
rolesanywhere:CreateSession
- Separation into a long-lived daemon (ideally socket-activated) that caches
AWS credentials in memory and a client for it, so that
CreateSession
rate limits aren't reached.
[1]: I wanted to use an SSH agent for the certificate authority, but this wasn't possible out of the box with the Go stdlib - and I didn't want to sink too much time into a PoC.