rails_template_18f 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.standard.yml +2 -0
  4. data/CHANGELOG.md +6 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +10 -0
  7. data/Gemfile.lock +132 -0
  8. data/LICENSE.md +21 -0
  9. data/README.md +140 -0
  10. data/Rakefile +10 -0
  11. data/bin/console +16 -0
  12. data/bin/setup +8 -0
  13. data/lib/generators/rails_template18f/circleci/circleci_generator.rb +116 -0
  14. data/lib/generators/rails_template18f/circleci/templates/Dockerfile.tt +13 -0
  15. data/lib/generators/rails_template18f/circleci/templates/bin/ci-server-start +8 -0
  16. data/lib/generators/rails_template18f/circleci/templates/circleci/config.yml.tt +413 -0
  17. data/lib/generators/rails_template18f/circleci/templates/docker-compose.ci.yml +26 -0
  18. data/lib/generators/rails_template18f/github_actions/github_actions_generator.rb +137 -0
  19. data/lib/generators/rails_template18f/github_actions/templates/github/actions/run-server/action.yml +28 -0
  20. data/lib/generators/rails_template18f/github_actions/templates/github/actions/setup-languages/action.yml.tt +20 -0
  21. data/lib/generators/rails_template18f/github_actions/templates/github/actions/setup-project/action.yml.tt +33 -0
  22. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/brakeman-analysis.yml +44 -0
  23. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/dependency-scans.yml +39 -0
  24. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml.tt +53 -0
  25. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml.tt +53 -0
  26. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-daily-scan.yml.tt +44 -0
  27. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-scan.yml.tt +47 -0
  28. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/pa11y.yml.tt +65 -0
  29. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/rspec.yml.tt +34 -0
  30. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-production.yml +79 -0
  31. data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-staging.yml +79 -0
  32. data/lib/rails_template18f/terraform_options.rb +68 -0
  33. data/lib/rails_template18f/version.rb +5 -0
  34. data/lib/rails_template_18f.rb +13 -0
  35. data/rails-template-18f.gemspec +40 -0
  36. data/railsrc +10 -0
  37. data/railsrc-hotwire +8 -0
  38. data/template.rb +506 -0
  39. data/templates/README.md.tt +213 -0
  40. data/templates/app/assets/images/uswds.js +5 -0
  41. data/templates/app/assets/stylesheets/uswds-settings.scss +7 -0
  42. data/templates/app/views/application/_banner_lock_icon.html.erb +19 -0
  43. data/templates/app/views/application/_demo_site_banner.html.erb +3 -0
  44. data/templates/app/views/application/_header.html.erb +26 -0
  45. data/templates/app/views/application/_usa_banner.html.erb +51 -0
  46. data/templates/bin/owasp-scan +49 -0
  47. data/templates/bin/pa11y-scan +10 -0
  48. data/templates/bin/with-server +35 -0
  49. data/templates/browserslistrc +5 -0
  50. data/templates/config/deployment/production.yml +3 -0
  51. data/templates/config/deployment/staging.yml +3 -0
  52. data/templates/config/environments/ci.rb +10 -0
  53. data/templates/config/environments/staging.rb +6 -0
  54. data/templates/config/locales/en.yml.tt +25 -0
  55. data/templates/config/locales/es.yml +19 -0
  56. data/templates/config/locales/fr.yml +22 -0
  57. data/templates/config/locales/zh.yml +16 -0
  58. data/templates/config/newrelic.yml +65 -0
  59. data/templates/doc/adr/0001-record-architecture-decisions.md.tt +21 -0
  60. data/templates/doc/adr/0002-initial-architecture-decisions.md.tt +24 -0
  61. data/templates/doc/adr/0003-security-scans.md.tt +44 -0
  62. data/templates/doc/adr/0004-rails-csp-compliant-script-tag-helpers.md.tt +53 -0
  63. data/templates/doc/compliance/README.md +37 -0
  64. data/templates/doc/compliance/apps/application.boundary.md.tt +80 -0
  65. data/templates/doc/compliance/apps/data.logical.md +21 -0
  66. data/templates/doc/compliance/rendered/apps/.keep +0 -0
  67. data/templates/editorconfig +5 -0
  68. data/templates/env +10 -0
  69. data/templates/githooks/pre-commit.tt +35 -0
  70. data/templates/lib/tasks/cf.rake +9 -0
  71. data/templates/lib/tasks/scanning.rake +63 -0
  72. data/templates/manifest.yml.tt +19 -0
  73. data/templates/pa11yci +9 -0
  74. data/templates/terraform/README.md.tt +148 -0
  75. data/templates/terraform/bootstrap/import.sh +12 -0
  76. data/templates/terraform/bootstrap/main.tf.tt +25 -0
  77. data/templates/terraform/bootstrap/providers.tf +16 -0
  78. data/templates/terraform/bootstrap/run.sh.tt +12 -0
  79. data/templates/terraform/bootstrap/teardown_creds.sh.tt +5 -0
  80. data/templates/terraform/bootstrap/variables.tf +2 -0
  81. data/templates/terraform/create_space_deployer.sh +33 -0
  82. data/templates/terraform/destroy_space_deployer.sh +19 -0
  83. data/templates/terraform/production/main.tf.tt +50 -0
  84. data/templates/terraform/production/providers.tf.tt +17 -0
  85. data/templates/terraform/production/variables.tf +2 -0
  86. data/templates/terraform/shared/database/main.tf.tt +23 -0
  87. data/templates/terraform/shared/database/providers.tf +16 -0
  88. data/templates/terraform/shared/database/variables.tf +42 -0
  89. data/templates/terraform/shared/domain/main.tf.tt +46 -0
  90. data/templates/terraform/shared/domain/providers.tf +16 -0
  91. data/templates/terraform/shared/domain/variables.tf +47 -0
  92. data/templates/terraform/shared/s3/main.tf +27 -0
  93. data/templates/terraform/shared/s3/providers.tf +16 -0
  94. data/templates/terraform/shared/s3/variables.tf +43 -0
  95. data/templates/terraform/staging/main.tf.tt +30 -0
  96. data/templates/terraform/staging/providers.tf.tt +17 -0
  97. data/templates/terraform/staging/variables.tf +2 -0
  98. data/templates/zap.conf +121 -0
  99. metadata +213 -0
