Skip to content

Commit a88e7de

Browse files
committed
Build: Make access key a run-time ENV instead of build-time ARG
This has a number of benefits: * Easier and more confident testing (the same image is tested and can then be deployed the same way). * No rebuild is needed to change between the modes, which allows for faster recovery and avoids e.g. having to accept new commits or changes to upstream base images as part of a rebuild when all you want is to switch modes. * The resulting image contains no private information, thus safe to publish to a public container registry. As example, I've enabled publishing to GitHub's ghcr.io registry, for use in another experiment. This can be removed later if we don't need it. Ref https://github.com/jquery/infrastructure/issues/474.
1 parent 9dfa3f9 commit a88e7de

7 files changed

Lines changed: 150 additions & 61 deletions

File tree

.github/workflows/CI.yaml

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,37 @@
11
name: CI
22
on:
3-
# Manually
43
workflow_dispatch:
5-
# Note that we deploy regularly from main.
6-
# To test ahead of time, push to a new branch first, or open a PR.
74
push:
85
pull_request:
96

107
jobs:
11-
docker-test-dev:
8+
docker-test:
129
# Includes PHP 7.4, Python 3, Node 14
1310
# https://github.com/actions/virtual-environments/blob/ubuntu20/20210816.1/images/linux/Ubuntu2004-README.md
1411
runs-on: ubuntu-20.04
1512
steps:
1613
- uses: actions/checkout@v2
1714

1815
- name: Build the image
19-
run: docker build -t codeorigin-dev:$GITHUB_SHA ./
16+
run: docker build -t codeorigin ./
2017

21-
- name: Test the container
18+
- name: Test the container in open mode
2219
run: |
23-
docker run --rm -p 4000:80/tcp --detach codeorigin-dev:$GITHUB_SHA
20+
docker run --rm -p 4000:80/tcp --detach codeorigin
2421
# The first error is "Empty reply from server", which curl
2522
# considers an HTTP failure rather than a connection or network failure.
2623
# once GitHub's images come with curl 7.71.0+, use --retry-all-errors
2724
# instead of hardcoded sleep. --krinkle 2021-08-21
28-
sleep 1
29-
curl -f --retry 5 --retry-delay 1 --retry-connrefused -I http://127.0.0.1:4000/jquery-3.0.0.js
30-
php test/static-dev.php
25+
sleep 2
26+
curl -f --retry 5 --retry-delay 1 --retry-connrefused -I http://localhost:4000/jquery-3.0.0.js
27+
php test/static-open.php
28+
docker kill $(docker ps -q -f ancestor=codeorigin)
3129
32-
docker-test-strict:
33-
runs-on: ubuntu-20.04
34-
env:
35-
CDN_ACCESS_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
36-
steps:
37-
- uses: actions/checkout@v2
38-
39-
- name: Build the image
40-
run: docker build -t codeorigin-strict:$GITHUB_SHA --build-arg "CDN_ACCESS_KEY=$CDN_ACCESS_KEY" ./
41-
42-
- name: Test the container
30+
- name: Test the container in strict mode
31+
env:
32+
CDN_ACCESS_KEY: aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbccccccccccccccccdddddddddddddddd
4333
run: |
44-
docker run --rm -p 4000:80/tcp --detach codeorigin-strict:$GITHUB_SHA
45-
sleep 1
46-
curl -f --retry 5 --retry-delay 1 --retry-connrefused -I http://127.0.0.1:4000/jquery-3.0.0.js
34+
docker run --rm -p 4000:80/tcp -e "CDN_ACCESS_KEY=$CDN_ACCESS_KEY" --detach codeorigin
35+
sleep 2
36+
curl -f --retry 5 --retry-delay 1 --retry-connrefused -I http://localhost:4000/jquery-3.0.0.js
4737
php test/static-strict.php

