Merge pull request #115 from GSA/main

Deploy restricted egress configuration to demo
This commit is contained in:
Ryan Ahearn
2022-11-15 13:33:25 -05:00
committed by GitHub
26 changed files with 356 additions and 8 deletions

42
.github/actions/deploy-proxy/action.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: Deploy egress proxy
description: Set egress space security groups and deploy proxy
inputs:
cf_space:
description: The space the target app exists in.
required: true
app:
description: application name to be proxied.
required: true
proxy_repo:
description: git repo for cg-egress-proxy
default: https://github.com/rahearn/cg-egress-proxy.git
proxy_version:
description: git ref to be deployed
default: main
runs:
using: composite
steps:
- name: Set restricted space egress
shell: bash
run: ./terraform/set_space_egress.sh -t -s ${{ inputs.cf_space }}
- name: Set public space egress
shell: bash
run: ./terraform/set_space_egress.sh -p -s ${{ inputs.cf_space }}-egress
- name: Create temp directory
shell: bash
id: create-temp-dir
run: echo "path=$(mktemp -d -t egress-XXXXXXXXXX --tmpdir=$RUNNER_TEMP)" >> $GITHUB_OUTPUT
- name: Clone cg-egress-proxy
shell: bash
run: git clone ${{ inputs.proxy_repo }} ${{ steps.create-temp-dir.outputs.path }}
- name: Switch to deploy ref
shell: bash
working-directory: ${{ steps.create-temp-dir.outputs.path }}
run: git checkout ${{ inputs.proxy_version }}
- name: Copy config files
shell: bash
run: cp ./deploy-config/egress_proxy/${{ inputs.app }}.*.acl ${{ steps.create-temp-dir.outputs.path }}
- name: Build and deploy proxy
shell: bash
working-directory: ${{ steps.create-temp-dir.outputs.path }}
run: make && ./bin/cf-deployproxy -a ${{ inputs.app }} -p egress-proxy -e egress_proxy

View File

@@ -18,7 +18,7 @@ jobs:
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v1.1.2
uses: tj-actions/changed-files@v34
with:
files: terraform/demo
- name: Terraform init
@@ -65,3 +65,18 @@ jobs:
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
--var AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID"
--var AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY"
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v34
with:
files: |
deploy-config/egress_proxy/notify-api-demo.*.acl
.github/actions/deploy-proxy/action.yml
.github/workflows/deploy-demo.yml
- name: Deploy egress proxy
if: steps.changed-egress-config.outputs.any_changed == 'true'
uses: ./.github/actions/deploy-proxy
with:
cf_space: notify-demo
app: notify-api-demo

View File

@@ -23,7 +23,7 @@ jobs:
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v1.1.2
uses: tj-actions/changed-files@v34
with:
files: terraform/staging
- name: Terraform init
@@ -71,6 +71,21 @@ jobs:
--var AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID"
--var AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY"
- name: Check for changes to egress config
id: changed-egress-config
uses: tj-actions/changed-files@v34
with:
files: |
deploy-config/egress_proxy/notify-api-staging.*.acl
.github/actions/deploy-proxy/action.yml
.github/workflows/deploy.yml
- name: Deploy egress proxy
if: steps.changed-egress-config.outputs.any_changed == 'true'
uses: ./.github/actions/deploy-proxy
with:
cf_space: notify-staging
app: notify-api-staging
bail:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}

6
.profile Normal file
View File

@@ -0,0 +1,6 @@
##
# Cloud Foundry app initialization script
# https://docs.cloudfoundry.org/devguide/deploy-apps/deploy-app.html#profile
##
export https_proxy=$egress_proxy

View File

@@ -425,5 +425,6 @@ configs = {
'test': Test,
'staging': Staging,
'demo': Demo,
'sandbox': Staging,
'production': Production
}

View File

@@ -3,5 +3,6 @@ web_instances: 1
web_memory: 1G
worker_instances: 1
worker_memory: 512M
scheduler_memory: 256M
public_api_route: notify-api-demo.app.cloud.gov
admin_base_url: https://notify-demo.app.cloud.gov

View File

@@ -0,0 +1,3 @@
email.us-west-2.amazonaws.com
sns.us-west-2.amazonaws.com
logs.us-west-2.amazonaws.com

View File

@@ -0,0 +1,3 @@
email.us-west-2.amazonaws.com
sns.us-west-2.amazonaws.com
logs.us-west-2.amazonaws.com

View File

@@ -0,0 +1 @@
Update this file to force a re-deploy of the egress proxy even when notify-api-staging.<allow|deny>.acl haven't changed

View File

@@ -3,5 +3,6 @@ web_instances: 2
web_memory: 1G
worker_instances: 1
worker_memory: 512M
scheduler_memory: 256M
public_api_route: notify-api.app.cloud.gov
admin_base_url: https://notify.app.cloud.gov

10
deploy-config/sandbox.yml Normal file
View File

