Share KMS key

parent c9a1f221
......@@ -4,21 +4,30 @@ from akinaka.libs import helpers, scan_resources_storage
from time import gmtime, strftime
import logging
import pprint
import boto3
@click.group()
@click.option("--region", required=True, help="Region your resources are located in")
@click.option("--role-arn", required=True, help="Role ARN which contains necessary assume permissions")
@click.option("--role-arn", required=True, help="ARN of a role the account to back up _to_, which can be assumed with STS")
@click.option("--live-account", required=True, help="Account ID of the account with data to backup")
@click.option("--backup-account", required=False, help="Account ID of the account to make the backups to")
@click.option("--dry-run", is_flag=True, help="Don't back anything up, just list would be backed up")
@click.pass_context
def backup(ctx, region, role_arn, dry_run):
def backup(ctx, region, role_arn, live_account, backup_account, dry_run):
"""
Backup subcommand. Does nothing by itself except pass the global options through to it's
subcommands via ctx
"""
if backup_account == None:
sts_client = boto3.client('sts')
backup_account = sts_client.get_caller_identity()['Account']
ctx.obj = {
'region': region,
'role_arn': role_arn,
'live_account': live_account,
'backup_account': backup_account,
'dry_run': dry_run,
'log_level': ctx.obj.get('log_level')
}
......@@ -32,6 +41,7 @@ def backup_all(ctx):
region = ctx.obj.get('region')
role_arn = ctx.obj.get('role_arn')
live_account = ctx.obj.get('live_account')
dry_run = ctx.obj.get('dry_run')
scanner = scan_resources_storage.ScanResources(region, role_arn)
......@@ -50,6 +60,7 @@ def aurora(ctx):
region = ctx.obj.get('region')
role_arn = ctx.obj.get('role_arn')
live_account = ctx.obj.get('live_account')
dry_run = ctx.obj.get('dry_run')
scanner = scan_resources_storage.ScanResources(region, role_arn)
......@@ -68,7 +79,9 @@ def rds(ctx):
region = ctx.obj.get('region')
role_arn = ctx.obj.get('role_arn')
live_account = ctx.obj.get('live_account')
dry_run = ctx.obj.get('dry_run')
backup_account = ctx.obj.get('backup_account')
scanner = scan_resources_storage.ScanResources(region, role_arn)
rds_arns = scanner.scan_rds()
......@@ -80,7 +93,12 @@ def rds(ctx):
exit(0)
from .rds import backup_rds
rds = backup_rds.BackupRDS(region=region, role_arn=role_arn)
rds = backup_rds.BackupRDS(
region=region,
assumable_role_arn=role_arn,
live_account=live_account,
backup_account=backup_account
)
rds.backup(rds_arns=rds_arns)
@backup.command()
......@@ -90,6 +108,7 @@ def s3(ctx):
region = ctx.obj.get('region')
role_arn = ctx.obj.get('role_arn')
live_account = ctx.obj.get('live_account')
dry_run = ctx.obj.get('dry_run')
scanner = scan_resources_storage.ScanResources(region, role_arn)
......
......@@ -12,23 +12,25 @@ destination account by using a KMS key in the destination account.
import boto3
from akinaka.client.aws_client import AWS_Client
from akinaka.libs import helpers
from akinaka.libs import helpers, kms_share
import logging
helpers.set_logger()
aws_client = AWS_Client()
class BackupRDS():
def __init__(self, region, role_arn):
def __init__(self, region, assumable_role_arn, live_account, backup_account):
self.region = region
self.role_arn = role_arn
self.assumable_role_arn = assumable_role_arn
self.live_account = live_account
self.backup_account = backup_account
def backup(self, rds_arns):
"""
1. Create KMS key in source account
1. Share KMS key with destination account (caller — this account)
1.
1. Create snapshot with this KMS key
1. Share KMS key with destination account
"""
print(rds_arns)
kms_sharer = kms_share.KMSShare(
region = self.region,
assumable_role_arn = self.assumable_role_arn,
share_from_account = self.live_account,
share_to_account = self.backup_account
)
shared_key = kms_sharer.get_kms_key(self.live_account)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Create shared KMS keys for use in encrypting and decrypting the same objects in different accounts.
Terminology used below:
share_from_account = The account to create the key in, where the resources to be encrypted are
share_to_account = The account you're calling this from
assumable_role_arn = Role in share_from_account that permits STS assume from share_to_account
"""
import boto3
from akinaka.libs import helpers
import logging
from akinaka.client.aws_client import AWS_Client
aws_client = AWS_Client()
helpers.set_logger()
class KMSShare():
def __init__(self, region, assumable_role_arn, share_from_account, share_to_account):
self.region = region
self.assumable_role_arn = assumable_role_arn
self.share_from_account = share_from_account
self.share_to_account = share_to_account
self.share_to_account_arn = "arn:aws:iam::{}:root".format(share_to_account)
def get_kms_key(self, share_from_account):
"""
Check to see if there is already a shared key. The name (alias) to check will be:
alias/RDSBackupRestoreSharedKeyWith[(account id of)share_to_account]. If there is no shared key,
call create_shared_key() to make one.
In both cases, return the ID of a shared KMS key
"""
live_kms_client = aws_client.create_client('kms', self.region, self.assumable_role_arn)
key_alias = 'alias/RDSBackupRestoreSharedKeyWith{}'.format(self.share_to_account)
logging.info("Searching for Customer Managed KMS Key with alias {} that is already shared with account {}...".format(key_alias, self.share_to_account))
try:
key = live_kms_client.describe_key(KeyId=key_alias)
logging.info("Found key: {}".format(key['KeyMetadata']['Arn']))
return key
except live_kms_client.exceptions.NotFoundException:
logging.info("No valid key found.")
key = self.create_shared_key(share_from_account, key_alias)
return key
def create_shared_key(self, share_from_account, key_alias):
"""
Create new KMS key in share_from_account, with a policy that shares it
to share_to_account (the account you're calling this from)
"""
logging.info("Creating Customer Managed KMS Key that is shared...")
live_kms_client = aws_client.create_client('kms', self.region, self.assumable_role_arn)
key_policy = """{
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::%s:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow use of the key by the %s account",
"Effect": "Allow",
"Principal": {
"AWS": "%s"
},
"Action": "kms:*",
"Resource": "*"
}
]
}""" % (self.share_from_account, self.share_to_account, self.share_to_account_arn)
kms_key = live_kms_client.create_key(
Description="Shared encryption key with AWS account {}".format(self.assumable_role_arn),
Policy=key_policy)
live_kms_client.create_alias(
AliasName=key_alias,
TargetKeyId=kms_key['KeyMetadata']['Arn']
)
logging.info("Created KMS Key {}, shared with account {}".format(kms_key['KeyMetadata']['Arn'], self.assumable_role_arn))
return kms_key
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Scans services that may contain data, and returns a list with information on any storage found:
......@@ -22,7 +25,6 @@ FIXME: EFS is currently out of scope, due to it not being treated as a native AW
]
"""
import boto3
from akinaka.client.aws_client import AWS_Client
......
......@@ -106,7 +106,7 @@ class CopyRDS():
"Resource": "*"
},
{
"Sid": "Allow use of the key by the %s",
"Sid": "Allow use of the key by %s",
"Effect": "Allow",
"Principal": {
"AWS": "%s"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment