From c36538ddd1e5ea5a45629e105a177c9d8d3cad0b Mon Sep 17 00:00:00 2001 From: Harold Spencer Jr Date: Mon, 14 Sep 2015 22:29:23 -0400 Subject: [PATCH 1/4] Added compute_resources.py for IAM tests with EC2 resource-level permissions --- setup.py | 2 +- .../resource_level_iam/compute_resources.py | 270 ++++++++++++++++++ 2 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 testcases/cloud_admin/resource_level_iam/compute_resources.py diff --git a/setup.py b/setup.py index ce01b1df..70ee3cc5 100644 --- a/setup.py +++ b/setup.py @@ -46,7 +46,7 @@ url = "http://open.eucalyptus.com", install_requires = ['paramiko >= 1.7','boto >= 2.5.2', 'jinja2 >= 2.7', 'argparse', 'futures', 'python-dateutil', 'mock', 'dnspython', 'pywinrm', - 'BeautifulSoup', 'requests >= 1', 'prettytable'], + 'BeautifulSoup', 'requests >= 1', 'prettytable', 'awacs'], packages = ["eutester","eucaops", "eucaweb", "testcases", "testcases.cloud_admin", "testcases.cloud_admin.riak_cs", "testcases.cloud_admin.riak_cs.templates", "testcases.cloud_user", "testcases.cloud_user.instances", diff --git a/testcases/cloud_admin/resource_level_iam/compute_resources.py b/testcases/cloud_admin/resource_level_iam/compute_resources.py new file mode 100644 index 00000000..dc9d20d5 --- /dev/null +++ b/testcases/cloud_admin/resource_level_iam/compute_resources.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +import re +import string +import random +import socket +import os +from eucaops import Eucaops +from eucaops import EC2ops +from eutester.euinstance import EuInstance +from eutester.eutestcase import EutesterTestCase +from awacs.aws import Action, Allow, Policy, Principal, Statement +from awacs.ec2 import ARN as EC2_ARN + + +class ComputeResourceLevelTest(EutesterTestCase): + def __init__(self): + self.setuptestcase() + self.setup_parser() + self.parser.add_argument('--clean_on_exit', + action='store_true', default=True, + help=("Boolean, used to flag whether " + + "to run clean up method after " + + "running test list")) + self.get_args() + self.tester = Eucaops(credpath=self.args.credpath, + config_file=self.args.config, + password=self.args.password) + self.testers = [] + self.regions = [] + self.reserved_addresses = [] + self.keypairs = [] + for region in self.tester.ec2.get_all_regions(): + region_info = {'name': str(region.name), + 'endpoint': str(region.endpoint)} + self.regions.append(region_info) + + def clean_method(self): + for tester in self.testers: + try: + tester.show_euare_whoami() + except: pass + tester.cleanup_artifacts() + self.tester.delete_account('ec2-account', + recursive=True) + for key in self.keypairs: + os.remove(key) + + def setup_users(self, account_name, group_name, user_name): + users = ['admin', user_name] + self.tester.info("Setting up users in " + account_name) + for user in users: + self.tester.debug("Create access key for " + + user + " user in " + account_name) + keys = self.tester.create_access_key(user_name=user, + delegate_account=account_name) + access_key = keys['access_key_id'] + secret_key = keys['secret_access_key'] + self.tester.debug("Creating EC2ops object with access key " + + access_key + " and secret key " + + secret_key) + new_tester = Eucaops(aws_access_key_id=access_key, + aws_secret_access_key=secret_key, + ec2_ip=self.tester.ec2.host, + ec2_path=self.tester.ec2.path, + iam_ip=self.tester.euare.host, + iam_path=self.tester.euare.path, + s3_ip=self.tester.s3.host, + s3_path=self.tester.s3.path, + sts_ip=self.tester.tokens.host, + sts_path=self.tester.tokens.path, + cw_ip=self.tester.cw.host, + cw_path=self.tester.cw.path, + as_ip=self.tester.autoscale.host, + as_path=self.tester.autoscale.path, + elb_ip=self.tester.elb.host, + elb_path=self.tester.elb.path, + username=user, account=account_name) + self.testers.append(new_tester) + if user != 'admin': + self.tester.debug("Adding " + user + " to " + + group_name) + self.tester.add_user_to_group(group_name, + user, + delegate_account=account_name) + + def group_policy_add(self, group_name, account_name): + policy_id = "EC2-Instance-Resource-Level-Permissions" + sid = "Stmt" + self.tester.id_generator() + pd = Policy( + Version="2012-10-17", + Id=policy_id, + Statement=[ + Statement( + Sid=sid, + Effect=Allow, + Action=[Action("ec2", "*")], + Resource=[EC2_ARN("instance/*"),], + ), + ], + ) + self.tester.debug("Applying " + policy_id + " policy to " + + group_name + " group") + self.tester.attach_policy_group(group_name, + policy_id, + pd.to_json(), + delegate_account=account_name) + + def setup_instance_resources(self, tester, region): + if re.search('^https', region['endpoint']): + ssl_flag = True + else: + ssl_flag = False + endpoint = region['endpoint'].strip(':8773/') + fqdn_endpoint = endpoint.strip('http://https://') + ip_endpoint = socket.gethostbyname(fqdn_endpoint) + tester.info("Establishing EC2 connection to " + region['name'] + + " region") + try: + tester.setup_ec2_connection( + endpoint=ip_endpoint, + region=region['name'], + aws_access_key_id=tester.ec2.aws_access_key_id, + aws_secret_access_key=tester.ec2.aws_secret_access_key, + port=8773, + path="services/compute", + is_secure=ssl_flag) + except Exception, e: + self.errormsg("Unable to establish EC2 connection to " + + "region " + region['name']) + raise e + zone = random.choice(tester.get_zones()) + keypair = tester.add_keypair("keypair-" + tester.id_generator()) + keypath = '%s/%s.pem' % (os.curdir, keypair.name) + self.keypairs.append(keypath) + group = tester.add_group("group-" + tester.id_generator()) + tester.authorize_group_by_name(group_name=group.name) + tester.authorize_group_by_name(group_name=group.name, + port=-1, + protocol="icmp") + if self.args.emi: + image = tester.get_emi(emi=self.args.emi) + else: + image = tester.get_emi(root_device_type="instance-store", + basic_image=True) + params = {'image': image, + 'user_data': self.args.user_data, + 'username': self.args.instance_user, + 'keypair': keypair.name, + 'group': group.name, + 'zone': zone, + 'return_reservation': True, + 'timeout': 600} + reservation = tester.run_image(**params) + for instance in reservation.instances: + self.assertTrue(tester.wait_for_reservation(reservation), + 'Instance did not go to running') + self.assertTrue(tester.ping(instance.ip_address), + 'Could not ping instance') + tester.show_addresses(None, True) + address = tester.allocate_address() + tester.info("Allocate address " + + address.public_ip + " for instance resource " + + "test in region " + region['name']) + addr_info = {'region': region['name'], + 'address': address} + self.reserved_addresses.append(addr_info) + + def test_instance_resources(self, tester, region): + if re.search('^https', region['endpoint']): + ssl_flag = True + else: + ssl_flag = False + endpoint = region['endpoint'].strip(':8773/') + fqdn_endpoint = endpoint.strip('http://https://') + ip_endpoint = socket.gethostbyname(fqdn_endpoint) + tester.info("Establishing EC2 connection to " + region['name'] + + " region") + try: + tester.setup_ec2_connection( + endpoint=ip_endpoint, + region=region['name'], + aws_access_key_id=tester.ec2.aws_access_key_id, + aws_secret_access_key=tester.ec2.aws_secret_access_key, + port=8773, + path="services/compute", + is_secure=ssl_flag) + except Exception, e: + self.errormsg("Unable to establish EC2 connection to " + + "region " + region['name']) + raise e + reservations = tester.ec2.get_all_reservations() + tester.info("Execute DescribeInstances as " + + tester.username + " user") + for reservation in reservations: + self.assertIsNotNone(reservation, + msg=("DescribeInstances failed for " + + region['name'] + " region.")) + + def remove_instance_resources(self, tester, region): + if re.search('^https', region['endpoint']): + ssl_flag = True + else: + ssl_flag = False + endpoint = region['endpoint'].strip(':8773/') + fqdn_endpoint = endpoint.strip('http://https://') + ip_endpoint = socket.gethostbyname(fqdn_endpoint) + tester.info("Establishing EC2 connection to " + region['name'] + + " region") + try: + tester.setup_ec2_connection( + endpoint=ip_endpoint, + region=region['name'], + aws_access_key_id=tester.ec2.aws_access_key_id, + aws_secret_access_key=tester.ec2.aws_secret_access_key, + port=8773, + path="services/compute", + is_secure=ssl_flag) + except Exception, e: + self.errormsg("Unable to establish EC2 connection to " + + "region " + region['name']) + raise e + if self.args.clean_on_exit: + tester.info("Terminate all instances in " + + "region " + region['name']) + reservations = tester.ec2.get_all_reservations() + for reservation in reservations: + tester.terminate_instances(reservation) + tester.info("Release allocated addresses in " + + "region " + region['name']) + for addr_info in self.reserved_addresses: + if addr_info['region'] == region['name']: + tester.release_address(addr_info['address']) + + def InstanceResourceLevel(self): + account_name = 'ec2-account' + group_name = 'ec2_instance_admins' + user_name = 'instance_admin' + self.tester.info("Creating " + account_name) + self.tester.create_account(account_name) + self.tester.info("Creating " + group_name + + " in account " + account_name) + self.tester.create_group(group_name, "/", + delegate_account=account_name) + self.group_policy_add(group_name, account_name) + self.tester.create_user(user_name, + "/", + delegate_account=account_name) + self.setup_users(account_name, + group_name, + user_name) + for resource_tester in self.testers: + if resource_tester.username == 'admin': + for region in self.regions: + self.setup_instance_resources(resource_tester, region) + else: + self.test_instance_resources(resource_tester, region) + for resource_tester in self.testers: + if resource_tester.username == 'admin': + for region in self.regions: + self.remove_instance_resources(resource_tester, region) + + +if __name__ == '__main__': + testcase = ComputeResourceLevelTest() + list = ['InstanceResourceLevel'] + unit_list = [] + for test in list: + unit_list.append(testcase.create_testunit_by_name(test)) + result = testcase.run_test_case_list(unit_list, clean_on_exit=testcase.args.clean_on_exit) + exit(result) From c6262e0f007b0eab3f795bdd3a72578b01bc70b1 Mon Sep 17 00:00:00 2001 From: Harold Spencer Jr Date: Tue, 15 Sep 2015 14:42:57 -0400 Subject: [PATCH 2/4] Added DescribeInstances, DescribeInstanceAttribute, GetConsoleOutput, CreateTags, DescribeTags, DescribeInstanceStatus for resource-level permission tests --- setup.cfg | 2 + .../resource_level_iam/compute_resources.py | 163 +++++++++--------- 2 files changed, 79 insertions(+), 86 deletions(-) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..700c19b7 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[easy_install] + diff --git a/testcases/cloud_admin/resource_level_iam/compute_resources.py b/testcases/cloud_admin/resource_level_iam/compute_resources.py index dc9d20d5..f340f581 100644 --- a/testcases/cloud_admin/resource_level_iam/compute_resources.py +++ b/testcases/cloud_admin/resource_level_iam/compute_resources.py @@ -1,14 +1,11 @@ #!/usr/bin/env python import re -import string import random import socket import os from eucaops import Eucaops -from eucaops import EC2ops -from eutester.euinstance import EuInstance from eutester.eutestcase import EutesterTestCase -from awacs.aws import Action, Allow, Policy, Principal, Statement +from awacs.aws import Action, Allow, Policy, Statement from awacs.ec2 import ARN as EC2_ARN @@ -27,7 +24,6 @@ def __init__(self): password=self.args.password) self.testers = [] self.regions = [] - self.reserved_addresses = [] self.keypairs = [] for region in self.tester.ec2.get_all_regions(): region_info = {'name': str(region.name), @@ -36,10 +32,11 @@ def __init__(self): def clean_method(self): for tester in self.testers: - try: - tester.show_euare_whoami() - except: pass - tester.cleanup_artifacts() + try: + tester.show_euare_whoami() + except: + pass + tester.cleanup_artifacts() self.tester.delete_account('ec2-account', recursive=True) for key in self.keypairs: @@ -55,7 +52,7 @@ def setup_users(self, account_name, group_name, user_name): delegate_account=account_name) access_key = keys['access_key_id'] secret_key = keys['secret_access_key'] - self.tester.debug("Creating EC2ops object with access key " + self.tester.debug("Creating Eucaops object with access key " + access_key + " and secret key " + secret_key) new_tester = Eucaops(aws_access_key_id=access_key, @@ -94,27 +91,32 @@ def group_policy_add(self, group_name, account_name): Sid=sid, Effect=Allow, Action=[Action("ec2", "*")], - Resource=[EC2_ARN("instance/*"),], + Resource=[EC2_ARN("instance/*")], ), ], ) self.tester.debug("Applying " + policy_id + " policy to " - + group_name + " group") + + group_name + " group") self.tester.attach_policy_group(group_name, policy_id, pd.to_json(), delegate_account=account_name) - def setup_instance_resources(self, tester, region): + def connect_to_ec2_endpoint(self, tester, region): if re.search('^https', region['endpoint']): ssl_flag = True else: ssl_flag = False endpoint = region['endpoint'].strip(':8773/') fqdn_endpoint = endpoint.strip('http://https://') - ip_endpoint = socket.gethostbyname(fqdn_endpoint) + try: + ip_endpoint = socket.gethostbyname(fqdn_endpoint) + except Exception as e: + self.errormsg("Unable to resolve Compute endpoint to " + + fqdn_endpoint + ": " + str(e)) + raise e tester.info("Establishing EC2 connection to " + region['name'] - + " region") + + " region") try: tester.setup_ec2_connection( endpoint=ip_endpoint, @@ -124,10 +126,14 @@ def setup_instance_resources(self, tester, region): port=8773, path="services/compute", is_secure=ssl_flag) - except Exception, e: + except Exception as e: self.errormsg("Unable to establish EC2 connection to " - + "region " + region['name']) + + "region " + region['name'] + ": " + str(e)) raise e + return tester + + def setup_instance_resources(self, tester, region): + tester = self.connect_to_ec2_endpoint(tester, region) zone = random.choice(tester.get_zones()) keypair = tester.add_keypair("keypair-" + tester.id_generator()) keypath = '%s/%s.pem' % (os.curdir, keypair.name) @@ -156,80 +162,63 @@ def setup_instance_resources(self, tester, region): 'Instance did not go to running') self.assertTrue(tester.ping(instance.ip_address), 'Could not ping instance') - tester.show_addresses(None, True) - address = tester.allocate_address() - tester.info("Allocate address " - + address.public_ip + " for instance resource " - + "test in region " + region['name']) - addr_info = {'region': region['name'], - 'address': address} - self.reserved_addresses.append(addr_info) def test_instance_resources(self, tester, region): - if re.search('^https', region['endpoint']): - ssl_flag = True - else: - ssl_flag = False - endpoint = region['endpoint'].strip(':8773/') - fqdn_endpoint = endpoint.strip('http://https://') - ip_endpoint = socket.gethostbyname(fqdn_endpoint) - tester.info("Establishing EC2 connection to " + region['name'] - + " region") - try: - tester.setup_ec2_connection( - endpoint=ip_endpoint, - region=region['name'], - aws_access_key_id=tester.ec2.aws_access_key_id, - aws_secret_access_key=tester.ec2.aws_secret_access_key, - port=8773, - path="services/compute", - is_secure=ssl_flag) - except Exception, e: - self.errormsg("Unable to establish EC2 connection to " - + "region " + region['name']) - raise e + tester = self.connect_to_ec2_endpoint(tester, region) reservations = tester.ec2.get_all_reservations() - tester.info("Execute DescribeInstances as " - + tester.username + " user") + tester.info("Execute DescribeInstances as " + + tester.username + " user") for reservation in reservations: self.assertIsNotNone(reservation, msg=("DescribeInstances failed for " + region['name'] + " region.")) + for instance in reservation.instances: + tester.info("Execute DescribeInstanceAttribute for " + + "instance " + instance.id + + "in region " + region['name']) + inst_attr = tester.ec2.get_instance_attribute( + instance.id, 'instanceType') + self.assertIsNotNone(inst_attr, + msg=("DescribeInstanceAttribute " + + "to grab instance type " + + "failed for " + instance.id + + " in region " + region['name'])) + tester.info("Execute GetConsoleOutput for " + + "instance " + instance.id + + "in region " + region['name']) + self.assertIsNotNone(instance.get_console_output().output, + msg=("GetConsoleOuptut failed " + + "for " + instance.id + + " in region " + region['name'])) + tester.info("Execute CreateTags for " + + "instance " + instance.id + + "in region " + region['name']) + try: + instance.add_tag("Purpose", + "Test CreateTags with " + instance.id) + except Exception as e: + self.errormsg("Failed to execute CreateTags with " + + instance.id + " in region " + + region['name'] + ": " + str(e)) + raise e + tester.info("Execute DescribeTags for " + + "instance " + instance.id + + "in region " + region['name']) + self.assertIsNotNone(instance.tags['Purpose'], + msg=("DescribeTags for instance " + + instance.id + "failed in " + + "region " + region['name'])) + tester.info("Execute DescribeInstanceStatus as " + + tester.username + " user") + stats = tester.ec2.get_all_instance_status() + for entry in stats: + self.assertIsNotNone(entry.state_name, + msg=("DescribeInstanceStatus failed for " + + "instance(s) in " + + "region " + region['name'])) def remove_instance_resources(self, tester, region): - if re.search('^https', region['endpoint']): - ssl_flag = True - else: - ssl_flag = False - endpoint = region['endpoint'].strip(':8773/') - fqdn_endpoint = endpoint.strip('http://https://') - ip_endpoint = socket.gethostbyname(fqdn_endpoint) - tester.info("Establishing EC2 connection to " + region['name'] - + " region") - try: - tester.setup_ec2_connection( - endpoint=ip_endpoint, - region=region['name'], - aws_access_key_id=tester.ec2.aws_access_key_id, - aws_secret_access_key=tester.ec2.aws_secret_access_key, - port=8773, - path="services/compute", - is_secure=ssl_flag) - except Exception, e: - self.errormsg("Unable to establish EC2 connection to " - + "region " + region['name']) - raise e - if self.args.clean_on_exit: - tester.info("Terminate all instances in " - + "region " + region['name']) - reservations = tester.ec2.get_all_reservations() - for reservation in reservations: - tester.terminate_instances(reservation) - tester.info("Release allocated addresses in " - + "region " + region['name']) - for addr_info in self.reserved_addresses: - if addr_info['region'] == region['name']: - tester.release_address(addr_info['address']) + tester = self.connect_to_ec2_endpoint(tester, region) def InstanceResourceLevel(self): account_name = 'ec2-account' @@ -238,7 +227,7 @@ def InstanceResourceLevel(self): self.tester.info("Creating " + account_name) self.tester.create_account(account_name) self.tester.info("Creating " + group_name - + " in account " + account_name) + + " in account " + account_name) self.tester.create_group(group_name, "/", delegate_account=account_name) self.group_policy_add(group_name, account_name) @@ -253,12 +242,12 @@ def InstanceResourceLevel(self): for region in self.regions: self.setup_instance_resources(resource_tester, region) else: - self.test_instance_resources(resource_tester, region) + for region in self.regions: + self.test_instance_resources(resource_tester, region) for resource_tester in self.testers: if resource_tester.username == 'admin': for region in self.regions: self.remove_instance_resources(resource_tester, region) - if __name__ == '__main__': testcase = ComputeResourceLevelTest() @@ -266,5 +255,7 @@ def InstanceResourceLevel(self): unit_list = [] for test in list: unit_list.append(testcase.create_testunit_by_name(test)) - result = testcase.run_test_case_list(unit_list, clean_on_exit=testcase.args.clean_on_exit) + result = testcase.run_test_case_list( + unit_list, + clean_on_exit=testcase.args.clean_on_exit) exit(result) From dc649457a75a4c761abd3bab1e3f5880bfe02d16 Mon Sep 17 00:00:00 2001 From: Harold Spencer Jr Date: Tue, 15 Sep 2015 16:28:44 -0400 Subject: [PATCH 3/4] Added documentation to compute_resources.py --- .../resource_level_iam/compute_resources.py | 112 +++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/testcases/cloud_admin/resource_level_iam/compute_resources.py b/testcases/cloud_admin/resource_level_iam/compute_resources.py index f340f581..c3ec5244 100644 --- a/testcases/cloud_admin/resource_level_iam/compute_resources.py +++ b/testcases/cloud_admin/resource_level_iam/compute_resources.py @@ -1,4 +1,11 @@ #!/usr/bin/env python +""" +Purpose: Testcase to confirm resoure-level permissions + for Compute (EC2) API actions are supported + in a Eucalyptus environment + +Author: Harold Spencer, Jr. (https://github.com/hspencer77) +""" import re import random import socket @@ -11,6 +18,14 @@ class ComputeResourceLevelTest(EutesterTestCase): def __init__(self): + """ + Function to initialize testcase to + confirm resource-level permissions are + supported for Compute (EC2) API actions + + param: --credpath: path to directory + location of Eucalyptus credentials + """ self.setuptestcase() self.setup_parser() self.parser.add_argument('--clean_on_exit', @@ -31,18 +46,35 @@ def __init__(self): self.regions.append(region_info) def clean_method(self): + """ + Function to clean up artifacts associated + with test case. + """ for tester in self.testers: try: tester.show_euare_whoami() except: pass tester.cleanup_artifacts() + # Delete account, groups, users self.tester.delete_account('ec2-account', recursive=True) + # Remove keypairs created for key in self.keypairs: os.remove(key) def setup_users(self, account_name, group_name, user_name): + """ + Function to set up users under a given account as + testers. For each user, the following is created: + - access key id + - secret key + - Eucaops object + + param: account_name: IAM (Euare) account + param: group_name: IAM (Euare) group + param: user_name: IAM (Euare) user + """ users = ['admin', user_name] self.tester.info("Setting up users in " + account_name) for user in users: @@ -73,6 +105,7 @@ def setup_users(self, account_name, group_name, user_name): elb_path=self.tester.elb.path, username=user, account=account_name) self.testers.append(new_tester) + # If not 'admin' user, add user to group if user != 'admin': self.tester.debug("Adding " + user + " to " + group_name) @@ -81,6 +114,14 @@ def setup_users(self, account_name, group_name, user_name): delegate_account=account_name) def group_policy_add(self, group_name, account_name): + """ + Function to create IAM access policy with resource-level + permission, then apply the policy to a group + under the account. + + param: group_name: IAM (Euare) group + param: account_name: IAM (Euare) account + """ policy_id = "EC2-Instance-Resource-Level-Permissions" sid = "Stmt" + self.tester.id_generator() pd = Policy( @@ -103,6 +144,13 @@ def group_policy_add(self, group_name, account_name): delegate_account=account_name) def connect_to_ec2_endpoint(self, tester, region): + """ + Function to establish EC2 connection to a + specific region (based on endpoint) + + param: tester: Eucaops object + param: region: region (i.e. cloud) name/endpoint information + """ if re.search('^https', region['endpoint']): ssl_flag = True else: @@ -133,6 +181,12 @@ def connect_to_ec2_endpoint(self, tester, region): return tester def setup_instance_resources(self, tester, region): + """ + Function to set up EC2 instance resources + + param: tester: Eucaops object ('admin' user) + param: region: region (i.e. cloud) name/endpoint information + """ tester = self.connect_to_ec2_endpoint(tester, region) zone = random.choice(tester.get_zones()) keypair = tester.add_keypair("keypair-" + tester.id_generator()) @@ -143,6 +197,7 @@ def setup_instance_resources(self, tester, region): tester.authorize_group_by_name(group_name=group.name, port=-1, protocol="icmp") + # Use supplied EMI, if not find instance-store backed EMI if self.args.emi: image = tester.get_emi(emi=self.args.emi) else: @@ -158,14 +213,25 @@ def setup_instance_resources(self, tester, region): 'timeout': 600} reservation = tester.run_image(**params) for instance in reservation.instances: + # Confirm instance reaches 'running' state self.assertTrue(tester.wait_for_reservation(reservation), 'Instance did not go to running') + # Confirm instance can be pinged self.assertTrue(tester.ping(instance.ip_address), 'Could not ping instance') def test_instance_resources(self, tester, region): + """ + Function to perform Compute (EC2) API actions + to confirm access to all instances under the + account. + + param: tester: Eucaops object ('instance_admin' user) + param: region: region (i.e. cloud) name/endpoint information + """ tester = self.connect_to_ec2_endpoint(tester, region) reservations = tester.ec2.get_all_reservations() + # Test DescribeInstances tester.info("Execute DescribeInstances as " + tester.username + " user") for reservation in reservations: @@ -173,6 +239,7 @@ def test_instance_resources(self, tester, region): msg=("DescribeInstances failed for " + region['name'] + " region.")) for instance in reservation.instances: + # Test DescribeInstanceAttribute tester.info("Execute DescribeInstanceAttribute for " + "instance " + instance.id + "in region " + region['name']) @@ -183,6 +250,7 @@ def test_instance_resources(self, tester, region): + "to grab instance type " + "failed for " + instance.id + " in region " + region['name'])) + # Test GetConsoleOutput tester.info("Execute GetConsoleOutput for " + "instance " + instance.id + "in region " + region['name']) @@ -190,6 +258,7 @@ def test_instance_resources(self, tester, region): msg=("GetConsoleOuptut failed " + "for " + instance.id + " in region " + region['name'])) + # Test CreateTags tester.info("Execute CreateTags for " + "instance " + instance.id + "in region " + region['name']) @@ -201,6 +270,7 @@ def test_instance_resources(self, tester, region): + instance.id + " in region " + region['name'] + ": " + str(e)) raise e + # Test DescribeTags tester.info("Execute DescribeTags for " + "instance " + instance.id + "in region " + region['name']) @@ -208,6 +278,7 @@ def test_instance_resources(self, tester, region): msg=("DescribeTags for instance " + instance.id + "failed in " + "region " + region['name'])) + # Test DescribeInstanceStatus tester.info("Execute DescribeInstanceStatus as " + tester.username + " user") stats = tester.ec2.get_all_instance_status() @@ -218,43 +289,80 @@ def test_instance_resources(self, tester, region): + "region " + region['name'])) def remove_instance_resources(self, tester, region): + """ + Function to remove instances from under account + + param: tester: Eucaops object ('admin' user of account) + param: region: region (i.e. cloud) name/endpoint information + """ tester = self.connect_to_ec2_endpoint(tester, region) + reservations = tester.ec2.get_all_reservations() + for reservation in reservations: + tester.terminate_instances(reservation) + + def InstanceResourceLevelTest(self): + """ + Function to execute testcase to confirm + support Compute API actions for resource-level defined ARN + for instance(s) under a given IAM (Euare) account. + + IAM access policy contains the following: + - Effect: Allow + - Action: All EC2 actions (i.e. ec2:*) + - Resource: All instances (i.e. arn:aws:ec2:::instance/*) - def InstanceResourceLevel(self): + The following is performed: + * IAM (Euare) account/user/group creation + * IAM access policy with resource ARN defined for all + instances. + * Creation of instances by 'admin' user of account + * Test API actions associated with instances under the + account by 'instance_admin' user + * Removal of instances by 'admin' user of account + """ account_name = 'ec2-account' group_name = 'ec2_instance_admins' user_name = 'instance_admin' self.tester.info("Creating " + account_name) + # Create 'ec2-account' account self.tester.create_account(account_name) self.tester.info("Creating " + group_name + " in account " + account_name) + # Create 'ec2_instance_admins' group and add IAM policy self.tester.create_group(group_name, "/", delegate_account=account_name) self.group_policy_add(group_name, account_name) + # Create 'instance_admin' user self.tester.create_user(user_name, "/", delegate_account=account_name) + # Set up test users self.setup_users(account_name, group_name, user_name) for resource_tester in self.testers: if resource_tester.username == 'admin': + # If 'admin' user, create EC2 resources for region in self.regions: self.setup_instance_resources(resource_tester, region) else: + # Test API actions against instance resources for region in self.regions: self.test_instance_resources(resource_tester, region) for resource_tester in self.testers: if resource_tester.username == 'admin': + # If 'admin' user, remove EC2 resources for region in self.regions: self.remove_instance_resources(resource_tester, region) if __name__ == '__main__': + # Define ComputeResourceLevelTest testcase testcase = ComputeResourceLevelTest() - list = ['InstanceResourceLevel'] + list = ['InstanceResourceLevelTest'] unit_list = [] for test in list: unit_list.append(testcase.create_testunit_by_name(test)) + # Execute testcase result = testcase.run_test_case_list( unit_list, clean_on_exit=testcase.args.clean_on_exit) From d624c7586a8333fc10935b6ff4a9457bb9ab53fe Mon Sep 17 00:00:00 2001 From: Harold Spencer Jr Date: Thu, 17 Sep 2015 17:03:35 -0400 Subject: [PATCH 4/4] Added testcases InstanceAccountResourceLevelTest, InstanceWildcardAccountResourceLevelTest and InstanceWildcardResourceLevelTest --- .../resource_level_iam/compute_resources.py | 268 +++++++++++++++--- 1 file changed, 230 insertions(+), 38 deletions(-) diff --git a/testcases/cloud_admin/resource_level_iam/compute_resources.py b/testcases/cloud_admin/resource_level_iam/compute_resources.py index c3ec5244..28bd1b24 100644 --- a/testcases/cloud_admin/resource_level_iam/compute_resources.py +++ b/testcases/cloud_admin/resource_level_iam/compute_resources.py @@ -13,7 +13,6 @@ from eucaops import Eucaops from eutester.eutestcase import EutesterTestCase from awacs.aws import Action, Allow, Policy, Statement -from awacs.ec2 import ARN as EC2_ARN class ComputeResourceLevelTest(EutesterTestCase): @@ -40,6 +39,7 @@ def __init__(self): self.testers = [] self.regions = [] self.keypairs = [] + self.accounts = [] for region in self.tester.ec2.get_all_regions(): region_info = {'name': str(region.name), 'endpoint': str(region.endpoint)} @@ -50,18 +50,26 @@ def clean_method(self): Function to clean up artifacts associated with test case. """ + # Delete account, groups, users + for account in self.accounts: + self.tester.delete_account(account, + recursive=True) + # Remove keypairs created + for key in self.keypairs: + os.remove(key) + + def remove_testers(self): + """ + Function to remove testers after testcase + has completed. + """ for tester in self.testers: try: tester.show_euare_whoami() except: pass tester.cleanup_artifacts() - # Delete account, groups, users - self.tester.delete_account('ec2-account', - recursive=True) - # Remove keypairs created - for key in self.keypairs: - os.remove(key) + self.testers.remove(tester) def setup_users(self, account_name, group_name, user_name): """ @@ -113,7 +121,9 @@ def setup_users(self, account_name, group_name, user_name): user, delegate_account=account_name) - def group_policy_add(self, group_name, account_name): + def group_policy_add(self, group_name, account_name, + account_id=None, region=None, + instance_id=None): """ Function to create IAM access policy with resource-level permission, then apply the policy to a group @@ -121,9 +131,21 @@ def group_policy_add(self, group_name, account_name): param: group_name: IAM (Euare) group param: account_name: IAM (Euare) account + param: account_id: Account ID + param: region: region (i.e. cloud) name/endpoint information + param: instance_id: instance ID """ policy_id = "EC2-Instance-Resource-Level-Permissions" sid = "Stmt" + self.tester.id_generator() + if account_id is None: + account_id = "" + if region is None: + region = "" + if instance_id is None: + instance_id = "*" + ec2_arn = ("arn:aws:ec2:" + region + ":" + + account_id + ":" + + "instance/" + instance_id) pd = Policy( Version="2012-10-17", Id=policy_id, @@ -132,16 +154,23 @@ def group_policy_add(self, group_name, account_name): Sid=sid, Effect=Allow, Action=[Action("ec2", "*")], - Resource=[EC2_ARN("instance/*")], + Resource=[ec2_arn], ), ], ) self.tester.debug("Applying " + policy_id + " policy to " + group_name + " group") - self.tester.attach_policy_group(group_name, - policy_id, - pd.to_json(), - delegate_account=account_name) + try: + self.tester.attach_policy_group(group_name, + policy_id, + pd.to_json(), + delegate_account=account_name) + except Exception as e: + self.tester.debug("Policy failed to be applied " + + "to group " + group_name + + ": " + str(e)) + return False + return True def connect_to_ec2_endpoint(self, tester, region): """ @@ -300,6 +329,163 @@ def remove_instance_resources(self, tester, region): for reservation in reservations: tester.terminate_instances(reservation) + def InstanceWildcardResourceLevelTest(self): + """ + [**] NEGATIVE TEST [**] + Function to execute testcase to confirm + malformed document error when IAM access policies for + Compute API actions with resource-level defined ARN + (containing wildcards for 'region' and 'account-id') for instance(s) + under a given IAM (Euare) account. + + IAM access policy contains the following: + - Effect: Allow + - Action: All EC2 actions (i.e. ec2:*) + - Resource: All instances (i.e. arn:aws:ec2:*:*:instance/*) + + The following is performed: + * IAM (Euare) account/user/group creation + * IAM access policy with resource ARN, which contains + wildcard (*) for 'region' and 'account-id', + defined for all instances. + """ + account_name = 'wildcards-acct-account' + self.accounts.append(account_name) + group_name = 'ec2_instance_admins' + self.tester.info("Creating " + account_name) + # Create 'ec2-account' account + self.tester.create_account(account_name) + self.tester.info("Creating " + group_name + + " in account " + account_name) + # Create 'ec2_instance_admins' group and add IAM policy + self.tester.create_group(group_name, "/", + delegate_account=account_name) + region_name = "*" + account_id = "*" + result = self.group_policy_add(group_name, account_name, + account_id=account_id, + region=region_name) + # Confirm IAM policy wasn't accepted + self.tester.debug("Applied malformed policy. Test should" + + " pass on failed upload of policy.") + self.assertFalse(result, msg=("Malformed IAM access policy " + + "with wildcard(*) value for 'region' and " + + "'account-id' was accepted")) + + def InstanceAccountResourceLevelTest(self): + """ + Function to execute testcase to confirm + support Compute API actions for resource-level defined ARN + (containing 'account-id') for instance(s) + under a given IAM (Euare) account. + + IAM access policy contains the following: + - Effect: Allow + - Action: All EC2 actions (i.e. ec2:*) + - Resource: All instances (i.e. arn:aws:ec2:::instance/*) + + The following is performed: + * IAM (Euare) account/user/group creation + * IAM access policy with resource ARN, which contains + 'account-id', defined for all instances. + * Creation of instances by 'admin' user of account + * Test API actions associated with instances under the + account by 'instance_admin' user + * Removal of instances by 'admin' user of account + """ + account_name = 'strict-acct-account' + self.accounts.append(account_name) + group_name = 'ec2_instance_admins' + user_name = 'instance_admin' + self.tester.info("Creating " + account_name) + # Create 'ec2-account' account + self.tester.create_account(account_name) + self.tester.info("Creating " + group_name + + " in account " + account_name) + # Create 'ec2_instance_admins' group and add IAM policy + self.tester.create_group(group_name, "/", + delegate_account=account_name) + try: + usr = self.tester.get_users_from_account( + delegate_account=account_name) + except Exception as e: + self.errormsg("Failed obtain 'admin' user " + + " from account " + + account_name + ": " + str(e)) + raise e + admin_user = usr[0] + account_id = str(admin_user['arn'].split(':')[4]) + result = self.group_policy_add(group_name, account_name, + account_id=account_id) + if result: + # Create 'instance_admin' user + self.tester.create_user(user_name, + "/", + delegate_account=account_name) + # Set up test users + self.setup_users(account_name, + group_name, + user_name) + for resource_tester in self.testers: + if resource_tester.username == 'admin': + # If 'admin' user, create EC2 resources + for region in self.regions: + self.setup_instance_resources(resource_tester, + region) + else: + # Test API actions against instance resources + for region in self.regions: + self.test_instance_resources(resource_tester, + region) + for resource_tester in self.testers: + if resource_tester.username == 'admin': + # If 'admin' user, remove EC2 resources + for region in self.regions: + self.remove_instance_resources(resource_tester, + region) + # Remove testers from self.testers list + self.remove_testers() + + def InstanceWildcardAccountResourceLevelTest(self): + """ + [**] NEGATIVE TEST [**] + Function to execute testcase to confirm + malformed document error for IAM access policy for + Compute API actions with resource-level defined ARN + (containing a wildcard for 'account-id') for instance(s) + under a given IAM (Euare) account. + + IAM access policy contains the following: + - Effect: Allow + - Action: All EC2 actions (i.e. ec2:*) + - Resource: All instances (i.e. arn:aws:ec2::*:instance/*) + + The following is performed: + * IAM (Euare) account/user/group creation + * IAM access policy with resource ARN, which contains + a wildcard for 'account-id', defined for all instances. + """ + account_name = 'wildcardacct-acct-account' + self.accounts.append(account_name) + group_name = 'ec2_instance_admins' + self.tester.info("Creating " + account_name) + # Create 'ec2-account' account + self.tester.create_account(account_name) + self.tester.info("Creating " + group_name + + " in account " + account_name) + # Create 'ec2_instance_admins' group and add IAM policy + self.tester.create_group(group_name, "/", + delegate_account=account_name) + account_id = "*" + result = self.group_policy_add(group_name, account_name, + account_id=account_id) + # Confirm IAM policy wasn't accepted + self.tester.debug("Applied malformed policy. Test should" + + " pass on failed upload of policy.") + self.assertFalse(result, msg=("Malformed IAM access policy " + + "with wildcard(*) value 'account-id' " + + "was accepted")) + def InstanceResourceLevelTest(self): """ Function to execute testcase to confirm @@ -320,7 +506,8 @@ def InstanceResourceLevelTest(self): account by 'instance_admin' user * Removal of instances by 'admin' user of account """ - account_name = 'ec2-account' + account_name = 'basic-account' + self.accounts.append(account_name) group_name = 'ec2_instance_admins' user_name = 'instance_admin' self.tester.info("Creating " + account_name) @@ -331,34 +518,39 @@ def InstanceResourceLevelTest(self): # Create 'ec2_instance_admins' group and add IAM policy self.tester.create_group(group_name, "/", delegate_account=account_name) - self.group_policy_add(group_name, account_name) - # Create 'instance_admin' user - self.tester.create_user(user_name, - "/", - delegate_account=account_name) - # Set up test users - self.setup_users(account_name, - group_name, - user_name) - for resource_tester in self.testers: - if resource_tester.username == 'admin': - # If 'admin' user, create EC2 resources - for region in self.regions: - self.setup_instance_resources(resource_tester, region) - else: - # Test API actions against instance resources - for region in self.regions: - self.test_instance_resources(resource_tester, region) - for resource_tester in self.testers: - if resource_tester.username == 'admin': - # If 'admin' user, remove EC2 resources - for region in self.regions: - self.remove_instance_resources(resource_tester, region) + result = self.group_policy_add(group_name, account_name) + if result: + # Create 'instance_admin' user + self.tester.create_user(user_name, + "/", + delegate_account=account_name) + # Set up test users + self.setup_users(account_name, + group_name, + user_name) + for resource_tester in self.testers: + if resource_tester.username == 'admin': + # If 'admin' user, create EC2 resources + for region in self.regions: + self.setup_instance_resources(resource_tester, region) + else: + # Test API actions against instance resources + for region in self.regions: + self.test_instance_resources(resource_tester, region) + for resource_tester in self.testers: + if resource_tester.username == 'admin': + # If 'admin' user, remove EC2 resources + for region in self.regions: + self.remove_instance_resources(resource_tester, region) + # Remove testers from self.testers list + self.remove_testers() if __name__ == '__main__': # Define ComputeResourceLevelTest testcase testcase = ComputeResourceLevelTest() - list = ['InstanceResourceLevelTest'] + list = ['InstanceResourceLevelTest', 'InstanceAccountResourceLevelTest', + 'InstanceWildcardAccountResourceLevelTest', + 'InstanceWildcardResourceLevelTest'] unit_list = [] for test in list: unit_list.append(testcase.create_testunit_by_name(test))