@@ -0,0 +1,10 @@
env: sandbox
web_instances: 1
web_memory: 1G
worker_instances: 1
worker_memory: 512M
public_api_route: notify-api-sandbox.app.cloud.gov
admin_base_url: https://notify-sandbox.app.cloud.gov
ADMIN_CLIENT_SECRET: dev-notify-secret-key
DANGEROUS_SALT: dev-notify-salt
SECRET_KEY: dev-notify-secret-key

View File

@@ -3,5 +3,6 @@ web_instances: 1
web_memory: 1G
worker_instances: 1
worker_memory: 512M
scheduler_memory: 256M
public_api_route: notify-api-staging.app.cloud.gov
admin_base_url: https://notify-staging.app.cloud.gov

View File

@@ -20,3 +20,42 @@ Configurations for these are located in [the `deploy-config` folder](../deploy-c
In the event that a deployment includes a Terraform change, that change will run before any code is deployed to the environment. Each environment has its own Terraform GitHub Action to handle that change.
Failures in any of these GitHub workflows will be surfaced in the Pull Request related to the code change, and in the case of `checks.yml` actively prevent the PR from being merged. Failure in the Terraform workflow will not actively prevent the PR from being merged, but reviewers should not approve a PR with a failing terraform plan.
## Egress Proxy
The API app runs in a [restricted egress space](https://cloud.gov/docs/management/space-egress/).
This allows direct communication to cloud.gov-brokered services, but
not to other APIs that we require.
As part of the deploy, we create an
[egress proxy application](https://github.com/GSA/cg-egress-proxy) that allows traffic out of our
application to a select list of allowed domains.
Update the allowed domains by updating `deploy-config/egress_proxy/notify-api-<env>.allow.acl`
and deploying an updated version of the application throught he normal deploy process.
## Sandbox environment
There is a sandbox space, complete with terraform and `deploy-config/sandbox.yml` file available
for experimenting with infrastructure changes without going through the full CI/CD cycle each time.
Rules for use:
1. Ensure that no other developer is using the environment, as there is nothing stopping changes from overwriting each other.
1. Clean up services you create when you are done. `terraform destroy` from within the `terraform/sandbox` directory should do it.
### Deploying to the sandbox
1. Set up services:
```
$ cd terraform/sandbox
$ ../create_service_account.sh -s notify-sandbox -u <your-name>-terraform -m > secrets.auto.tfvars
$ terraform init
$ terraform plan
$ terraform apply
```
1. start a pipenv shell as a shortcut to load `.env` file variables: `$ pipenv shell`
1. Deploy the application:
```
cf push --vars-file deploy-config/sandbox.yml --var AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID --var AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY
```

View File

@@ -23,6 +23,10 @@ applications:
instances: ((worker_instances))
memory: ((worker_memory))
command: ./scripts/run_app_paas.sh celery -A run_celery.notify_celery worker --loglevel=INFO --concurrency=4
- type: scheduler
instances: 1
memory: ((scheduler_memory))
command: ./scripts/run_app_paas.sh celery -A run_celery.notify_celery beat --loglevel=INFO
env:
NOTIFY_APP_NAME: api
@@ -43,3 +47,5 @@ applications:
AWS_REGION: us-west-2
AWS_PINPOINT_REGION: us-west-2
AWS_US_TOLL_FREE_NUMBER: +18446120782
REQUESTS_CA_BUNDLE: "/etc/ssl/certs/ca-certificates.crt"

View File

@@ -7,14 +7,18 @@ $0: Create a Service User Account for a given space
Usage:
$0 -h
$0 -s <SPACE NAME> -u <USER NAME> [-r <ROLE NAME>] [-o <ORG NAME>]
$0 -s <SPACE NAME> -u <USER NAME> [-r <ROLE NAME>] [-o <ORG NAME>] [-m]
Options:
-h: show help and exit
-s <SPACE NAME>: configure the space to act on. Required
-u <USER NAME>: set the service user name. Required
-r <ROLE NAME>: set the service user's role to either space-deployer or space-auditor. Default: space-deployer
-m: If provided, make the service user an OrgManager
-o <ORG NAME>: configure the organization to act on. Default: $org
Notes:
OrgManager is required for terraform to create <env>-egress spaces
"
set -e
@@ -23,8 +27,9 @@ set -o pipefail
space=""
service=""
role="space-deployer"
org_manager="false"
while getopts ":hs:u:r:o:" opt; do
while getopts ":hms:u:r:o:" opt; do
case "$opt" in
s)
space=${OPTARG}
@@ -38,6 +43,9 @@ while getopts ":hs:u:r:o:" opt; do
o)
org=${OPTARG}
;;
m)
org_manager="true"
;;
h)
echo "$usage"
exit 0
@@ -60,13 +68,17 @@ cf create-service-key $service service-account-key 1>&2
# output service key to stdout in secrets.auto.tfvars format
creds=`cf service-key $service service-account-key | tail -n 4`
username=`echo $creds | jq '.username'`
password=`echo $creds | jq '.password'`
username=`echo $creds | jq -r '.username'`
password=`echo $creds | jq -r '.password'`
if [[ $org_manager = "true" ]]; then
cf set-org-role $username $org OrgManager 1>&2
fi
cat << EOF
# generated with $0 -s $space -u $service -r $role -o $org
# revoke with $(dirname $0)/destroy_service_account.sh -s $space -u $service -o $org
cf_user = $username
cf_password = $password
cf_user = "$username"
cf_password = "$password"
EOF