.github/workflows/deploy.yaml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: deploy
2+
on:
3+
# On commit to the main branch
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
docker-push:
10+
# For included software, refer to:
11+
# https://github.com/actions/virtual-environments/blob/ubuntu20/20210816.1/images/linux/Ubuntu2004-README.md
12+
runs-on: ubuntu-20.04
13+
# https://docs.github.com/en/actions/reference/authentication-in-a-workflow#permissions-for-the-github_token
14+
permissions:
15+
packages: write
16+
env:
17+
IMAGE_NAME: codeorigin
18+
IMAGE_ID: ghcr.io/jquery/codeorigin
19+
IMAGE_VERSION: ${{ github.sha }}
20+
steps:
21+
- uses: actions/checkout@v2
22+
23+
- name: Build the image
24+
run: docker build -t ${{ env.IMAGE_NAME }} ./
25+
26+
- name: Test the container
27+
run: |
28+
docker run --rm -p 4000:80/tcp --detach ${{ env.IMAGE_NAME }}
29+
sleep 2
30+
curl -f --retry 5 --retry-delay 1 --retry-connrefused -I http://127.0.0.1:4000/jquery-3.0.0.js
31+
php test/static-open.php
32+
33+
# https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#upgrading-a-workflow-that-accesses-ghcrio
34+
- name: Login to Container Registry
35+
run: |
36+
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.repository_owner }} --password-stdin
37+
38+
# https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#upgrading-a-workflow-that-accesses-ghcrio
39+
- name: Publish image to Container Registry
40+
run: |
41+
docker tag $IMAGE_NAME $IMAGE_ID:$IMAGE_VERSION
42+
docker tag $IMAGE_NAME $IMAGE_ID:latest
43+
docker push $IMAGE_ID:$IMAGE_VERSION
44+
docker push $IMAGE_ID:latest

Dockerfile

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Allow patch updates
22
# https://hub.docker.com/_/nginx/
3-
FROM nginx:1-alpine
3+
FROM nginx:1.21.1-alpine
44

55
RUN apk add vim openrc
66

@@ -35,23 +35,12 @@ COPY cdn/ /var/www/cdn/
3535
RUN find /var/www/cdn/ -type f -print0 | TZ=UTC xargs -0 -P 4 -n 50 touch --date='1991-10-18 12:00:00' {} +
3636

3737
# Define the environment variable that will be used in the origin pull magic header
38-
ARG CDN_ACCESS_KEY=''
38+
# Set this via `docker run`
39+
ENV CDN_ACCESS_KEY=''
3940

4041
# Copy in the necessary config files
4142
COPY cfg/vimrc /etc/vim/vimrc
42-
COPY cfg/default.conf /etc/nginx/conf.d/default.conf
43-
44-
# If the CDN_ACCESS_KEY environment variable is *not* set, operate in "break glass" mode where the
45-
# container serves files without restriction. Otherwise, it responds with redirects to requests
46-
# that don't carry an x-cdn-access request header with the correct key.
47-
#
48-
# Note: We're substituting the config file because nginx does not currently have a way to
49-
# access environment variables without significant workarounds.
50-
RUN if [ -n "$CDN_ACCESS_KEY" ]; then \
51-
sed -i s/CDN_ACCESS_KEY_PLACEHOLDER/$CDN_ACCESS_KEY/g /etc/nginx/conf.d/default.conf; \
52-
else \
53-
sed -i s/^.*REQUIRE_XCDNACCESS$//g /etc/nginx/conf.d/default.conf; \
54-
fi
43+
COPY cfg/default.conf /etc/nginx/templates/default.conf.template
5544

5645
EXPOSE 80
5746

README.md

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,20 @@ Prerequisites:
1717

1818
### Local development
1919

20-
**Test the container**:
20+
**Build the image**:
21+
22+
Build the image, by running the following in a clone of this repo:
23+
24+
```
25+
docker build -t codeorigin ./
26+
```
27+
28+
**Test the container in open mode**:
2129

