Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ELB Logging in CloudFormation #1860

Merged
merged 10 commits into from
Jul 6, 2017
70 changes: 61 additions & 9 deletions deployment/stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
import boto3
from cfn_pyplates import core, functions

from utils import load_config, load_tags, save_s3_config
from utils import (load_config, load_tags,
save_s3_config, ensure_s3_bucket)

VERSION = '1.1'

Expand All @@ -42,6 +43,7 @@ def main():
sys.stdout.write("{}\n".format(template))
elif args.command == 'create':
template = make_template(config, config_yaml)
ensure_s3_bucket(config['S3_LOG_BUCKET'])
cloudformation = boto3.client('cloudformation')
response = cloudformation.create_stack(
StackName=config['STACK_NAME'],
Expand Down Expand Up @@ -522,6 +524,18 @@ def make_template(config, config_yaml):
cft.resources.elb = core.Resource(
'LoadBalancer', 'AWS::ElasticLoadBalancing::LoadBalancer',
{
'AccessLoggingPolicy': {
'EmitInterval': functions.ref('LogInterval'),
'Enabled': True,
'S3BucketName': config['S3_LOG_BUCKET'],
# 'S3BucketPrefix' unused
},
'AvailabilityZones': [
functions.get_att('WebInstance', 'AvailabilityZone')
],
'ConnectionSettings': {
'IdleTimeout': 1800 # seconds
},
'HealthCheck': {
'HealthyThreshold': '2',
'Interval': '30',
Expand All @@ -530,18 +544,21 @@ def make_template(config, config_yaml):
'UnhealthyThreshold': '4'
},
'Instances': [functions.ref('WebInstance')],

'LoadBalancerName': config['STACK_NAME'],
'Listeners': listeners,
'SecurityGroups': [
functions.get_att('ELBSecurityGroup', 'GroupId')],
"Tags": instance_tags, # todo: Should be different?
'AvailabilityZones': [
functions.get_att('WebInstance', 'AvailabilityZone')
],
'ConnectionSettings': {
'IdleTimeout': 1800 # seconds
'Tags': load_tags(),
})
cft.parameters.add(
core.Parameter('LogInterval', 'Number', {
'Default': 60,
'Description':
"How often, in minutes, the ELB emits its logs to the "
"configured S3 bucket. The ELB log facility restricts "
"this to be 5 or 60.",
}
}
)
)

# Cognito Identity Pool for Developer Authenticated Identities Authflow
Expand Down Expand Up @@ -643,6 +660,41 @@ def make_template(config, config_yaml):
)
)

# See http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-access-logs.html#attach-bucket-policy # noqa: E501
# for full list of region--principal identifiers.
cft.mappings.region = core.Mapping(
'Region',
{'us-east-1': {'ELBPrincipal': '127311923021'}})

cft.resources.log_policy = core.Resource(
'LogBucketPolicy', 'AWS::S3::BucketPolicy',
core.Properties({
'Bucket': config['S3_LOG_BUCKET'],
'PolicyDocument': {
'Statement': [{
"Action": [
"s3:PutObject"
],
"Effect": "Allow",
"Resource":
functions.join(
"",
"arn:aws:s3:::",
config['S3_LOG_BUCKET'],
"/AWSLogs/",
functions.ref("AWS::AccountId"), "/*"),
"Principal": {
"AWS": [
functions.find_in_map(
'Region',
functions.ref("AWS::Region"), 'ELBPrincipal'),
]
}
}]
}
})
)

return cft


Expand Down
11 changes: 7 additions & 4 deletions deployment/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,19 +61,21 @@ def load_config():
# Generate warning for old keys that we no longer use.
report_obsolete_keys(config)

report_missing_keys(config)

# Generate some special keys that are optional to specify.
generated_config = {}
if 'ADMIN_PASSWORD' not in config:
generated_config['ADMIN_PASSWORD'] = random_password(8)

config_bucket_name = config['S3_BUCKET_NAME_BASE'] + "-config"
generated_config['S3_CONFIG_BUCKET'] = config_bucket_name
log_bucket_name = config['S3_BUCKET_NAME_BASE'] + "-log"
generated_config['S3_LOG_BUCKET'] = log_bucket_name

# Update the config, by adding the automatically generated keys.
config.update(generated_config)

report_missing_keys(config)

# Optional in `config.yaml`
if 'RDS_NAME' not in config:
config['RDS_NAME'] = config['STACK_NAME']
Expand Down Expand Up @@ -162,14 +164,15 @@ def report_missing_keys(config):
Prints to stderr, then raises exception if there are missing keys.
"""

required = ['ADMIN_PASSWORD', 'KEY_NAME', 'RDS_SUPERUSER_PASSWORD',
required = ['KEY_NAME', 'RDS_SUPERUSER_PASSWORD',
'S3_BUCKET_NAME_BASE',
'SITE_NAME', 'SITE_URL', 'STACK_NAME']
bad = []
for key in required:
if key not in config:
bad.append(key)
if bad:
sys.stderr.write("aws-config\ must have values for:\n{!r}\n".format(
sys.stderr.write("aws-config/ must have values for:\n{!r}\n".format(
bad))
raise RuntimeError
return True
Expand Down