Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 `<new-proxy>.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 <new-proxy>.opensafely.org
```
3) Generate certs for the new domain (existing valid certs will be skipped)
```
dokku letsencrypt:enable proxy
```
17 changes: 17 additions & 0 deletions ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
42 changes: 42 additions & 0 deletions release.opensafely.org.conf.template
Original file line number Diff line number Diff line change
@@ -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;
}
}