From 0feeae3ae230791605cc480d3753ae9e8c6d2b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johnny=20Marie=CC=81thoz?= Date: Tue, 1 Sep 2026 07:53:40 +0200 Subject: [PATCH] fix(certificates): generate instead of versioning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One private key was versioned in this public repository, exposed since 2018: the TLS key of the development server, and its copy in the HAProxy bundle. Both certificates had expired in 2019, which left the HTTPS development server unusable by any modern browser. Certificates are now generated where they are needed: * `scripts/gen-certs` produces the development pair in `.certs/`, using mkcert when available so that browsers accept it without warning, and falling back to openssl elsewhere. The bootstrap calls it, never regenerates an existing pair unless `--force-certs` is given, and skips the step on deploy. * The nginx and HAProxy images generate their own at build time. HAProxy needs a separate stage, its base image ships no openssl; that stage borrows the nginx image the stack already pulls. The tests need nothing: none of them reads a certificate file, and the E2E suite already sets `ignore_https_errors`. `scripts/server` now verifies the pair before starting anything. The check used to sit in the `invenio run` arguments, after the celery worker had been spawned, and the cleanup trap was registered later still, so a missing pair left an orphaned worker behind. Its `getopt` call is also gone. The result was never consumed, but it did reject unknown options on GNU getopt, so the `(*) break` of the parsing loop becomes an error instead of dropping that check. The one in the bootstrap stays for the same reason, and gains `force-certs`: without it GNU getopt rejects the new option. And it gains `--help`, like the other scripts. Its option loop moves ahead of the virtual environment check, so that the help answers outside one, as `setup --help` already does. Co-Authored-By: Johnny MariƩthoz Co-Authored-By: Claude Opus 5 (1M context) --- .dockerignore | 2 + .gitignore | 3 ++ INSTALL.md | 22 +++++++++ docker/haproxy/Dockerfile | 13 ++++- docker/haproxy/haproxy_cert.pem | 83 -------------------------------- docker/nginx/Dockerfile | 10 +++- docker/nginx/conf.d/default.conf | 5 +- docker/nginx/test.crt | 31 ------------ docker/nginx/test.key | 52 -------------------- scripts/bootstrap | 12 ++++- scripts/gen-certs | 58 ++++++++++++++++++++++ scripts/server | 68 +++++++++++++++++--------- 12 files changed, 164 insertions(+), 195 deletions(-) delete mode 100644 docker/haproxy/haproxy_cert.pem delete mode 100644 docker/nginx/test.crt delete mode 100644 docker/nginx/test.key create mode 100755 scripts/gen-certs diff --git a/.dockerignore b/.dockerignore index f24d25e19a..93997c19c8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -19,3 +19,5 @@ docker-compose-dev.yml Procfile* ui/node_modules + +.certs diff --git a/.gitignore b/.gitignore index 7c6dc791b2..8ddce050ac 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,6 @@ tests/data/documents_items_lofi_items.json # Files /data/files + +# Development TLS pair, generated by scripts/gen-certs +/.certs/ diff --git a/INSTALL.md b/INSTALL.md index e4e61c62ba..ac1f155c74 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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): diff --git a/docker/haproxy/Dockerfile b/docker/haproxy/Dockerfile index 9f56545955..e21d17acd6 100644 --- a/docker/haproxy/Dockerfile +++ b/docker/haproxy/Dockerfile @@ -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 diff --git a/docker/haproxy/haproxy_cert.pem b/docker/haproxy/haproxy_cert.pem deleted file mode 100644 index ec578cdaa4..0000000000 --- a/docker/haproxy/haproxy_cert.pem +++ /dev/null @@ -1,83 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFQjCCAyoCCQCaGdyzrli5PzANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJD -SDEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEKMAgGA1UECgwBLjEKMAgGA1UECwwB -LjESMBAGA1UEAwwJbG9jYWxob3N0MRAwDgYJKoZIhvcNAQkBFgEuMB4XDTE4MDgy -ODA4Mzc1NFoXDTE5MDgyODA4Mzc1NFowYzELMAkGA1UEBhMCQ0gxCjAIBgNVBAgM -AS4xCjAIBgNVBAcMAS4xCjAIBgNVBAoMAS4xCjAIBgNVBAsMAS4xEjAQBgNVBAMM -CWxvY2FsaG9zdDEQMA4GCSqGSIb3DQEJARYBLjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAJebicw6pb1ICusUgriI1Ftd3/6lUEJ/As6nAQ8nrsK8GeHP -R8Uu6h+xXcOyf9wYUaMhaN/oaSzNHOrM+02rtyN6l7OpP9qZ8kIoaFv/v6XxbYEw -/gSi47YzhNZbL7k5lvO4vLQvS68MoVQKR248xB1WaszOZYGz1+Yw/tM38AurtVlM -kjSQX67GZ3/7EIEBxpvCgUb47FUGFa9zixO1ekuv0+OoW6sPG62CT1CeVi5YiQFv -NlyKhC6sk420m19Iy4wCTwzR/lTlXHmjCBUXTLiqtopEKSecsQULQCcu7GrXa8WC -dKokpAB8OBkYVIoyUnEL01W6WJ8sx1RdIZNvG+P7oYQcwcYegqSy3i62wtmrHdNG -4MVQVdTpbNjndDReEDBXqVbpvUVfDCO2Klrc0Sk+8mzYLOb6AQsTajr911z1kyUH -50if4FGH9CQZee/vcKsZs26LzC3jBTh9vVSFPXbD4aVzMj2szUh1UKhzZzlv55tU -R+Ht4GHn910ily5dVX1/VCn6tBb8drPIIzdYfoR3eDh+6TfIhlOhUI/Ftp9+X2n4 -70MV7NHsWZSCiINOIwpBxpz4X+64SJUGecMStzZA5YZEcIHlAY/Gaod7ilAwpCGJ -lagLat3OUtXjgU1qvKh41ICt5/QAMglLxSobWiAKq4vHH8CHsxJ79Kw/cTUZAgMB -AAEwDQYJKoZIhvcNAQELBQADggIBAHV5+5G8E2Fcc/SnIg2XFv8LslLKIhJG5amb -Y9+sXa5PL+mRlRx9Ckrh00tXKY68sfv+ESVf44FedeBKYQCQGJNqCbmFpozcYKgI -y+6lnZy73ayjH7aldhq6JbnBVSXKjT3IKAEkhPqTjpaGwIMkj1GhbTUxWfUza3nO -w6gfNbD2MP0PW2ZYSInGYab48UurPcbgpp37piJwS4WFFyywi2pDcIkZ/vGa/T7R -5b3KP5Egh26A+X+CdyuafYqnwT+tk5/FXI4kDZnWpkBNyTeL5GJdRNXp7doCiw7+ -4eNOiu3kl5zNGGypuf6pLP+yirH01310x19LEC1UqwJmQiAq79k5lcHTBCnxSc+U -FzossgEkuLoWFOAcvXK5EIjqqFaapbKHksbPeMp2xTcmYXoAsld8z1/Xsan393nj -DShbRYol6dyXtmgRTgONtUEt0MMXfsism0JpvcCaM2+bJG5Vaki4eFN5C6XGeHt6 -KHsni/G3pM0PcjLlj5CmC8MngndOLClq+TyCGeCPycRYrQPIBbt87Qdp3V34xcmF -EnAmtQsZY2Yf0wbcG2NhbJrROO10uyrGuRY+49ZkNS9EEoh0keB6nEzpaOWIvM1n -uCj68k/F/mSwJUe4siKz7U9MHjsNsQ03h8iLNRDs7fYRcHaE0Ym/k4cL1QuRBxOf -LT+QbkTl ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -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 -4vE5Ov5keKrEY2UToi8JDGges8iwOeQ= ------END PRIVATE KEY----- diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile index 31d7775e0f..5711d7b6e5 100644 --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -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 diff --git a/docker/nginx/conf.d/default.conf b/docker/nginx/conf.d/default.conf index 97115262ca..4e2f8eb23b 100644 --- a/docker/nginx/conf.d/default.conf +++ b/docker/nginx/conf.d/default.conf @@ -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; diff --git a/docker/nginx/test.crt b/docker/nginx/test.crt deleted file mode 100644 index f1a5f974f0..0000000000 --- a/docker/nginx/test.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFQjCCAyoCCQCaGdyzrli5PzANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJD -SDEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEKMAgGA1UECgwBLjEKMAgGA1UECwwB -LjESMBAGA1UEAwwJbG9jYWxob3N0MRAwDgYJKoZIhvcNAQkBFgEuMB4XDTE4MDgy -ODA4Mzc1NFoXDTE5MDgyODA4Mzc1NFowYzELMAkGA1UEBhMCQ0gxCjAIBgNVBAgM -AS4xCjAIBgNVBAcMAS4xCjAIBgNVBAoMAS4xCjAIBgNVBAsMAS4xEjAQBgNVBAMM -CWxvY2FsaG9zdDEQMA4GCSqGSIb3DQEJARYBLjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAJebicw6pb1ICusUgriI1Ftd3/6lUEJ/As6nAQ8nrsK8GeHP -R8Uu6h+xXcOyf9wYUaMhaN/oaSzNHOrM+02rtyN6l7OpP9qZ8kIoaFv/v6XxbYEw -/gSi47YzhNZbL7k5lvO4vLQvS68MoVQKR248xB1WaszOZYGz1+Yw/tM38AurtVlM -kjSQX67GZ3/7EIEBxpvCgUb47FUGFa9zixO1ekuv0+OoW6sPG62CT1CeVi5YiQFv -NlyKhC6sk420m19Iy4wCTwzR/lTlXHmjCBUXTLiqtopEKSecsQULQCcu7GrXa8WC -dKokpAB8OBkYVIoyUnEL01W6WJ8sx1RdIZNvG+P7oYQcwcYegqSy3i62wtmrHdNG -4MVQVdTpbNjndDReEDBXqVbpvUVfDCO2Klrc0Sk+8mzYLOb6AQsTajr911z1kyUH -50if4FGH9CQZee/vcKsZs26LzC3jBTh9vVSFPXbD4aVzMj2szUh1UKhzZzlv55tU -R+Ht4GHn910ily5dVX1/VCn6tBb8drPIIzdYfoR3eDh+6TfIhlOhUI/Ftp9+X2n4 -70MV7NHsWZSCiINOIwpBxpz4X+64SJUGecMStzZA5YZEcIHlAY/Gaod7ilAwpCGJ -lagLat3OUtXjgU1qvKh41ICt5/QAMglLxSobWiAKq4vHH8CHsxJ79Kw/cTUZAgMB -AAEwDQYJKoZIhvcNAQELBQADggIBAHV5+5G8E2Fcc/SnIg2XFv8LslLKIhJG5amb -Y9+sXa5PL+mRlRx9Ckrh00tXKY68sfv+ESVf44FedeBKYQCQGJNqCbmFpozcYKgI -y+6lnZy73ayjH7aldhq6JbnBVSXKjT3IKAEkhPqTjpaGwIMkj1GhbTUxWfUza3nO -w6gfNbD2MP0PW2ZYSInGYab48UurPcbgpp37piJwS4WFFyywi2pDcIkZ/vGa/T7R -5b3KP5Egh26A+X+CdyuafYqnwT+tk5/FXI4kDZnWpkBNyTeL5GJdRNXp7doCiw7+ -4eNOiu3kl5zNGGypuf6pLP+yirH01310x19LEC1UqwJmQiAq79k5lcHTBCnxSc+U -FzossgEkuLoWFOAcvXK5EIjqqFaapbKHksbPeMp2xTcmYXoAsld8z1/Xsan393nj -DShbRYol6dyXtmgRTgONtUEt0MMXfsism0JpvcCaM2+bJG5Vaki4eFN5C6XGeHt6 -KHsni/G3pM0PcjLlj5CmC8MngndOLClq+TyCGeCPycRYrQPIBbt87Qdp3V34xcmF -EnAmtQsZY2Yf0wbcG2NhbJrROO10uyrGuRY+49ZkNS9EEoh0keB6nEzpaOWIvM1n -uCj68k/F/mSwJUe4siKz7U9MHjsNsQ03h8iLNRDs7fYRcHaE0Ym/k4cL1QuRBxOf -LT+QbkTl ------END CERTIFICATE----- diff --git a/docker/nginx/test.key b/docker/nginx/test.key deleted file mode 100644 index 5df95f4924..0000000000 --- a/docker/nginx/test.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCXm4nMOqW9SArr -FIK4iNRbXd/+pVBCfwLOpwEPJ67CvBnhz0fFLuofsV3Dsn/cGFGjIWjf6GkszRzq -zPtNq7cjepezqT/amfJCKGhb/7+l8W2BMP4EouO2M4TWWy+5OZbzuLy0L0uvDKFU -CkduPMQdVmrMzmWBs9fmMP7TN/ALq7VZTJI0kF+uxmd/+xCBAcabwoFG+OxVBhWv -c4sTtXpLr9PjqFurDxutgk9QnlYuWIkBbzZcioQurJONtJtfSMuMAk8M0f5U5Vx5 -owgVF0y4qraKRCknnLEFC0AnLuxq12vFgnSqJKQAfDgZGFSKMlJxC9NVulifLMdU -XSGTbxvj+6GEHMHGHoKkst4utsLZqx3TRuDFUFXU6WzY53Q0XhAwV6lW6b1FXwwj -tipa3NEpPvJs2Czm+gELE2o6/ddc9ZMlB+dIn+BRh/QkGXnv73CrGbNui8wt4wU4 -fb1UhT12w+GlczI9rM1IdVCoc2c5b+ebVEfh7eBh5/ddIpcuXVV9f1Qp+rQW/Haz -yCM3WH6Ed3g4fuk3yIZToVCPxbaffl9p+O9DFezR7FmUgoiDTiMKQcac+F/uuEiV -BnnDErc2QOWGRHCB5QGPxmqHe4pQMKQhiZWoC2rdzlLV44FNaryoeNSAref0ADIJ -S8UqG1ogCquLxx/Ah7MSe/SsP3E1GQIDAQABAoICABytAsdqYGcI9We5nXAiEWim -vPOBeUjalUFZPU4Mh3W19n9LoZueYBJWjhGx9ZU/1FEyxI91ek9LDKks1GvAiwde -7MrCvJ0weS4iTnr4SNuuarQA/EsMNF4GKJ4S6VfzmKXIz4iVWY9mqC0pNJGZVM7n -xZOvhmueBLw5kDmLXxAWDlsxrIXcuPxp8rG2X0zHdTWI+MLf2nPKjtd811WHAfbG -7yEkexJD7UxIVZA+IrbpcCChVHr0iEP7yH0JKEpBjp6Q4TrfjWepanBncePrYEud -nqFQeyhO7vyxQBUpPvaGgI//OOnlvE+LL2EvxO3OzuErB3IzqQeShLFkXDFy1qdi -K0wCM5zpT+AofIPSG4v458R50Wu2pOcRFsTPepO8Y2m10BSni2Iji3lTGNcInHbZ -7t7RBo/0iftKdsUqq6M5B30kWdcWBF+pIPf3oUJqkhwSWtFBSHQMFVIyYQk+/NN5 -dLmzLCdCIMwDwIDneFT8QDTqMBTq+NcHv83vnL3pvMdaJBUPtBFmJjDflOjscN/P -3V/hRUC60LtuABEbKaG0iAImDQ7R2blNW4tStYX/u8W0XJwxiHF23GpvMJVZfIuZ -+N8DyIJ8Lo8ct2IkxOxw8peXva8ClBWwodnWfDW41rRhEqAFSrNU68X7kAO/8rTj -pox2qZhy38O1o15BBZgBAoIBAQDIe3hc5zEUyxOhOswZ03b0w9x+V6pmyGSo4+1B -vAzoXCPPhMToFPTKhq9d3/mfqi90+xKv+8nLkvT3H7M2oFqXcZRfzyAEFCNwkH2c -ujI4eSAIhvzY1wH3EH7B1vhSYAiI1Mi27bzLtEOzkNerF2hin4bxO03iP0ZgbJS0 -1jXDGZGyEvrtJm8IhFOJ5O2ir1Nc9t5rJIjhYE0Lqsv1EH443ly2Dn4UVfJeaGo0 -W24me8XlsSEkd1U2GLROK0Pd0Rc0J78M0S9DI+aUxLb7TTA+XrNRUJv8G+SWdJEc -qXI0vpymNOCs/m6ttWBMBhNkwfkxrKy+A5DDZ4cs3lpGBFxZAoIBAQDBl0FpyOwr -Z5KMYPZtCTLfk+0AwIDECTtCesFjex+iKeLfVtPFBd/Cv4Jq6kN4ZF0y14hrUrcD -w8Du5ayshCAXsZZcHd7Ct35ayKgLYu5Kk0k4L+v0rYYJAWZjsOvFCX32JtGAwIeF -+xILlulRP8gJIGdwvcoXE0/j68/K9k3NT/jgRBdugs/Oc5gMjzuOc34UCZOZkZcx -dwRhXhSJzEbQKpbXq41vHYpnwo+JDsBKj0f93SJR3x7Z4IxNrk9quEB0J+h+l4LH -Lsu9HrFFsICO6iNpejB79K2eJUKKFaRrraTmDotFVmJjHAhCSWM1WIbfCZuaX7Hy -OGiVvEfOOYbBAoIBAQDIcIGnSNIOevbWRMXYsE6HauchPh61And5T+Bez1eMXMrE -6HZBnuobLktYrNyTIICwEXHelvtrWDMoe/4eiHXqYfk5TQkyarwAB8Y+kv4w7hDz -1ZooZdamIEzLGlVNbow52ZwPIGOYulLRzeeCPDX2HI660lMM97wyHX0Ty/MWa5L6 -GfXccoeiWUJ4ZHSeKXJMSNmY/38G7dQ57PpburCnxNGmh5jQVhmq//PnAt5/BVKX -Ud/T6Kc+HbbvTFxfAnSThYWCmAYLt3IKnR5g5Axu3xmjG9fIWE7TihmnMVZom4Zc -uhNDgmI/tE5h09VB8Jlna7+Nfl0WsIQ6fNh+lk+JAoIBAQCojOzjEZCuacrqdjGX -1X55su2SzghOpkr2IrLlznlIU3e4RHy0+dxMTzlZpQWBeMC8V3PrHWVC3djJJyck -PRVEC9aB9W/R0Lkuia3l3Ajk3T/Or8qcicg9Zv6ipZNhRlZpTnrxwq88M9EOTiJu -Hz4m1BO70UJ2vNW9974/VBuBOrPRbveHvFT5xyMHLF4KNPv8rinYdy5QXvNtYuA2 -6aht7ebPeAn4SJwMoxUdm75ILF/d4/7RkZXm1N7Tc4Spwio799MxBPFUQqYYzvRB -XN9RjwGuADjec7YGDyLT3uv85H7B3r/bas7Ww71dGVBLTfx/jHnx+oeQARTt6bQL -FYMBAoIBAFUEJBpH/lsmMFwrgy5m4fccv7U4EZQQ13vFfmwrdZJagNZ5yUqLD2r5 -5rSBYoSKXIhwL4JRAP5TEQWgxZ8oi1P2RKmI1FvgBgqo3rbbOOWM0U6Wc6JrqAad -pvYu22pVNbVDc5Mg6BugK1beBKA5Tc5UkpvOFvyyu5mNHGi6X2bZ9uLdC4CaZjTD -y9HiEJ3Y+c2C0bURrU7DqMUG3NxrIDDybrvO5viZiIfCpfsjHikiNXTYR52QxX2N -gNofht/dcAgLDM23CzIi0fY7W8Bg3dMKUJDTD4cuwFGCcO7Q/lAEiVU3b0+EnO+K -4vE5Ov5keKrEY2UToi8JDGges8iwOeQ= ------END PRIVATE KEY----- diff --git a/scripts/bootstrap b/scripts/bootstrap index 002e735b96..d403c06ae0 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -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 @@ -25,6 +25,7 @@ flags=() deploy=false cmd="uv sync --frozen" ci=false +force_certs=false tgz_file="" while test $# -gt 0 do @@ -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}" ]] @@ -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 diff --git a/scripts/gen-certs b/scripts/gen-certs new file mode 100755 index 0000000000..35c178af3b --- /dev/null +++ b/scripts/gen-certs @@ -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}" diff --git a/scripts/server b/scripts/server index 79e0209a1c..a2bff4b061 100755 --- a/scripts/server +++ b/scripts/server @@ -10,30 +10,31 @@ PROGRAM=`basename $0` set -e +# Print the usage on standard error, as every other message of this script. +usage() { + cat >&2 </dev/null' EXIT + # Start Worker and Beat if $worker; then celery --app rero_ils.celery worker --loglevel ${CELERY_LOG_LEVEL} --beat --scheduler rero_ils.schedulers.DatabaseScheduler & PID_CELERY=$! fi # Start Server -invenio run \ - --cert "$SCRIPT_PATH"/../docker/nginx/test.crt \ - --key "$SCRIPT_PATH"/../docker/nginx/test.key & PID_SERVER=$! - -trap 'kill $PID_CELERY $PID_SERVER &>/dev/null' EXIT +invenio run --cert "${CERT}" --key "${KEY}" & PID_SERVER=$! wait $PID_CELERY $PID_SERVER