diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 4421508..9589c95 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -16,5 +16,7 @@ jobs: run: just build - name: Run tests for opensafely.org run: just test opensafely.org + - name: Run tests for emis-staging.opensafely.org + run: just test emis-staging.opensafely.org - name: Run tests ted.bennettoxford.org run: just test ted.bennettoxford.org diff --git a/README.md b/README.md index 1efe338..8e9e56d 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ To secure and limit access to external services, the OpenSAFELY platform maintains a proxy service. OpenSAFELY backends explicitly use these proxies when they need to access external data. -This repository produces a Docker image that uses nginx to host four proxy -domains, configured across three nginx config files: +This repository produces a Docker image that uses nginx to host five proxy +domains, configured across four nginx config files: * github-proxy.opensafely.org: this provides access to *only* opensafely repositories hosted on https://github.com, and not other repositories. It @@ -21,7 +21,10 @@ domains, configured across three nginx config files: http://security.ubuntu.com so that machines running within the secure environments can be kept up-to-date. -Whilst the last one is very simple, the first two require some shenanigans in + * release.opensafely.org: this provides proxy access to the /api/v2/releases/* and + /api/v2/airlock/events job-server API endpoints on https://jobs.opensafely.org. + +Whilst the last two are very simple, the first two require some shenanigans in order to proxy git http protocol and docker registry API v2.0 protocol. Of particular note is that ghcr.io issues 307 redirects for blob urls to @@ -119,3 +122,18 @@ dokku:~$ dokku nginx:set proxy proxy-buffer-size 16k =====> Setting proxy-buffer-size to 16k dokku:~$ dokku ps:restart proxy ``` + +### Adding a new proxied domain + +Adding a `.conf.template` file to this repo and merging will build and deploy the +dokku app automatically. To get the new domain working, you also need to: + +1) Add a DNS record for the proxied domain in cloudflare, pointing at dokku4's IP. +2) Add a domain to the dokku app. On dokku4: + ``` + dokku domains:add proxy .opensafely.org + ``` +3) Generate certs for the new domain (existing valid certs will be skipped) + ``` + dokku letsencrypt:enable proxy + ``` diff --git a/ci-tests.sh b/ci-tests.sh index 408e163..0171f7e 100755 --- a/ci-tests.sh +++ b/ci-tests.sh @@ -14,6 +14,7 @@ GITHUB_PROXY_HOST=github-proxy.${BASE_DOMAIN} DOCKER_PROXY_HOST=docker-proxy.${BASE_DOMAIN} UBUNTU_ARCHIVE_PROXY_HOST=archive-ubuntu.${BASE_DOMAIN} UBUNTU_SECURITY_PROXY_HOST=security-ubuntu.${BASE_DOMAIN} +RELEASE_PROXY_HOST=release.${BASE_DOMAIN} #CHANGELOGS_PROXY_HOST=changelogs.${BASE_DOMAIN} url= @@ -53,6 +54,7 @@ try() { curl_args+=(--connect-to "${DOCKER_PROXY_HOST}:80:127.0.0.1:8080") curl_args+=(--connect-to "${UBUNTU_ARCHIVE_PROXY_HOST}:80:127.0.0.1:8080") curl_args+=(--connect-to "${UBUNTU_SECURITY_PROXY_HOST}:80:127.0.0.1:8080") + curl_args+=(--connect-to "${RELEASE_PROXY_HOST}:80:127.0.0.1:8080") #curl_args+=(--connect-to "${CHANGELOGS_PROXY_HOST}:80:127.0.0.1:8080") # Conditionally token if set. Only used for docker-proxy tests. @@ -227,6 +229,21 @@ assert-in-body 'dists/' try "${UBUNTU_SECURITY_PROXY_HOST}/ubuntu/" 403 '' 'Googlebot' +### $RELEASE_PROXY_HOST ### +# Confirm that the proxied URL resolves; we aren't providing a valid token or +# user, so 403 is expected +# Only test with opensafely.org domains; others (e.g. ted.bennettoxford.org) don't +# have /api/v2/ endpoints to proxy +if [ "$BASE_DOMAIN" == *opensafely.org ]; then + try "${RELEASE_PROXY_HOST}/api/v2/releases/workspace/test-age-distribution" 403 + assert-in-body 'Invalid user or token' + # test robots is disallowed + try "${RELEASE_PROXY_HOST}/robots.txt" 200 + assert-in-body 'User-agent: *' + assert-in-body 'Disallow: /' + assert-header 'Content-Type: text/plain; charset=UTF-8' +fi + ### $CHANGELOGS_PROXY_HOST ### # This allows us to use the do-release-upgrade tool to perform major backend OS upgrades. # Disabled as we don't typically needed unless we are using do-release-upgrade diff --git a/release.opensafely.org.conf.template b/release.opensafely.org.conf.template new file mode 100644 index 0000000..4a9f29a --- /dev/null +++ b/release.opensafely.org.conf.template @@ -0,0 +1,42 @@ + +# Proxy the job-server API +# +server { + + server_name release.${BASE_DOMAIN}; + root /var/www/html; + listen ${PORT}; + + location = /robots.txt { + add_header 'Content-Type' 'text/plain; charset=UTF-8' always; + return 200 "User-agent: *\nDisallow: /\n"; + } + + # Allow any method to releases endpoints; this covers Airlock auth + # and releasing. Headers are passed through unmodified. + location /api/v2/releases/ { + proxy_pass https://jobs.${BASE_DOMAIN}; + proxy_redirect default; + # ensure Host header and SNI domain match + proxy_ssl_server_name on; + + # Released files are uploaded one at a time; L4 files have a + # max allowed size of 16Mb, so set max body size to 20Mb to allow some + # overhead + # Note: POSTS to the upload endpoint typically take 100-300ms, and there + # have been none over 400ms in the past 60 days (as of 2026-08-19) so we + # shouldn't need to increase proxy_read_timeout / proxy_send_timeout from the + # default 60s. We may need to revisit this if we see 504s on this endpoint. + client_max_body_size 20m; + } + + # Allow any method to airlock/events/ endpoint only (with or without trailing slash), + # for Airlock notifications + location ~ ^/api/v2/airlock/events/?$ { + proxy_pass https://jobs.${BASE_DOMAIN}; + proxy_redirect default; + # ensure Host header and SNI domain match + proxy_ssl_server_name on; + } +} +