From d00f50906c8ac0bcd4f16ebe508bea55ad82c07c Mon Sep 17 00:00:00 2001 From: Ryan Ahearn Date: Thu, 3 Oct 2024 12:12:44 -0400 Subject: [PATCH 1/3] Re-run terraform generator --- bin/ops/create_service_account.sh | 2 +- bin/ops/destroy_service_account.sh | 3 --- terraform/staging/main.tf | 6 ------ 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/bin/ops/create_service_account.sh b/bin/ops/create_service_account.sh index 69b9d8d..d90acf4 100755 --- a/bin/ops/create_service_account.sh +++ b/bin/ops/create_service_account.sh @@ -78,7 +78,7 @@ username=`echo $creds | jq -r '.username'` password=`echo $creds | jq -r '.password'` if [[ "$org_manager" = "true" ]]; then - cf set-org-role $username $org OrgManager >&2 + cf set-org-role $username $org OrgManager 1>&2 fi cat << EOF diff --git a/bin/ops/destroy_service_account.sh b/bin/ops/destroy_service_account.sh index adb4c63..b4a757d 100755 --- a/bin/ops/destroy_service_account.sh +++ b/bin/ops/destroy_service_account.sh @@ -46,8 +46,5 @@ fi cf target -o $org -s $space -# destroy service key -cf delete-service-key $service service-account-key -f - # destroy service cf delete-service $service -f diff --git a/terraform/staging/main.tf b/terraform/staging/main.tf index 75c0d9e..b9902e1 100644 --- a/terraform/staging/main.tf +++ b/terraform/staging/main.tf @@ -23,10 +23,4 @@ module "redis" { redis_plan_name = "redis-dev" } -module "egress_space" { - source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v1.0.0" - cf_org_name = local.cf_org_name - cf_space_name = "${local.cf_space_name}-egress" - deployers = ["ryan.ahearn@gsa.gov", var.cf_user] -} From 194e089f5aaa571df28577f55143d8440b722242 Mon Sep 17 00:00:00 2001 From: Ryan Ahearn Date: Thu, 3 Oct 2024 12:42:03 -0400 Subject: [PATCH 2/3] Run egress proxy generator --- .github/actions/deploy-proxy/action.yml | 23 +------ .github/workflows/deploy-production.yml | 9 +++ .github/workflows/deploy-staging.yml | 2 +- README.md | 12 ++++ bin/ops/deploy_egress_proxy.rb | 54 +++++++++++++++ ...continuous_monitoring-production.allow.acl | 0 .../continuous_monitoring-production.deny.acl | 0 .../continuous_monitoring-staging.allow.acl | 1 - doc/compliance/apps/application.boundary.md | 6 ++ .../cg-egress-proxy/component-definition.json | 68 +++++++++++++++++++ doc/compliance/oscal/trestle-config.yaml | 1 + terraform/README.md | 6 +- terraform/production/main.tf | 11 +++ terraform/staging/main.tf | 10 +++ 14 files changed, 177 insertions(+), 26 deletions(-) create mode 100755 bin/ops/deploy_egress_proxy.rb create mode 100644 config/deployment/egress_proxy/continuous_monitoring-production.allow.acl create mode 100644 config/deployment/egress_proxy/continuous_monitoring-production.deny.acl create mode 100644 doc/compliance/oscal/component-definitions/cg-egress-proxy/component-definition.json diff --git a/.github/actions/deploy-proxy/action.yml b/.github/actions/deploy-proxy/action.yml index 978b5d3..6154ffa 100644 --- a/.github/actions/deploy-proxy/action.yml +++ b/.github/actions/deploy-proxy/action.yml @@ -26,30 +26,9 @@ runs: run: | cf api api.fr.cloud.gov cf auth - - name: Set restricted space egress - shell: bash - run: ./bin/ops/set_space_egress.sh -t -s ${{ inputs.cf_space }} - - name: Set public space egress - shell: bash - run: ./bin/ops/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 ./config/deployment/egress_proxy/${{ inputs.app }}.*.acl ${{ steps.create-temp-dir.outputs.path }} - name: Target space shell: bash run: cf target -o gsa-tts-devtools-prototyping -s ${{ inputs.cf_space }} - name: Deploy proxy shell: bash - working-directory: ${{ steps.create-temp-dir.outputs.path }} - run: ./bin/cf-deployproxy -a ${{ inputs.app }} -p egress-proxy -e egress_proxy + run: ./bin/ops/deploy_egress_proxy.rb -a ${{ inputs.app }} -s ${{ inputs.cf_space }} -r ${{ inputs.proxy_repo }} -v ${{ inputs.proxy_version }} diff --git a/.github/workflows/deploy-production.yml b/.github/workflows/deploy-production.yml index 1dff626..c1e4b96 100644 --- a/.github/workflows/deploy-production.yml +++ b/.github/workflows/deploy-production.yml @@ -40,3 +40,12 @@ jobs: cf_org: gsa-tts-devtools-prototyping cf_space: rahearn cf_command: push --vars-file config/deployment/production.yml --var rails_master_key="${{ secrets.RAILS_MASTER_KEY }}" --strategy rolling + + - name: Deploy egress proxy + uses: ./.github/actions/deploy-proxy + env: + CF_USERNAME: ${{ secrets.CF_USERNAME }} + CF_PASSWORD: ${{ secrets.CF_PASSWORD }} + with: + cf_space: rahearn + app: continuous_monitoring-production diff --git a/.github/workflows/deploy-staging.yml b/.github/workflows/deploy-staging.yml index 428b48b..c7086a0 100644 --- a/.github/workflows/deploy-staging.yml +++ b/.github/workflows/deploy-staging.yml @@ -41,7 +41,7 @@ jobs: cf_space: rahearn cf_command: push --vars-file config/deployment/staging.yml --var rails_master_key="${{ secrets.RAILS_MASTER_KEY }}" --strategy rolling - - name: Deploy proxy + - name: Deploy egress proxy uses: ./.github/actions/deploy-proxy env: CF_USERNAME: ${{ secrets.CF_USERNAME }} diff --git a/README.md b/README.md index f4a7083..8c6a9d7 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,18 @@ Otherwise, they are set as a `((variable))` within `manifest.yml` and the variab Configuration that changes from staging to production, but is public, should be added to `config/deployment/staging.yml` and `config/deployment/production.yml` +### Public Egress Proxy + +Traffic to be delivered to the public internet or s3 must be proxied through the [cg-egress-proxy](https://github.com/GSA-TTS/cg-egress-proxy) app. + +To deploy the proxy manually: + +1. Ensure terraform state is up to date. +1. Update the acl files in `config/deployment/egress_proxy` +1. Deploy the proxy to staging: `bin/ops/deploy_egress_proxy.rb -s rahearn -a continuous_monitoring-staging` +1. Deploy the proxy to production: `bin/ops/deploy_egress_proxy.rb -s rahearn -a continuous_monitoring-production` + +See the [ruby troubleshooting doc](https://github.com/GSA-TTS/cg-egress-proxy/blob/main/docs/ruby.md) first if you have any problems making outbound connections through the proxy. ## Documentation ### Auditree Control Validation diff --git a/bin/ops/deploy_egress_proxy.rb b/bin/ops/deploy_egress_proxy.rb new file mode 100755 index 0000000..e55d2b6 --- /dev/null +++ b/bin/ops/deploy_egress_proxy.rb @@ -0,0 +1,54 @@ +#!/usr/bin/env ruby + +require "tmpdir" +require "optparse" + +options = {} +parser = OptionParser.new do |opt| + opt.on("-s", "--space SPACE", "The space apps are running in") { |o| options[:space] = o unless o == "" } + opt.on("-a", "--apps APPLICATION", "Comma-separated list of cloud.gov apps to be proxied") { |o| options[:apps] = o unless o == "" } + opt.on("-r", "--repo PROXY_REPOSITORY", "Address of egress proxy git repo. Default: https://github.com/GSA-TTS/cg-egress-proxy.git") { |o| options[:repo] = o unless o == "" } + opt.on("-v", "--version PROXY_VERSION", "Git ref (sha, tag, branch) to deploy from repo. Default: main") { |o| options[:version] = o unless o == "" } +end +parser.parse! + +if options[:space].nil? + warn "--space is a required argument" + puts parser + exit 1 +end +if options[:apps].nil? + warn "--apps is a required argument" + puts parser + exit 1 +end +proxy_repo = options[:repo].nil? ? "https://github.com/GSA-TTS/cg-egress-proxy.git" : options[:repo] +proxy_version = options[:version].nil? ? "main" : options[:version] + +def run(command) + system(command) or exit $?.exitstatus +end + +directory = File.dirname(__FILE__) + +run "#{File.join(directory, "set_space_egress.sh")} -s #{options[:space]} -t" +run "#{File.join(directory, "set_space_egress.sh")} -s #{options[:space]}-egress -p" + +Dir.mktmpdir do |dir| + run "git clone #{proxy_repo} #{dir}" + run "cd #{dir}; git checkout #{proxy_version}" + config_dir = File.join(directory, "../../config/deployment/egress_proxy") + options[:apps].split(",").each do |app| + begin + FileUtils.cp File.join(config_dir, "#{app}.allow.acl"), dir + rescue + warn "config/deployment/egress_proxy/#{app}.allow.acl did not exist. Please create it if you need to customize the app's allow rules" + end + begin + FileUtils.cp File.join(config_dir, "#{app}.deny.acl"), dir + rescue + warn "config/deployment/egress_proxy/#{app}.deny.acl did not exist. Please create it if you need to customize the app's deny rules" + end + end + run "cd #{dir}; bin/cf-deployproxy -a #{options[:apps]} -p ep -e egress_proxy" +end diff --git a/config/deployment/egress_proxy/continuous_monitoring-production.allow.acl b/config/deployment/egress_proxy/continuous_monitoring-production.allow.acl new file mode 100644 index 0000000..e69de29 diff --git a/config/deployment/egress_proxy/continuous_monitoring-production.deny.acl b/config/deployment/egress_proxy/continuous_monitoring-production.deny.acl new file mode 100644 index 0000000..e69de29 diff --git a/config/deployment/egress_proxy/continuous_monitoring-staging.allow.acl b/config/deployment/egress_proxy/continuous_monitoring-staging.allow.acl index 0b07794..0f21fdc 100644 --- a/config/deployment/egress_proxy/continuous_monitoring-staging.allow.acl +++ b/config/deployment/egress_proxy/continuous_monitoring-staging.allow.acl @@ -1,2 +1 @@ raw.githubusercontent.com -*.apps.internal diff --git a/doc/compliance/apps/application.boundary.md b/doc/compliance/apps/application.boundary.md index 7e1994a..7ce8816 100644 --- a/doc/compliance/apps/application.boundary.md +++ b/doc/compliance/apps/application.boundary.md @@ -29,6 +29,11 @@ Boundary(aws, "AWS GovCloud") { Boundary(atob, "ATO boundary") { Boundary(space, "Restricted-egress cloud.gov space") { System_Boundary(inventory, "Application") { + Boundary(restricted_space, "Restricted egress space") { + } + Boundary(egress_space, "Public egress space") { + Container(proxy, "<&layers> Egress Proxy", "Caddy, cg-egress-proxy", "Proxy with allow-list of external connections") + } Container(app, "<&layers> Continuous Monitoring", "Ruby 3.3.4, Rails 7.1.3.4", "TKTK Application Description") ContainerDb(app_db, "Application DB", "AWS RDS (PostgreSQL)", "Primary data storage") Container(worker, "<&layers> Sidekiq workers", "Ruby 3.3.4, Sidekiq", "Perform background work and data processing") @@ -64,6 +69,7 @@ Rel(egress_proxy, external, "Request file content", "https (443)") Rel(developer, githuball, "Publish code", "git ssh (22)") Rel(githuball, cg_api, "Deploy App", "Auth: SpaceDeployer Service Account, https (443)") +Rel(app, proxy, "Proxy outbound connections", "https (443)") @enduml ``` diff --git a/doc/compliance/oscal/component-definitions/cg-egress-proxy/component-definition.json b/doc/compliance/oscal/component-definitions/cg-egress-proxy/component-definition.json new file mode 100644 index 0000000..b0f4f73 --- /dev/null +++ b/doc/compliance/oscal/component-definitions/cg-egress-proxy/component-definition.json @@ -0,0 +1,68 @@ +{ + "component-definition": { + "uuid": "d2f3e1b7-363a-4c8a-afb9-7cee1e825bdc", + "metadata": { + "title": "cg-egress-proxy Egress Proxy Component Definition.", + "last-modified": "2024-10-03T13:28:05.931086+00:00", + "version": "0.0.1", + "oscal-version": "1.1.2" + }, + "components": [ + { + "uuid": "1acb8ab7-4191-46c6-b79f-659a2f195b5a", + "type": "software", + "title": "cg-egress-proxy", + "description": "The cg-egress-proxy caddy server with forward_proxy configured", + "props": [ + { + "name": "Rule_Id", + "value": "prod-space-restricted", + "remarks": "rule_prod_space_restricted" + }, + { + "name": "Rule_Description", + "value": "The production space where the system app is running must not have the public-networks-egress ASG applied to it", + "remarks": "rule_prod_space_restricted" + } + ], + "control-implementations": [ + { + "uuid": "eba1125b-5fd7-46c3-8edc-bf22d67d98cf", + "source": "https://raw.githubusercontent.com/usnistgov/oscal-content/refs/tags/v1.3.0/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_catalog.json", + "description": "Controls implemented via use of the cg-egress-proxy outbound connection proxy", + "implemented-requirements": [ + { + "uuid": "09de7f16-6339-4daa-b09a-333c5e33185c", + "control-id": "sc-7", + "description": "", + "props": [ + { + "name": "implementation-status", + "value": "partial" + } + ], + "statements": [ + { + "statement-id": "sc-7_smt.c", + "uuid": "b56aa629-2452-4052-a5c0-7d245a8122a2", + "description": "eg-egress-proxy provides a control point for allowing network traffic to specific hostnames or IP addresses. Outbound connections are compared to the following list in order:\n\n1. A `deny_file` list of hostnames and/or IP addresses to deny connections to.\n1. An `allow_file` list of hostnames and/or IP addresses to allow connections to.\n1. A `deny all` rule to deny all connections that did not match one of the first two rules.\n\nThe connection is allowed or denied based on the first matching rule.", + "props": [ + { + "name": "Rule_Id", + "value": "prod-space-restricted" + }, + { + "name": "implementation-status", + "value": "implemented" + } + ] + } + ] + } + ] + } + ] + } + ] + } +} diff --git a/doc/compliance/oscal/trestle-config.yaml b/doc/compliance/oscal/trestle-config.yaml index 497a65e..7a53103 100644 --- a/doc/compliance/oscal/trestle-config.yaml +++ b/doc/compliance/oscal/trestle-config.yaml @@ -6,3 +6,4 @@ components: - cloud_gov - devtools_cloud_gov - github_actions + - cg-egress-proxy diff --git a/terraform/README.md b/terraform/README.md index 2e95825..50196f8 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -64,7 +64,9 @@ These steps are run once per project. A [SpaceDeployer](https://cloud.gov/docs/services/cloud-gov-service-account/) account is required to run terraform or deploy the application from the CI/CD pipeline. Create a new account by running: -`../bin/ops/create_service_account.sh -s -u ` +`../bin/ops/create_service_account.sh -s -u -m` + +Passing the `-m` flag to `create_service_account.sh` is required for the account that will run terraform. ## Set up a new environment manually @@ -80,7 +82,7 @@ The below steps rely on you first configuring access to the Terraform state in s # something that communicates the purpose of the deployer # for example: circleci-deployer for the credentials CircleCI uses to # deploy the application or -terraform for credentials to run terraform manually - ../../bin/ops/create_service_account.sh -s -u > secrets.auto.tfvars + ../../bin/ops/create_service_account.sh -s -u -m > secrets.auto.tfvars ``` The script will output the `username` (as `cf_user`) and `password` (as `cf_password`) for your ``. Read more in the [cloud.gov service account documentation](https://cloud.gov/docs/services/cloud-gov-service-account/). diff --git a/terraform/production/main.tf b/terraform/production/main.tf index beba652..abdfc62 100644 --- a/terraform/production/main.tf +++ b/terraform/production/main.tf @@ -42,3 +42,14 @@ module "redis" { # domain_name = "TKTK-production-domain-name" # host_name = "TKTK-production-hostname (optional)" # } + +module "egress_space" { + source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v1.0.0" + + cf_org_name = local.cf_org_name + cf_space_name = "${local.cf_space_name}-egress" + # deployers should include any user or service account ID that will deploy the egress proxy + deployers = [ + var.cf_user + ] +} diff --git a/terraform/staging/main.tf b/terraform/staging/main.tf index b9902e1..b4b0f5b 100644 --- a/terraform/staging/main.tf +++ b/terraform/staging/main.tf @@ -23,4 +23,14 @@ module "redis" { redis_plan_name = "redis-dev" } +module "egress_space" { + source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v1.0.0" + cf_org_name = local.cf_org_name + cf_space_name = "${local.cf_space_name}-egress" + # deployers should include any user or service account ID that will deploy the egress proxy + deployers = [ + "ryan.ahearn@gsa.gov", + var.cf_user + ] +} From bdaa4d1cee3df69a51e5709ee0cf3a2ce443250d Mon Sep 17 00:00:00 2001 From: Ryan Ahearn Date: Thu, 3 Oct 2024 13:04:58 -0400 Subject: [PATCH 3/3] Update control statements for sc7 --- .../oscal/control-statements/sc/sc-7.md | 20 ++++++++++++++++++ .../system-security-plan.json | 8 +++++++ .../system-security-plan/metadata.json | 2 +- .../system-implementation.json | 21 +++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/doc/compliance/oscal/control-statements/sc/sc-7.md b/doc/compliance/oscal/control-statements/sc/sc-7.md index 2d808ed..940919b 100644 --- a/doc/compliance/oscal/control-statements/sc/sc-7.md +++ b/doc/compliance/oscal/control-statements/sc/sc-7.md @@ -20,6 +20,10 @@ x-trestle-comp-def-rules: description: Production spaces should disable ssh access - name: ssh-access-disabled description: Production spaces should disable ssh access + cg-egress-proxy: + - name: prod-space-restricted + description: The production space where the system app is running must not have + the public-networks-egress ASG applied to it x-trestle-rules-params: DevTools Cloud.gov: - name: gov.cloud.space-names @@ -191,4 +195,20 @@ Application owners are responsible for ensuring their application does not excha #### Implementation Status: partial +### cg-egress-proxy + +eg-egress-proxy provides a control point for allowing network traffic to specific hostnames or IP addresses. Outbound connections are compared to the following list in order: + +1. A `deny_file` list of hostnames and/or IP addresses to deny connections to. +1. An `allow_file` list of hostnames and/or IP addresses to allow connections to. +1. A `deny all` rule to deny all connections that did not match one of the first two rules. + +The connection is allowed or denied based on the first matching rule. + +#### Rules: + + - prod-space-restricted + +#### Implementation Status: implemented + ______________________________________________________________________ diff --git a/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan.json b/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan.json index 034843e..3495496 100644 --- a/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan.json +++ b/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan.json @@ -1086,6 +1086,14 @@ "implementation-status": { "state": "implemented" } + }, + { + "component-uuid": "1acb8ab7-4191-46c6-b79f-659a2f195b5a", + "uuid": "be70b12c-2fe6-4723-9b2f-16d957c5cf8a", + "description": "eg-egress-proxy provides a control point for allowing network traffic to specific hostnames or IP addresses. Outbound connections are compared to the following list in order:\n\n1. A `deny_file` list of hostnames and/or IP addresses to deny connections to.\n1. An `allow_file` list of hostnames and/or IP addresses to allow connections to.\n1. A `deny all` rule to deny all connections that did not match one of the first two rules.\n\nThe connection is allowed or denied based on the first matching rule.", + "implementation-status": { + "state": "implemented" + } } ] } diff --git a/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/metadata.json b/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/metadata.json index 618f427..5d6456e 100644 --- a/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/metadata.json +++ b/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/metadata.json @@ -1,7 +1,7 @@ { "metadata": { "title": "Continuous Monitoring Proof of Concept SSPP", - "last-modified": "2024-09-26T14:56:30.202245+00:00", + "last-modified": "2024-10-03T17:04:19.631173+00:00", "version": "0.0.1", "oscal-version": "1.1.2", "roles": [ diff --git a/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/system-implementation.json b/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/system-implementation.json index efadf50..47515fb 100644 --- a/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/system-implementation.json +++ b/doc/compliance/oscal/system-security-plans/continuous_monitoring/system-security-plan/system-implementation.json @@ -285,6 +285,27 @@ "state": "operational" } }, + { + "uuid": "1acb8ab7-4191-46c6-b79f-659a2f195b5a", + "type": "software", + "title": "cg-egress-proxy", + "description": "The cg-egress-proxy caddy server with forward_proxy configured", + "props": [ + { + "name": "Rule_Id", + "value": "prod-space-restricted", + "remarks": "rule_prod_space_restricted" + }, + { + "name": "Rule_Description", + "value": "The production space where the system app is running must not have the public-networks-egress ASG applied to it", + "remarks": "rule_prod_space_restricted" + } + ], + "status": { + "state": "operational" + } + }, { "uuid": "3dd05e37-06f1-4f8b-a4b7-7a80f2a0101b", "type": "this-system",