Blog
Temporäre AWS-Anmeldeinformationen generieren

Für unser Produkt Instruqt bauen wir Herausforderungen, um die AWS-Technologie zu erlernen. Die Benutzer von instruqt benötigen einen temporären Zugang, um ihre Ressourcen in AWS zu erstellen, zu aktualisieren und zu zerstören. Wir möchten den Benutzern das Gefühl geben, ein eigenes AWS-Konto zu haben. Zugriffsschlüssel zur Verwendung in der CLI oder dem programmatischen Zugriff und der Management-Konsole im Browser. Dieser Blogbeitrag beschreibt, wie Sie ein AWS-Konto für diese Nutzung vorbereiten und wie Sie temporäre Zugriffsschlüssel und einen magischen Link zur Verwaltungskonsole generieren und verwenden.
Voraussetzungen
- AWS-Konto. Sie benötigen ein AWS-Konto mit einem Benutzer und Zugriffsschlüsseln mit Berechtigungen zum Auflisten, Lesen, Erstellen, Aktualisieren und Löschen von IAM-Gruppen, Benutzern, Rollen und Richtlinien.
- Terraform. Sie müssen Terraform installiert haben.
- Python. Stellen Sie sicher, dass Sie eine aktuelle Python-Version mit pip haben. Auf einem Mac empfehle ich diese Einstellung.
- AWS CLI. Installieren Sie die AWS CLI mit pip. (
pip install awscli --upgrade --user) - Keybase. Installieren und erstellen Sie ein Keybase-Konto(keybase.io/)
Rollen und Föderation
Neben allgemeinen Entitäten wie Benutzern und Gruppen bietet AWS auch "Rollen". Eine Rolle in AWS kann z.B. einem Server zugewiesen werden, der der auf dem Server laufenden Software den Zugriff auf AWS-Ressourcen ermöglicht. Eine Rolle kann auch von einem Benutzer übernommen werden, wodurch er Zugriff auf die Ressourcen erhält. Auch bekannt als "Föderation". Eine Rolle enthält zwei Arten von Richtlinien. Eine Richtlinie, die den Typ des Dienstes beschreibt, der die Rolle übernehmen darf (eine ec2-Instanz oder ein AWS-Konto mit Benutzern). Die andere Richtlinie beschreibt die Berechtigungsstufe für die angegebenen Ressourcen.
Für diesen Anwendungsfall erstellen wir eine Rolle, die von einem AWS IAM Benutzer übernommen werden kann. Ein einfaches Vertrauen reicht nicht aus, der Benutzer muss auch die Berechtigung haben, die Rolle mit AssumeRolePolicy zu übernehmen. Nachdem die Rolle übernommen wurde, werden die AccessPolicy-Berechtigungen vorübergehend an den Benutzer vergeben, indem eine Reihe von Schlüsseln gesendet wird.
+------------------+
| |
| User <-----------+
| | |
+---------+--------+ |
| |
| AssumeRolePolicy | TrustPolicy
| |
+---------v--------+ |
| | |
+ Role +-----------+
| |
+---------+--------+
|
| AccessPolicy
|
+---------v--------+
| |
+ Resources |
| |
+---------+--------+
Einrichtung
Für die Einrichtung von Benutzer, Rolle, Richtlinien usw. können Sie CloudFormation, Terraform, die CLI oder die Management-Konsole verwenden. In diesem Beispiel habe ich Terraform verwendet, aber Sie können dies einfach als Dokumentation verwenden, um es manuell oder mit CloudFormation zu tun.
Erstellen Sie
# CONFIGURATION AND PARAMETERS
variable "aws" {
content = "Enter the aws profile to deploy."
}
variable "keybase" {
content = "Enter the keybase profile to encrypt the secret_key (to decrypt: terraform output secret_key | base64 --decode | keybase pgp decrypt)"
}
variable "region" {
default = "eu-west-1"
}
provider "aws" {
profile = "${var.aws}"
region = "${var.region}"
}
data "aws_caller_identity" "current" {}
# RESOURCES
resource "aws_iam_user" "instruqt" {
name = "instruqt"
}
resource "aws_iam_access_key" "instruqt" {
user = "${aws_iam_user.instruqt.name}"
pgp_key = "keybase:${var.keybase}"
}
resource "aws_iam_user_policy" "instruqt_assume_role" {
name = "test"
user = "${aws_iam_user.instruqt.name}"
policy = <<eof
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sts:Assume*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}
resource "aws_iam_role" "S3AccessRole" {
name = "InstruqtS3Access"
assume_role_policy = <<eof
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}
resource "aws_iam_role_policy" "S3AccessPolicy" {
name = "InstruqtS3AccessPolicy"
role = "${aws_iam_role.S3AccessRole.id}"
policy = <<eof
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}
# OUTPUT
output "role_arn" {
value = "${aws_iam_role.S3AccessRole.arn}"
}
output "access_key" {
value = "${aws_iam_access_key.instruqt.id}"
}
output "secret_key" {
value = "${aws_iam_access_key.instruqt.encrypted_secret}"
}
Setzen Sie den Stack mit den folgenden Befehlen ein. Wählen Sie eines der unter
terraform init
terraform apply
Wenn Terraform abgeschlossen ist, enthält die Ausgabe Zugangsschlüssel für die nächsten Schritte. Der geheime Schlüssel wird mit keybase verschlüsselt. Entschlüsseln Sie den generierten geheimen Schlüssel mit dem nächsten Befehl:
terraform output secret_key | base64 --decode | keybase pgp decrypt
Führen Sie die Python-Datei generate_keys.py aus
Installieren Sie zunächst die Abhängigkeiten, z.B. mit pip, anhand des folgenden Beispiels. Wenn Sie Fehlermeldungen über fehlende Abhängigkeiten erhalten, installieren Sie auch diese.
pip install requests boto3
Erstellen Sie eine Datei generate_keys.py mit der folgenden Quelle:
#!/usr/bin/env python
import os
import sys
import boto3
import json
import requests
import argparse
import ConfigParser
from botocore.exceptions import ClientError
def get_credentials_for_role(role_arn, session_name):
sts = boto3.client('sts')
try:
response = sts.assume_role(RoleArn=role_arn,
RoleSessionName=session_name)
return response['Credentials']
except ClientError as e:
sys.stderr.write('ERROR: %sn' % e.response['Error']['Message'])
sys.exit(1)
def write_credentials(profile, credentials):
filename = os.path.expanduser('~/.aws/credentials')
dirname = os.path.dirname(filename)
if not os.path.exists(dirname):
os.makedirs(dirname)
config = ConfigParser.ConfigParser()
config.read(filename)
if not config.has_section(profile):
config.add_section(profile)
config.set(profile, 'aws_access_key_id', credentials['AccessKeyId'])
config.set(profile, 'aws_secret_access_key', credentials['SecretAccessKey'])
config.set(profile, 'aws_session_token', credentials['SessionToken'])
with open(filename, 'w') as fp:
config.write(fp)
def generate_console_link(credentials):
session = json.dumps({'sessionId': credentials['AccessKeyId'],
'sessionKey': credentials['SecretAccessKey'],
'sessionToken': credentials['SessionToken']})
r = requests.get("https://signin.aws.amazon.com/federation",
params={'Action': 'getSigninToken',
'SessionDuration': 43200,
'Session': session})
signin_token = r.json()
console = requests.Request('GET',
'https://signin.aws.amazon.com/federation',
params={'Action': 'login',
'Issuer': 'Instruqt',
'Destination': 'https://console.aws.amazon.com/',
'SigninToken': signin_token['SigninToken']})
prepared_link = console.prepare()
return prepared_link.url
if __name__ == '__main__':
parser = argparse.ArgumentParser(content='generate keys')
parser.add_argument("--output", "-o", required=False,
dest="output", help="output format", metavar="STRING",
default="json", choices=['link', 'json', 'write'])
parser.add_argument("--role-arn", "-r", required=True,
dest="role_arn", help="to assume", metavar="STRING")
parser.add_argument("--session-name", "-s", required=True,
dest="session_name", help="to use", metavar="STRING")
options = parser.parse_args()
credentials = get_credentials_for_role(options.role_arn,
options.session_name)
if options.output == 'link':
print generate_console_link(credentials)
elif options.output == 'write':
write_credentials(options.session_name, credentials)
elif options.output == 'json':
print(json.dumps({'AccessKeyId': credentials['AccessKeyId'],
'SecretAccessKey': credentials['SecretAccessKey'],
'SessionToken': credentials['SessionToken'],
'ConsoleMagicLink': generate_console_link(credentials)}))
Das Skript ./generate_keys.py benötigt 3 Parameter:
- -session-name. Der Sitzungsname ist eine eindeutige ID des Benutzers, der die temporären Anmeldedaten verwenden wird. (Beispiel: martijn@binx.io)
- -Rolle-arn. Die Rolle arn ist Teil der Ausgabe des Terraform-Skripts. (Beispiel: arn:aws:iam::AWS_ACCOUNT_ID:role/InstruqtS3Access). Sie können dies aus der Ausgabe von terraform kopieren.
- -Output. Dies kann nur enthalten: json | write | link, die Standardausgabe ist json.
generate_keys.py verwendet Boto (AWS SDK für Python). Es verwendet Umgebungsvariablen für Zugriffsschlüssel. Verwenden Sie das erste Beispiel oder kopieren Sie die access_key und role_arn Ausgabe von terraform, den entschlüsselten secret_key und ersetzen Sie die Beispielvariablen im zweiten Beispiel.
AWS_ACCESS_KEY_ID=$(terraform output access_key)
AWS_SECRET_ACCESS_KEY=$(terraform output secret_key | base64 --decode | keybase pgp decrypt)
python ./generate_keys.py --session-name identified@domain.ext
--role-arn $(terraform output role_arn)
--output json
AWS_ACCESS_KEY_ID=AKIA34K435KLR12KDT345
AWS_SECRET_ACCESS_KEY=lk45hJSFkl35ADfsdDFtkl34fFADFhlktjrfaewr
python ./generate_keys.py --session-name identified@domain.ext
--role-arn arn:aws:iam::AWS_ACCOUNT_ID:role/InstruqtS3Access
--output json
Wenn Sie einen Fehler erhalten, erscheint am Ende des Stack Trace die Meldung: "Das in der Anfrage enthaltene Sicherheits-Token ist ungültig." Das liegt wahrscheinlich daran, dass Sie die Werte von AWS_ACCESS_KEY_ID und AWS_SECRET_ACCESS_KEY nicht ersetzt haben.
Verwenden Sie die generierten temporären Schlüssel
Mit einem Browser
Öffnen Sie einen Browser und kopieren Sie den magischen Link "console_access". Sie werden automatisch eingeloggt. 
Mit der CLI
Es gibt mehrere Möglichkeiten, die temporären Zugangsdaten zu verwenden. Um den Berechtigungsnachweis einmal hinzuzufügen und ihn bei den nächsten Befehlen einfach zu verwenden, erstellen Sie ein neues Profil in ~/.aws/credentials. Mit --output write wird der Abschnitt direkt in die Credentials-Datei geschrieben und kann verwendet werden.
aws s3 ls --profile tmpinstruqt
Programmatischer Zugang
Und Sie können diese Anmeldeinformationen natürlich für den programmatischen Zugriff verwenden. Dies ist nicht der empfohlene Weg, um Geheimnisse in Ihren Code einzufügen, sondern nur für dieses Beispiel hart kodiert.
import boto3
client = boto3.resource(
's3',
aws_access_key_id='ASIAL34H2K3423KL4JLKJLKJ',
aws_secret_access_key='e0hvLM234LKJ23KDAdsf23DFAXiBrNu8Ht',
aws_session_token='F4adsJL2sdafK3J42K3LJ4erg2K3J4....',
)
for bucket in client.buckets.all():
print(bucket.name)
Letzte Worte
Jetzt ist alles eingerichtet und getestet, versuchen Sie nur noch, es zum Erfolg zu führen. Geben Sie den temporären Benutzern mehr Rechte, um andere Dinge als S3 zu tun, die neuen Schlüssel zu generieren und in der CLI, in Skripten oder über die Management-Konsole zu verwenden.
Um aufzuräumen, entfernen Sie einfach die temporären Anmeldeinformationen aus Ihrem terraform destroy aus, um die AWS-Einrichtung zu entfernen.
Sie können den Quellcode von github herunterladen: Generate temp aws credentials
Wenn Sie Fragen haben, können Sie mich gerne kontaktieren. Wir freuen uns über Ihr Feedback, das wir zur Verbesserung dieses Blogbeitrags und zukünftiger Beiträge verwenden werden.
NB. Vielen Dank an meinen Kollegen Mark van Holsteijn. Der Python-Code, der in der ersten Version verwendet wurde, wurde aus der AWS-Dokumentation kopiert. Er war nicht sehr sauber. Also haben wir ihn zu dem Skript umgestaltet, das jetzt in diesem Blogbeitrag verwendet wird. Das Skript hat uns auch zu neuen Blogbeiträgen inspiriert. Bleiben Sie also dran für Updates und ähnliche Dinge.
Verfasst von
Martijn van Dongen
Unsere Ideen
Weitere Blogs
Contact