22-
1. Build the image, by running in a clone of this repo:
23-
`docker build -t codeorigin-dev ./`
2430
1. Start a container, exposing port 80:
25-
`docker run --rm -p 4000:80/tcp codeorigin-dev`
31+
```
32+
docker run --rm -p 4000:80/tcp codeorigin
33+
```
2634
1. The site is now running at <http://localhost:4000/jquery-3.0.0.js>.
2735
To stop the container, press `ctrl+c`
2836

@@ -32,22 +40,33 @@ Run it with a shell entrypoint and set interactive/tty mode by adding the parame
3240
Note that when inspecting the container, nginx will not be started.
3341

3442
```
35-
docker run --rm -p 4000:80/tcp -i -t --entrypoint /bin/sh codeorigin-dev
43+
docker run --rm -p 4000:80/tcp -it --entrypoint /bin/sh codeorigin
44+
45+
# /docker-entrypoint.d/20-envsubst-on-templates.sh 3>&1
46+
...
47+
# cat /etc/nginx/conf.d/default.conf
48+
...
49+
```
50+
51+
Inspect restricted mode:
52+
53+
```
54+
docker run --rm -p 4000:80/tcp -e "CDN_ACCESS_KEY=$CDN_ACCESS_KEY" -it --entrypoint /bin/sh codeorigin
3655
```
3756

3857
**Debug nginx**:
3958

40-
In `cfg/defualt.cong`, change `error_log … crit` to `error_log … debug` and then build+run as usual.
59+
In `cfg/defualt.conf`, change `error_log … crit` to `error_log … debug` and then build+run as usual.
4160

4261
**Test the container in restricted mode**:
4362

4463
There is a restricted mode, which responds to unauthorized static file requests with a redirect instead of serving files.
4564

