diff --git a/.dockerignore b/.dockerignore index 1750cd364..961436bbf 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,8 +1,12 @@ .git secmonkey.env +secmonkey.local.env +secmonkey.push.env boto.cfg .travis.yml #docs supervisor config-default.py generate-docs.py +postgres-data +docker-compose*.yml diff --git a/.gitignore b/.gitignore index d968a1ebe..2c8b0ce65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ *.py[cod] .*.swp +# ECS Deploy files +secmonkey.local.env +secmonkey.push.env + # C extensions *.so diff --git a/docker-compose-front.yml b/docker-compose-front.yml new file mode 100644 index 000000000..25349f270 --- /dev/null +++ b/docker-compose-front.yml @@ -0,0 +1,41 @@ +--- + +### +# +# Documentation: http://securitymonkey.readthedocs.io/en/latest/index.html +# http://securitymonkey.readthedocs.io/en/latest/docker.html +# +### + +version: '2' +services: + api: + image: "${SECURITY_MONKEY_ECS_IMAGE}:latest" + environment: + ECS_BUILD_TIME: "${ECS_BUILD_TIME}" + env_file: + - secmonkey.env + - secmonkey.local.env + entrypoint: ["/usr/local/src/security_monkey/docker/api-start.sh"] + logging: + driver: awslogs + options: + awslogs-group: "${SECURITY_MONKEY_ECS_AWSLOGS_GROUP}" + awslogs-region: "${AWS_REGION}" + awslogs-stream-prefix: "api" + mem_limit: 512m + nginx: + image: "${SECURITY_MONKEY_ECS_NGINX_IMAGE}:latest" + working_dir: /etc/nginx + ports: + - 80 + - 443 + links: + - api:smapi + logging: + driver: awslogs + options: + awslogs-group: "${SECURITY_MONKEY_ECS_AWSLOGS_GROUP}" + awslogs-region: "${AWS_REGION}" + awslogs-stream-prefix: "nginx" + mem_limit: 64m \ No newline at end of file diff --git a/docker-compose-scheduler.yml b/docker-compose-scheduler.yml new file mode 100644 index 000000000..334224119 --- /dev/null +++ b/docker-compose-scheduler.yml @@ -0,0 +1,26 @@ +--- + +### +# +# Documentation: http://securitymonkey.readthedocs.io/en/latest/index.html +# http://securitymonkey.readthedocs.io/en/latest/docker.html +# +### + +version: '2' +services: + scheduler: + image: "${SECURITY_MONKEY_ECS_IMAGE}:latest" + environment: + ECS_BUILD_TIME: "${ECS_BUILD_TIME}" + env_file: + - secmonkey.env + - secmonkey.local.env + entrypoint: ["/usr/local/src/security_monkey/docker/scheduler-start.sh"] + logging: + driver: awslogs + options: + awslogs-group: "${SECURITY_MONKEY_ECS_AWSLOGS_GROUP}" + awslogs-region: "${AWS_REGION}" + awslogs-stream-prefix: "scheduler" + mem_limit: 128m \ No newline at end of file diff --git a/docker-compose-worker.yml b/docker-compose-worker.yml new file mode 100644 index 000000000..9c70d0cdc --- /dev/null +++ b/docker-compose-worker.yml @@ -0,0 +1,26 @@ +--- + +### +# +# Documentation: http://securitymonkey.readthedocs.io/en/latest/index.html +# http://securitymonkey.readthedocs.io/en/latest/docker.html +# +### + +version: '2' +services: + worker: + image: "${SECURITY_MONKEY_ECS_IMAGE}:latest" + environment: + ECS_BUILD_TIME: "${ECS_BUILD_TIME}" + env_file: + - secmonkey.env + - secmonkey.local.env + entrypoint: ["/usr/local/src/security_monkey/docker/worker-start.sh"] + logging: + driver: awslogs + options: + awslogs-group: "${SECURITY_MONKEY_ECS_AWSLOGS_GROUP}" + awslogs-region: "${AWS_REGION}" + awslogs-stream-prefix: "worker" + mem_limit: ${SECURITY_MONKEY_WORKER_MEMORY} \ No newline at end of file diff --git a/docker/api-start.sh b/docker/api-start.sh index 047bcb852..3e5b9a711 100755 --- a/docker/api-start.sh +++ b/docker/api-start.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # wait the database sleep 10 diff --git a/docker/scheduler-start.sh b/docker/scheduler-start.sh index d4d78b919..c3da3f16c 100755 --- a/docker/scheduler-start.sh +++ b/docker/scheduler-start.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # wait the database sleep 10 diff --git a/docker/worker-start.sh b/docker/worker-start.sh index 10338ab9f..c9f4ba8df 100755 --- a/docker/worker-start.sh +++ b/docker/worker-start.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # wait for the scheduler sleep 20 diff --git a/ecs_push.sh b/ecs_push.sh new file mode 100755 index 000000000..79bb83fd5 --- /dev/null +++ b/ecs_push.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +set -e +set -a + +# Load the .env files. They should override in alphabetical order! +# These env vars are used in this script, and in the docker compose YAML. +# The actual containers will load their environments based on the YAML declarations +for env_file in *.env +do + # This mess is required because + # - `source` only takes real files (in older bash) + # - The .env files don't have quotes because docker compose (or at least ecs's compose) doesn't allow it + # So we add quotes to all env vars, write them to a temp file, and source the thing + temp=$(mktemp) + sed -E "s/^([A-Z_]+=)(.*$)/\1'\2'/g" <${env_file} >$temp + source $temp + rm $temp +done + +if [ -z "$AWS_REGION" ]; then + AWS_REGION='us-west-2' +fi + +export ECS_BUILD_TIME=$(date +%s) + +# Create the three task definitions +ecs-cli compose --file docker-compose-worker.yml -r ${AWS_REGION} --task-role-arn ${SECURITY_MONKEY_ECS_WORKER_ROLE} --aws-profile ${AWS_PROFILE} -p security_monkey_worker create +ecs-cli compose --file docker-compose-front.yml -r ${AWS_REGION} --task-role-arn ${SECURITY_MONKEY_ECS_FRONT_ROLE} --aws-profile ${AWS_PROFILE} -p security_monkey_fe create +ecs-cli compose --file docker-compose-scheduler.yml -r ${AWS_REGION} --task-role-arn ${SECURITY_MONKEY_ECS_SCHEDULER_ROLE} --aws-profile ${AWS_PROFILE} -p security_monkey_scheduler create + +# Build our docker images (ECS Compose doesn't build for you...) +docker build -t secmonkey . +docker build -t secmonkey-nginx -f docker/nginx/Dockerfile . + +# Tag them locally +docker tag secmonkey:latest ${SECURITY_MONKEY_ECS_IMAGE}:latest +docker tag secmonkey:latest ${SECURITY_MONKEY_ECS_IMAGE}:$(git describe --tags) +docker tag secmonkey-nginx:latest ${SECURITY_MONKEY_ECS_NGINX_IMAGE}:latest +docker tag secmonkey-nginx:latest ${SECURITY_MONKEY_ECS_NGINX_IMAGE}:$(git describe --tags) + +# Log into AWS ECR +$(aws --profile ${AWS_PROFILE} ecr get-login --no-include-email --region ${AWS_REGION}) + +# Push everything +docker push ${SECURITY_MONKEY_ECS_IMAGE}:latest +docker push ${SECURITY_MONKEY_ECS_IMAGE}:$(git describe --tags) +docker push ${SECURITY_MONKEY_ECS_NGINX_IMAGE}:latest +docker push ${SECURITY_MONKEY_ECS_NGINX_IMAGE}:$(git describe --tags) + +# Give AWS a moment to settle (probably not required, but why not) +sleep 2 + +# Update the services to the newest task definition +aws --profile ${AWS_PROFILE} --region ${AWS_REGION} ecs update-service --cluster ${AWS_ECS_CLUSTER} --service secmonkey_sched --force-new-deployment --task-definition security_monkey_scheduler +aws --profile ${AWS_PROFILE} --region ${AWS_REGION} ecs update-service --cluster ${AWS_ECS_CLUSTER} --service secmonkey_fe --force-new-deployment --task-definition security_monkey_fe +aws --profile ${AWS_PROFILE} --region ${AWS_REGION} ecs update-service --cluster ${AWS_ECS_CLUSTER} --service secmonkey_worker --force-new-deployment --task-definition security_monkey_worker diff --git a/ecs_readme.md b/ecs_readme.md new file mode 100644 index 000000000..8f53868b9 --- /dev/null +++ b/ecs_readme.md @@ -0,0 +1,59 @@ +Here are the files/vars required to get the ecs deployment script to work. +Some may actually be optional, but these are what I use to deploy Security monkey. + +`secmonkey.local.env` +```bash +# These point to RDS for me, but you can point them wherever +SECURITY_MONKEY_POSTGRES_USER= +SECURITY_MONKEY_POSTGRES_HOST= +SECURITY_MONKEY_POSTGRES_PASSWORD= +SECURITY_MONKEY_ACTIVE_PROVIDERS=onelogin + +SECURITY_MONKEY_SETTINGS=/usr/local/src/security_monkey/env-config/config-docker.py +SECURITY_MONKEY_FQDN= +SESSION_COOKIE_SECURE=True + +# These configure Onelogin (or Okta) +SECURITY_MONKEY_ONELOGIN_EMAIL_FIELD=email +SECURITY_MONKEY_ONELOGIN_USE_CUSTOM=True +SECURITY_MONKEY_ONELOGIN_ENTITY_ID= +SECURITY_MONKEY_ONELOGIN_SSO_URL= +SECURITY_MONKEY_ONELOGIN_SLO_URL= +SECURITY_MONKEY_ONELOGIN_IDP_CERT= + +SECURITY_MONKEY_REDIS_HOST= + +# These are the ARNs for the ECR images +SECURITY_MONKEY_ECS_IMAGE= +SECURITY_MONKEY_ECS_NGINX_IMAGE= + +# The ECS IAM Roles to be assumed by each process +SECURITY_MONKEY_ECS_WORKER_ROLE= +SECURITY_MONKEY_ECS_SCHEDULER_ROLE= +SECURITY_MONKEY_ECS_FRONT_ROLE= + +SECURITY_MONKEY_ECS_AWSLOGS_GROUP=secmonkey + +SECURITY_MONKEY_CELERY_WORKER_COUNT=5 + +SECURITY_MONKEY_SECRET_KEY= +SECURITY_MONKEY_SECURITY_PASSWORD_SALT= + +# On our install, secmonkey needs _a lot_ of RAM. You may be fine with less. +SECURITY_MONKEY_WORKER_MEMORY=4096m + +SM_CONSOLE_LOG_LEVEL=INFO + +SECURITY_MONKEY_EMAIL_DEFAULT_SENDER= +SECURITY_MONKEY_SES_REGION=us-west-2 +SECURITY_MONKEY_SMTP=False + +SENTRY_DSN= +``` + +`secmonkey.push.env` +```bash +AWS_PROFILE=secinfra +AWS_REGION=us-west-2 +AWS_ECS_CLUSTER=default +``` diff --git a/secmonkey.env b/secmonkey.env index be4751f93..c29fec259 100644 --- a/secmonkey.env +++ b/secmonkey.env @@ -1,6 +1,6 @@ -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= SECURITY_MONKEY_POSTGRES_HOST=postgres SECURITY_MONKEY_FQDN=127.0.0.1 # Must be false if HTTP -SESSION_COOKIE_SECURE=False \ No newline at end of file +SESSION_COOKIE_SECURE=False + +SECURITY_MONKEY_WORKER_MEMORY=1024m \ No newline at end of file diff --git a/security_monkey/celeryconfig.py b/security_monkey/celeryconfig.py index 12a2978a3..22a265f39 100644 --- a/security_monkey/celeryconfig.py +++ b/security_monkey/celeryconfig.py @@ -17,7 +17,7 @@ ) # How many processes per worker instance? -worker_concurrency = 10 +worker_concurrency = int(os.getenv('SECURITY_MONKEY_CELERY_WORKER_COUNT', 10)) # Schedule tasks at full hour or scheduler boot up time schedule_at_full_hour = False