View File

@@ -53,3 +53,17 @@ module "contact_list_bucket" {
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-contact-list-bucket-${local.env}"
}
module "egress-space" {
source = "../shared/egress_space"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_restricted_space_name = local.cf_space_name
deployers = [
var.cf_user,
"ryan.ahearn@gsa.gov",
"steven.reilly@gsa.gov"
]
}

View File

@@ -54,6 +54,18 @@ module "contact_list_bucket" {
s3_service_name = "${local.app_name}-contact-list-bucket-${local.env}"
}
module "egress-space" {
source = "../shared/egress_space"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_restricted_space_name = local.cf_space_name
deployers = [
var.cf_user
]
}
###########################################################################
# The following lines need to be commented out for the initial `terraform apply`
# It can be re-enabled after:

69
terraform/sandbox/main.tf Normal file
View File

@@ -0,0 +1,69 @@
locals {
cf_org_name = "gsa-tts-benefits-studio-prototyping"
cf_space_name = "notify-sandbox"
env = "sandbox"
app_name = "notify-api"
recursive_delete = true
}
module "database" {
source = "github.com/18f/terraform-cloudgov//database"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
env = local.env
app_name = local.app_name
recursive_delete = local.recursive_delete
rds_plan_name = "micro-psql"
}
module "redis" {
source = "github.com/18f/terraform-cloudgov//redis"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
env = local.env
app_name = local.app_name
recursive_delete = local.recursive_delete
redis_plan_name = "redis-dev"
}
module "csv_upload_bucket" {
source = "github.com/18f/terraform-cloudgov//s3"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-csv-upload-bucket-${local.env}"
}
module "contact_list_bucket" {
source = "github.com/18f/terraform-cloudgov//s3"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-contact-list-bucket-${local.env}"
}
module "egress-space" {
source = "../shared/egress_space"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_restricted_space_name = local.cf_space_name
deployers = [
var.cf_user,
"ryan.ahearn@gsa.gov",
"steven.reilly@gsa.gov"
]
}

View File

@@ -0,0 +1,17 @@
terraform {
required_version = "~> 1.0"
required_providers {
cloudfoundry = {
source = "cloudfoundry-community/cloudfoundry"
version = "0.15.5"
}
}
backend "s3" {
bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0"
key = "api.tfstate.sandbox"
encrypt = "true"
region = "us-gov-west-1"
profile = "notify-terraform-backend"
}
}

View File

@@ -0,0 +1,4 @@
variable "cf_password" {
sensitive = true
}
variable "cf_user" {}

View File

@@ -0,0 +1,36 @@
###
# Target space/org
###
data "cloudfoundry_org" "org" {
name = var.cf_org_name
}
###
# Egress Space
###
resource "cloudfoundry_space" "public_egress" {
name = "${var.cf_restricted_space_name}-egress"
org = data.cloudfoundry_org.org.id
}
###
# User roles
###
data "cloudfoundry_user" "users" {
for_each = var.deployers
name = each.key
org_id = data.cloudfoundry_org.org.id
}
locals {
user_ids = [for user in data.cloudfoundry_user.users : user.id]
}
resource "cloudfoundry_space_users" "deployers" {
space = cloudfoundry_space.public_egress.id
managers = local.user_ids
developers = local.user_ids
}

View File

@@ -0,0 +1,16 @@
terraform {
required_version = "~> 1.0"
required_providers {
cloudfoundry = {
source = "cloudfoundry-community/cloudfoundry"
version = "~> 0.15"
}
}
}
provider "cloudfoundry" {
api_url = "https://api.fr.cloud.gov"
user = var.cf_user
password = var.cf_password
app_logs_max = 30
}

View File

@@ -0,0 +1,10 @@
variable "cf_password" {
type = string
sensitive = true
}
variable "cf_user" {}
variable "cf_org_name" {}
variable "cf_restricted_space_name" {}
variable "deployers" {
type = set(string)
}

View File

@@ -53,3 +53,17 @@ module "contact_list_bucket" {
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-contact-list-bucket-${local.env}"
}
module "egress-space" {
source = "../shared/egress_space"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_restricted_space_name = local.cf_space_name
deployers = [
var.cf_user,
"ryan.ahearn@gsa.gov",
"steven.reilly@gsa.gov"
]
}