rails_template_18f 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.standard.yml +2 -0
- data/CHANGELOG.md +6 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +132 -0
- data/LICENSE.md +21 -0
- data/README.md +140 -0
- data/Rakefile +10 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/lib/generators/rails_template18f/circleci/circleci_generator.rb +116 -0
- data/lib/generators/rails_template18f/circleci/templates/Dockerfile.tt +13 -0
- data/lib/generators/rails_template18f/circleci/templates/bin/ci-server-start +8 -0
- data/lib/generators/rails_template18f/circleci/templates/circleci/config.yml.tt +413 -0
- data/lib/generators/rails_template18f/circleci/templates/docker-compose.ci.yml +26 -0
- data/lib/generators/rails_template18f/github_actions/github_actions_generator.rb +137 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/actions/run-server/action.yml +28 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/actions/setup-languages/action.yml.tt +20 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/actions/setup-project/action.yml.tt +33 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/brakeman-analysis.yml +44 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/dependency-scans.yml +39 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-production.yml.tt +53 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/deploy-staging.yml.tt +53 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-daily-scan.yml.tt +44 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/owasp-scan.yml.tt +47 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/pa11y.yml.tt +65 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/rspec.yml.tt +34 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-production.yml +79 -0
- data/lib/generators/rails_template18f/github_actions/templates/github/workflows/terraform-staging.yml +79 -0
- data/lib/rails_template18f/terraform_options.rb +68 -0
- data/lib/rails_template18f/version.rb +5 -0
- data/lib/rails_template_18f.rb +13 -0
- data/rails-template-18f.gemspec +40 -0
- data/railsrc +10 -0
- data/railsrc-hotwire +8 -0
- data/template.rb +506 -0
- data/templates/README.md.tt +213 -0
- data/templates/app/assets/images/uswds.js +5 -0
- data/templates/app/assets/stylesheets/uswds-settings.scss +7 -0
- data/templates/app/views/application/_banner_lock_icon.html.erb +19 -0
- data/templates/app/views/application/_demo_site_banner.html.erb +3 -0
- data/templates/app/views/application/_header.html.erb +26 -0
- data/templates/app/views/application/_usa_banner.html.erb +51 -0
- data/templates/bin/owasp-scan +49 -0
- data/templates/bin/pa11y-scan +10 -0
- data/templates/bin/with-server +35 -0
- data/templates/browserslistrc +5 -0
- data/templates/config/deployment/production.yml +3 -0
- data/templates/config/deployment/staging.yml +3 -0
- data/templates/config/environments/ci.rb +10 -0
- data/templates/config/environments/staging.rb +6 -0
- data/templates/config/locales/en.yml.tt +25 -0
- data/templates/config/locales/es.yml +19 -0
- data/templates/config/locales/fr.yml +22 -0
- data/templates/config/locales/zh.yml +16 -0
- data/templates/config/newrelic.yml +65 -0
- data/templates/doc/adr/0001-record-architecture-decisions.md.tt +21 -0
- data/templates/doc/adr/0002-initial-architecture-decisions.md.tt +24 -0
- data/templates/doc/adr/0003-security-scans.md.tt +44 -0
- data/templates/doc/adr/0004-rails-csp-compliant-script-tag-helpers.md.tt +53 -0
- data/templates/doc/compliance/README.md +37 -0
- data/templates/doc/compliance/apps/application.boundary.md.tt +80 -0
- data/templates/doc/compliance/apps/data.logical.md +21 -0
- data/templates/doc/compliance/rendered/apps/.keep +0 -0
- data/templates/editorconfig +5 -0
- data/templates/env +10 -0
- data/templates/githooks/pre-commit.tt +35 -0
- data/templates/lib/tasks/cf.rake +9 -0
- data/templates/lib/tasks/scanning.rake +63 -0
- data/templates/manifest.yml.tt +19 -0
- data/templates/pa11yci +9 -0
- data/templates/terraform/README.md.tt +148 -0
- data/templates/terraform/bootstrap/import.sh +12 -0
- data/templates/terraform/bootstrap/main.tf.tt +25 -0
- data/templates/terraform/bootstrap/providers.tf +16 -0
- data/templates/terraform/bootstrap/run.sh.tt +12 -0
- data/templates/terraform/bootstrap/teardown_creds.sh.tt +5 -0
- data/templates/terraform/bootstrap/variables.tf +2 -0
- data/templates/terraform/create_space_deployer.sh +33 -0
- data/templates/terraform/destroy_space_deployer.sh +19 -0
- data/templates/terraform/production/main.tf.tt +50 -0
- data/templates/terraform/production/providers.tf.tt +17 -0
- data/templates/terraform/production/variables.tf +2 -0
- data/templates/terraform/shared/database/main.tf.tt +23 -0
- data/templates/terraform/shared/database/providers.tf +16 -0
- data/templates/terraform/shared/database/variables.tf +42 -0
- data/templates/terraform/shared/domain/main.tf.tt +46 -0
- data/templates/terraform/shared/domain/providers.tf +16 -0
- data/templates/terraform/shared/domain/variables.tf +47 -0
- data/templates/terraform/shared/s3/main.tf +27 -0
- data/templates/terraform/shared/s3/providers.tf +16 -0
- data/templates/terraform/shared/s3/variables.tf +43 -0
- data/templates/terraform/staging/main.tf.tt +30 -0
- data/templates/terraform/staging/providers.tf.tt +17 -0
- data/templates/terraform/staging/variables.tf +2 -0
- data/templates/zap.conf +121 -0
- 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
|
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,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,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,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
|