Skip to content

Commit

Permalink
Merge pull request #27 from seroanalytics/digitalocean
Browse files Browse the repository at this point in the history
Digitalocean
  • Loading branch information
hillalex authored Nov 27, 2024
2 parents 536388e + 5282684 commit 37441bf
Show file tree
Hide file tree
Showing 17 changed files with 82 additions and 397 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ jobs:

- name: 🚢 Push image
run: ./scripts/push

- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}

- name: Deploy
run: doctl apps create-deployment ${{ secrets.DIGITALOCEAN_APP_ID }} --force-rebuild=true
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ VOLUME /run/proxy
COPY build /usr/share/nginx/html
COPY proxy/nginx.conf /etc/nginx/nginx.conf.template
COPY proxy/bin /usr/local/bin
COPY proxy/ssl /usr/local/share/ssl

ENTRYPOINT ["/usr/local/bin/seroviz-proxy"]
100 changes: 51 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,66 +47,68 @@ API JSON schema specifications. Generated types are saved into `src/generated.d.
## Deployment

### Docker
The app is deployed using a Dockerised `nginx` server which also proxies the `serovizr` API.
The app is deployed using a Dockerised `nginx` server.
See the [proxy/README.md](proxy/README.md) for details.

* To build the Docker image run `.scripts/build`.
* To push an image to DockerHub run `./scripts/push`
* To start a copy of the app locally with a self-signed SSL certificate run `./scripts/run`.
* To start a copy of the Dockerised app locally run `./scripts/run`.

### Secrets
Secrets (at the moment this is just the real SSL private key and certificate) are stored in
HashiCorp Cloud Vault. To access the secrets in Vault, you need to create an account with [HashiCorp Cloud](https://portal.cloud.hashicorp.com/sign-in)
and ask Alex to add you to the organization.

To deploy the app, ensure that you have the `hcp` CLI installed on your machine.
Installation instructions [here](https://developer.hashicorp.com/hcp/docs/cli/install).

### Deploying the app
The app is deployed onto an EC2 instance called `seroviz`. You will need to ask Alex for AWS console access,
and to add your IP to the inbound security rules for ssh access.

Then:
1. Retrieve `hcp` service principal credentials by running *on your own machine* (after `hcp auth login`):
```shell
hcp vs secrets open production_id --app=seroviz
hcp vs secrets open production_secret --app=seroviz
```
1. ssh onto the server
1. Navigate to the `seroviz` directory
1. Run:
```shell
./scripts/clear-docker.sh
./scripts/deploy
```

The `deploy` script will prompt you for the client id and secret from step 1.

You can also export these as environment variables which may be more convenient in case deployment fails
for any reason and has to be re-run:
```shell
CLIENT_ID=<client_id>
CLIENT_SECRET=<client_secret>
```

### Setting up a new EC2 instance
(unless otherwise specified, all steps are run on the remote machine)
* Install `git`:
```shell
sudo yum -y install git
```
* Install Docker, following instructions [here](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-docker.html).
* Install the `hcp cli` for secret retrieval by following the [instructions](https://developer.hashicorp.com/hcp/docs/cli/install) for Amazon Linux.
* On your own machine, install the `hcp cli` if you haven't already, and retrieve the production service principal id and secret, stored at `production_id` and `production_secret`:
```shell
hcp vs secrets open production_id --app=seroviz
hcp vs secrets open production_secret --app=seroviz
```
* On the remote server, clone this GitHub repo using https:
```shell
git clone https://github.com/seroanalytics/seroviz.git
```
* Follow the instructions above to deploy the app
The app is deployed on DigitalOcean's App Platform. The Seroviz app has 2 services,
each deployed using Docker images. One is this app, and the other is the [serovizr API](https://github.com/seroanalytics/serovizr).
The app topology should look like this:

```yaml
alerts:
- rule: DEPLOYMENT_FAILED
- rule: DOMAIN_FAILED
domains:
- domain: seroviz.seroanalytics.org
type: PRIMARY
features:
- buildpack-stack=ubuntu-22
ingress:
rules:
- component:
name: serovizr
match:
path:
prefix: /api
- component:
name: seroviz
match:
path:
prefix: /
name: seroviz
region: lon
services:
- http_port: 8888
image:
registry: seroanalytics
registry_type: DOCKER_HUB
repository: serovizr
tag: main
instance_count: 1
instance_size_slug: apps-s-1vcpu-0.5gb
name: serovizr
- http_port: 80
image:
registry: seroanalytics
registry_type: DOCKER_HUB
repository: seroviz
tag: main
instance_count: 1
instance_size_slug: apps-s-1vcpu-0.5gb
name: seroviz
```
The domain and SSL are also configured on DigitalOcean under the Networking section.
## Domain name
The domain name `seroanalytics.org` is registered with NameCheap.
Expand Down
39 changes: 1 addition & 38 deletions proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,4 @@ docker run seroviz-proxy seroanalytics.org

## SSL certificates

The server will not start until the files `/run/proxy/certificate.pem` and `/run/proxy/key.pem` exist -
you can get these into the container however you like; the proxy will poll for them and start within a second of them appearing.

The production SSl key and certificate are stored on the [Vault cloud platform](https://www.hashicorp.com/products/vault). You will
need an account and to be added to the `seroanalytics` project to access these secrets.

## Self-signed certificate

For testing it is useful to use a self-signed certificate. These are not in any way secure.
To generate a self-signed certificate, there is a utility in the proxy container self-signed-certificate that will
generate one on demand after receiving key components of the CSR.

There is a self-signed certificate in the repo for testing generated with (on metal)

```
./proxy/bin/self-signed-certificate proxy/ssl GB London LSHTM seroanalytics localhost
```

These can be used in the container by execing `self-signed-certificate /run/proxy` in the container while it polls for certificates.
Alternatively, to generate certificates with a custom CSR (which takes a couple of seconds) you can exec something like:

```
self-signed-certificate GB London LSHTM seroanalytics seroanalytics.org
```

## dhparams (Diffie-Hellman key exchange parameters)

We require a `dhparams.pem` file (see [here](https://security.stackexchange.com/questions/94390/whats-the-purpose-of-dh-parameters)) for details.
The file in this directory is built into the Docker image, but can be overwritten when deploying the app, by copying your own into the container at `/run/proxy/dhparams.pem` before
getting the certificates in place.

To regenerate the file in this directory, run

```
./proxy/bin/dhparams proxy/ssl
```

This takes quite a while to run (several minutes).
The proxy just serves the app on port 80. SSL must be handled at the point of deployment.
12 changes: 0 additions & 12 deletions proxy/bin/dhparams

This file was deleted.

33 changes: 0 additions & 33 deletions proxy/bin/self-signed-certificate

This file was deleted.

34 changes: 1 addition & 33 deletions proxy/bin/seroviz-proxy
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,10 @@ else
exit 1
fi

echo "We will listen on ports 80 (http) and 443 (https)"
echo "We will listen on port 80 (http)"
echo "with hostname $HTTP_HOST"

envsubst '$HTTP_HOST' \
< /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf

# These paths must match the paths as used in the nginx.conf
PATH_CONFIG=/run/proxy
PATH_CERT="$PATH_CONFIG/certificate.pem"
PATH_KEY="$PATH_CONFIG/key.pem"
PATH_DHPARAM="$PATH_CONFIG/dhparam.pem"

mkdir -p $PATH_CONFIG

# We'll copy this one directly into place - if the user wants to
# override it they should just copy theirs in place before the
# certificate.
cp /usr/local/share/ssl/dhparam.pem $PATH_DHPARAM

if [[ -v "SSL_KEY" ]]; then
echo "Found SSL_KEY in env. Copying into place."
echo "$SSL_KEY" > $PATH_KEY
chmod 600 $PATH_KEY
fi

if [[ -v "SSL_CERT" ]]; then
echo "Found SSL_CERT in env. Copying into place."
echo "$SSL_CERT" > $PATH_CERT
chmod 644 $PATH_CERT
fi

while [ ! -e $PATH_CERT ] || [ ! -e $PATH_KEY ]; do
# Wait for the ssl certificates to be copied in or generated
echo "Waiting for certificates at $PATH_CERT and $PATH_KEY"
sleep 1
done

echo "Certificate files detected. Running nginx"
exec nginx -g "daemon off;"
54 changes: 1 addition & 53 deletions proxy/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -21,57 +21,16 @@ http {
access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

# Main server configuration. See below for redirects.
server {
listen 443 ssl;
listen 80;
server_name localhost ${HTTP_HOST};

# Enable HTTP Strict Transport Security (HSTS)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

# https://scotthelme.co.uk/content-security-policy-an-introduction/
# https://content-security-policy.com/examples/nginx/
# add_header Content-Security-Policy "default-src 'self';" always;
# However, this one does work:
add_header Content-Security-Policy "default-src https:; script-src ${HTTP_HOST}; style-src 'self' 'unsafe-inline'; img-src 'self' ${HTTP_HOST} blob: data:;" always;

# https://scotthelme.co.uk/hardening-your-http-response-headers/#x-frame-options
# https://geekflare.com/add-x-frame-options-nginx/
add_header X-Frame-Options "SAMEORIGIN";

# https://scotthelme.co.uk/hardening-your-http-response-headers/#x-content-type-options
add_header X-Content-Type-Options "nosniff" always;

# https://scotthelme.co.uk/a-new-security-header-referrer-policy/
add_header Referrer-Policy 'origin' always;

# https://scotthelme.co.uk/goodbye-feature-policy-and-hello-permissions-policy/
# Actual values adopted from securityheaders.com :)
add_header Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=(), interest-cohort=()" always;

# Certificate
ssl_certificate /run/proxy/certificate.pem;
ssl_certificate_key /run/proxy/key.pem;

# SSL settings as recommended by this generator
# https://ssl-config.mozilla.org/
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_dhparam /run/proxy/dhparam.pem;

root /usr/share/nginx/html;
index index.html index.htm;

location /api {
proxy_pass http://serovizr:8888;
}

# Don't cache these files
location ~* \.(?:manifest|appcache|html?|xml|json)$ {
expires -1;
Expand All @@ -96,15 +55,4 @@ http {
try_files $uri $uri/ /index.html;
}
}

# Redirect all http requests to the SSL endpoint and the correct domain name
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;

location / {
return 301 https://${HTTP_HOST}$request_uri;
}
}
}
32 changes: 0 additions & 32 deletions proxy/ssl/certificate.pem

This file was deleted.

13 changes: 0 additions & 13 deletions proxy/ssl/dhparam.pem

This file was deleted.

Loading

0 comments on commit 37441bf

Please sign in to comment.