diff --git a/CHANGELOG.md b/CHANGELOG.md index af49471..0cef09a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## Version 0.5.x +* Add an SSL cipher list policy pindown: implicitly (no YAML entry needed) + or explicitly (with YAML entry) ## Version 0.5.3 * Improve message content when cfn_create raises an exception and fails. diff --git a/README.rst b/README.rst index 127e8f2..88e949c 100644 --- a/README.rst +++ b/README.rst @@ -147,3 +147,23 @@ You can enable encryption for your DB by adding the following:: **NOTE:** AWS does not support RDS encryption for the *db.t2.** instance classes. More details on supported instance classes are available `here `_ + +SSL cipher list pindown (updated 29/06/2015) +============================================ +Amazon provides default policies for cipher lists: + +* Type: SSLNegotiationPolicyType +* Name: Reference-Security-Policy + +More info: + +https://aws.amazon.com/blogs/aws/elastic-load-balancing-perfect-forward-secrecy-and-other-security-enhancements/ + +http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-security-policy-options.html + +http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-ssl-security-policy.html + +http://docs.aws.amazon.com/ElasticLoadBalancing/latest/DeveloperGuide/elb-security-policy-table.html + +The policy currently in use by default is: ELBSecurityPolicy-2015-05. + diff --git a/bootstrap_cfn/config.py b/bootstrap_cfn/config.py index f83c003..444595a 100644 --- a/bootstrap_cfn/config.py +++ b/bootstrap_cfn/config.py @@ -9,7 +9,7 @@ from troposphere.autoscaling import LaunchConfiguration, \ AutoScalingGroup, BlockDeviceMapping, EBSBlockDevice, Tag from troposphere.elasticloadbalancing import LoadBalancer, HealthCheck, \ - ConnectionDrainingPolicy + ConnectionDrainingPolicy, Policy from troposphere.ec2 import SecurityGroup from troposphere.route53 import RecordSetGroup, RecordSet, AliasTarget from troposphere.ec2 import Route, Subnet, InternetGateway, VPC, \ @@ -25,7 +25,11 @@ class ProjectConfig: config = None def __init__(self, config, environment, passwords=None): - self.config = self.load_yaml(config)[environment] + try: + self.config = self.load_yaml(config)[environment] + except KeyError: + raise errors.BootstrapCfnError("Environment " + environment + " not found") + if passwords: passwords_dict = self.load_yaml(passwords)[environment] self.config = utils.dict_merge(self.config, passwords_dict) @@ -401,8 +405,14 @@ def elb(self): Enabled=True, Timeout=120, ), + Policies=[ + Policy( + Attributes=[{'Name': "Reference-Security-Policy", 'Value': "ELBSecurityPolicy-2015-05"}], + PolicyType='SSLNegotiationPolicyType', + PolicyName='PinDownSSLNegotiationPolicy201505' + ) + ] ) - if "health_check" in elb: load_balancer.HealthCheck = HealthCheck(**elb['health_check']) @@ -426,6 +436,12 @@ def elb(self): ":server-certificate/", "{0}-{1}".format(cert_name, self.stack_name)] ) + # if not present, add the default cipher policy + if 'PolicyNames' not in listener: + logging.debug( + "ELB Listener for port 443 has no SSL Policy. " + + "Using default ELBSecurityPolicy-2015-05") + listener['PolicyNames'] = ['PinDownSSLNegotiationPolicy201505'] elb_list.append(load_balancer) diff --git a/tests/test.py b/tests/test.py index 3ad7b62..e72e47b 100644 --- a/tests/test.py +++ b/tests/test.py @@ -47,7 +47,8 @@ def setUp(self): 'Protocol': 'TCP'}, {'InstancePort': 443, 'LoadBalancerPort': 443, - 'Protocol': 'TCP'}], + 'Protocol': 'TCP', + 'PolicyNames': 'PinDownSSLNegotiationPolicy201505'}], 'name': 'test-dev-external', 'scheme': 'internet-facing'}, {'hosted_zone': 'kyrtest.pf.dsd.io.', diff --git a/tests/tests.py b/tests/tests.py old mode 100644 new mode 100755 index 23f8e0d..90663b3 --- a/tests/tests.py +++ b/tests/tests.py @@ -11,7 +11,7 @@ from troposphere.route53 import RecordSetGroup from troposphere.elasticloadbalancing import LoadBalancer, HealthCheck,\ - ConnectionDrainingPolicy + ConnectionDrainingPolicy, Policy from troposphere.iam import PolicyType import bootstrap_cfn.errors as errors @@ -233,6 +233,13 @@ def test_elb(self): SecurityGroups=[Ref("DefaultSGtestdevinternal")], LoadBalancerName="ELB-test-dev-internal", Scheme="internal", + Policies=[ + Policy( + Attributes=[{'Name': "Reference-Security-Policy", 'Value': "ELBSecurityPolicy-2015-05"}], + PolicyType='SSLNegotiationPolicyType', + PolicyName='PinDownSSLNegotiationPolicy201505' + ) + ] ) pt1 = PolicyType( @@ -333,6 +340,13 @@ def test_elb(self): SecurityGroups=[Ref("DefaultSGtestdevexternal")], LoadBalancerName="ELB-test-dev-external", Scheme="internet-facing", + Policies=[ + Policy( + Attributes=[{'Name': "Reference-Security-Policy", 'Value': "ELBSecurityPolicy-2015-05"}], + PolicyType='SSLNegotiationPolicyType', + PolicyName='PinDownSSLNegotiationPolicy201505' + ) + ], ) known = [lb, lb2, pt1, pt2, rs, rsg] expected_sgs = [ @@ -653,10 +667,18 @@ def test_elb_with_ssl(self): {"InstancePort": 443, "SSLCertificateId": Join( "", ["arn:aws:iam::", Ref("AWS::AccountId"), ":server-certificate/", "my-cert-my-stack-name"]), - "LoadBalancerPort": 443, "Protocol": "HTTPS"}], + "LoadBalancerPort": 443, "Protocol": "HTTPS", + "PolicyNames": ["PinDownSSLNegotiationPolicy201505"]}], SecurityGroups=[Ref("DefaultSGdockerregistryservice")], LoadBalancerName="ELB-docker-registryservice", Scheme="internet-facing", + Policies=[ + Policy( + Attributes=[{'Name': "Reference-Security-Policy", 'Value': "ELBSecurityPolicy-2015-05"}], + PolicyType='SSLNegotiationPolicyType', + PolicyName='PinDownSSLNegotiationPolicy201505' + ) + ], ) Policydockerregistryservice = PolicyType( @@ -755,6 +777,13 @@ def test_elb_with_healthcheck(self): SecurityGroups=[Ref("DefaultSGdockerregistryservice")], LoadBalancerName="ELB-docker-registryservice", Scheme="internet-facing", + Policies=[ + Policy( + Attributes=[{'Name': "Reference-Security-Policy", 'Value': "ELBSecurityPolicy-2015-05"}], + PolicyType='SSLNegotiationPolicyType', + PolicyName='PinDownSSLNegotiationPolicy201505' + ) + ], ) Policydockerregistryservice = PolicyType( @@ -831,6 +860,13 @@ def test_elb_with_reserved_chars(self): SecurityGroups=[Ref("DefaultSGdevdockerregistryservice")], LoadBalancerName="ELB-dev_docker-registryservice", Scheme="internet-facing", + Policies=[ + Policy( + Attributes=[{'Name': "Reference-Security-Policy", 'Value': "ELBSecurityPolicy-2015-05"}], + PolicyType='SSLNegotiationPolicyType', + PolicyName='PinDownSSLNegotiationPolicy201505' + ) + ], ) DNSdevdockerregistryservice = RecordSetGroup(