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 .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ docker-compose-dev.yml

Procfile*
ui/node_modules

.certs
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,6 @@ tests/data/documents_items_lofi_items.json
# Files
/data/files


# Development TLS pair, generated by scripts/gen-certs
/.certs/
22 changes: 22 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@ cd rero-ils
uv run ./scripts/bootstrap
```

### TLS certificates

The development server is served over HTTPS. The bootstrap generates its
certificate and private key in `.certs/`, which is not versioned: a pair
committed to a public repository is a published private key, and it expires.
An existing pair is kept; `--force-certs` replaces it.

If [mkcert](https://github.com/FiloSottile/mkcert) is installed, the pair is
signed by a local certificate authority and browsers accept it without warning.
Run `mkcert -install` once to trust that authority. Otherwise the script falls
back to a self-signed certificate from `openssl`, which works but makes browsers
warn about the unknown issuer.

To regenerate the pair on its own:

```console
uv run ./scripts/gen-certs --force
```

The nginx and HAProxy images of `docker-compose.full.yml` build their own
self-signed certificate, so nothing has to be generated for them.

Start all dependent services using docker-compose (this will start PostgreSQL,
Elasticsearch 6, RabbitMQ and Redis):

Expand Down
13 changes: 12 additions & 1 deletion docker/haproxy/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
# SPDX-FileCopyrightText: Fondation RERO+
# SPDX-License-Identifier: AGPL-3.0-or-later

# The haproxy image ships no openssl, so the pair is built in a separate stage.
# It borrows the nginx image, which the stack already pulls for its own service,
# rather than adding one just for a certificate.
FROM nginx AS certs
RUN openssl req -x509 -newkey rsa:4096 -sha256 -days 825 -nodes \
-keyout /key.pem -out /crt.pem \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1" \
&& cat /crt.pem /key.pem > /cert.pem \
&& chmod 600 /cert.pem

FROM haproxy:1.8
RUN mkdir -p /usr/local/var/lib/haproxy/
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg
COPY haproxy_cert.pem /usr/local/etc/cert.pem
COPY --from=certs /cert.pem /usr/local/etc/cert.pem
83 changes: 0 additions & 83 deletions docker/haproxy/haproxy_cert.pem

This file was deleted.

10 changes: 8 additions & 2 deletions docker/nginx/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
COPY conf.d/* /etc/nginx/conf.d/
COPY test.key /etc/ssl/private/test.key
COPY test.crt /etc/ssl/certs/test.crt

# Self-signed pair for this example stack, generated at build time rather than
# versioned. The nginx image ships openssl, so no extra stage is needed.
RUN openssl req -x509 -newkey rsa:4096 -sha256 -days 825 -nodes \
-keyout /etc/ssl/private/test.key -out /etc/ssl/certs/test.crt \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1" \
&& chmod 600 /etc/ssl/private/test.key
5 changes: 3 additions & 2 deletions docker/nginx/conf.d/default.conf
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ server {

# SSL configuration according to best practices from
# https://mozilla.github.io/server-side-tls/ssl-config-generator/
# The provided certificate (test.crt) and private key (test.key) is only for
# testing and must never be used in production environment.
# The certificate (test.crt) and private key (test.key) are generated by the
# Dockerfile, are self-signed, and must never be used in a production
# environment.
ssl_certificate /etc/ssl/certs/test.crt;
ssl_certificate_key /etc/ssl/private/test.key;
ssl_session_timeout 1d;
Expand Down
31 changes: 0 additions & 31 deletions docker/nginx/test.crt

This file was deleted.

52 changes: 0 additions & 52 deletions docker/nginx/test.key

This file was deleted.

12 changes: 11 additions & 1 deletion scripts/bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ set -e
msg "PROGRAM: ${PROGRAM}"

# options may be followed by one colon to indicate they have a required argument
if ! options=$(getopt -o dct: -l deploy,ci,tgz_package: -- "$@")
if ! options=$(getopt -o dct: -l deploy,ci,force-certs,tgz_package: -- "$@")
then
# something went wrong, getopt will put out an error message for us
exit 1
Expand All @@ -25,6 +25,7 @@ flags=()
deploy=false
cmd="uv sync --frozen"
ci=false
force_certs=false
tgz_file=""
while test $# -gt 0
do
Expand All @@ -35,6 +36,8 @@ do
-c|--ci)
# We are probably inside travis, check for env variables
ci=true ;;
--force-certs)
force_certs=true ;;
-t|--tgz_package)
tgz_file=$2
if [[ ! -f "${tgz_file}" ]]
Expand Down Expand Up @@ -113,6 +116,13 @@ info_msg "Compile translations"
uv run pybabel compile -d rero_ils/translations

if ! $deploy ; then
# The development server needs a TLS pair; a deployment brings its own.
certs_flags=()
if $force_certs ; then
certs_flags+=("--force")
fi
"$(dirname "${BASH_SOURCE[0]}")"/gen-certs "${certs_flags[@]}"

info_msg "Install Playwright browsers (Chromium, Firefox, WebKit) for E2E tests"
uv run playwright install chromium firefox webkit
fi
Expand Down
58 changes: 58 additions & 0 deletions scripts/gen-certs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Fondation RERO+
# SPDX-License-Identifier: AGPL-3.0-or-later

# Colored message helpers: msg, info_msg, error_msg, error_msg+exit,
# success_msg, colored_msg.
. "$(dirname "${BASH_SOURCE[0]}")/messages"

PROGRAM=`basename $0`

set -e

# Displays program name
msg "PROGRAM: ${PROGRAM}"

CERTS_DIR="$(dirname "${BASH_SOURCE[0]}")/../.certs"
CERT="${CERTS_DIR}/dev.crt"
KEY="${CERTS_DIR}/dev.key"
HOSTS=(localhost 127.0.0.1 ::1)

force=false
while test $# -gt 0
do
case "$1" in
-f|--force)
force=true ;;
(--) shift; break;;
(*) error_msg+exit "Option $1 not recognized" ;;
esac
shift
done

if [[ -f "${CERT}" && -f "${KEY}" ]] && ! $force ; then
info_msg "Certificates already exist, skipping. Use --force to regenerate."
exit 0
fi

mkdir -p "${CERTS_DIR}"

if command -v mkcert &> /dev/null; then
# mkcert signs with a locally trusted CA, so a browser opens the development
# server without an interstitial.
info_msg "Generate the pair with mkcert"
mkcert -cert-file "${CERT}" -key-file "${KEY}" "${HOSTS[@]}"
info_msg "Run 'mkcert -install' once if the certificate is not trusted yet."
else
# Fallback for the CI and for any environment without mkcert. A browser warns
# about the unknown issuer; curl needs -k and Playwright already ignores it.
info_msg "mkcert not found, generate a self-signed pair with openssl"
openssl req -x509 -newkey rsa:4096 -sha256 -days 825 -nodes \
-keyout "${KEY}" -out "${CERT}" \
-subj "/CN=localhost" \
-addext "subjectAltName=DNS:localhost,IP:127.0.0.1,IP:::1"
fi

chmod 600 "${KEY}"

success_msg "Certificates generated in ${CERTS_DIR}"
Loading