@@ -0,0 +1,44 @@
1
+ # 3. Security Scans
2
+
3
+ Date: <%= Date.today.iso8601 %>
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ In order to maintain a secure system, it is important that we are kept notified of any potential
12
+ vulnerabilities as early as possible, so we can mitigate them.
13
+
14
+ ## Decision
15
+
16
+ We will add four new scans to our CI/CD Pipeline.
17
+
18
+ ### Brakeman
19
+
20
+ Brakeman is a static code scanner designed to find security issues in Ruby on Rails code. It can flag
21
+ potential SQL injection, Command Injection, open redirects, and other common vulnerabilities.
22
+
23
+ ### Bundle Audit
24
+
25
+ bundle-audit checks our Ruby dependencies against a database of known CVE numbers.
26
+
27
+ ### Yarn Audit
28
+
29
+ yarn audit checks our Javascript dependencies against a database of known CVE numbers.
30
+
31
+ ### OWASP ZAP
32
+
33
+ [OWASP ZAP](https://www.zaproxy.org/) is a dynamic security scanner that can simulate actual attacks on a running server.
34
+
35
+ An additional `RAILS_ENV` has been created called `ci`. It inherits from `production` to ensure
36
+ that the system being tested is as close as possible to `production` while allowing for overrides such
37
+ as bypassing authentication in a secure way.
38
+
39
+ ## Consequences
40
+
41
+ We now have real-time information on any security vulnerabilities we may have introduced, as well as continuous
42
+ monitoring and alerting of discovered vulnerabilities in our software dependencies.
43
+
44
+ Our system security posture is overall improved by these additions.
@@ -0,0 +1,53 @@
1
+ # 4. Rails CSP compliant script tag helpers
2
+
3
+ Date: <%= Date.today.iso8601 %>
4
+
5
+ ## Status
6
+
7
+ Accepted
8
+
9
+ ## Context
10
+
11
+ The [Content-Security-Policy](https://content-security-policy.com/) header generated by the
12
+ [secure_headers](https://github.com/github/secure_headers) gem does not work with Rails UJS AJAX forms.
13
+
14
+ The Rails UJS AJAX forms might be used if this project does not use a full-on SPA library.
15
+
16
+ ## Decision
17
+
18
+ Using Rails built-in CSP controls while keeping SecureHeaders in place for other headers results
19
+ in a secure system that works seamlessly.
20
+
21
+ ## Consequences
22
+
23
+ In order to define an inline `<script>` tag, use the `nonce: true` option.
24
+
25
+ ```
26
+ <%%= javascript_tag nonce: true do %>
27
+ alert("my js runs here");
28
+ <%% end %>
29
+ ```
30
+
31
+ ### Nonce pitfall
32
+
33
+ [source](https://content-security-policy.com/nonce/#:~:text=Avoid%20this%20common%20nonce%20mistake)
34
+
35
+ If you are outputting variables inside a nonce protected script tag, you could cancel out the XSS protection that CSP is giving you.
36
+
37
+ For example assume you have a URL such as `/example/?id=123` and you are outputting that id value from the URL in your script block:
38
+
39
+ ```
40
+ <%%= javascript_tag nonce: true do %>
41
+ var id = <%%= params[:id] %>
42
+ <%% end %>
43
+ ```
44
+
45
+ Now an attacker could request the URL: `/example/?id=doSomethingBad()`, and your application would send back:
46
+
47
+ ```
48
+ <script nonce="rAnd0m">
49
+ var id = doSomethingBad()
50
+ </script>
51
+ ```
52
+
53
+ As you can see we just threw away all of the cross site scripting protections of CSP by improperly using the nonce.
@@ -0,0 +1,37 @@
1
+ # Compliance artifacts
2
+
3
+ ## What is this?
4
+
5
+ In order to maintain and revise compliance materials with minimal fuss, we store all artifacts as text source (eg Markdown, PlantUML, OSCAL), then generate rendered materials for consumption by downstream entities in the assessment and authorization process.
6
+
7
+ This directory initially just contains system architecture diagrams corresponding to sections 1-12 of a typical System Security Plan (SSP) document.
8
+
9
+ The source for other things (OSCAL for control descriptions, evidence generation scripts, etc) will appear here over time.
10
+
11
+ ## Development
12
+
13
+ These plugins may be helpful for editing diagrams.
14
+
15
+ - vscode: [PlantUML extension](https://marketplace.visualstudio.com/items?itemName=jebbs.plantuml)
16
+ - Use "PlantUML: Export Current File Diagrams" to render the diagram in the current file (eg while iterating)
17
+ - Use "PlantUML: Export Workspace Diagrams" to render all diagrams (eg before pushing a branch)
18
+
19
+ ### VSCode PlantUML Settings
20
+
21
+ | Setting name | Value |
22
+ | ------------ | ----- |
23
+ | Diagrams Root | `doc/compliance` |
24
+ | Export Format | `svg` |
25
+ | Export Out Dir | `doc/compliance/rendered` |
26
+ | Export Sub Folder | unchecked |
27
+ | File Extensions | append `.md` |
28
+ | Render | `PlantUMLServer` |
29
+ | Server | `http://localhost:8080` |
30
+
31
+ ### PlantUML Server
32
+
33
+ The plugin default settings use the public server, https://www.plantuml.com/plantuml, which may **leak sensitive information**. Instead, run a local plantuml server:
34
+
35
+ ```bash
36
+ docker run -d -p 8080:8080 plantuml/plantuml-server:jetty
37
+ ```
@@ -0,0 +1,80 @@
1
+ # Application boundary view
2
+
3
+ ![application boundary view](../rendered/apps/application.boundary.svg)
4
+
5
+ ```plantuml
6
+ @startuml
7
+ !include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
8
+ ' uncomment the following line and comment the first to use locally
9
+ ' !include C4_Container.puml
10
+ LAYOUT_WITH_LEGEND()
11
+ title application boundary view
12
+
13
+ Person_Ext(public, "Public", "A member of the public")
14
+
15
+ Boundary(device, "Computing Device", "Windows, OS X, Linux, iOS, Android"){
16
+ System_Ext(browser, "Web Browser", "any modern version")
17
+ }
18
+ Rel(public, browser, "uses", "")
19
+
20
+ note as EncryptionNote
21
+ All connections depicted are encrypted with TLS 1.2 unless otherwise noted.
22
+ end note
23
+ Boundary(aws, "AWS GovCloud") {
24
+ Boundary(cloudgov, "cloud.gov") {
25
+ System_Ext(cg_api, "cloud.gov API")
26
+ System_Ext(aws_alb, "cloud.gov load-balancer", "AWS ALB")
27
+ System_Ext(cloudgov_router, "<&layers> cloud.gov routers", "Cloud Foundry traffic service")
28
+ Boundary(atob, "ATO boundary") {
29
+ System_Boundary(inventory, "Application") {
30
+ Container(app, "<&layers> <%= app_name.titleize %>", "Ruby <%= @ruby_version %>, Rails <%= Rails.version %>", "TKTK Application Description")
31
+ ContainerDb(app_db, "Application DB", "AWS RDS (PostgreSQL)", "Primary data storage")
32
+ <% if !skip_active_storage? %>
33
+ ContainerDb(app_s3, "File Storage", "AWS S3", "User-uploaded file storage")
34
+ <% end %>
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+
41
+ Boundary(gsa_saas, "GSA-authorized SaaS") {
42
+ <% if @dap %>
43
+ System_Ext(dap, "DAP", "Analytics collection")
44
+ <% end %>
45
+ <% if @newrelic %>
46
+ System_Ext(newrelic, "New Relic", "Monitoring SaaS")
47
+ <% end %>
48
+ }
49
+ <% if @dap %>
50
+ Rel(browser, dap, "reports usage", "https (443)")
51
+ <% end %>
52
+ <% if @newrelic %>
53
+ Rel(app, newrelic, "reports telemetry (ruby agent)", "tcp (443)")
54
+ Rel(browser, newrelic, "reports ux metrics (javascript agent)", "https (443)")
55
+ <% end %>
56
+
57
+ Rel(browser, aws_alb, "request info, submit requests", "https GET/POST (443)")
58
+ Rel(aws_alb, cloudgov_router, "proxies requests", "https GET/POST (443)")
59
+ Rel(cloudgov_router, app, "proxies requests", "https GET/POST (443)")
60
+ Rel(app, app_db, "reads/writes primary data", "psql (5432)")
61
+ <% if !skip_active_storage? %>
62
+ Rel(app, app_s3, "reads/writes file data", "https (443)")
63
+ <% end %>
64
+
65
+ Person(developer, "Developer", "Application developers")
66
+ Boundary(cicd, "CI/CD Pipeline") {
67
+ }
68
+
69
+ <% if @dap %>
70
+ Rel(developer, dap, "View traffic statistics", "https GET (443)")
71
+ <% end %>
72
+ <% if @newrelic %>
73
+ Rel(developer, newrelic, "Manage performance", "https (443)")
74
+ <% end %>
75
+ @enduml
76
+ ```
77
+
78
+ ### Notes
79
+
80
+ * See the help docs for [C4 variant of PlantUML](https://github.com/RicardoNiepel/C4-PlantUML) for syntax help.
@@ -0,0 +1,21 @@
1
+ # Logical Data Model
2
+
3
+ ![logical data model view](../rendered/apps/data.logical.svg)
4
+
5
+ ```plantuml
6
+ @startuml
7
+ scale 0.65
8
+
9
+ ' avoid problems with angled crows feet
10
+ skinparam linetype ortho
11
+
12
+ class TKTK_Example {
13
+ * id : integer <<generated>>
14
+ }
15
+ @enduml
16
+ ```
17
+
18
+ ### Notes
19
+
20
+ * See the help docs for [Entity Relationship Diagram](https://plantuml.com/ie-diagram) and [Class Diagram](https://plantuml.com/class-diagram) for syntax help.
21
+ * We're using the `*` visibility modifier to denote fields that cannot be `null`.
File without changes
@@ -0,0 +1,5 @@
1
+ # EditorConfig is awesome: https://EditorConfig.org
2
+
3
+ [zap.conf]
4
+ indent_size = 4
5
+ indent_style = tab
data/templates/env ADDED
@@ -0,0 +1,10 @@
1
+ ###################################################################################################
2
+ # Important: NEVER ADD SECRETS OR CREDENTIALS TO THIS FILE
3
+ # Any consistent secrets required in development should be added to config/credentials.yml.enc
4
+ # Overrides that you do not want to check into git should be added to .env.local and/or .env.test.local
5
+ #
6
+ # This file is not loaded in production-like environments
7
+ ###################################################################################################
8
+
9
+ # SHOW_DEMO_BANNER controls whether to show the TEST SITE banner in Rails.env.development
10
+ SHOW_DEMO_BANNER=false
@@ -0,0 +1,35 @@
1
+ #! /usr/bin/env bash
2
+ #
3
+ # This hook runs on `git commit` and will prevent you from committing without
4
+ # approval from the linter and tests.
5
+ #
6
+ # To run, this file must be symlinked to:
7
+ # .git/hooks/pre-commit
8
+ #
9
+ # To bypass this hook, run:
10
+ # $ git commit --no-verify
11
+ # $ git commit -n
12
+
13
+ echo "Running linter..."
14
+ bundle exec rake standard
15
+ linter_status=$?
16
+
17
+ if [ $linter_status -ne 0 ]; then
18
+ echo "Fix above before committing. Run 'git commit -n' to bypass linter."
19
+ exit 1
20
+ fi
21
+
22
+ <% if @terraform %>
23
+ echo "Running Terraform formatter"
24
+ # imitates https://github.com/HHS/Head-Start-TTADP/blob/3b72ff05d94fab4cda877c63d8cd6970f0eeffc7/.githooks/pre-commit
25
+
26
+ files=$(git diff --cached --name-only terraform)
27
+ for f in $files
28
+ do
29
+ # Format any *.tf files that were cached/staged
30
+ if [ -e "$f" ] && [[ $f == *.tf ]]; then
31
+ terraform fmt "$f"
32
+ git add "$f"
33
+ fi
34
+ done
35
+ <% end %>
@@ -0,0 +1,9 @@
1
+ namespace :cf do
2
+ desc "Only run on the first application instance"
3
+ task :on_first_instance do
4
+ instance_index = Integer(ENV["CF_INSTANCE_INDEX"])
5
+ exit(0) unless instance_index == 0
6
+ rescue
7
+ exit(0)
8
+ end
9
+ end
@@ -0,0 +1,63 @@
1
+ desc "Run brakeman with potential non-0 return code"
2
+ task :brakeman do
3
+ # -z flag makes it return non-0 if there are any warnings
4
+ # -q quiets output
5
+ unless system("brakeman -z -q") # system is true if return is 0, false otherwise
6
+ abort("Brakeman detected one or more code problems, please run it manually and inspect the output.")
7
+ end
8
+ end
9
+
10
+ namespace :bundler do
11
+ require "bundler/audit/cli"
12
+
13
+ desc "Updates the ruby-advisory-db and runs audit"
14
+ task :audit do
15
+ %w[update check].each do |command|
16
+ Bundler::Audit::CLI.start [command]
17
+ end
18
+ end
19
+ rescue LoadError
20
+ # no-op, probably in a production environment
21
+ end
22
+
23
+ namespace :yarn do
24
+ desc "Run yarn audit"
25
+ task :audit do
26
+ require "open3"
27
+ stdout, stderr, status = Open3.capture3("yarn audit --json")
28
+ unless status.success?
29
+ puts stderr
30
+ parsed = JSON.parse("[#{stdout.lines.join(",")}]")
31
+ puts JSON.pretty_generate(parsed)
32
+ if /503 Service Unavailable/.match?(stderr)
33
+ puts "Ignoring unavailable server"
34
+ elsif all_issues_ignored?(parsed)
35
+ puts "Ignoring known and accepted yarn audit results"
36
+ else
37
+ puts "Failed with exit code #{status.exitstatus}"
38
+ exit status.exitstatus
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def all_issues_ignored?(issues)
45
+ present_advisories_with_frequencies = Hash.new { |hash, key| hash[key] = 0 }
46
+
47
+ # Only look at audit advisories, and not audit summaries
48
+ issues.select { |issue_json| issue_json["type"] == "auditAdvisory" }.each do |issue_json|
49
+ present_advisories_with_frequencies[issue_json["data"]["advisory"]["id"]] += 1
50
+ end
51
+
52
+ # Advisory ID to be ignored with number of times it appears in project dependencies
53
+ # And, a comment as to why we're ignoring
54
+ ignored_advisories_with_frequencies = {
55
+ # 1005154 => 2, # high - inefficient regex in dev server and at build time
56
+ }
57
+
58
+ pp "Present advisories: #{present_advisories_with_frequencies}"
59
+ pp "Ignored advisories: #{ignored_advisories_with_frequencies}"
60
+ present_advisories_with_frequencies == ignored_advisories_with_frequencies
61
+ end
62
+
63
+ task default: ["standard", "brakeman", "bundler:audit", "yarn:audit"]
@@ -0,0 +1,19 @@
1
+ ---
2
+ applications:
3
+ - name: <%= app_name %>-((env))
4
+ buildpacks:
5
+ - nodejs_buildpack
6
+ - ruby_buildpack
7
+ env:
8
+ RAILS_MASTER_KEY: ((rails_master_key))
9
+ RAILS_ENV: ((env))
10
+ RAILS_LOG_TO_STDOUT: true
11
+ RAILS_SERVE_STATIC_FILES: true<% if @newrelic %>
12
+ NEW_RELIC_LOG: stdout<% end %>
13
+ processes:
14
+ - type: web
15
+ instances: ((web_instances))
16
+ memory: ((web_memory))
17
+ command: bundle exec rake cf:on_first_instance db:migrate && bundle exec rails s -b 0.0.0.0 -p $PORT -e $RAILS_ENV
18
+ services:
19
+ - <%= app_name %>-rds-((env))
data/templates/pa11yci ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "defaults": {
3
+ "standard": "WCAG2AA",
4
+ "runners": ["axe"]
5
+ },
6
+ "urls": [
7
+ "http://localhost:3000"
8
+ ]
9
+ }
@@ -0,0 +1,148 @@
1
+ # Terraform
2
+
3
+ This directory holds the terraform modules for maintaining your complete persistent infrastructure.
4
+
5
+ Prerequisite: install the `jq` JSON processor: `brew install jq`
6
+
7
+ ## Initial setup
8
+
9
+ 1. Manually run the bootstrap module following instructions under `Terraform State Credentials`
10
+ 1. Setup CI/CD Pipeline to run Terraform
11
+ 1. Copy bootstrap credentials to your CI/CD secrets using the instructions in the base README
12
+ 1. Create a cloud.gov SpaceDeployer by following the instructions under `SpaceDeployers`
13
+ 1. Copy SpaceDeployer credentials to your CI/CD secrets using the instructions in the base README
14
+ 1. Manually Running Terraform
15
+ 1. Follow instructions under `Set up a new environment` to create your infrastructure
16
+
17
+ ## Terraform State Credentials
18
+
19
+ The bootstrap module is used to create an s3 bucket for later terraform runs to store their state in.
20
+
21
+ ### Bootstrapping the state storage s3 buckets for the first time
22
+
23
+ 1. Run `terraform init`
24
+ 1. Run `./run.sh plan` to verify that the changes are what you expect
25
+ 1. Run `./run.sh apply` to set up the bucket and retrieve credentials
26
+ 1. Follow instructions under `Use bootstrap credentials`
27
+ 1. Ensure that `import.sh` includes a line and correct IDs for any resources created
28
+ 1. Run `./teardown_creds.sh` to remove the space deployer account used to create the s3 bucket
29
+
30
+ ### To make changes to the bootstrap module
31
+
32
+ *This should not be necessary in most cases*
33
+
34
+ 1. Run `terraform init`
35
+ 1. If you don't have terraform state locally:
36
+ 1. run `./import.sh`
37
+ 1. optionally run `./run.sh apply` to include the existing outputs in the state file
38
+ 1. Make your changes
39
+ 1. Continue from step 2 of the boostrapping instructions
40
+
41
+ ### Retrieving existing bucket credentials
42
+
43
+ 1. Run `./run.sh show`
44
+ 1. Follow instructions under `Use bootstrap credentials`
45
+
46
+ #### Use bootstrap credentials
47
+
48
+ 1. Add the following to `~/.aws/credentials`
49
+ ```
50
+ [<%= app_name %>-terraform-backend]
51
+ aws_access_key_id = <access_key_id from bucket_credentials>
52
+ aws_secret_access_key = <secret_access_key from bucket_credentials>
53
+ ```
54
+
55
+ 1. Copy `bucket` from `bucket_credentials` output to the backend block of `staging/providers.tf` and `production/providers.tf`
56
+
57
+ ## SpaceDeployers
58
+
59
+ A [SpaceDeployer](https://cloud.gov/docs/services/cloud-gov-service-account/) account is required to run terraform or
60
+ deploy the application from the CI/CD pipeline. Create a new account by running:
61
+
62
+ `./create_space_deployer.sh <SPACE_NAME> <ACCOUNT_NAME>`
63
+
64
+ ## Set up a new environment manually
65
+
66
+ The below steps rely on you first configuring access to the Terraform state in s3 as described in [Terraform State Credentials](#terraform-state-credentials).
67
+
68
+ 1. `cd` to the environment you are working in
69
+
70
+ 1. Set up a SpaceDeployer
71
+ ```bash
72
+ # create a space deployer service instance that can log in with just a username and password
73
+ # the value of < SPACE_NAME > should be `staging` or `prod` depending on where you are working
74
+ # the value for < ACCOUNT_NAME > can be anything, although we recommend
75
+ # something that communicates the purpose of the deployer
76
+ # for example: circleci-deployer for the credentials CircleCI uses to
77
+ # deploy the application or <your_name>-terraform for credentials to run terraform manually
78
+ ../create_space_deployer.sh <SPACE_NAME> <ACCOUNT_NAME> > secrets.auto.tfvars
79
+ ```
80
+
81
+ The script will output the `username` (as `cf_user`) and `password` (as `cf_password`) for your `<ACCOUNT_NAME>`. Read more in the [cloud.gov service account documentation](https://cloud.gov/docs/services/cloud-gov-service-account/).
82
+
83
+ The easiest way to use this script is to redirect the output directly to the `secrets.auto.tfvars` file it needs to be used in
84
+
85
+ 1. Run terraform from your new environment directory with
86
+ ```bash
87
+ terraform init
88
+ terraform plan
89
+ ```
90
+
91
+ 1. Apply changes with `terraform apply`.
92
+
93
+ 1. Remove the space deployer service instance if it doesn't need to be used again, such as when manually running terraform once.
94
+ ```bash
95
+ # <SPACE_NAME> and <ACCOUNT_NAME> have the same values as used above.
96
+ ../destroy_space_deployer.sh <SPACE_NAME> <ACCOUNT_NAME>
97
+ ```
98
+
99
+ ## Structure
100
+
101
+ Each environment has its own module, which relies on a shared module for everything except the providers code and environment specific variables and settings.
102
+
103
+ ```
104
+ - bootstrap/
105
+ |- main.tf
106
+ |- providers.tf
107
+ |- variables.tf
108
+ |- run.sh
109
+ |- teardown_creds.sh
110
+ |- import.sh
111
+ - <env>/
112
+ |- main.tf
113
+ |- providers.tf
114
+ |- secrets.auto.tfvars
115
+ |- variables.tf
116
+ - shared/
117
+ |- s3/
118
+ |- main.tf
119
+ |- providers.tf
120
+ |- variables.tf
121
+ |- database/
122
+ |- main.tf
123
+ |- providers.tf
124
+ |- variables.tf
125
+ |- domain/
126
+ |- main.tf
127
+ |- providers.tf
128
+ |- variables.tf
129
+ ```
130
+
131
+ In the shared modules:
132
+ - `providers.tf` contains set up instructions for Terraform about Cloud Foundry and AWS
133
+ - `main.tf` sets up the data and resources the application relies on
134
+ - `variables.tf` lists the required variables and applicable default values
135
+
136
+ In the environment-specific modules:
137
+ - `providers.tf` lists the required providers
138
+ - `main.tf` calls the shared Terraform code, but this is also a place where you can add any other services, resources, etc, which you would like to set up for that environment
139
+ - `variables.tf` lists the variables that will be needed, either to pass through to the child module or for use in this module
140
+ - `secrets.auto.tfvars` is a file which contains the information about the service-key and other secrets that should not be shared
141
+
142
+ In the bootstrap module:
143
+ - `providers.tf` lists the required providers
144
+ - `main.tf` sets up s3 bucket to be shared across all environments. It lives in `prod` to communicate that it should not be deleted
145
+ - `variables.tf` lists the variables that will be needed. Most values are hard-coded in this module
146
+ - `run.sh` Helper script to set up a space deployer and run terraform. The terraform action (`show`/`plan`/`apply`/`destroy`) is passed as an argument
147
+ - `teardown_creds.sh` Helper script to remove the space deployer setup as part of `run.sh`
148
+ - `import.sh` Helper script to create a new local state file in case terraform changes are needed
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+
3
+ read -p "Are you sure you want to import terraform state (y/n)? " verify
4
+
5
+ if [[ $verify == "y" ]]; then
6
+ echo "Importing bootstrap state"
7
+ ./run.sh import module.s3.cloudfoundry_service_instance.bucket TKTK
8
+ ./run.sh import cloudfoundry_service_key.bucket_creds TKTK
9
+ ./run.sh plan
10
+ else
11
+ echo "Not importing bootstrap state"
12
+ fi
@@ -0,0 +1,25 @@
1
+ locals {
2
+ cf_api_url = "https://api.fr.cloud.gov"
3
+ s3_service_name = "<%= app_name %>-terraform-state"
4
+ }
5
+
6
+ module "s3" {
7
+ source = "../shared/s3"
8
+
9
+ cf_api_url = local.cf_api_url
10
+ cf_user = var.cf_user
11
+ cf_password = var.cf_password
12
+ cf_org_name = "<%= @cloud_gov_organization %>"
13
+ cf_space_name = "<%= @cloud_gov_production_space %>"
14
+ s3_service_name = local.s3_service_name<% if @cloud_gov_organization == "sandbox-gsa" %>
15
+ s3_plan_name = "basic-sandbox"<% end %>
16
+ }
17
+
18
+ resource "cloudfoundry_service_key" "bucket_creds" {
19
+ name = "${local.s3_service_name}-access"
20
+ service_instance = module.s3.bucket_id
21
+ }
22
+
23
+ output "bucket_credentials" {
24
+ value = cloudfoundry_service_key.bucket_creds.credentials
25
+ }
@@ -0,0 +1,16 @@
1
+ terraform {
2
+ required_version = "~> 1.0"
3
+ required_providers {
4
+ cloudfoundry = {
5
+ source = "cloudfoundry-community/cloudfoundry"
6
+ version = "0.15.0"
7
+ }
8
+ }
9
+ }
10
+
11
+ provider "cloudfoundry" {
12
+ api_url = local.cf_api_url
13
+ user = var.cf_user
14
+ password = var.cf_password
15
+ app_logs_max = 30
16
+ }
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+
3
+ if [[ ! -f "secrets.auto.tfvars" ]]; then
4
+ ../create_space_deployer.sh <%= @cloud_gov_production_space %> config-bootstrap-deployer > secrets.auto.tfvars
5
+ fi
6
+
7
+ if [[ $# -gt 0 ]]; then
8
+ echo "Running terraform $@"
9
+ terraform $@
10
+ else
11
+ echo "Not running terraform"
12
+ fi
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+
3
+ ../destroy_space_deployer.sh <%= @cloud_gov_production_space %> config-bootstrap-deployer
4
+
5
+ rm secrets.auto.tfvars
@@ -0,0 +1,2 @@
1
+ variable "cf_password" {}
2
+ variable "cf_user" {}
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+ set -o pipefail
5
+
6
+ if [[ $# -lt 2 ]]; then
7
+ echo "$0 <<SPACE_NAME>> <<ACCOUNT_NAME>>"
8
+ exit 1;
9
+ fi
10
+
11
+ space=$1
12
+ service=$2
13
+
14
+ cf target -s $space 1>&2
15
+
16
+ # create space deployer service
17
+ cf create-service cloud-gov-service-account space-deployer $service 1>&2
18
+
19
+ # create service key
20
+ cf create-service-key $service space-deployer-key 1>&2
21
+
22
+ # output service key to stdout in secrets.auto.tfvars format
23
+ creds=`cf service-key $service space-deployer-key | tail -n 4`
24
+ username=`echo $creds | jq '.username'`
25
+ password=`echo $creds | jq '.password'`
26
+
27
+ cat << EOF
28
+ # generated with $0 $space $service
29
+ # revoke with $(dirname $0)/destroy_space_deployer.sh $space $service
30
+
31
+ cf_user = $username
32
+ cf_password = $password
33
+ EOF