├── .github
└── workflows
│ ├── blank.yml
│ └── book-deploy.yml
├── .gitignore
├── README.md
├── book.toml
└── src
├── 01-prerequisites.md
├── 02-compute-resources.md
├── 03-domain-configuration.md
├── 04-rekor.md
├── 05-dex.md
├── 06-fulcio.md
├── 07-certifcate-transparency.md
├── 08-configure-registry.md
├── 09-cosign.md
├── 10-sign-container.md
├── README.md
├── SUMMARY.md
├── contributors.md
├── images
├── app-reg.png
├── ca_type.png
├── create.png
├── ecp384.png
├── enable_gcp_ca.png
├── glass.gif
├── label.png
├── oauth-consent.png
├── oauth-credentials.png
├── oauth-creds.png
├── oauth-redirect.png
├── rev.png
├── scopes.png
├── subj_name.png
├── user-email.png
├── view.png
└── view_two.png
└── misc
└── contributors.md
/.github/workflows/blank.yml:
--------------------------------------------------------------------------------
1 | name: Linter
2 | on: pull_request
3 | jobs:
4 | build:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v2
8 | - uses: xt0rted/markdownlint-problem-matcher@v1
9 | - name: "Run Markdown linter"
10 | uses: docker://avtodev/markdown-lint:v1
11 | with:
12 | args: src/*.md
13 |
14 |
--------------------------------------------------------------------------------
/.github/workflows/book-deploy.yml:
--------------------------------------------------------------------------------
1 | name: publish
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | deploy:
11 | runs-on: ubuntu-20.04
12 | concurrency:
13 | group: ${{ github.workflow }}-${{ github.ref }}
14 | steps:
15 | - uses: actions/checkout@v2
16 |
17 | - name: Setup mdBook
18 | uses: peaceiris/actions-mdbook@v1
19 | with:
20 | mdbook-version: '0.4.10'
21 | # mdbook-version: 'latest'
22 |
23 | - run: mdbook build
24 |
25 | - name: Deploy
26 | uses: peaceiris/actions-gh-pages@v3
27 | if: ${{ github.ref == 'refs/heads/main' }}
28 | with:
29 | github_token: ${{ secrets.GITHUB_TOKEN }}
30 | publish_dir: ./book
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | book
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sigstore the hardway
2 |
3 | 
4 |
5 | sigstore the hard way is hosted at and is available for free.
6 |
7 |
8 | ## contributing
9 |
10 | Should you wish to contribute, you're in the right place.
11 |
12 | Building the sigstore the hard way requires [mdBook].
13 |
14 | [mdBook]: https://github.com/rust-lang-nursery/mdBook
15 |
16 | ```bash
17 | cargo install mdbook
18 | ```
19 |
20 | Once installed you can use the `mdbook` command to view in realtime the sigstore-the-hardway documentation.
21 |
22 | ```bash
23 | mdbook serve --open
24 | ```
25 |
26 | You do not need to build before making a pull request, we have a CI action that will automatically
27 | build the site and push it to the live site.
28 |
29 | Before you push, please test:
30 |
31 | To run the tests:
32 |
33 | ```bash
34 | mdbook test
35 | ```
36 |
37 | ### Translations
38 |
39 | We'd love help translating the sigstore-the-hardway! See the [Translations] label to join in
40 | efforts. Open a new issue to start working on a new language! We're waiting on [mdbook support] for multiple languages
41 | before we merge any in, but feel free to start!
42 |
43 | [Translations]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations
44 | [mdbook support]: https://github.com/rust-lang-nursery/mdBook/issues/5
45 |
--------------------------------------------------------------------------------
/book.toml:
--------------------------------------------------------------------------------
1 | [book]
2 | authors = ["Luke Hinds"]
3 | language = "en"
4 | multilingual = false
5 | src = "src"
6 | title = "sigstore the hard way"
7 | description = "How to deploy sigstore from the ground up."
8 | default_theme = "Ayu"
9 |
--------------------------------------------------------------------------------
/src/01-prerequisites.md:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 |
3 | ## Google Cloud Platform
4 |
5 | ### Install the Google Cloud SDK
6 |
7 | Follow the Google Cloud SDK [documentation](https://cloud.google.com/sdk/) to install and configure the `gcloud` command line utility.
8 |
9 | Verify the Google Cloud SDK version is 338.0.0 or higher:
10 |
11 | ```bash
12 | gcloud version
13 | ```
14 |
15 | ### Set a Default Compute Region and Zone
16 |
17 | This tutorial assumes a default compute region and zone have been configured.
18 |
19 | If you are using the `gcloud` command-line tool for the first time `init` is the easiest way to do this:
20 |
21 | ```bash
22 | gcloud init
23 | ```
24 |
25 | `gcloud init` will give you an opportunity to create a new project and set a zone. If you're going to copy and paste CLI commands then for ease of use name it `sigstore-the-hard-way-proj`
26 |
27 | Be sure to authorize gcloud to access the Cloud Platform with your Google user credentials:
28 |
29 | ```bash
30 | gcloud auth login
31 | ```
32 |
33 | Next set a default compute region and compute zone:
34 |
35 | ```bash
36 | gcloud config set compute/region europe-west1
37 | ```
38 |
39 | Set a default compute zone:
40 |
41 | ```bash
42 | gcloud config set compute/zone europe-west1-b
43 | ```
44 |
45 | > 📝 Use the `gcloud compute zones list` command to view additional regions and zones.
46 |
--------------------------------------------------------------------------------
/src/02-compute-resources.md:
--------------------------------------------------------------------------------
1 | # Provisioning Compute / Network Resources
2 |
3 | ## Network Resources
4 |
5 | We next need to create a network for our compute resources:
6 |
7 | ```bash
8 | gcloud compute networks create sigstore-the-hard-way-proj --subnet-mode custom
9 | ```
10 |
11 | > 📝 if you receive an `reason: UREQ_PROJECT_BILLING_NOT_FOUND` error, you need
12 | to [enable billing on the API](https://support.google.com/googleapi/answer/6158867?hl=en)
13 |
14 | We can now create a subnet with an internal range:
15 |
16 | ```bash
17 | gcloud compute networks subnets create sigstore \
18 | --network sigstore-the-hard-way-proj \
19 | --range 10.240.0.0/24
20 | ```
21 |
22 | Create some firewall rules to allow tcp, udp and icmp protocols:
23 |
24 | ```bash
25 | gcloud compute firewall-rules create sigstore-the-hard-way-proj-allow-internal \
26 | --allow tcp,udp,icmp \
27 | --network sigstore-the-hard-way-proj \
28 | --source-ranges 10.240.0.0/24
29 | ```
30 |
31 | ```bash
32 | gcloud compute firewall-rules create sigstore-the-hard-way-allow-external \
33 | --allow tcp:22,tcp:80,tcp:443,icmp \
34 | --network sigstore-the-hard-way-proj \
35 | --source-ranges 0.0.0.0/0
36 | ```
37 |
38 | To verify the rules were created run the following command:
39 |
40 | ```bash
41 | gcloud compute firewall-rules list --filter="network:sigstore-the-hard-way-proj"
42 | ```
43 |
44 | You should see an output similar to the following:
45 |
46 | ```bash
47 | NAME NETWORK DIRECTION PRIORITY ALLOW DENY DISABLED
48 | sigstore-the-hard-way-allow-external sigstore-the-hard-way-proj INGRESS 1000 tcp:22,tcp:80,tcp:443,icmp False
49 | sigstore-the-hard-way-proj-allow-internal sigstore-the-hard-way-proj INGRESS 1000 tcp,udp,icmp False
50 | ```
51 |
52 | ## Compute Resources
53 |
54 | Now we need to create four compute nodes for each service.
55 |
56 | ```bash
57 | gcloud compute instances create sigstore-rekor \
58 | --async \
59 | --boot-disk-size 200GB \
60 | --image-family debian-11 \
61 | --image-project debian-cloud \
62 | --machine-type e2-small \
63 | --private-network-ip 10.240.0.10 \
64 | --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
65 | --subnet sigstore \
66 | --tags sigstore-the-hard-way-proj,sigstore-rekor
67 | ```
68 |
69 | ```bash
70 | gcloud compute instances create sigstore-fulcio \
71 | --async \
72 | --boot-disk-size 200GB \
73 | --image-family debian-11 \
74 | --image-project debian-cloud \
75 | --machine-type e2-small \
76 | --private-network-ip 10.240.0.11 \
77 | --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
78 | --subnet sigstore \
79 | --tags sigstore-the-hard-way-proj,sigstore-fulcio
80 | ```
81 |
82 | ```bash
83 | gcloud compute instances create sigstore-oauth2 \
84 | --async \
85 | --boot-disk-size 200GB \
86 | --image-family debian-11 \
87 | --image-project debian-cloud \
88 | --machine-type e2-small \
89 | --private-network-ip 10.240.0.12 \
90 | --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
91 | --subnet sigstore \
92 | --tags sigstore-the-hard-way-proj,sigstore-oauth2
93 | ```
94 |
95 | ```bash
96 | gcloud compute instances create sigstore-ctl \
97 | --async \
98 | --boot-disk-size 200GB \
99 | --image-family debian-11 \
100 | --image-project debian-cloud \
101 | --machine-type e2-small \
102 | --private-network-ip 10.240.0.13 \
103 | --scopes compute-rw,storage-ro,service-management,service-control,logging-write,monitoring \
104 | --subnet sigstore \
105 | --tags sigstore-the-hard-way-proj,sigstore-ctl
106 | ```
107 |
108 | Verify all compute instances are in a `RUNNING` state.
109 |
110 | ```bash
111 | gcloud compute instances list --filter="tags.items=sigstore-the-hard-way-proj"
112 | ```
113 |
114 | The output should be as follows:
115 |
116 | ```bash
117 | NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
118 | sigstore-ctl europe-west1-c e2-small 10.240.0.13 35.241.198.188 RUNNING
119 | sigstore-fulcio europe-west1-c e2-small 10.240.0.11 35.241.201.91 RUNNING
120 | sigstore-oauth2 europe-west1-c e2-small 10.240.0.12 35.240.60.139 RUNNING
121 | sigstore-rekor europe-west1-c e2-small 10.240.0.10 35.233.82.12 RUNNING
122 | ```
123 |
--------------------------------------------------------------------------------
/src/03-domain-configuration.md:
--------------------------------------------------------------------------------
1 | # Domain configuration
2 |
3 | Now that are instances are running, lets grab the external IP's and set up domains.
4 |
5 | > 📝 A cheap temp domain can be grabbed from [Google Cloud Domains](https://console.cloud.google.com/net-services/domains/). Just type in random, nonsensical string
6 | and you should easily be able to get a domain
7 | for $1. There are also lots of other providers. Use whatever works for you.
8 |
9 | ## Configuration
10 |
11 | Export a variable that will point to the domain you just bought. In this example we'll be using `example.com`:
12 |
13 | ```bash
14 | export DOMAIN="example.com"
15 | ```
16 |
17 | We'll be using this variable throughout the following code-snippets.
18 |
19 | ### rekor.example.com
20 |
21 | Grab your external / public IP:
22 |
23 | ```bash
24 | Rekor_PIP=$(gcloud compute instances describe sigstore-rekor \
25 | --format='get(networkInterfaces[0].accessConfigs[0].natIP)')
26 | ```
27 |
28 | You now want to make an "A Record" to a subdomain or "rekor" and to your external IP from the above command
29 |
30 | To create resource records on Google:
31 |
32 | 1. Go to [Google Domains](https://domains.google.com/)
33 | 2. Click on your domain from the homepage
34 | 3. DNS > Manage Custom Records
35 |
36 | If you're using GCP as the DNS provider this can be done as follows:
37 |
38 | ```bash
39 | export ZONE="example-com"
40 |
41 | gcloud dns record-sets create rekor.$DOMAIN. \
42 | --rrdatas=$Rekor_PIP \
43 | --type=A --ttl=60 --zone=$ZONE
44 | ```
45 |
46 | |Type|Host| Value|
47 | |---|---|---|
48 | | A Record|rekor|x.x.x.x|
49 |
50 | ### fulcio.example.com
51 |
52 | Now repeat the same for fulcio, and dex:
53 |
54 | ```bash
55 | Fulcio_PIP=$(gcloud compute instances describe sigstore-fulcio \
56 | --format='get(networkInterfaces[0].accessConfigs[0].natIP)')
57 | ```
58 |
59 | If you're using GCP as the DNS provider this can be done as follows:
60 |
61 | ```bash
62 | export ZONE="example-com"
63 |
64 | gcloud dns record-sets create fulcio.$DOMAIN. \
65 | --rrdatas=$Fulcio_PIP \
66 | --type=A --ttl=60 --zone=$ZONE
67 | ```
68 |
69 | |Type|Host| Value|
70 | |---|---|---|
71 | | A Record|fulcio|x.x.x.x|
72 |
73 | ### oauth2.example.com
74 |
75 | ```bash
76 | oauth2_PIP=$(gcloud compute instances describe sigstore-oauth2 \
77 | --format='get(networkInterfaces[0].accessConfigs[0].natIP)')
78 | ```
79 |
80 | If you're using GCP as the DNS provider this can be done as follows:
81 |
82 | ```bash
83 | gcloud dns record-sets create oauth2.$DOMAIN. \
84 | --rrdatas=$oauth2_PIP \
85 | --type=A --ttl=60 --zone=$ZONE
86 | ```
87 |
88 | |Type|Host| Value|
89 | |---|---|---|
90 | | A Record|oauth2|x.x.x.x|
91 |
92 | Check the records-sets generated during this process:
93 |
94 | ```bash
95 | gcloud dns record-sets list --zone $ZONE
96 | ```
97 |
98 | > 📝 We do not need a domain for the certificate transparency log. This only
99 | communicate over a private network to Fulcio.
100 |
--------------------------------------------------------------------------------
/src/04-rekor.md:
--------------------------------------------------------------------------------
1 | # rekor
2 |
3 | Rekor is sigstores signature transparency log.
4 |
5 | Rekor requires running instances of trillian's log server and signer, with a database backend. A few different databases
6 | can be used by trillian, for this example we will use mariadb.
7 |
8 | Let's start by logging in:
9 |
10 | ```bash
11 | gcloud compute ssh sigstore-rekor
12 | ```
13 |
14 | ## Dependencies
15 |
16 | We need a few dependencies installed.
17 |
18 | Update your system:
19 |
20 | ```bash
21 | sudo apt-get update -y
22 | ```
23 |
24 | If you want to save up some time, remove man-db first:
25 |
26 | ```bash
27 | sudo apt-get remove -y --purge man-db
28 | ```
29 |
30 | Grab the following packages:
31 |
32 | ```bash
33 | sudo apt-get install mariadb-server git redis-server haproxy certbot -y
34 | ```
35 |
36 | > 📝 redis-server is optional, but useful for a quick indexed search should you decide you need it. If you don't install it,
37 | you need to start rekor with `--enable_retrieve_api=false`
38 |
39 | ### Install latest golang compiler
40 |
41 | Download and run the golang installer (system package are often older than what rekor requires):
42 |
43 | ```bash
44 | curl -O https://storage.googleapis.com/golang/getgo/installer_linux
45 | ```
46 |
47 | ```bash
48 | chmod +x installer_linux
49 | ```
50 |
51 | ```bash
52 | ./installer_linux
53 | ```
54 |
55 | e.g.
56 |
57 | ```bash
58 | Welcome to the Go installer!
59 | Downloading Go version go1.20.4 to /home/luke/.go
60 | This may take a bit of time...
61 | Downloaded!
62 | Setting up GOPATH
63 | GOPATH has been set up!
64 |
65 | One more thing! Run `source /home/$USER/.bash_profile` to persist the
66 | new environment variables to your current session, or open a
67 | new shell prompt.
68 | ```
69 |
70 | As suggested run:
71 |
72 | ```bash
73 | source /home/$USER/.bash_profile
74 |
75 | go version
76 | go version go1.20.4 linux/amd64
77 | ```
78 |
79 | ### Install rekor
80 |
81 | We will work with the rekor repo (we grab the whole repo as we will need a some scripts):
82 |
83 | ```bash
84 | mkdir -p ~/go/src/github.com/sigstore && cd "$_"
85 | ```
86 |
87 | ```bash
88 | git clone https://github.com/sigstore/rekor.git && cd rekor/
89 | ```
90 |
91 | And let's install both the server and the CLI:
92 |
93 | ```bash
94 | go build -o rekor-cli ./cmd/rekor-cli
95 | ```
96 |
97 | ```bash
98 | sudo cp rekor-cli /usr/local/bin/
99 | ```
100 |
101 | ```bash
102 | go build -o rekor-server ./cmd/rekor-server
103 | ```
104 |
105 | ```bash
106 | sudo cp rekor-server /usr/local/bin/
107 | ```
108 |
109 | ### Database
110 |
111 | Trillian requires a database, let's first run `mysql_secure_installation` to
112 | remove test accounts etc:
113 |
114 | ```bash
115 | sudo mysql_secure_installation
116 | ```
117 |
118 | The script is interactive. The following snippet captures the answers to
119 | the script's prompts:
120 | ```bash
121 | NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
122 | SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!
123 |
124 | In order to log into MariaDB to secure it, we'll need the current
125 | password for the root user. If you've just installed MariaDB, and
126 | you haven't set the root password yet, the password will be blank,
127 | so you should just press enter here.
128 |
129 | Enter current password for root (enter for none):
130 | OK, successfully used password, moving on...
131 |
132 | Setting the root password ensures that nobody can log into the MariaDB
133 | root user without the proper authorization.
134 |
135 | Set root password? [Y/n] n
136 | ... skipping.
137 |
138 | By default, a MariaDB installation has an anonymous user, allowing anyone
139 | to log into MariaDB without having to have a user account created for
140 | them. This is intended only for testing, and to make the installation
141 | go a bit smoother. You should remove them before moving into a
142 | production environment.
143 |
144 | Remove anonymous users? [Y/n] Y
145 | ... Success!
146 |
147 | Normally, root should only be allowed to connect from 'localhost'. This
148 | ensures that someone cannot guess at the root password from the network.
149 |
150 | Disallow root login remotely? [Y/n] Y
151 | ... Success!
152 |
153 | By default, MariaDB comes with a database named 'test' that anyone can
154 | access. This is also intended only for testing, and should be removed
155 | before moving into a production environment.
156 |
157 | Remove test database and access to it? [Y/n] Y
158 | - Dropping test database...
159 | ... Success!
160 | - Removing privileges on test database...
161 | ... Success!
162 |
163 | Reloading the privilege tables will ensure that all changes made so far
164 | will take effect immediately.
165 |
166 | Reload privilege tables now? [Y/n] Y
167 | ... Success!
168 |
169 | Cleaning up...
170 |
171 | All done! If you've completed all of the above steps, your MariaDB
172 | installation should now be secure.
173 |
174 | Thanks for using MariaDB!
175 | ```
176 |
177 | We can now build the database.
178 |
179 | Within the rekor repository is a `scripts/createdb.sh` script.
180 |
181 | Edit this script and populate the root password `ROOTPASS` you set for the system
182 | and then run the script (leave blank if not):
183 |
184 | ```bash
185 | cd scripts/
186 | sudo ./createdb.sh
187 | Creating test database and test user account
188 | Loading table data..
189 | ```
190 |
191 | ### Install trillian components
192 |
193 | ```bash
194 | go install github.com/google/trillian/cmd/trillian_log_server@v1.3.14-0.20210713114448-df474653733c
195 | ```
196 |
197 | ```bash
198 | sudo cp ~/go/bin/trillian_log_server /usr/local/bin/
199 | ```
200 |
201 | ```bash
202 | go install github.com/google/trillian/cmd/trillian_log_signer@v1.3.14-0.20210713114448-df474653733c
203 | ```
204 |
205 | ```bash
206 | sudo cp ~/go/bin/trillian_log_signer /usr/local/bin/
207 | ```
208 |
209 | ### Run trillian
210 |
211 | The following are best run in two terminals, which are then left open (this
212 | helps for debugging):
213 |
214 | ```bash
215 | trillian_log_server -http_endpoint=localhost:8090 -rpc_endpoint=localhost:8091 --logtostderr ...
216 | ```
217 |
218 | ```bash
219 | trillian_log_signer --logtostderr --force_master --http_endpoint=localhost:8190 -rpc_endpoint=localhost:8191 --batch_size=1000 --sequencer_guard_window=0 --sequencer_interval=200ms
220 | ```
221 |
222 | Alternatively, create bare minimal systemd services:
223 |
224 | ```bash
225 | sudo bash -c 'cat << EOF > /etc/systemd/system/trillian_log_server.service
226 | [Unit]
227 | Description=trillian_log_server
228 | After=network-online.target
229 | Wants=network-online.target
230 | StartLimitIntervalSec=600
231 | StartLimitBurst=5
232 |
233 | [Service]
234 | ExecStart=/usr/local/bin/trillian_log_server -http_endpoint=localhost:8090 -rpc_endpoint=localhost:8091 --logtostderr ...
235 | Restart=on-failure
236 | RestartSec=5s
237 |
238 | [Install]
239 | WantedBy=multi-user.target
240 | EOF'
241 | ```
242 |
243 | ```bash
244 | sudo bash -c 'cat << EOF > /etc/systemd/system/trillian_log_signer.service
245 | [Unit]
246 | Description=trillian_log_signer
247 | After=network-online.target
248 | Wants=network-online.target
249 | StartLimitIntervalSec=600
250 | StartLimitBurst=5
251 |
252 | [Service]
253 | ExecStart=/usr/local/bin/trillian_log_signer --logtostderr --force_master --http_endpoint=localhost:8190 -rpc_endpoint=localhost:8191 --batch_size=1000 --sequencer_guard_window=0 --sequencer_interval=200ms
254 | Restart=on-failure
255 | RestartSec=5s
256 |
257 | [Install]
258 | WantedBy=multi-user.target
259 | EOF'
260 | ```
261 |
262 | Enable systemd services:
263 |
264 | ```bash
265 | sudo systemctl daemon-reload
266 | sudo systemctl enable trillian_log_server.service
267 | sudo systemctl enable trillian_log_signer.service
268 | sudo systemctl start trillian_log_server.service
269 | sudo systemctl start trillian_log_signer.service
270 | sudo systemctl status trillian_log_server.service
271 | sudo systemctl status trillian_log_signer.service
272 | ```
273 |
274 | After the systemd services have been enabled, the output from the last command should be similar to:
275 | ```bash
276 | ● trillian_log_server.service - trillian_log_server
277 | Loaded: loaded (/etc/systemd/system/trillian_log_server.service; enabled; vendor preset: enabled)
278 | Active: active (running) since Thu 2021-09-30 17:41:49 UTC; 8s ago
279 | ● trillian_log_signer.service - trillian_log_signer
280 | Loaded: loaded (/etc/systemd/system/trillian_log_signer.service; enabled; vendor preset: enabled)
281 | Active: active (running) since Thu 2021-09-30 17:42:05 UTC; 12s ago
282 | ```
283 |
284 | ### Start rekor
285 |
286 | Start rekor:
287 |
288 | ```bash
289 | rekor-server serve --rekor_server.address=0.0.0.0 --trillian_log_server.port=8091
290 | ```
291 |
292 | Note: Rekor runs on port 3000 on all interfaces by default.
293 |
294 | Alternatively, you may create a bare minimal systemd service similar to trillian above:
295 |
296 | ```bash
297 | sudo bash -c 'cat << EOF > /etc/systemd/system/rekor.service
298 | [Unit]
299 | Description=rekor
300 | After=network-online.target
301 | Wants=network-online.target
302 | StartLimitIntervalSec=600
303 | StartLimitBurst=5
304 |
305 | [Service]
306 | ExecStart=/usr/local/bin/rekor-server serve --rekor_server.address=0.0.0.0 --trillian_log_server.port=8091
307 | Restart=on-failure
308 | RestartSec=5s
309 |
310 | [Install]
311 | WantedBy=multi-user.target
312 | EOF'
313 | ```
314 |
315 | Enable systemd services:
316 |
317 | ```bash
318 | sudo systemctl daemon-reload
319 | sudo systemctl enable rekor.service
320 | sudo systemctl start rekor.service
321 | sudo systemctl status rekor.service
322 | ```
323 |
324 | The last command should print:
325 | ```bash
326 | rekor.service - rekor
327 | Loaded: loaded (/etc/systemd/system/rekor.service; enabled; vendor preset: enabled)
328 | Active: active (running) since Mon 2023-05-08 11:09:20 UTC; 2h 0min ago
329 | Main PID: 21612 (rekor-server)
330 | Tasks: 8 (limit: 2353)
331 | Memory: 21.8M
332 | CPU: 649ms
333 | CGroup: /system.slice/rekor.service
334 | └─21612 /usr/local/bin/rekor-server serve --rekor_server.address=0.0.0.0 --trillian_log_server.port=8091
335 | ```
336 |
337 | ### Let's encrypt (TLS) & HA Proxy config
338 |
339 | Let's create a HAProxy config, set `DOMAIN` to your registered domain and your
340 | private IP address:
341 |
342 | ```bash
343 | DOMAIN="rekor.example.com"
344 | IP="10.240.0.10"
345 | ```
346 |
347 | Let's now run certbot to obtain our TLS certs:
348 |
349 | ```bash
350 | sudo certbot certonly --standalone --preferred-challenges http \
351 | --http-01-address ${IP} --http-01-port 80 -d ${DOMAIN} \
352 | --non-interactive --agree-tos --email youremail@domain.com
353 | ```
354 |
355 | Move the PEM chain into place:
356 |
357 | ```bash
358 | sudo cat "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" \
359 | "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" \
360 | | sudo tee "/etc/ssl/private/${DOMAIN}.pem" > /dev/null
361 | ```
362 |
363 | Now we need to change certbot configuration for automatic renewal.
364 |
365 | Prepare post renewal script:
366 |
367 | ```bash
368 | cat /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
369 | #!/bin/bash
370 |
371 | DOMAIN="rekor.example.com"
372 |
373 | cat "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" \
374 | "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" \
375 | > "/etc/ssl/private/${DOMAIN}.pem"
376 |
377 | systemctl reload haproxy.service
378 | ```
379 |
380 | Make sure the script has executable flag set:
381 |
382 | ```bash
383 | sudo chmod +x /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
384 | ```
385 |
386 | Replace port and address in the certbot's renewal configuration file for the domain (pass ACME request through the haproxy to certbot):
387 |
388 | ```bash
389 | sudo vim /etc/letsencrypt/renewal/rekor.example.com.conf
390 | ```
391 |
392 | ```bash
393 | http01_port = 9080
394 | http01_address = 127.0.0.1
395 | ```
396 |
397 | Append new line:
398 |
399 | ```bash
400 | post_hook = /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
401 | ```
402 |
403 | Prepare haproxy configuration:
404 |
405 | ```bash
406 | cat > haproxy.cfg < /dev/null
97 | ```
98 |
99 | Now we need to change certbot configuration for automatic renewal.
100 |
101 | Prepare post renewal script:
102 |
103 | ```bash
104 | cat /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
105 | #!/bin/bash
106 |
107 | DOMAIN="oauth2.example.com"
108 |
109 | cat "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" \
110 | "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" \
111 | > "/etc/ssl/private/${DOMAIN}.pem"
112 |
113 | systemctl reload haproxy.service
114 | ```
115 |
116 | Make sure the script has executable flag set:
117 |
118 | ```bash
119 | sudo chmod +x /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
120 | ```
121 |
122 | Replace port and address in the certbot's renewal configuration file for the domain (pass ACME request through the haproxy to certbot):
123 |
124 | ```bash
125 | sudo vim /etc/letsencrypt/renewal/oauth2.example.com.conf
126 | ```
127 |
128 | ```bash
129 | http01_port = 9080
130 | http01_address = 127.0.0.1
131 | ```
132 |
133 | Append new line
134 |
135 | ```bash
136 | post_hook = /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
137 | ```
138 |
139 | Prepare haproxy configuration:
140 |
141 | ```bash
142 | cat > haproxy.cfg < 📝 We re using Google here, you can do the same for github and microsoft too.
240 | The placeholders are already within `config.yaml`
241 |
242 | 1. Head to the [credentials page](https://console.cloud.google.com/apis/credentials)
243 |
244 | 2. Select 'CONFIGURE CONSENT SCREEN'
245 |
246 | Select 'Internal'
247 |
248 | 
249 |
250 | NOTE: If you're not a Google Workspace user, the 'Internal' option will not be available. You can only make your app available to external (general audience) users only. In such a case, the 'External' User Type works fine as well.
251 |
252 | Fill out the app registration details
253 |
254 | 
255 |
256 | 3. Set scopes
257 |
258 | Select 'ADD OR REMOVE SCOPES' and set the `userinfo.email` scope
259 |
260 | 
261 |
262 | Select "SAVE AND CONTINUE"
263 |
264 | Select "BACK TO DASHBOARD" and select 'Credentials'
265 |
266 | 4. Create OAuth Client ID
267 |
268 | 
269 |
270 | Select "OAuth client ID". Select "Web Application" and fill out the "Authorized Redirect URIs"
271 |
272 | Select "CREATE"
273 |
274 | 
275 |
276 | 5. Note down tour Client ID and Secret and keep them safe (we will need them for dex)
277 |
278 | ### Configure Dex
279 |
280 | Set up the configuration file for dex.
281 |
282 | Provide saved OIDC details as variables:
283 |
284 | ```bash
285 | GOOGLE_CLIENT_ID="..."
286 | GOOGLE_CLIENT_SECRET="..."
287 | ```
288 |
289 | ```bash
290 | cat > dex-config.yaml < /etc/systemd/system/dex.service
369 | [Unit]
370 | Description=dex
371 | After=network-online.target
372 | Wants=network-online.target
373 | StartLimitIntervalSec=600
374 | StartLimitBurst=5
375 |
376 | [Service]
377 | ExecStart=/usr/local/bin/dex serve --web-http-addr=0.0.0.0:6000 /etc/dex/dex-config.yaml
378 | Restart=on-failure
379 | RestartSec=5s
380 |
381 | [Install]
382 | WantedBy=multi-user.target
383 | EOF'
384 | ```
385 |
386 | ```bash
387 | sudo systemctl daemon-reload
388 | sudo systemctl enable dex.service
389 | sudo systemctl start dex.service
390 | sudo systemctl status dex.service
391 | ```
392 |
--------------------------------------------------------------------------------
/src/06-fulcio.md:
--------------------------------------------------------------------------------
1 | # Fulcio
2 |
3 | Now it's time to install the Fulcio WebPKI.
4 |
5 | Fulcio requires a means to manage certificates. We have two options here,
6 | we can use a SoftHSM or Google Certificate Authority service.
7 |
8 | > 📝 As of time of writing, plans are in place to support AWS Cloud HSM and
9 | Azure Dedicated HSM.
10 |
11 | SSH into the Fulcio Compute instance
12 |
13 | ```bash
14 | gcloud compute ssh sigstore-fulcio
15 | ```
16 |
17 | ## Dependencies
18 |
19 | We need a few dependencies installed
20 |
21 | Update your system
22 |
23 | ```bash
24 | sudo apt-get update -y
25 | ```
26 |
27 | If you want to save up some time, remove man-db first
28 |
29 | ```bash
30 | sudo apt-get remove -y --purge man-db
31 | ```
32 |
33 | Grab the following packages
34 |
35 | ```bash
36 | sudo apt-get install git gcc haproxy softhsm certbot opensc -y
37 | ```
38 |
39 | > 📝 If you plan to use GCP Certificate Service, you can drop SoftHSM and opensc
40 |
41 | ### Install latest golang compiler
42 |
43 | Download and run the golang installer (system package are often older than what Fulcio requires):
44 |
45 | ```bash
46 | curl -O https://storage.googleapis.com/golang/getgo/installer_linux
47 | ```
48 |
49 | ```bash
50 | chmod +x installer_linux
51 | ```
52 |
53 | ```bash
54 | ./installer_linux
55 | ```
56 |
57 | e.g.
58 |
59 | ```
60 | Welcome to the Go installer!
61 | Downloading Go version go1.20.4 to /home/luke/.go
62 | This may take a bit of time...
63 | Downloaded!
64 | Setting up GOPATH
65 | GOPATH has been set up!
66 |
67 | One more thing! Run `source /home/$USER/.bash_profile` to persist the
68 | new environment variables to your current session, or open a
69 | new shell prompt.
70 | ```
71 |
72 | As suggested run
73 |
74 | ```bash
75 | source /home/$USER/.bash_profile
76 | go version
77 | go version go1.20.4 linux/amd64
78 | ```
79 |
80 | ### Install Fulcio
81 |
82 | ```bash
83 | go install github.com/sigstore/fulcio@v1.3.1
84 | ```
85 |
86 | ```bash
87 | sudo cp ~/go/bin/fulcio /usr/local/bin/
88 | ```
89 |
90 | ### Let's encrypt (TLS) & HA Proxy config
91 |
92 | Let's create a HAProxy config, set `DOMAIN` to your registered domain and your
93 | private `IP` address
94 |
95 | ```bash
96 | DOMAIN="fulcio.example.com"
97 | IP="10.240.0.11"
98 | ```
99 |
100 | Let's now run certbot to obtain our TLS certs.
101 |
102 | ```bash
103 | sudo certbot certonly --standalone --preferred-challenges http \
104 | --http-01-address ${IP} --http-01-port 80 -d ${DOMAIN} \
105 | --non-interactive --agree-tos --email youremail@domain.com
106 | ```
107 |
108 | Move the PEM chain into place
109 |
110 | ```bash
111 | sudo cat "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" \
112 | "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" \
113 | | sudo tee "/etc/ssl/private/${DOMAIN}.pem" > /dev/null
114 | ```
115 |
116 | Now we need to change certbot configuration for automatic renewal
117 |
118 | Prepare post renewal script
119 |
120 | ```bash
121 | cat /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
122 | #!/bin/bash
123 |
124 | DOMAIN="fulcio.example.com"
125 |
126 | cat "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" \
127 | "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" \
128 | > "/etc/ssl/private/${DOMAIN}.pem"
129 |
130 | systemctl reload haproxy.service
131 | ```
132 |
133 | Make sure the script has executable flag set
134 |
135 | ```bash
136 | sudo chmod +x /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
137 | ```
138 |
139 | Replace port and address in the certbot's renewal configuration file for the domain (pass ACME request through the haproxy to certbot)
140 |
141 | ```bash
142 | sudo vim /etc/letsencrypt/renewal/fulcio.example.com.conf
143 | ```
144 |
145 | ```
146 | http01_port = 9080
147 | http01_address = 127.0.0.1
148 | ```
149 |
150 | Append new line
151 |
152 | ```
153 | post_hook = /etc/letsencrypt/renewal-hooks/post/haproxy-ssl-renew.sh
154 | ```
155 |
156 | Prepare haproxy configuration
157 |
158 | ```bash
159 | cat > haproxy.cfg < **Note**
256 | > You will need the file_ca_pub.pem file for the TUF root of cosign, with the sign-container section towards the end
257 |
258 | # SoftHSM Installation
259 |
260 | > By default SoftHSM stores tokens in `/var/lib/softhsm/tokens/` directory, which is defined
261 | in `/etc/softhsm/softhsm2.conf` configuration file, below we will define a custom configuration for fulcio.
262 |
263 | ```bash
264 | mkdir -p $HOME/fulcio-config/config
265 | mkdir $HOME/fulcio-config/tokens
266 | ```
267 |
268 | ```bash
269 | cat < /dev/null
270 | directories.tokendir = $HOME/fulcio-config/tokens
271 | objectstore.backend = file
272 | log.level = INFO
273 | slots.removable = false
274 | EOF
275 | ```
276 |
277 | ```bash
278 | export SOFTHSM2_CONF="$HOME/fulcio-config/config/softhsm2.cfg"
279 | ```
280 |
281 | ```bash
282 | echo 'export SOFTHSM2_CONF="$HOME/fulcio-config/config/softhsm2.cfg"' >> ~/.bash_profile
283 | ```
284 |
285 | ```bash
286 | softhsm2-util --init-token --slot 0 --label fulcio --pin 2324 --so-pin 2324
287 | ```
288 |
289 | Tokens will now be generated in `fulcio-config\tokens`
290 |
291 | ```bash
292 | ls -la $HOME/fulcio-config/tokens
293 | ```
294 |
295 | For example:
296 |
297 | ```bash
298 | softhsm2-util --init-token --slot 0 --label fulcio
299 | === SO PIN (4-255 characters) ===
300 | Please enter SO PIN: ****
301 | Please reenter SO PIN: ****
302 | === User PIN (4-255 characters) ===
303 | Please enter user PIN: ****
304 | Please reenter user PIN: ******
305 | ERROR: The entered PINs are not equal.
306 | === User PIN (4-255 characters) ===
307 | Please enter user PIN: ****
308 | Please reenter user PIN: ****
309 | The token has been initialized and is reassigned to slot 1773686385
310 | ```
311 |
312 | Lets create a SoftHSM config for Fulcio
313 |
314 | ```bash
315 | cat < /dev/null
316 | {
317 | "Path" : "/usr/lib/softhsm/libsofthsm2.so",
318 | "TokenLabel": "fulcio",
319 | "Pin" : "2324"
320 | }
321 | EOF
322 | ```
323 |
324 | > **Note**
325 | > The Path may vary for different OS versions.
326 |
327 | Now let's create a private key within the HSM
328 |
329 | ```bash
330 | pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --login --login-type user --keypairgen --id 1 --label PKCS11CA --key-type EC:secp384r1
331 | ```
332 |
333 | For example:
334 |
335 | ```bash
336 | pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so --login --login-type user --keypairgen --id 1 --label PKCS11CA --key-type EC:secp384r1
337 | Using slot 0 with a present token (0x69b84e71)
338 | Logging in to "fulcio".
339 | Please enter User PIN:
340 | Key pair generated:
341 | Private Key Object; EC
342 | label: PKCS11CA
343 | ID: 01
344 | Usage: decrypt, sign, unwrap, derive
345 | Access: sensitive, always sensitive, never extractable, local
346 | Public Key Object; EC EC_POINT 384 bits
347 | EC_POINT: 046104b04911577ad1a655ba469b32ae63832d6c0d19482058af1822c2b42f54934da3613cd87171594a9b00ff1f0b298c75fa9383470ec46f0b4a35e73b54c34cf2ecc664ada2d0a818a5ac2390d952cb3b8d66ebea974a1bb2465f323cbebc50927d
348 | EC_PARAMS: 06052b81040022
349 | label: PKCS11CA
350 | ID: 01
351 | Usage: encrypt, verify, wrap, derive
352 | Access: local
353 | ```
354 |
355 | Now its time to create a Root CA using our newly minted private key:
356 |
357 | ```bash
358 | cd $HOME/fulcio-config/
359 | fulcio createca --org={ORG} --country={UK} --locality={TOWN} --province={PROVINCE} --postal-code={POST_CODE} --street-address={STREET} --hsm-caroot-id 1 --out fulcio-root.pem
360 | ```
361 |
362 | An example:
363 |
364 | ```bash
365 | cd $HOME/fulcio-config/
366 | fulcio createca --org=acme --country=USA --locality=Anytown --province=AnyPlace --postal-code=ABCDEF --street-address=123 Main St --hsm-caroot-id 1 --out fulcio-root.pem
367 | 2021-10-01T18:09:16.284Z INFO app/createca.go:48 binding to PKCS11 HSM
368 | 2021-10-01T18:09:16.289Z INFO app/createca.go:68 finding slot for private key: PKCS11CA
369 | 2021-10-01T18:09:16.304Z INFO app/createca.go:108 Root CA:
370 | -----BEGIN CERTIFICATE-----
371 | MIICJDCCAaqgAwIBAgIIVUu5cbwBx8EwCgYIKoZIzj0EAwMwVjELMAkGA1UEBhMC
372 | TFYxCzAJBgNVBAgTAkxWMQswCQYDVQQHEwJMVjENMAsGA1UECRMESG9tZTEPMA0G
373 | A1UEERMGTFYxMDI2MQ0wCwYDVQQKEwRhY21lMB4XDTIxMTAwMTE4MDkxNloXDTMx
374 | MTAwMTE4MDkxNlowVjELMAkGA1UEBhMCTFYxCzAJBgNVBAgTAkxWMQswCQYDVQQH
375 | EwJMVjENMAsGA1UECRMESG9tZTEPMA0GA1UEERMGTFYxMDI2MQ0wCwYDVQQKEwRh
376 | Y21lMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEk4wYXHkLhdDlUlASZc65GI+5VDv3
377 | OqmFdOI7/TwnPfrqFBNCxTPp0qNh7//s55tRac5pkXV4Af+xWUETlRd6RqBKcjjX
378 | PHMZ0f+J/pZui4pPmw3ItvVCqfmNvCtASksSo0UwQzAOBgNVHQ8BAf8EBAMCAQYw
379 | EgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUOXQnhKM/yhGTICrrgO78QyVN
380 | nUMwCgYIKoZIzj0EAwMDaAAwZQIwEd1VjWI+P3eXMwUOGXbWJMYzrpcLakwj0JPW
381 | Bx6oFXBadm4jZoKQX1FfNXMWgu0mAjEA4nz6OBtF8YJGRS9bTnWfe4V/lwukRczk
382 | OPl9CeCgaJqQRXlMSw8uf3nO0rYXTGCF
383 | -----END CERTIFICATE-----
384 |
385 | 2021-10-01T18:09:16.324Z INFO app/createca.go:122 root CA created with PKCS11 ID: 1
386 | 2021-10-01T18:09:16.324Z INFO app/createca.go:138 root CA saved to file: fulcio-root.pem
387 | ```
388 |
389 | Check Root CA key usage
390 |
391 | ```bash
392 | openssl x509 -in fulcio-root.pem -noout -ext extendedKeyUsage,keyUsage
393 | X509v3 Key Usage: critical
394 | Certificate Sign, CRL Sign
395 | ```
396 |
397 | Transfer the root certificate over to the certificate transparency log (or copy / paste into a text file for later).
398 |
399 | ```bash
400 | gcloud compute scp fulcio-root.pem @sigstore-ctl:~/
401 | ```
402 |
403 | # Google Certificate Authority Service
404 |
405 | Navigate to the [Certificate Authority Service API](https://console.cloud.google.com/marketplace/product/google/privateca.googleapis.com) and enable the service
406 |
407 | 
408 |
409 | On the Google Cloud Console page, go to Security > Certificate Authority Service > Create CA
410 |
411 | 1. Set the CA type (DevOps)
412 |
413 | 
414 |
415 | 2. Set the cert subject details
416 |
417 | 
418 |
419 | 3. Set the key and algorithm to Ecliptic Curve P384
420 |
421 | 
422 |
423 | 4. Leave Configure Artifacts as it is
424 |
425 | 
426 |
427 | 5. Label (don't need one)
428 |
429 | 
430 |
431 | 6. Create the CA
432 |
433 | 
434 |
435 | 7. Note down the Root CA and Resource name
436 |
437 | 
438 |
439 | 
440 |
441 | # Fulcio Config
442 |
443 | Set the DNS for the OAuth2 / Dex Server
444 |
445 | ```bash
446 | OAUTH2_DOMAIN="oauth2.example.com"
447 | ```
448 |
449 | ```bash
450 | cat > $HOME/fulcio-config/config.json < 📝 Don't worry that the Certificate Transparency Log is not up yet. We will
529 | set this up next.
530 |
531 | ## Google Certificate Authority Service
532 |
533 | ```bash
534 | fulcio serve --ca googleca --gcp_private_ca_parent=${resource_name} --ct-log-url=http://sigstore-ctl:6105/sigstore --host=0.0.0.0 --port=5000
535 | ```
536 |
537 | > 📝 Your resource name is a long POSIX type path string, e.g. `projects/sigstore-the-hard-way-proj/locations/europe-west1/caPools/sigstore-the-hard-way/certificateAuthorities/xxxx`
538 |
539 | For example
540 |
541 | ```
542 | fulcio serve --ca googleca --gcp_private_ca_parent=projects/sigstore-the-hard-way-proj/locations/europe-west1/caPools/sigstore-the-hard-way/certificateAuthorities/xxxx --ctl-log-url=http://sigstore-ctl:6105/sigstore
543 | ```
544 |
--------------------------------------------------------------------------------
/src/07-certifcate-transparency.md:
--------------------------------------------------------------------------------
1 | # Certificate transparency log
2 |
3 | We will now install the Certificate transparency log (CTL).
4 |
5 | CTL requires running instances of trillian's log server and signer
6 |
7 | Let's start by logging in:
8 |
9 | ```bash
10 | gcloud compute ssh sigstore-ctl
11 | ```
12 |
13 | ## Dependencies
14 |
15 | ```bash
16 | sudo apt-get update -y
17 | ```
18 |
19 | If you want to save up some time, remove man-db first
20 |
21 | ```bash
22 | sudo apt-get remove -y --purge man-db
23 | ```
24 |
25 | ```bash
26 | sudo apt-get install mariadb-server git wget -y
27 | ```
28 |
29 | ### Install latest golang compiler
30 |
31 | Download and run the golang installer (system package are often older than what Trillian requires):
32 |
33 | ```bash
34 | curl -O https://storage.googleapis.com/golang/getgo/installer_linux
35 | ```
36 |
37 | ```bash
38 | chmod +x installer_linux
39 | ```
40 |
41 | ```bash
42 | ./installer_linux
43 | ```
44 |
45 | e.g.
46 |
47 | ```bash
48 | Welcome to the Go installer!
49 | Downloading Go version go1.20.4 to /home/luke/.go
50 | This may take a bit of time...
51 | Downloaded!
52 | Setting up GOPATH
53 | GOPATH has been set up!
54 |
55 | One more thing! Run `source /home/$USER/.bash_profile` to persist the
56 | new environment variables to your current session, or open a
57 | new shell prompt.
58 | ```
59 |
60 | As suggested run
61 |
62 | ```bash
63 | source /home/$USER/.bash_profile
64 | go version
65 | go version go1.20.4 linux/amd64
66 | ```
67 |
68 | ### Database
69 |
70 | Trillian requires a database, let's first run `mysql_secure_installation`
71 |
72 | ```bash
73 | sudo mysql_secure_installation
74 | ```
75 |
76 | The script is interactive. The following snippet captures the answers to
77 | the script's prompts:
78 | ```bash
79 | NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
80 | SERVERS IN PRODUCTION USE! PLEASE READ EACH STEP CAREFULLY!
81 |
82 | In order to log into MariaDB to secure it, we'll need the current
83 | password for the root user. If you've just installed MariaDB, and
84 | you haven't set the root password yet, the password will be blank,
85 | so you should just press enter here.
86 |
87 | Enter current password for root (enter for none):
88 | OK, successfully used password, moving on...
89 |
90 | Setting the root password ensures that nobody can log into the MariaDB
91 | root user without the proper authorisation.
92 |
93 | Set root password? [Y/n] n
94 | ... skipping.
95 |
96 | By default, a MariaDB installation has an anonymous user, allowing anyone
97 | to log into MariaDB without having to have a user account created for
98 | them. This is intended only for testing, and to make the installation
99 | go a bit smoother. You should remove them before moving into a
100 | production environment.
101 |
102 | Remove anonymous users? [Y/n] Y
103 | ... Success!
104 |
105 | Normally, root should only be allowed to connect from 'localhost'. This
106 | ensures that someone cannot guess at the root password from the network.
107 |
108 | Disallow root login remotely? [Y/n] Y
109 | ... Success!
110 |
111 | By default, MariaDB comes with a database named 'test' that anyone can
112 | access. This is also intended only for testing, and should be removed
113 | before moving into a production environment.
114 |
115 | Remove test database and access to it? [Y/n] Y
116 | - Dropping test database...
117 | ... Success!
118 | - Removing privileges on test database...
119 | ... Success!
120 |
121 | Reloading the privilege tables will ensure that all changes made so far
122 | will take effect immediately.
123 |
124 | Reload privilege tables now? [Y/n] Y
125 | ... Success!
126 |
127 | Cleaning up...
128 |
129 | All done! If you've completed all of the above steps, your MariaDB
130 | installation should now be secure.
131 |
132 | Thanks for using MariaDB!
133 | ```
134 |
135 | We can now import the database as we used for rekor
136 |
137 | ```bash
138 | wget https://raw.githubusercontent.com/sigstore/rekor/main/scripts/createdb.sh
139 | ```
140 |
141 | ```bash
142 | wget https://raw.githubusercontent.com/sigstore/rekor/main/scripts/storage.sql
143 | ```
144 |
145 | ```bash
146 | chmod +x createdb.sh
147 | ```
148 |
149 | ```bash
150 | sudo ./createdb.sh
151 | ```
152 |
153 | E.g.
154 |
155 | ```
156 | sudo ./createdb.sh
157 | Creating test database and test user account
158 | Loading table data..
159 | ```
160 |
161 | ### Install trillian components
162 |
163 | ```bash
164 | go install github.com/google/trillian/cmd/trillian_log_server@v1.5.1
165 | ```
166 |
167 | ```bash
168 | sudo cp ~/go/bin/trillian_log_server /usr/local/bin/
169 | ```
170 |
171 | ```bash
172 | go install github.com/google/trillian/cmd/trillian_log_signer@v1.5.1
173 | ```
174 |
175 | ```bash
176 | sudo cp ~/go/bin/trillian_log_signer /usr/local/bin/
177 | ```
178 |
179 | ```bash
180 | go install github.com/google/trillian/cmd/createtree@v1.5.1
181 | ```
182 |
183 | ```bash
184 | sudo cp ~/go/bin/createtree /usr/local/bin/
185 | ```
186 |
187 | ### Run trillian
188 |
189 | The following is best run in two terminals which are then left open (this helps for debugging)
190 |
191 | ```bash
192 | trillian_log_server -http_endpoint=localhost:8090 -rpc_endpoint=localhost:8091 --logtostderr ...
193 | ```
194 |
195 | ```bash
196 | trillian_log_signer --logtostderr --force_master --http_endpoint=localhost:8190 -rpc_endpoint=localhost:8191 --batch_size=1000 --sequencer_guard_window=0 --sequencer_interval=200ms
197 | ```
198 |
199 | Alternatively, create bare minimal systemd services
200 |
201 | ```bash
202 | sudo bash -c 'cat << EOF > /etc/systemd/system/trillian_log_server.service
203 | [Unit]
204 | Description=trillian_log_server
205 | After=network-online.target
206 | Wants=network-online.target
207 | StartLimitIntervalSec=600
208 | StartLimitBurst=5
209 |
210 | [Service]
211 | ExecStart=/usr/local/bin/trillian_log_server -http_endpoint=localhost:8090 -rpc_endpoint=localhost:8091 --logtostderr ...
212 | Restart=on-failure
213 | RestartSec=5s
214 |
215 | [Install]
216 | WantedBy=multi-user.target
217 | EOF'
218 | ```
219 |
220 | ```bash
221 | sudo bash -c 'cat << EOF > /etc/systemd/system/trillian_log_signer.service
222 | [Unit]
223 | Description=trillian_log_signer
224 | After=network-online.target
225 | Wants=network-online.target
226 | StartLimitIntervalSec=600
227 | StartLimitBurst=5
228 |
229 | [Service]
230 | ExecStart=/usr/local/bin/trillian_log_signer --logtostderr --force_master --http_endpoint=localhost:8190 -rpc_endpoint=localhost:8191 --batch_size=1000 --sequencer_guard_window=0 --sequencer_interval=200ms
231 | Restart=on-failure
232 | RestartSec=5s
233 |
234 | [Install]
235 | WantedBy=multi-user.target
236 | EOF'
237 | ```
238 |
239 | Enable systemd services
240 |
241 | ```bash
242 | sudo systemctl daemon-reload
243 | sudo systemctl enable trillian_log_server.service
244 | sudo systemctl start trillian_log_server.service
245 | sudo systemctl status trillian_log_server.service
246 | sudo systemctl enable trillian_log_signer.service
247 | sudo systemctl start trillian_log_signer.service
248 | sudo systemctl status trillian_log_signer.service
249 | ```
250 |
251 | ```bash
252 | Created symlink /etc/systemd/system/multi-user.target.wants/trillian_log_server.service → /etc/systemd/system/trillian_log_server.service.
253 | ● trillian_log_server.service - trillian_log_server
254 | Loaded: loaded (/etc/systemd/system/trillian_log_server.service; enabled; vendor preset: enabled)
255 | Active: active (running) since Thu 2021-09-30 17:41:49 UTC; 8s ago
256 | Created symlink /etc/systemd/system/multi-user.target.wants/trillian_log_signer.service → /etc/systemd/system/trillian_log_signer.service.
257 | ● trillian_log_signer.service - trillian_log_signer
258 | Loaded: loaded (/etc/systemd/system/trillian_log_signer.service; enabled; vendor preset: enabled)
259 | Active: active (running) since Thu 2021-09-30 17:42:05 UTC; 12s ago
260 | ```
261 |
262 | ### Install CTFE server
263 |
264 | ```bash
265 | go install github.com/google/certificate-transparency-go/trillian/ctfe/ct_server@latest
266 | ```
267 |
268 | ```bash
269 | sudo cp ~/go/bin/ct_server /usr/local/bin/
270 | ```
271 |
272 | ### Create a private key
273 |
274 | > **Warning**
275 | > The following section dumps out keys into the home directory. This is only recommended if you do not greatly care about the security of this machine
276 | > If you do care, place them into a more secure location and chmod to a secure level of file permissions.
277 |
278 | Create a key pair with the following command:
279 |
280 | ```bash
281 | openssl ecparam -genkey -name prime256v1 -noout -out unenc.key
282 | openssl ec -in unenc.key -out privkey.pem -des3
283 | ```
284 |
285 | Extract the public key from the key-pair:
286 |
287 | ```bash
288 | openssl ec -in privkey.pem -pubout -out ctfe_public.pem
289 | ```
290 |
291 | Feel free to remove the unencrypted key:
292 |
293 | ```bash
294 | rm unenc.key
295 | ```
296 |
297 | > **Note**
298 | > The private key needs a passphrase, remember it as you will need it for `your_passphrase` when we create the `ct.cfg` further down.
299 |
300 | > **Note**
301 | > You will need the ctfe_public.pem file for the TUF root of cosign, with the sign-container section towards the end
302 |
303 | ### Create a Tree ID
304 |
305 | > **Note**
306 | > `trillian_log_server` needs to be running for this command to execute
307 |
308 | ```bash
309 | LOG_ID="$(createtree --admin_server localhost:8091)"
310 | ```
311 |
312 | ### Set up the config file
313 |
314 | ```bash
315 | cat > ct.cfg <` to the one you used
331 | when generating the private key.
332 |
333 | > **Note**
334 | > `fulcio-root.pem` is the root ID certificate, we created in [06-fulcio](06-fulcio.md).
335 |
336 | ```bash
337 | sudo mkdir -p /etc/ctfe-config/
338 | sudo cp ct.cfg /etc/ctfe-config/
339 | sudo cp fulcio-root.pem /etc/ctfe-config/
340 | sudo cp privkey.pem /etc/ctfe-config/
341 | ```
342 |
343 | ### Start the CT log
344 |
345 | ```bash
346 | ct_server -logtostderr -log_config /etc/ctfe-config/ct.cfg -log_rpc_server localhost:8091 -http_endpoint 0.0.0.0:6105
347 | ```
348 |
349 | > 📝 The `-http_endpoint` flag uses the internal private IP. We don't need this facing externally
350 | (for this tutorial at least)
351 |
352 | You may create a bare minimal systemd service
353 |
354 | ```bash
355 | sudo bash -c 'cat << EOF > /etc/systemd/system/ct_server.service
356 | [Unit]
357 | Description=ct_server
358 | After=network-online.target
359 | Wants=network-online.target
360 | StartLimitIntervalSec=600
361 | StartLimitBurst=5
362 |
363 | [Service]
364 | ExecStart=/usr/local/bin/ct_server -logtostderr -log_config /etc/ctfe-config/ct.cfg -log_rpc_server localhost:8091 -http_endpoint 0.0.0.0:6105
365 | Restart=on-failure
366 | RestartSec=5s
367 |
368 | [Install]
369 | WantedBy=multi-user.target
370 | EOF'
371 | ```
372 |
373 | ```bash
374 | sudo systemctl daemon-reload
375 | sudo systemctl enable ct_server.service
376 | sudo systemctl start ct_server.service
377 | sudo systemctl status ct_server.service
378 | ```
379 |
--------------------------------------------------------------------------------
/src/08-configure-registry.md:
--------------------------------------------------------------------------------
1 | # Configure Container Registry
2 |
3 | To switch things up, we will use Github Container registry (ghcr.io)
4 | to push an image and a signature with cosign. You can however
5 | using an OCI registry, [see here](https://github.com/sigstore/cosign#registry-support)
6 | for a list of those currently supported by cosign.
7 |
8 | First, let's create an image. You can use the following `Dockerfile` or any existing image
9 | you already have locally:
10 |
11 | ```bash
12 | cat > Dockerfile < --password-stdin
31 | ```
32 |
33 | ## Tag and push an image
34 |
35 | Now we can tag and push our image:
36 |
37 | ```bash
38 | docker tag SOURCE_IMAGE_NAME:VERSION ghcr.io/TARGET_OWNER/TARGET_IMAGE_NAME:VERSION
39 | ```
40 |
41 | Push re-tagged imaged to the container registry:
42 |
43 | ```bash
44 | docker push ghcr.io/OWNER/IMAGE_NAME:VERSION
45 | ```
46 |
47 | Example:
48 |
49 | ```bash
50 | docker tag sigstore-thw:latest ghcr.io/lukehinds/sigstore-thw:latest
51 | docker push ghcr.io/lukehinds/sigstore-thw:latest
52 | The push refers to repository [ghcr.io/lukehinds/sigstore-thw]
53 | cb381a32b229: Pushed
54 | latest: digest: sha256:568999d4aedd444465c442617666359ddcd4dc117b22375983d2576c3847c9ba size: 528
55 | ```
56 |
--------------------------------------------------------------------------------
/src/09-cosign.md:
--------------------------------------------------------------------------------
1 | # Cosign
2 |
3 | We will now install cosign. It is assumed from now, that cosign will
4 | be run on a machine local to you (such as your laptop or PC), and outside of the sigstore infrastructure.
5 |
6 | ## Install cosign
7 |
8 | Head the [releases page for cosign v1.0](https://github.com/sigstore/cosign/releases/tag/v1.11.1)
9 | and download a release specific to your hardware (MacOS, Linux, Windows)
10 |
11 | Also download the cosign public key, signature for your architecture.
12 |
13 | * `release-cosign.pub`
14 | * `cosign-$OS-$ARCH.sig`
15 |
16 | Verify the signing.
17 |
18 | ### Linux binary
19 |
20 | Download required files:
21 |
22 | ```bash
23 | curl -fsSL --remote-name-all https://github.com/sigstore/cosign/releases/download/v1.11.1/{cosign-linux-amd64,release-cosign.pub,cosign-linux-amd64.sig}
24 | ```
25 |
26 | Verify signature:
27 |
28 | ```bash
29 | openssl dgst -sha256 -verify release-cosign.pub -signature <(cat cosign-linux-amd64.sig | base64 -d) cosign-linux-amd64
30 | Verified OK
31 | ```
32 |
33 | Remove signature files:
34 |
35 | ```bash
36 | rm cosign-linux-amd64.sig release-cosign.pub
37 | ```
38 |
39 | Install cosign:
40 |
41 | ```bash
42 | chmod +x cosign-linux-amd64
43 | sudo cp cosign-linux-amd64 /usr/local/bin/cosign
44 | ```
45 |
46 | ### MacOS binary
47 |
48 | Download required files:
49 |
50 | ```bash
51 | curl -fsSL --remote-name-all https://github.com/sigstore/cosign/releases/download/v1.11.1/{cosign-darwin-amd64,release-cosign.pub,cosign-darwin-amd64.sig}
52 | ```
53 |
54 | Verify signature:
55 |
56 | ```bash
57 | openssl dgst -sha256 -verify release-cosign.pub -signature <(cat cosign-darwin-amd64.sig | base64 -D) cosign-darwin-amd64
58 | Verified OK
59 | ```
60 |
61 | Remove signature files:
62 |
63 | ```bash
64 | rm cosign-darwin-amd64.sig release-cosign.pub
65 | ```
66 |
67 | Install cosign:
68 |
69 | ```bash
70 | chmod +x cosign-darwin-amd64
71 | sudo cp cosign-darwin-amd64 /usr/local/bin/cosign
72 | ```
73 |
--------------------------------------------------------------------------------
/src/10-sign-container.md:
--------------------------------------------------------------------------------
1 | # Sign Container
2 |
3 | We are now ready to sign our container using our own sigstore infrastructure
4 |
5 | But before we do that, we need to use our own TUF public key file, you might remember created this when deploying the certificate transparency server.
6 |
7 | Have this file locally and set it as an environment variable:
8 |
9 | ```bash
10 | export SIGSTORE_CT_LOG_PUBLIC_KEY_FILE="/path/to/ctfe_public.pem"
11 | ```
12 |
13 | ```bash
14 | COSIGN_EXPERIMENTAL=1 cosign sign --oidc-issuer "https://oauth2.example.com/auth" --fulcio-url "https://fulcio.example.com" --rekor-url "https://rekor.example.com" ghcr.io//sigstore-thw:latest
15 | ```
16 |
17 | > :notebook: `COSIGN_EXPERIMENTAL` does as it says, you're trying out an experimental feature here.
18 |
19 | > 📝 If you receive an `UNAUTHORIZED: authentication required` error. You need
20 | to reauthenticate with your PAT in GitHub Container Registry again, refer to [Configure registry](08-configure-registry.md)
21 |
22 | An example run:
23 |
24 | ```bash
25 | COSIGN_EXPERIMENTAL=1 cosign sign -oidc-issuer https://oauth2.decodebytes.sh/auth -fulcio-url https://fulcio.decodebytes.sh --rekor-url https://rekor.decodebytes.sh ghcr.io/lukehinds/sigstore-thw:latest
26 | Generating ephemeral keys...
27 | Retrieving signed certificate...
28 | Your browser will now be opened to:
29 | https://oauth2.decodebytes.sh/auth/auth?access_type=online&client_id=sigstore&code_challenge=ZP91ElDffEaUAJxCTYpr_RfpvLHTx8a9WEuiDJiMQT0&code_challenge_method=S256&nonce=1vzuVUvfZ4caqLwqJlUsm0lJglb&redirect_uri=http%3A%2F%2Flocalhost%3A5556%2Fauth%2Fcallback&response_type=code&scope=openid+email&state=1vzuVUvXnKzS2hJnLzxkiDt0qOw
30 | warning: uploading to the transparency log at https://rekor.decodebytes.sh for a private image, please confirm [Y/N]: Y
31 | tlog entry created with index: 11
32 | Pushing signature to: ghcr.io/lukehinds/sigstore-thw:latest:sha256-568999d4aedd444465c442617666359ddcd4dc117b22375983d2576c3847c9ba.sig
33 | ```
34 |
35 | ## Verifying the signing
36 |
37 | We will now verify the signing, but before we do we need to tell cosign about our fulcio root.
38 |
39 | Grab your `fulcio-root.pem` cerficate you generated on the fulcio server (and also copied to the certificate transparency server)
40 |
41 | Set the following environment variable:
42 |
43 | ```bash
44 | export SIGSTORE_ROOT_FILE="$HOME/fulcio-root.pem"
45 | ```
46 |
47 | Download the Rekor public key:
48 |
49 | ```bash
50 | wget -O publicKey.pem https://rekor.example.com/api/v1/log/publicKey
51 | ```
52 |
53 | Set it in the appropriate environment variable:
54 |
55 | ```bash
56 | export SIGSTORE_REKOR_PUBLIC_KEY="$PWD/publicKey.pem"
57 | ```
58 |
59 | We can now verify:
60 |
61 | ```bash
62 | COSIGN_EXPERIMENTAL=1 cosign verify --certificate-identity --certificate-oidc-issuer https:///auth --rekor-url https://rekor.example.com ghcr.io//sigstore-thw:latest
63 | ```
64 |
65 | Replace `` with the e-mail address of the identity you used to sign to your Dex instance.
66 | An example:
67 |
68 | ```bash
69 | COSIGN_EXPERIMENTAL=1 cosign verify --certificate-identity lhinds@redhat.com --certificate-oidc-issuer https://oauth2.decodebytes.sh/auth --rekor-url https://rekor.decodebytes.sh ghcr.io/lukehinds/sigstore-thw
70 |
71 | Verification for ghcr.io/lukehinds/sigstore-thw:latest --
72 | The following checks were performed on each of these signatures:
73 | - The cosign claims were validated
74 | - The claims were present in the transparency log
75 | - The signatures were integrated into the transparency log when the certificate was valid
76 | - Any certificates were verified against the Fulcio roots.
77 | Certificate subject: [lhinds@redhat.com]
78 | {"critical":{"identity":{"docker-reference":"ghcr.io/lukehinds/sigstore-thw"},"image":{"docker-manifest-digest":"sha256:568999d4aedd444465c442617666359ddcd4dc117b22375983d2576c3847c9ba"},"type":"cosign container image signature"},"optional":null}
79 | ```
80 |
81 | ## Congrats
82 |
83 | If you got this far well done for completing the tutorial!
84 |
85 | 
86 |
87 | ## What Next
88 |
89 | If you're not already part of the sigstore community, come and join us on our slack channel; [Invite link](https://join.slack.com/t/sigstore/shared_invite/zt-mhs55zh0-XmY3bcfWn4XEyMqUUutbUQ)
90 | and tell us abour your ideas!
91 |
92 | If you want to improve this guide, please make an issue or better still a pull request!
93 |
94 | Don't forget to delete your instances and not take on unwanted costs!
95 |
96 | ## Having issues, not working?
97 |
98 | Raise an issue (best option, as others can learn) or message me on the [sigstore slack](https://join.slack.com/t/sigstore/shared_invite/zt-mhs55zh0-XmY3bcfWn4XEyMqUUutbUQ), I'm always happy to help.
99 |
--------------------------------------------------------------------------------
/src/README.md:
--------------------------------------------------------------------------------
1 | # Sigstore the Hard Way
2 |
3 | Welcome to sigstore the hard way.
4 |
5 | The driver for this project is to get potential users, developers or collaborators familiar with the inner workings of
6 | sigstore's infrastructure.
7 |
8 | To best achieve a good familiarity with sigstore, we will walk through the whole process manually.
9 |
10 | Building "by hand" provides a view of how each component project in sigstore glues together, while deliberately avoiding automation. This
11 | means no Dockerfiles or deployment framework playbooks. Everything is set up manually.
12 |
13 | With 'sigstore the hard way' we will install, configure and run the following components to provide a 'keyless' signing
14 | infrastructure.
15 |
16 | 1. [Fulcio](https://github.com/sigstore/fulcio) WebPKI
17 | 2. [Rekor](https://github.com/sigstore/rekor), signature transparency log and timestamping authority
18 | 3. [Certificate Transparency Log](https://github.com/google/certificate-transparency-go/tree/master/trillian)
19 | 4. [Dex](https://github.com/dexidp/dex), OpenID Connect provider
20 | 5. [Cosign](https://github.com/sigstore/cosign), container (and more) signing and verifying tool
21 |
22 | ## Requirements
23 |
24 | This tutorial leverages the [GCP](https://cloud.google.com/) for the provisioning of the compute
25 | infrastructure required to bootstrap the sigstore infra from the ground up.
26 | Free credits are available on [Sign up](https://cloud.google.com/free/). For when it comes to saving costs, the recommendation
27 | is to shutdown any instances when you're not using them and once you have completed the tutorial, delete
28 | all the instances, networks etc.
29 |
30 | You can of course use local machines if you have them, or any other provider such as AWS, Azure (pull requests welcomed!)
31 |
32 | The only other requirement is a domain name, where you have the ability to create some subdomains. We need a domain
33 | for an OpenID Connect session (providers don't always like redirect_urls to IP addresses). It's up to you who you use, any provider will do. If you already have a domain, it makes sense to use that. We won't be messing with the root domain if you're already running something there, just creating subdomains (e.g. rekor.example.com, fulcio.example.com)
34 |
35 | ## Certificate Authority
36 |
37 | For the Certificate Authority we will have three options to choose from:
38 |
39 | * File CA
40 | * [SoftHSM](http://www.softhsm.org/)
41 | * Google's Certificate Transparency Service
42 |
43 | The above are listed in order of setup ease. If you just want to kick the tyres and don't need a secure CA, you can use the File CA.
44 |
45 | Google's is a paid service, but easy to set up. SoftHSM is completely free, but requires a little more setup (but nothing
46 | too challenging)
47 |
48 | Last of all we will sign a container image using cosign.
49 |
50 | ### Copyright
51 |
52 | If you have not guessed by name, this is based off, and comes with credit to [Kelsey Hightower's Kubernetes the Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way)
53 |
54 | 
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
55 |
56 | ## Having issues, something not working?
57 |
58 | [Raise an issue](https://github.com/lukehinds/sigstore-the-hard-way/issues/new/choose) (best option, as others can learn) or message me on the [sigstore slack](https://join.slack.com/t/sigstore/shared_invite/zt-mhs55zh0-XmY3bcfWn4XEyMqUUutbUQ), I'm always happy to help.
59 |
--------------------------------------------------------------------------------
/src/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Summary
2 |
3 | [Introduction](README.md)
4 |
5 | - [Prerequisites](./01-prerequisites.md)
6 | - [Compute Resources](./02-compute-resources.md)
7 | - [Domain Configuration](./03-domain-configuration.md)
8 | - [rekor](./04-rekor.md)
9 | - [Dex (oauth2)](./05-dex.md)
10 | - [Fulcio](./06-fulcio.md)
11 | - [Certificate Transparency](./07-certifcate-transparency.md)
12 | - [Configure OCI Registry](./08-configure-registry.md)
13 | - [Setup Cosign](./09-cosign.md)
14 | - [Sign a Container](./10-sign-container.md)
15 |
16 | -----------
17 |
18 | [Contributors](misc/contributors.md)
19 |
--------------------------------------------------------------------------------
/src/contributors.md:
--------------------------------------------------------------------------------
1 | # Thanks
2 |
--------------------------------------------------------------------------------
/src/images/app-reg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/app-reg.png
--------------------------------------------------------------------------------
/src/images/ca_type.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/ca_type.png
--------------------------------------------------------------------------------
/src/images/create.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/create.png
--------------------------------------------------------------------------------
/src/images/ecp384.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/ecp384.png
--------------------------------------------------------------------------------
/src/images/enable_gcp_ca.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/enable_gcp_ca.png
--------------------------------------------------------------------------------
/src/images/glass.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/glass.gif
--------------------------------------------------------------------------------
/src/images/label.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/label.png
--------------------------------------------------------------------------------
/src/images/oauth-consent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/oauth-consent.png
--------------------------------------------------------------------------------
/src/images/oauth-credentials.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/oauth-credentials.png
--------------------------------------------------------------------------------
/src/images/oauth-creds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/oauth-creds.png
--------------------------------------------------------------------------------
/src/images/oauth-redirect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/oauth-redirect.png
--------------------------------------------------------------------------------
/src/images/rev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/rev.png
--------------------------------------------------------------------------------
/src/images/scopes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/scopes.png
--------------------------------------------------------------------------------
/src/images/subj_name.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/subj_name.png
--------------------------------------------------------------------------------
/src/images/user-email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/user-email.png
--------------------------------------------------------------------------------
/src/images/view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/view.png
--------------------------------------------------------------------------------
/src/images/view_two.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stacklok/sigstore-the-hard-way/9f37d7c94e4ba072512e5948c1f6134414d97a73/src/images/view_two.png
--------------------------------------------------------------------------------
/src/misc/contributors.md:
--------------------------------------------------------------------------------
1 | # Contributors
2 |
3 | Here is a list of the contributors who have helped improving sigstore the hardway. Big
4 | shout-out to them!
5 |
6 | - Nathan Smith ([nsmith5](https://github.com/nsmith5))
7 | - Viacheslav Vasilyev ([avoidik](https://github.com/avoidik))
8 | - Ayush Ambastha ([AyushAmbastha](https://github.com/AyushAmbastha))
9 | - Axel Simon ([axelsimon](https://github.com/axelsimon))
10 | - Steve Morgan ([rebelopsio](https://github.com/rebelopsio))
11 | - [mc-slava](https://github.com/mc-slava)
12 | - [Juan Antonio Osorio](https://github.com/JAORMX)
13 |
14 | If you feel you're missing from this list, feel free to add yourself in a PR.
15 |
--------------------------------------------------------------------------------