4665
1. Run `export CDN_ACCESS_KEY="$(openssl rand -hex 32)"`
47-
1. Build the image, by running in a clone of this repo:
48-
`docker build -t codeorigin-strict --build-arg "CDN_ACCESS_KEY=$CDN_ACCESS_KEY" ./`
49-
1. Start a container, exposing port 80:
50-
`docker run --rm -p 4000:80/tcp codeorigin-strict`
66+
1. Start a container, exposing port 80, and given the environment variable:
67+
```
68+
docker run --rm -p 4000:80/tcp -e "CDN_ACCESS_KEY=$CDN_ACCESS_KEY" codeorigin
69+
````
5170
1. The site is now running at <http://localhost:4000/jquery-3.0.0.js>.
5271
To stop the container, press `ctrl+c`
5372
@@ -60,7 +79,7 @@ In the restricted mode:
6079
### Production deployment
6180
6281
1. First, generate a CDN access key: `openssl rand -hex 32`.
63-
1. At a hosting platform of your choosing, build a container from the Dockerfile in this repository, and pass the CDN access key as build arguments.
82+
1. At a hosting platform of your choosing, build or pull the container, and pass the CDN access key as run environment variable.
6483
1. Finally, configure the CDN to use the container address as its origin, with special handling to augment origin pulls with a `Host: code.jquery.com` header, and a `x-cdn-access` header with the access key.
6584
6685
### In case of emergency

cfg/default.conf

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,25 @@
1515
# Increase map_hash_bucket_size to accommodate longer CDN tokens
1616
map_hash_bucket_size 128;
1717

18-
# Do not change the following line. The Dockerfile will insert the access key or remove the line.
19-
map $http_x_cdn_access $reroute_to_cdn { default 1; CDN_ACCESS_KEY_PLACEHOLDER 0; } #REQUIRE_XCDNACCESS
18+
# If the CDN_ACCESS_KEY environment variable is *not* set, operate in "break glass" mode where the
19+
# container openly serves files without restriction. Otherwise, it responds with redirects to requests
20+
# that don't carry a matching x-cdn-access request header.
21+
#
22+
# NOTE: We're using the "envsubst" feature that comes with docker-nginx because nginx does not
23+
# currently have a way to access environment variables without significant workarounds.
24+
# This is interpreted at run-time just before the server starts.
25+
#
26+
# This is preferred over substitution at build-time in Dockerfile as that would put the
27+
# credentials inside the image, making it unsuitable to push to a public container registry.
28+
map "${CDN_ACCESS_KEY}" $origin_mode {
29+
"" open;
30+
default key;
31+
}
32+
map $origin_mode $key_input { key $http_x_cdn_access; default none; }
33+
map $origin_mode $key_expect {
34+
key "${CDN_ACCESS_KEY}";
35+
default none;
36+
}
2037

2138
server {
2239
listen 80;
@@ -60,10 +77,8 @@ server {
6077
# Applies to charset_types (mainly for JS and CSS)
6178
charset utf-8;
6279

63-
# Let rerouting responses be cached for a shorter time for fast recovery
64-
#
65-
# Do not change the following line. The Dockerfile will remove the line if needed.
66-
if ($reroute_to_cdn) { expires 5m; return 301 https://code.jquery.com$uri; } #REQUIRE_XCDNACCESS
80+
# Let rerouting responses be cached for a shorter time for faster recovery from mistakes
81+
if ($key_expect != $key_input) { expires 5m; return 302 https://code.jquery.com$uri; }
6782
}
6883

6984
# Legacy redirects for renamed files
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
/**
33
* Usage:
44
*
5-
* $ php test/static-dev.php
5+
* $ php test/static-open.php
66
*
7-
* $ php test/static-dev.php "localhost:4000"
7+
* $ php test/static-open.php "localhost:4000"
88
*/
99

1010
require_once __DIR__ . '/Unit.php';
@@ -80,6 +80,25 @@
8080
'accept-ranges' => 'bytes',
8181
] );
8282

83+
// Static asset with a key when no key is required
84+
85+
Unit::testHttp( $server, '/jquery-3.0.0.js', [
86+
"x-cdn-access: there-is-no-spoon"
87+
], [
88+
'status' => '200 OK',
89+
'server' => 'nginx',
90+
'content-type' => 'application/javascript; charset=utf-8',
91+
'content-length' => '263268',
92+
'last-modified' => 'Fri, 18 Oct 1991 12:00:00 GMT',
93+
'connection' => 'keep-alive',
94+
'vary' => 'Accept-Encoding, x-cdn-access',
95+
'etag' => '"28feccc0-40464"',
96+
'expires' => 'Thu, 31 Dec 2037 23:55:55 GMT',
97+
'cache-control' => 'max-age=315360000, public, no-transform',
98+
'access-control-allow-origin' => '*',
99+
'accept-ranges' => 'bytes',
100+
] );
101+
83102
// Renamed files
84103

85104
Unit::testHttp( $server, '/jquery-git2.js', [], [

test/static-strict.php

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
'location' => 'https://releases.jquery.com/',
2828
] );
2929

30-
// Static asset with key
30+
// Static asset with correct key
3131

3232
Unit::testHttp( $server, '/jquery-3.0.0.js', [
3333
"x-cdn-access: $key"
@@ -46,10 +46,23 @@
4646
'accept-ranges' => 'bytes',
4747
] );
4848

49-
// Static asset without key
49+
// Reroute asset without key
5050

5151
Unit::testHttp( $server, '/jquery-3.0.0.js', [], [
52-
'status' => '301 Moved Permanently',
52+
'status' => '302 Moved Temporarily',
53+
'server' => 'nginx',
54+
'location' => 'https://code.jquery.com/jquery-3.0.0.js',
55+
'vary' => 'x-cdn-access',
56+
'cache-control' => 'max-age=300, public, no-transform',
57+
'access-control-allow-origin' => '*',
58+
] );
59+
60+
// Reroute asset with incorrect key
61+
62+
Unit::testHttp( $server, '/jquery-3.0.0.js', [
63+
"x-cdn-access: there-is-no-spoon"
64+
], [
65+
'status' => '302 Moved Temporarily',
5366
'server' => 'nginx',
5467
'location' => 'https://code.jquery.com/jquery-3.0.0.js',
5568
'vary' => 'x-cdn-access',

0 commit comments

Comments
 (0)