rails_template_18f 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +5 -3
- data/lib/generators/rails_template18f/active_storage/active_storage_generator.rb +4 -3
- data/lib/generators/rails_template18f/auditree/auditree_generator.rb +36 -6
- data/lib/generators/rails_template18f/auditree/templates/gitlab/auditree.yml.tt +48 -0
- data/lib/generators/rails_template18f/cloud_gov_config/cloud_gov_config_generator.rb +0 -8
- data/lib/generators/rails_template18f/cloud_gov_config/templates/app/models/cloud_gov_config.rb +6 -15
- data/lib/generators/rails_template18f/cloud_gov_config/templates/spec/models/cloud_gov_config_spec.rb +13 -19
- data/lib/generators/rails_template18f/github_actions/github_actions_generator.rb +0 -4
- data/lib/generators/rails_template18f/gitlab_ci/gitlab_ci_generator.rb +0 -9
- data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab/rails.yml +2 -2
- data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab/terraform.yml +8 -3
- data/lib/generators/rails_template18f/gitlab_ci/templates/gitlab-ci.yml.tt +2 -1
- data/lib/generators/rails_template18f/oscal/oscal_generator.rb +15 -1
- data/lib/generators/rails_template18f/oscal/templates/bin/trestle.tt +10 -1
- data/lib/generators/rails_template18f/oscal/templates/gitlab/trestle.yml.tt +29 -0
- data/lib/generators/rails_template18f/public_egress/public_egress_generator.rb +1 -1
- data/lib/generators/rails_template18f/sidekiq/templates/config/initializers/redis.rb +1 -1
- data/lib/generators/rails_template18f/terraform/templates/gitlab_bootstrap/apply.sh +25 -0
- data/lib/generators/rails_template18f/terraform/templates/gitlab_bootstrap/main.tf.tt +98 -0
- data/lib/generators/rails_template18f/terraform/templates/gitlab_bootstrap/setup_shadowenv.sh +59 -0
- data/lib/generators/rails_template18f/terraform/templates/s3_bootstrap/common/templates/backend_config.tftpl +6 -0
- data/lib/generators/rails_template18f/terraform/templates/s3_bootstrap/common/templates/bot_secrets.tftpl +5 -0
- data/lib/generators/rails_template18f/terraform/templates/s3_bootstrap/common/users.auto.tfvars +5 -0
- data/lib/generators/rails_template18f/terraform/templates/{full_bootstrap → s3_bootstrap/full}/main.tf.tt +2 -9
- data/lib/generators/rails_template18f/terraform/templates/{sandbox_bootstrap → s3_bootstrap/sandbox}/main.tf.tt +2 -2
- data/lib/generators/rails_template18f/terraform/templates/terraform/.shadowenv.d/.gitignore +3 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/README.md.tt +38 -26
- data/lib/generators/rails_template18f/terraform/templates/terraform/app.tf.tt +1 -0
- data/lib/generators/rails_template18f/terraform/templates/terraform/main.tf.tt +3 -3
- data/lib/generators/rails_template18f/terraform/templates/terraform/providers.tf.tt +2 -8
- data/lib/generators/rails_template18f/terraform/templates/terraform/staging.tfvars.tt +5 -5
- data/lib/generators/rails_template18f/terraform/templates/terraform/terraform.sh.tt +40 -15
- data/lib/generators/rails_template18f/terraform/templates/terraform/variables.tf.tt +6 -6
- data/lib/generators/rails_template18f/terraform/terraform_generator.rb +78 -5
- data/lib/rails_template18f/version.rb +1 -1
- data/template.rb +32 -15
- metadata +21 -13
- data/lib/generators/rails_template18f/terraform/templates/terraform/bootstrap/templates/backend_config.tftpl +0 -8
- /data/lib/generators/rails_template18f/{github_actions → oscal}/templates/github/workflows/assemble-ssp.yml.tt +0 -0
- /data/lib/generators/rails_template18f/{github_actions → oscal}/templates/github/workflows/validate-ssp.yml +0 -0
- /data/lib/generators/rails_template18f/terraform/templates/{terraform/bootstrap/templates → gitlab_bootstrap}/bot_secrets.tftpl +0 -0
- /data/lib/generators/rails_template18f/terraform/templates/{terraform/bootstrap → gitlab_bootstrap}/users.auto.tfvars +0 -0
- /data/lib/generators/rails_template18f/terraform/templates/{terraform/bootstrap → s3_bootstrap/common}/apply.sh +0 -0
- /data/lib/generators/rails_template18f/terraform/templates/{full_bootstrap → s3_bootstrap/full}/imports.tf.tftpl +0 -0
- /data/lib/generators/rails_template18f/terraform/templates/{sandbox_bootstrap → s3_bootstrap/sandbox}/imports.tf.tftpl +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bab13c0d757c29b86c139635bed3f016cc486d38299b7aa90533d888b6995d69
|
4
|
+
data.tar.gz: 447e73554b32dd86782cf52c4cda2699f55c4e0706c05f813e82515a2e61912f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cad79214c9110e3fb33968b8c6d1fc98477c6c979bc58724e9b88f57985456ca7d404515dd004891dade32d4e95183f1a90c12d4d05ee87753f52867f9efde94
|
7
|
+
data.tar.gz: ecb278387c12d47fc9bc611f26c0787fa7c4c2dda43fa14ce8f176bfb4cf9d0d2c6b04718ab0faa3569b4f5897e9d65cdc0b0944972c64648e8e401d9b341881
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [2.2.0] - 2025-06-27
|
4
|
+
|
5
|
+
- Prevent non-compliant hostnames by replacing underscores with dashes
|
6
|
+
- use shadowenv for configuring terraform backend secrets
|
7
|
+
- use GitLab http backend for terraform state storage whenever configuring both terraform and GitLab CI
|
8
|
+
- Create GitLabCI jobs for oscal and auditree generators
|
9
|
+
- fixes for deploying to the sandbox-gsa cloug.gov org
|
10
|
+
|
3
11
|
## [2.1.0] - 2025-04-29
|
4
12
|
|
5
13
|
- Terraform generator updates to remove the old cloudfoundy-community provider and reduce the need for cloud.gov service accounts
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rails_template_18f (2.
|
4
|
+
rails_template_18f (2.2.0)
|
5
5
|
activesupport (~> 8.0.1)
|
6
6
|
colorize (~> 1.1)
|
7
7
|
railties (~> 8.0.1)
|
@@ -85,8 +85,9 @@ GEM
|
|
85
85
|
date
|
86
86
|
stringio
|
87
87
|
racc (1.8.1)
|
88
|
-
rack (3.1.
|
89
|
-
rack-session (2.
|
88
|
+
rack (3.1.16)
|
89
|
+
rack-session (2.1.1)
|
90
|
+
base64 (>= 0.1.0)
|
90
91
|
rack (>= 3.0.0)
|
91
92
|
rack-test (2.2.0)
|
92
93
|
rack (>= 1.3)
|
@@ -177,6 +178,7 @@ GEM
|
|
177
178
|
|
178
179
|
PLATFORMS
|
179
180
|
arm64-darwin-23
|
181
|
+
arm64-darwin-24
|
180
182
|
x86_64-darwin-20
|
181
183
|
x86_64-darwin-21
|
182
184
|
x86_64-linux
|
@@ -20,12 +20,13 @@ module RailsTemplate18f
|
|
20
20
|
environment "config.active_storage.service = :local", env: "ci"
|
21
21
|
append_to_file "config/storage.yml", <<~EOYAML
|
22
22
|
|
23
|
+
<% cgc = CloudGovConfig.new %>
|
23
24
|
amazon:
|
24
25
|
service: S3
|
25
|
-
access_key_id: <%=
|
26
|
-
secret_access_key: <%=
|
26
|
+
access_key_id: <%= cgc.dig(:s3, :credentials, :access_key_id) %>
|
27
|
+
secret_access_key: <%= cgc.dig(:s3, :credentials, :secret_access_key) %>
|
27
28
|
region: us-gov-west-1
|
28
|
-
bucket: <%=
|
29
|
+
bucket: <%= cgc.dig(:s3, :credentials, :bucket) %>
|
29
30
|
EOYAML
|
30
31
|
end
|
31
32
|
|
@@ -41,7 +41,7 @@ module RailsTemplate18f
|
|
41
41
|
plant-helper -f /tmp/rspec.json -c assessment-plans -d "RSpec run assessment plan"
|
42
42
|
-t 31536000 -l #{auditree_evidence_locker}
|
43
43
|
|
44
|
-
- name:
|
44
|
+
- name: Plant assessment results in evidence locker
|
45
45
|
uses: ./.github/actions/auditree-cmd
|
46
46
|
env:
|
47
47
|
GITHUB_TOKEN: ${{ secrets.AUDITREE_GITHUB_TOKEN }}
|
@@ -54,6 +54,19 @@ PLANT_HELPER_STEPS
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
def copy_gitlab_actions
|
58
|
+
if file_exists? ".gitlab-ci.yml"
|
59
|
+
directory "gitlab", ".gitlab"
|
60
|
+
insert_into_file ".gitlab-ci.yml", " - local: \".gitlab/auditree.yml\"\n", after: /^include:\n/
|
61
|
+
insert_into_file ".gitlab-ci.yml", " AUDITREE_VERSION: #{docker_auditree_tag}\n", after: /^variables:\n/
|
62
|
+
insert_into_file ".gitlab-ci.yml", <<EOY, after: /^\s+- bundle exec rspec\n/
|
63
|
+
artifacts:
|
64
|
+
paths:
|
65
|
+
- tmp/oscal/**/*
|
66
|
+
EOY
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
57
70
|
def update_readme
|
58
71
|
if file_content("README.md").match?("## Documentation")
|
59
72
|
insert_into_file "README.md", readme_contents, after: "## Documentation\n"
|
@@ -74,7 +87,7 @@ PLANT_HELPER_STEPS
|
|
74
87
|
end
|
75
88
|
|
76
89
|
def auditree_evidence_locker
|
77
|
-
options[:evidence_locker].present? ? options[:evidence_locker] : "
|
90
|
+
options[:evidence_locker].present? ? options[:evidence_locker] : "REPLACE_THIS_WITH_YOUR_EVIDENCE_LOCKER_REPO"
|
78
91
|
end
|
79
92
|
|
80
93
|
def git_email
|
@@ -98,10 +111,9 @@ PLANT_HELPER_STEPS
|
|
98
111
|
1. Docker desktop must be running
|
99
112
|
1. Initialize the config file with `bin/auditree init`
|
100
113
|
1. Create an evidence locker repository with a default or blank README
|
101
|
-
1.
|
102
|
-
|
103
|
-
|
104
|
-
1. Copy the `devtools_cloud_gov` component definition into the project with the latest docker-trestle
|
114
|
+
1. Update `config/auditree.template.json` with the repo address for your locker
|
115
|
+
#{ci_readme_contents.chomp}
|
116
|
+
1. Copy the `devtools_cloud_gov` component definition into the project with the latest docker-trestle after [setting up docker-trestle](#initial-trestle-setup)
|
105
117
|
|
106
118
|
#### Ongoing use
|
107
119
|
|
@@ -109,6 +121,24 @@ PLANT_HELPER_STEPS
|
|
109
121
|
auditree and using new checks.
|
110
122
|
README
|
111
123
|
end
|
124
|
+
|
125
|
+
def ci_readme_contents
|
126
|
+
if file_exists? ".gitlab-ci.yml"
|
127
|
+
<<~README
|
128
|
+
1. Remove the `repo_integrity` section of `config/auditree.template.json`
|
129
|
+
1. Create a gitlab personal access token with `write_repository` scope to interact with the code repo and evidence locker and set as `AUDITREE_GITLAB_TOKEN` secret within your CI/CD variables.
|
130
|
+
#{options[:evidence_locker].blank? ? "1. Update `.gitlab/auditree.yml` with the locker repository URL" : ""}
|
131
|
+
README
|
132
|
+
elsif file_exists? ".github/workflows"
|
133
|
+
<<~README
|
134
|
+
1. Update `config/auditree.template.json` with the repo address for your code repos
|
135
|
+
1. Create a github personal access token to interact with the code repo and evidence locker and set as `AUDITREE_GITHUB_TOKEN` secret within your Github Actions secrets.
|
136
|
+
#{options[:evidence_locker].blank? ? "1. Update `.github/workflows/rspec.yml` with the locker repository URL" : ""}
|
137
|
+
README
|
138
|
+
else
|
139
|
+
""
|
140
|
+
end
|
141
|
+
end
|
112
142
|
end
|
113
143
|
end
|
114
144
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
.auditree:setup:
|
2
|
+
inherit:
|
3
|
+
default: false
|
4
|
+
image: "ghcr.io/gsa-tts/auditree:${AUDITREE_VERSION}"
|
5
|
+
variables:
|
6
|
+
CDEF: "${CI_PROJECT_DIR}/doc/compliance/oscal/component-definitions/devtools_cloud_gov/component-definition.json"
|
7
|
+
AUDITREE_CONFIG: "${CI_PROJECT_DIR}/config/auditree.template.json"
|
8
|
+
before_script:
|
9
|
+
- git config --global user.name "$GITLAB_USER_NAME"
|
10
|
+
- git config --global user.email "$GITLAB_USER_EMAIL"
|
11
|
+
- cf api api.fr.cloud.gov
|
12
|
+
- cd $HOME
|
13
|
+
- export GITLAB_TOKEN="auditree-gitlab-token:${AUDITREE_GITLAB_TOKEN}"
|
14
|
+
|
15
|
+
auditree:
|
16
|
+
extends: .auditree:setup
|
17
|
+
stage: scan
|
18
|
+
script:
|
19
|
+
- fetch -c "$CDEF" -t "$AUDITREE_CONFIG"
|
20
|
+
- check -c "$CDEF" -t "$AUDITREE_CONFIG" -o "$CI_PROJECT_DIR"
|
21
|
+
artifacts:
|
22
|
+
paths:
|
23
|
+
- auditree.json
|
24
|
+
rules:
|
25
|
+
- if: $CI_PIPELINE_SOURCE == "schedule"
|
26
|
+
|
27
|
+
rspec:plant:
|
28
|
+
extends: .auditree:setup
|
29
|
+
stage: test
|
30
|
+
needs: ["rspec"]
|
31
|
+
variables:
|
32
|
+
PLAN_FILE: "${CI_PROJECT_DIR}/tmp/oscal/assessment-plans/rspec/assessment-plan.json"
|
33
|
+
RESULT_FILE: "${CI_PROJECT_DIR}/tmp/oscal/assessment-results/rspec/assessment-results.json"
|
34
|
+
script:
|
35
|
+
- |
|
36
|
+
if [ -f "$PLAN_FILE" ]; then
|
37
|
+
plant-helper -f "$PLAN_FILE" -c assessment-plans -d "RSpec run assessment plan" -t 31536000 -l "<%= auditree_evidence_locker %>"
|
38
|
+
else
|
39
|
+
echo "No plan file, skipping plant"
|
40
|
+
fi
|
41
|
+
- |
|
42
|
+
if [ -f "$RESULT_FILE" ]; then
|
43
|
+
plant-helper -f "$RESULT_FILE" -c assessment-results -d "RSpec run assessment results" -t 31536000 -l "<%= auditree_evidence_locker %>"
|
44
|
+
else
|
45
|
+
echo "No result file, skipping plant"
|
46
|
+
fi
|
47
|
+
rules:
|
48
|
+
- if: $CI_PIPELINE_SOURCE != "schedule"
|
@@ -12,14 +12,6 @@ module RailsTemplate18f
|
|
12
12
|
Install a helper class to retrieve configuration from ENV["VCAP_SERVICES"]
|
13
13
|
DESC
|
14
14
|
|
15
|
-
def install_climate_control
|
16
|
-
return if gem_installed?("climate_control")
|
17
|
-
gem_group :test do
|
18
|
-
gem "climate_control", "~> 1.2"
|
19
|
-
end
|
20
|
-
bundle_install
|
21
|
-
end
|
22
|
-
|
23
15
|
def install_model_and_test
|
24
16
|
copy_file "app/models/cloud_gov_config.rb"
|
25
17
|
copy_file "spec/models/cloud_gov_config_spec.rb"
|
data/lib/generators/rails_template18f/cloud_gov_config/templates/app/models/cloud_gov_config.rb
CHANGED
@@ -1,23 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class CloudGovConfig
|
4
|
-
|
4
|
+
attr_reader :vcap_services
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
first, *rest = path
|
9
|
-
vcap_services[first]&.first&.dig(*rest)
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.vcap_services
|
13
|
-
if Rails.env.test?
|
14
|
-
parse_env
|
15
|
-
else
|
16
|
-
@vcap_services ||= parse_env
|
17
|
-
end
|
6
|
+
def initialize(env = ENV["VCAP_SERVICES"])
|
7
|
+
@vcap_services = env.blank? ? {} : JSON.parse(env).with_indifferent_access
|
18
8
|
end
|
19
9
|
|
20
|
-
|
21
|
-
|
10
|
+
def dig(*path)
|
11
|
+
first, *rest = path
|
12
|
+
vcap_services[first]&.first&.dig(*rest)
|
22
13
|
end
|
23
14
|
end
|
@@ -3,35 +3,29 @@
|
|
3
3
|
require "rails_helper"
|
4
4
|
|
5
5
|
RSpec.describe CloudGovConfig, type: :model do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
describe "#dig" do
|
7
|
+
[nil, "", "{}"].each do |blank|
|
8
|
+
context "VCAP_SERVICES is #{blank.inspect}" do
|
9
|
+
subject { described_class.new blank }
|
10
|
+
it "returns nil" do
|
11
|
+
expect(subject.dig(:s3, :credentials, :bucket)).to be_nil
|
12
|
+
end
|
12
13
|
end
|
13
14
|
end
|
14
15
|
|
15
16
|
context "VCAP_SERVICES is set" do
|
17
|
+
subject { described_class.new vcap }
|
16
18
|
let(:bucket_name) { "bucket-name" }
|
17
19
|
let(:vcap) {
|
18
20
|
{
|
19
|
-
s3: [
|
20
|
-
{
|
21
|
-
|
22
|
-
bucket: bucket_name
|
23
|
-
}
|
21
|
+
s3: [{
|
22
|
+
credentials: {
|
23
|
+
bucket: bucket_name
|
24
24
|
}
|
25
|
-
]
|
26
|
-
}
|
25
|
+
}]
|
26
|
+
}.to_json
|
27
27
|
}
|
28
28
|
|
29
|
-
around do |example|
|
30
|
-
ClimateControl.modify VCAP_SERVICES: vcap.to_json do
|
31
|
-
example.run
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
29
|
it "can find a path" do
|
36
30
|
expect(subject.dig(:s3, :credentials, :bucket)).to eq bucket_name
|
37
31
|
end
|
@@ -17,10 +17,6 @@ module RailsTemplate18f
|
|
17
17
|
|
18
18
|
def install_actions
|
19
19
|
directory "github", ".github"
|
20
|
-
if !oscal_dir_exists?
|
21
|
-
remove_file ".github/workflows/validate-ssp.yml"
|
22
|
-
remove_file ".github/workflows/assemble-ssp.yml"
|
23
|
-
end
|
24
20
|
if !terraform_manage_spaces?
|
25
21
|
remove_file ".github/workflows/terraform-production.yml"
|
26
22
|
remove_file ".github/workflows/deploy-production.yml"
|
@@ -79,7 +79,6 @@ EOB
|
|
79
79
|
| `CF_USERNAME` | cloud.gov SpaceDeployer username |
|
80
80
|
| `CF_PASSWORD` | cloud.gov SpaceDeployer password |
|
81
81
|
| `RAILS_MASTER_KEY` | `config/master.key` |
|
82
|
-
#{terraform_secret_values}
|
83
82
|
EOM
|
84
83
|
end
|
85
84
|
|
@@ -96,7 +95,6 @@ EOB
|
|
96
95
|
| `CF_USERNAME` | cloud.gov SpaceDeployer username |
|
97
96
|
| `CF_PASSWORD` | cloud.gov SpaceDeployer password |
|
98
97
|
| `PRODUCTION_RAILS_MASTER_KEY` | `config/credentials/production.key`. Should be marked as `Protected`. |
|
99
|
-
#{terraform_secret_values}
|
100
98
|
EOM
|
101
99
|
else
|
102
100
|
"Production deploys are not supported in the sandbox organization."
|
@@ -114,13 +112,6 @@ EOB
|
|
114
112
|
|
115
113
|
private
|
116
114
|
|
117
|
-
def terraform_secret_values
|
118
|
-
<<~EOM
|
119
|
-
| `TERRAFORM_PUBLIC_BACKEND_CONFIG` | File-type variable containing all entries from secrets.backend.tfvars _except_ `secret_key`. Marked as `Visible` |
|
120
|
-
| `TERRAFORM_SECRET_BACKEND_CONFIG` | File-type variable containing the `secret_key` line from secrets.backend.tfvars. Masked and hidden. |
|
121
|
-
EOM
|
122
|
-
end
|
123
|
-
|
124
115
|
def postgres_version
|
125
116
|
options[:postgres_version]
|
126
117
|
end
|
@@ -30,7 +30,7 @@ include:
|
|
30
30
|
alias: pg
|
31
31
|
before_script:
|
32
32
|
- !reference [.setup-ruby]
|
33
|
-
- export DATABASE_URL="postgres://postgres:${POSTGRES_PASSWORD}@${
|
33
|
+
- export DATABASE_URL="postgres://postgres:${POSTGRES_PASSWORD}@${WSR_SERVICE_HOST_pg}:5432/${POSTGRES_DB}"
|
34
34
|
- bin/rails db:prepare
|
35
35
|
|
36
36
|
.run-server:
|
@@ -47,7 +47,7 @@ include:
|
|
47
47
|
- sleep 5
|
48
48
|
|
49
49
|
.owasp:setup:
|
50
|
-
stage:
|
50
|
+
stage: scan
|
51
51
|
extends: .run-server
|
52
52
|
image: "rcahearngsa/owasp-ruby:${RUBY_VERSION}"
|
53
53
|
variables:
|
@@ -8,11 +8,16 @@
|
|
8
8
|
entrypoint: ["sh"]
|
9
9
|
variables:
|
10
10
|
CF_API_URL: https://api.fr.cloud.gov
|
11
|
-
|
11
|
+
TF_STATE_NAME: staging
|
12
|
+
TF_HTTP_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${TF_STATE_NAME}
|
13
|
+
TF_HTTP_LOCK_ADDRESS: ${TF_HTTP_ADDRESS}/lock
|
14
|
+
TF_HTTP_UNLOCK_ADDRESS: ${TF_HTTP_ADDRESS}/lock
|
15
|
+
TF_HTTP_USERNAME: gitlab-ci-token
|
16
|
+
TF_HTTP_PASSWORD: ${CI_JOB_TOKEN}
|
12
17
|
dependencies: []
|
13
18
|
before_script:
|
14
19
|
- cd terraform
|
15
|
-
- terraform init
|
20
|
+
- terraform init
|
16
21
|
rules:
|
17
22
|
- if: $CI_PIPELINE_SOURCE != "schedule"
|
18
23
|
|
@@ -25,4 +30,4 @@
|
|
25
30
|
dependencies: null
|
26
31
|
variables:
|
27
32
|
CF_USER: $CF_USERNAME
|
28
|
-
|
33
|
+
TF_STATE_NAME: "production"
|
@@ -10,6 +10,7 @@ workflow:
|
|
10
10
|
stages:
|
11
11
|
- build
|
12
12
|
- test
|
13
|
+
- scan
|
13
14
|
- deploy
|
14
15
|
|
15
16
|
variables:
|
@@ -80,7 +81,7 @@ rspec:
|
|
80
81
|
- if: $CI_PIPELINE_SOURCE != "schedule"
|
81
82
|
|
82
83
|
pa11y_scan:
|
83
|
-
stage:
|
84
|
+
stage: scan
|
84
85
|
extends: .run-server
|
85
86
|
script:
|
86
87
|
- !reference [.install-puppet-deps]
|
@@ -49,6 +49,16 @@ module RailsTemplate18f
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
def copy_gitlab_ci
|
53
|
+
if use_gitlab_ci?
|
54
|
+
directory "gitlab", ".gitlab"
|
55
|
+
if file_exists? ".gitlab-ci.yml"
|
56
|
+
insert_into_file ".gitlab-ci.yml", " - local: \".gitlab/trestle.yml\"\n", after: /^include:\n/
|
57
|
+
insert_into_file ".gitlab-ci.yml", " TRESTLE_VERSION: #{docker_trestle_tag}\n", after: /^variables:\n/
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
52
62
|
def update_readme
|
53
63
|
if file_content("README.md").match?("## Documentation")
|
54
64
|
insert_into_file "README.md", readme_contents, after: "## Documentation\n"
|
@@ -85,13 +95,17 @@ module RailsTemplate18f
|
|
85
95
|
end
|
86
96
|
|
87
97
|
def docker_trestle_tag
|
88
|
-
options[:tag].present? ? options[:tag] : "
|
98
|
+
options[:tag].present? ? options[:tag] : "20250603"
|
89
99
|
end
|
90
100
|
|
91
101
|
def use_github_actions?
|
92
102
|
options[:ci] == "github" || file_exists?(".github/workflows")
|
93
103
|
end
|
94
104
|
|
105
|
+
def use_gitlab_ci?
|
106
|
+
options[:ci] == "gitlab" || file_exists?(".gitlab-ci.yml")
|
107
|
+
end
|
108
|
+
|
95
109
|
def readme_contents
|
96
110
|
content = <<~README
|
97
111
|
|
@@ -2,8 +2,17 @@
|
|
2
2
|
|
3
3
|
trestle_tag="<%= docker_trestle_tag %>"
|
4
4
|
|
5
|
+
if [ "$1" = "-h" ]; then
|
6
|
+
echo """
|
7
|
+
Usage: $0 [-h] [CMD [CMD ARGS]]
|
8
|
+
|
9
|
+
CMD defaults to 'bash'
|
10
|
+
"""
|
11
|
+
exit 0
|
12
|
+
fi
|
13
|
+
|
5
14
|
command="bash"
|
6
|
-
if [ "$1"
|
15
|
+
if [ -n "$1" ]; then
|
7
16
|
command=$1
|
8
17
|
shift 1
|
9
18
|
fi
|
@@ -0,0 +1,29 @@
|
|
1
|
+
.trestle:setup:
|
2
|
+
inherit:
|
3
|
+
default: false
|
4
|
+
image: "ghcr.io/gsa-tts/trestle:${TRESTLE_VERSION}"
|
5
|
+
before_script:
|
6
|
+
- cd doc/compliance/oscal
|
7
|
+
- export PATH="/app/bin:$PATH"
|
8
|
+
|
9
|
+
validate_ssp:
|
10
|
+
extends: .trestle:setup
|
11
|
+
stage: test
|
12
|
+
script:
|
13
|
+
- validate-ssp-json
|
14
|
+
rules:
|
15
|
+
- if: $CI_PIPELINE_SOURCE != "schedule"
|
16
|
+
|
17
|
+
assemble_ssp:
|
18
|
+
extends: .trestle:setup
|
19
|
+
stage: deploy
|
20
|
+
script:
|
21
|
+
- trestle assemble -n <%= app_name %> system-security-plan
|
22
|
+
- render-ssp
|
23
|
+
artifacts:
|
24
|
+
expose_as: "<%= app_name %> SSPP"
|
25
|
+
paths:
|
26
|
+
- doc/compliance/oscal/dist/system-security-plans/<%= app_name %>.json
|
27
|
+
- doc/compliance/oscal/ssp-render/<%= app_name %>_ssp.md
|
28
|
+
rules:
|
29
|
+
- if: $CI_PIPELINE_SOURCE != "schedule"
|
@@ -103,7 +103,7 @@ EOB
|
|
103
103
|
|
104
104
|
cf_org_name = local.cf_org_name
|
105
105
|
cf_space_name = "${var.cf_space_name}-egress"
|
106
|
-
allow_ssh = var.
|
106
|
+
allow_ssh = var.allow_ssh
|
107
107
|
deployers = local.space_deployers
|
108
108
|
developers = var.space_developers
|
109
109
|
auditors = var.space_auditors
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Rails.application.config.to_prepare do
|
4
|
-
redis_url = CloudGovConfig.dig "aws-elasticache-redis", "credentials", "uri"
|
4
|
+
redis_url = CloudGovConfig.new.dig "aws-elasticache-redis", "credentials", "uri"
|
5
5
|
if redis_url.present?
|
6
6
|
Sidekiq.configure_server do |config|
|
7
7
|
config.redis = {url: redis_url, ssl: true}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
if ! command -v terraform &> /dev/null
|
4
|
+
then
|
5
|
+
echo "terraform must be installed before running this script"
|
6
|
+
exit 1
|
7
|
+
fi
|
8
|
+
|
9
|
+
if [ -z "$GITLAB_PROJECT_ID" ] || [ -z "$GITLAB_HOSTNAME" ]; then
|
10
|
+
echo "GITLAB_PROJECT_ID or GITLAB_HOSTNAME has not been set. Run ./setup_shadowenv.sh first"
|
11
|
+
exit 1
|
12
|
+
fi
|
13
|
+
|
14
|
+
set -e
|
15
|
+
|
16
|
+
# ensure we're logged in via cli
|
17
|
+
cf spaces &> /dev/null || cf login -a api.fr.cloud.gov --sso
|
18
|
+
|
19
|
+
tf_state_address="https://$GITLAB_HOSTNAME/api/v4/projects/$GITLAB_PROJECT_ID/terraform/state/bootstrap"
|
20
|
+
terraform init \
|
21
|
+
-backend-config="address=$tf_state_address" \
|
22
|
+
-backend-config="lock_address=$tf_state_address/lock" \
|
23
|
+
-backend-config="unlock_address=$tf_state_address/lock"
|
24
|
+
|
25
|
+
terraform apply "$@"
|
@@ -0,0 +1,98 @@
|
|
1
|
+
terraform {
|
2
|
+
required_version = "~> 1.10"
|
3
|
+
required_providers {
|
4
|
+
cloudfoundry = {
|
5
|
+
source = "cloudfoundry/cloudfoundry"
|
6
|
+
version = "~> 1.7"
|
7
|
+
}
|
8
|
+
}
|
9
|
+
backend "http" {
|
10
|
+
lock_method = "POST"
|
11
|
+
unlock_method = "DELETE"
|
12
|
+
retry_wait_min = 5
|
13
|
+
}
|
14
|
+
}
|
15
|
+
# empty config will let terraform borrow cf-cli's auth
|
16
|
+
provider "cloudfoundry" {}
|
17
|
+
|
18
|
+
locals {
|
19
|
+
org_name = "<%= cloud_gov_organization %>"
|
20
|
+
space_name = "<%= terraform_manage_spaces? ? "#{ cloud_gov_production_space }-mgmt" : cloud_gov_staging_space %>"
|
21
|
+
}
|
22
|
+
|
23
|
+
data "cloudfoundry_org" "org" {
|
24
|
+
name = local.org_name
|
25
|
+
}
|
26
|
+
<% if terraform_manage_spaces? %>
|
27
|
+
variable "terraform_users" {
|
28
|
+
type = set(string)
|
29
|
+
description = "The list of developer emails and service account usernames who should be granted access to retrieve state bucket credentials"
|
30
|
+
|
31
|
+
validation {
|
32
|
+
condition = length(var.terraform_users) > 0
|
33
|
+
error_message = "terraform_users must include at least the current user calling apply.sh"
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
module "mgmt_space" {
|
38
|
+
source = "github.com/gsa-tts/terraform-cloudgov//cg_space?ref=v2.3.0"
|
39
|
+
|
40
|
+
cf_org_name = local.org_name
|
41
|
+
cf_space_name = local.space_name
|
42
|
+
developers = var.terraform_users
|
43
|
+
}
|
44
|
+
<% else %>
|
45
|
+
data "cloudfoundry_space" "space" {
|
46
|
+
name = local.space_name
|
47
|
+
org = data.cloudfoundry_org.org.id
|
48
|
+
}
|
49
|
+
<% end %>
|
50
|
+
data "cloudfoundry_service_plans" "cg_service_account" {
|
51
|
+
name = "<%= terraform_manage_spaces? ? "space-auditor" : "space-deployer" %>"
|
52
|
+
service_offering_name = "cloud-gov-service-account"
|
53
|
+
}
|
54
|
+
locals {
|
55
|
+
sa_service_name = "<%= app_name %>-cicd-deployer"
|
56
|
+
sa_key_name = "cicd-deployer-access-key"
|
57
|
+
sa_bot_credentials = jsondecode(data.cloudfoundry_service_credential_binding.runner_sa_key.credential_bindings.0.credential_binding).credentials
|
58
|
+
sa_cf_username = nonsensitive(local.sa_bot_credentials.username)
|
59
|
+
sa_cf_password = local.sa_bot_credentials.password
|
60
|
+
}
|
61
|
+
resource "cloudfoundry_service_instance" "runner_service_account" {
|
62
|
+
name = local.sa_service_name
|
63
|
+
type = "managed"
|
64
|
+
service_plan = data.cloudfoundry_service_plans.cg_service_account.service_plans.0.id<% if terraform_manage_spaces? %>
|
65
|
+
space = module.mgmt_space.space_id
|
66
|
+
depends_on = [module.mgmt_space]<% else %>
|
67
|
+
space = data.cloudfoundry_space.space.id<% end %>
|
68
|
+
}
|
69
|
+
resource "cloudfoundry_service_credential_binding" "runner_sa_key" {
|
70
|
+
name = local.sa_key_name
|
71
|
+
service_instance = cloudfoundry_service_instance.runner_service_account.id
|
72
|
+
type = "key"
|
73
|
+
}
|
74
|
+
data "cloudfoundry_service_credential_binding" "runner_sa_key" {
|
75
|
+
name = local.sa_key_name
|
76
|
+
service_instance = cloudfoundry_service_instance.runner_service_account.id
|
77
|
+
depends_on = [cloudfoundry_service_credential_binding.runner_sa_key]
|
78
|
+
}<% if terraform_manage_spaces? %>
|
79
|
+
data "cloudfoundry_user" "sa_user" {
|
80
|
+
name = local.sa_cf_username
|
81
|
+
}
|
82
|
+
resource "cloudfoundry_org_role" "sa_org_manager" {
|
83
|
+
user = data.cloudfoundry_user.sa_user.users.0.id
|
84
|
+
type = "organization_manager"
|
85
|
+
org = data.cloudfoundry_org.org.id
|
86
|
+
}<% end %>
|
87
|
+
|
88
|
+
resource "local_sensitive_file" "bot_secrets_file" {
|
89
|
+
filename = "${path.module}/secrets.cicd.tfvars"
|
90
|
+
file_permission = "0600"
|
91
|
+
|
92
|
+
content = templatefile("${path.module}/bot_secrets.tftpl", {
|
93
|
+
service_name = local.sa_service_name,
|
94
|
+
key_name = local.sa_key_name,
|
95
|
+
username = local.sa_cf_username,
|
96
|
+
password = local.sa_cf_password
|
97
|
+
})
|
98
|
+
}
|