Moving to DR model

parent ebbf68d1
import click
from akinaka.client.aws_client import AWS_Client
from akinaka.libs import helpers, scan_resources_storage
from akinaka.libs import helpers, kms_share
from time import gmtime, strftime
import logging
import pprint
import boto3
aws_client = AWS_Client()
helpers.set_logger()
@click.group()
......@@ -14,9 +16,9 @@ helpers.set_logger()
@click.option("--destination-role-arn", required=True, help="ARN of an assumable role in the account to back up _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, source_role_arn, destination_role_arn, dry_run):
def dr(ctx, region, source_role_arn, destination_role_arn, dry_run):
"""
Backup subcommand. Does nothing by itself except pass the global options through to it's
Disaster recovery subcommand. Does nothing by itself except pass the global options through to it's
subcommands via ctx
"""
......@@ -30,14 +32,54 @@ def backup(ctx, region, source_role_arn, destination_role_arn, dry_run):
pass
@backup.command()
def get_shared_kms_key(region, source_role_arn, source_account, destination_account):
"""
Create and return shared KMS account between [source_account] and [destination_account]
"""
kms_sharer = kms_share.KMSShare(
region = region,
assumable_role_arn = source_role_arn,
share_from_account = source_account,
share_to_account = destination_account
)
return kms_sharer.get_kms_key(source_account)
def create_kms_key(region, assumable_role_arn):
"""
Search for a key name that should exists if this has been run before. If not found,
create it. In both cases, return the key.
"""
kms_client = aws_client.create_client('kms', region, assumable_role_arn)
key_alias = "alias/Akinaka"
try:
kms_key = kms_client.describe_key(KeyId=key_alias)
logging.info("Found key: {}".format(kms_key['KeyMetadata']['Arn']))
except kms_client.exceptions.NotFoundException:
kms_key = kms_client.create_key()
logging.info("No existing key found, so we created one: {}".format(kms_key['KeyMetadata']['Arn']))
kms_client.create_alias(
AliasName=key_alias,
TargetKeyId=kms_key['KeyMetadata']['Arn']
)
return kms_key
@dr.command()
@click.pass_context
@click.option("--take-snapshot", is_flag=True, help="TODO: Boolean, default false. Take a live snapshot now, or take the existing latest snapshot")
@click.option("--db-arns", required=False, help="Comma separated list of either DB names or ARNs to transfer")
def rds(ctx, take_snapshot, db_arns):
@click.option("--service", required=False, help="The service to transfer backups for. Defaults to all (RDS, S3)")
def transfer(ctx, take_snapshot, db_arns, service):
"""
Backup all RDS instances found if --db-arns is omitted, else look for the latest
snapshots for those DB names given and transfer them to the destination account
Backup [service] from owning account of [ctx.source_role_arn] to owning account
of [ctx.destination_role_arn].
"""
region = ctx.obj.get('region')
......@@ -45,6 +87,43 @@ def rds(ctx, take_snapshot, db_arns):
destination_role_arn = ctx.obj.get('destination_role_arn')
dry_run = ctx.obj.get('dry_run')
source_sts_client = aws_client.create_client('sts', region, source_role_arn)
source_account = source_sts_client.get_caller_identity()['Account']
destination_sts_client = aws_client.create_client('sts', region, destination_role_arn)
destination_account = destination_sts_client.get_caller_identity()['Account']
source_kms_key = get_shared_kms_key(region, source_role_arn, source_account, destination_account)
destination_kms_key = create_kms_key(region, destination_role_arn)
if service == 'rds':
rds(
dry_run,
region,
source_role_arn,
destination_role_arn,
take_snapshot,
db_arns,
source_kms_key,
destination_kms_key,
source_account,
destination_account)
def rds(
dry_run,
region,
source_role_arn,
destination_role_arn,
take_snapshot,
db_arns,
source_kms_key,
destination_kms_key,
source_account,
destination_account):
"""
Call the RDS class to transfer snapshots
"""
if db_arns:
db_arns = [db_arns.replace(' ','')]
else:
......@@ -61,8 +140,14 @@ def rds(ctx, take_snapshot, db_arns):
rds = transfer_snapshot.TransferSnapshot(
region=region,
source_role_arn=source_role_arn,
destination_role_arn=destination_role_arn
destination_role_arn=destination_role_arn,
source_kms_key=source_kms_key,
destination_kms_key=destination_kms_key
)
shared_kms_key = rds.get_shared_kms_key()
rds.transfer_snapshot(take_snapshot=take_snapshot, db_arns=db_arns, source_kms_key=shared_kms_key)
rds.transfer_snapshot(
take_snapshot=take_snapshot,
db_arns=db_arns,
source_account=source_account,
destination_account=destination_account
)
......@@ -17,7 +17,7 @@ method; transfer_snapshot()
from datetime import datetime
from operator import itemgetter
from akinaka.client.aws_client import AWS_Client
from akinaka.libs import helpers, kms_share
from akinaka.libs import helpers
import logging
import time
......@@ -29,80 +29,40 @@ class TransferSnapshot():
self,
region,
source_role_arn,
destination_role_arn
destination_role_arn,
source_kms_key,
destination_kms_key
):
self.region = region
self.source_role_arn = source_role_arn
self.destination_role_arn = destination_role_arn
self.source_kms_key = source_kms_key
self.destination_kms_key = destination_kms_key
source_sts_client = aws_client.create_client('sts', self.region, self.source_role_arn)
self.source_account = source_sts_client.get_caller_identity()['Account']
destination_sts_client = aws_client.create_client('sts', self.region, self.destination_role_arn)
self.destination_account = destination_sts_client.get_caller_identity()['Account']
def get_shared_kms_key(self):
"""
Create and return shared KMS account between [self.source_account] and [self.destination_account]
"""
kms_sharer = kms_share.KMSShare(
region = self.region,
assumable_role_arn = self.source_role_arn,
share_from_account = self.source_account,
share_to_account = self.destination_account
)
return kms_sharer.get_kms_key(self.source_account)
def create_local_kms_key(self):
"""
Search for a key name that should exists if this has been run before. If not found,
create it. In both cases, return the key.
"""
destination_kms_client = aws_client.create_client('kms', self.region, self.destination_role_arn)
key_alias = "alias/{}".format(self.source_account)
try:
kms_key = destination_kms_client.describe_key(KeyId=key_alias)
logging.info("Found key: {}".format(kms_key['KeyMetadata']['Arn']))
except destination_kms_client.exceptions.NotFoundException:
kms_key = destination_kms_client.create_key()
logging.info("No existing key found, so we created one: {}".format(kms_key['KeyMetadata']['Arn']))
destination_kms_client.create_alias(
AliasName=key_alias,
TargetKeyId=kms_key['KeyMetadata']['Arn']
)
return kms_key
def transfer_snapshot(self, take_snapshot, db_arns, source_kms_key):
def transfer_snapshot(self, take_snapshot, db_arns, source_account, destination_account):
"""
For every DB in [db_arns], call methods to:
1. Either take a new snapshot (TODO), or use the latest automatically created one
2. Recrypt the snapshot with [source_kms_key]. This key must be shared between accounts
2. Recrypt the snapshot with [self.source_kms_key]. This key must be shared between accounts
3. Share it with self.destination_account
4. Copy it to self.destination_account with the [destination_kms_key]
"""
for arn in db_arns:
if take_snapshot:
source_snapshot = self.take_snapshot(arn, source_kms_key)
source_snapshot = self.take_snapshot(arn, self.source_kms_key)
else:
source_snapshot = self.get_latest_snapshot(arn)
source_rds_client = aws_client.create_client('rds', self.region, self.source_role_arn)
recrypted_snapshot = self.recrypt_snapshot(source_rds_client, source_snapshot, source_kms_key)
recrypted_snapshot = self.recrypt_snapshot(source_rds_client, source_snapshot, self.source_kms_key, source_account)
self.share_snapshot(recrypted_snapshot, self.destination_account)
self.share_snapshot(recrypted_snapshot, destination_account)
destination_rds_client = aws_client.create_client('rds', self.region, self.destination_role_arn)
self.recrypt_snapshot(destination_rds_client, recrypted_snapshot, self.create_local_kms_key())
self.recrypt_snapshot(destination_rds_client, recrypted_snapshot, self.destination_kms_key, destination_account)
def get_latest_snapshot(self, db_arn):
"""
......@@ -124,17 +84,17 @@ class TransferSnapshot():
return latest
def make_snapshot_name(self, db_name):
def make_snapshot_name(self, db_name, account):
date = datetime.utcnow().strftime('%Y%m%d-%H%M')
return "{}-{}-{}".format(db_name, date, self.destination_account)
return "{}-{}-{}".format(db_name, date, account)
def recrypt_snapshot(self, rds_client, snapshot, kms_key, tags=None):
def recrypt_snapshot(self, rds_client, snapshot, kms_key, destination_account, tags=None):
"""
Recrypt a snapshot [snapshot] with the KMS key [kms_key]. Return the recrypted snapshot.
"""
new_snapshot_id = self.make_snapshot_name(snapshot['DBInstanceIdentifier'])
new_snapshot_id = self.make_snapshot_name(snapshot['DBInstanceIdentifier'], destination_account)
try:
recrypted_snapshot = rds_client.copy_db_snapshot(
......@@ -185,7 +145,7 @@ class TransferSnapshot():
logging.info("Snapshot {} complete and available!".format(snapshot['DBSnapshotIdentifier']))
break
else:
logging.info("Snapshot {} in progress, {}% complete".format(snapshot['DBSnapshotIdentifier'], snapshotcheck['PercentProgress']))
logging.info("Snapshot {} is in progress; {}% complete".format(snapshot['DBSnapshotIdentifier'], snapshotcheck['PercentProgress']))
time.sleep(10)
def take_snapshot(self, db_name, source_kms_key):
......
......@@ -38,7 +38,7 @@ class KMSShare():
live_kms_client = aws_client.create_client('kms', self.region, self.assumable_role_arn)
key_alias = 'alias/RDSBackupRestoreSharedKeyWith{}'.format(self.share_to_account)
key_alias = 'alias/SharedKeyWithAccount{}'.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:
......@@ -47,7 +47,7 @@ class KMSShare():
return key
except live_kms_client.exceptions.NotFoundException:
logging.info("No valid key found.")
logging.info("No valid key found")
key = self.create_shared_key(share_from_account, key_alias)
return key
......@@ -58,7 +58,7 @@ class KMSShare():
to share_to_account (the account you're calling this from)
"""
logging.info("Creating Customer Managed KMS Key that is shared...")
logging.info("Creating a shared KMS key")
live_kms_client = aws_client.create_client('kms', self.region, self.assumable_role_arn)
......@@ -88,7 +88,7 @@ class KMSShare():
}""" % (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),
Description="Shared encryption key with AWS account {}".format(self.share_to_account),
Policy=key_policy)
live_kms_client.create_alias(
......@@ -96,6 +96,6 @@ class KMSShare():
TargetKeyId=kms_key['KeyMetadata']['Arn']
)
logging.info("Created KMS Key {}, shared with account {}".format(kms_key['KeyMetadata']['Arn'], self.assumable_role_arn))
logging.info("Created KMS Key {}, shared with account {}".format(kms_key['KeyMetadata']['Arn'], self.share_to_account))
return kms_key
......@@ -10,7 +10,8 @@ def main():
from akinaka.reporting.commands import reporting as reporting_commands
from akinaka.container.commands import container as container_commands
from akinaka.k8s.commands import k8s as k8s_commands
from akinaka.backup.commands import backup as backup_commands
from akinaka.dr.commands import dr as dr_commands
@click.group()
@click.option("--log-level", '-l', default="INFO", type=click.Choice(["INFO", "ERROR", "DEBUG"]), help="How much information to show in logging. Default is INFO")
......@@ -24,7 +25,7 @@ def main():
cli.add_command(reporting_commands)
cli.add_command(container_commands)
cli.add_command(k8s_commands)
cli.add_command(backup_commands)
cli.add_command(dr_commands)
cli()
......
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