diff --git a/.dockerignore b/.dockerignore index 43ef69b62..5c976e079 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,3 +17,5 @@ Procfile* celerybeat-schedule .venv/ + +.certs/ diff --git a/.gitignore b/.gitignore index 81ba9b5ad..6e6afdd71 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,6 @@ celerybeat-schedule* data/wiki/_index/ .python-version + +# Development certificates, generated by scripts/gen-certs +.certs/ diff --git a/INSTALL.md b/INSTALL.md index ecce9692b..d7e26f52f 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -73,6 +73,37 @@ Start the webserver and the celery worker: uv run poe server ``` +The server listens on . Its certificate is generated by +the bootstrap into `.certs/`, which is never versioned. Run `uv run poe certs` +to create it again, or `./scripts/gen-certs --force` to renew it. + +If [mkcert](https://github.com/FiloSottile/mkcert) is installed, the certificate +is signed by a local authority. Install that authority once so that browsers +accept the certificate without warning: + +```console +mkcert -install +``` + +This matters for SWITCH edu-ID authentication: the identity provider answers +with an auto-submitted POST, and clicking through a browser warning loses it. + +### SWITCH edu-ID authentication + +SAML authentication is disabled until a service provider key pair is configured. +The certificate identifies the instance to the identity providers, so it has to +be registered in the SWITCH edu-ID resource registry: it cannot be generated +locally. Provide the pair outside of the repository and reference it in +`invenio.cfg`: + +```python +SHIBBOLETH_SERVICE_PROVIDER_CERTIFICATE = "/path/to/sp-cert.pem" +SHIBBOLETH_SERVICE_PROVIDER_PRIVATE_KEY = "/path/to/sp-key.pem" +``` + +Without them the application runs normally and no edu-ID button is offered on +the login page. + Start a Python shell: ```console diff --git a/MANIFEST.in b/MANIFEST.in index ab714f50f..504c2f8d6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -43,7 +43,7 @@ prune docs/_build recursive-include _sources *.txt recursive-include _static *.css *.js *.png recursive-include data *.crt *.csv *.jpg *.json *.md *.png -recursive-include docker *.cfg *.conf *.crt *.ini *.key *.pem *.sh +recursive-include docker *.cfg *.conf *.ini *.sh recursive-include docs *.bat *.py *.rst *.txt Makefile recursive-include sonar *.babelrc *.eslintignore *.gitkeep *.json *.html *.js *.scss *.css *.png *.jpg *.svg *.po *.pot *.mo *.py *.txt *.woff *.woff2 recursive-include tests *.doc *.json *.pdf *.py *.xml *.jpg *.csv diff --git a/data/idp_certificates/eduid.crt b/data/idp_certificates/eduid.crt index 8c39d0e24..2f18881a7 100644 --- a/data/idp_certificates/eduid.crt +++ b/data/idp_certificates/eduid.crt @@ -1,17 +1,21 @@ -MIIDDDCCAfSgAwIBAgIVALoCDnpVfF76D6WKc9mDL9jtreW1MA0GCSqGSIb3DQEB -CwUAMBMxETAPBgNVBAMMCGVkdWlkLmNoMB4XDTE4MDExNzEyMDg0OFoXDTIxMDEx -NzEyMDg0OFowEzERMA8GA1UEAwwIZWR1aWQuY2gwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCbkUjiqUSI7m8LCNeIW/A3V+frMWd+DPvHzpEwIXLvzDPX -ts6lCGzI3Yo6lNkF7VMUh6mxEXegu3t8AbaOsfrroFijIxMdQ4Pq3Z/Ps2ssa0KA -xkcpHnWP011la1TN7hsxq07Xsat++CHVescJ8zk7Qd+uZmmfW5Kfj5KQhOPoQBMU -LVtak/Kjeolo4Ur1fwHtu8gvTvwrBVMy0wYMeylBmLL73dKYFO2mZLs5lP0FKXTb -mAUPF+WfkOckuExYvetWxLDyiDaxs5j3/KqJsDSiPQq1oGZkqvPfC4NzfCUTsuW5 -RdPierusXpnR4Wd8gj3feS4d6fGrt0arFUYkiyjFAgMBAAGjVzBVMB0GA1UdDgQW -BBR5UYvm41G9T4lRYQnVBamV5QQ2czA0BgNVHREELTArgghlZHVpZC5jaIYfaHR0 -cHM6Ly9lZHVpZC5jaC9pZHAvc2hpYmJvbGV0aDANBgkqhkiG9w0BAQsFAAOCAQEA -OHXDaEWgKwegR4pBMqSw4KmPNCn06Jf5cZ3sVCHKtDGhiVz9JwevujDVo94pcGPJ -XyJdvCn9gCRpheWJIYv94L3Do/eh7/jwWSp93+5vJdbVvE9EcMbooRO2fSIWZg6y -KVavIKLrItH9fUaY8mE1YSTzpAyj/bYHGZzGhd+23gaKZSMU2Oyjc8FwTWjDcZLC -+iUG7QOeK1rigVuP/HWqnawRfV5PQNCumhwvLTfgwj2d7hksXIimdfzUtQ1khGTo -mbpfFePiqKvBj5O5CmwuD3ccg0zFI1TQHUSussK/tOGz54MAyKG8M1txL/aTC92e -Sa95LKJp5+2fy/XNy2hBAg== \ No newline at end of file +MIID6jCCAlKgAwIBAgIUQYBVlHR1BTuCRvxNID/1YViWvXswDQYJKoZIhvcNAQEL +BQAwEzERMA8GA1UEAxMIZWR1aWQuY2gwHhcNMjMxMTAxMTAwMDA0WhcNMzMxMjE4 +MTAwMDA0WjATMREwDwYDVQQDEwhlZHVpZC5jaDCCAaIwDQYJKoZIhvcNAQEBBQAD +ggGPADCCAYoCggGBAJ7otyXaPNpqa2T4sIw3D+G9qdns5fGWYPyKkqkbSwM/xcWY +86JTyG4bNaI9za1mVmX/+JZ3rNjmihHh1pNc8Y4U3sroX4YmpHme7U0o5QzgpXgt +g23NyWGSTKJG4Z4LhoPXvBvcwhTE2wRbcIiiHQExXrmntq6QXafum2eSy0wuQcsd +y+jiJX1shyDC29Epf1ObglfbUYS1GkfBaV9QfsKDbEBWixdALTpubcxmEHONdmNn +8wrr2IPkVod+pBgLGaacRHKt/O7aw3R5FlAOw8KANJMU9MDrp7yhH3HzQPMyc5E8 +HNIbh284NY3etkj0rDtI8Py2DRjMkZeUdkqyCmrskwd0aUXKTOjBT0u3TD4hayuI +UUx7w3Fb3MaEWY16mFVrlw5/6AsRIiFQGbxXwXV41oTxiSBGAYBoux27Db5j6H3X +EseKamk5oH8z/4HqhtzCA5oMCYkwWpf2NXwJks6T378bSw0VSklQXcoBcwPjByyr +ZVHpV+8bOzJdbEfRHQIDAQABozYwNDATBgNVHREEDDAKgghlZHVpZC5jaDAdBgNV +HQ4EFgQUUXM4hK0yc1TiLRbbgkfB6FZ6jyowDQYJKoZIhvcNAQELBQADggGBAJN1 +C/JIa4hfpb7HwdSGQ/fnxRV+m/V1wp5LEZKEd/P1eIjb3ETkUL4wP90a+B22ndJI +F46FqmRwu1KtVufCQE1ptYJ7JqmQEg5v1rHKMzV8CYEIZm9Wlb/dto15TAs/CkQS +Hyzpaeg4cNpruGtiIPUB1q01UM8NEWWU9vgrvwJjj3j3/mJPN6FWtPtZDCnIojED +V3QaeXzxQhPn5UsJuCjWX2Y81AQdVjSkbKAxyy9BWIRV2Ib6FWBZzVQwbjFADZKT +iIortzAVYCrJHHU/c5enI19U6Y8/+xzu97w3STiQLgvlUUhNNgmaYMKYtRWSrqW1 +aaSIQ4tiylNLEb0QdoG03mDZ60WUsRF87ZrlTYU+vMNj3d4dsF70mLK+0IEyKud2 +r/76LQg8AEF6ESX8i3n6IGG3Tt1zeBbVZ4zNtFjvzmef7CYyLsu5MP1QBGI4QLV1 +9rBn+6JszkavM972cTuZTVZ1BU2/g/4ZSq85+XoKRmA2GH+Wega5gX63BIOnAQ== diff --git a/data/idp_certificates/eduidtest.crt b/data/idp_certificates/eduidtest.crt index f72f255b7..1065eb6e1 100644 --- a/data/idp_certificates/eduidtest.crt +++ b/data/idp_certificates/eduidtest.crt @@ -1,17 +1,22 @@ -MIIDIDCCAgigAwIBAgIVANt+in4DKHpdPEdzf7EKsoI+xrUyMA0GCSqGSIb3DQEB -CwUAMBgxFjAUBgNVBAMMDXRlc3QuZWR1aWQuY2gwHhcNMTcxMDMxMDgyNTQzWhcN -MjAxMDMxMDgyNTQzWjAYMRYwFAYDVQQDDA10ZXN0LmVkdWlkLmNoMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAobHkbul8u6SEpc4C3/fkkwGYcthsFQC+ -ILWKrK7D2Mnt/OdZrkFrNVCaDi6dl/T6tN8ESrpCxqG9jUxlgxHr0L2LlN2ilqiB -FkY1zM/OkJa2WarQZ9dkP4dfjLdp2T17MkPZvTCMoHqzyK7WvFCGAa5JxXubKmh+ -KjuNzGA/4Y3Vm+Y8Qs/wbpHeMXVk+Oeke6Qnqs1JfMXIWGD81AYbUjHaBIhF1J5u -8iJe0zTJ8MSuPdMOLEY/4KSWKtT0HM/LRAH4iRKdq+B3Cl+WZGKgd864GQ1HkxHZ -1xx7H1TisWzUIyeeuO1n6MNa9+kcLal2p2C7q+wuC8BWLDXlut+VxwIDAQABo2Ew -XzAdBgNVHQ4EFgQUfGdP74lJsTJldXc4hVRkM5KX6oEwPgYDVR0RBDcwNYINdGVz -dC5lZHVpZC5jaIYkaHR0cHM6Ly90ZXN0LmVkdWlkLmNoL2lkcC9zaGliYm9sZXRo -MA0GCSqGSIb3DQEBCwUAA4IBAQCUBMahAkFjp+9B5fMjqM6ZCj/zC7UgM2DBBxm4 -YQaSSfjNGz1YC0fVDpoH1qVf0m9Ig7tI4QOAipzlwrKgCbB++KTxsXQBtfw2EBbg -hfNphXCU0b855FUirnJWN+T2t3APq68dII3KkRPQ8uSq7JN7Ccm38RzmbQ9gt1RR -4IVqHf/TstovqXC1sf/bDM9IBqvsZyD/043i1uwOQyiuZyOUS5J15BnKmo2N0NLz -9E5IDu+3NpnlwBcqCaXPZhYSoTGZJfPE8MPzlP3tDoElEK07dOzyOF8mlnioqd0q -OKNguza3bi3RRsZUlDAJHSGHm+Fc3QLUPCDLZc0+OKcQbThC \ No newline at end of file +MIIEETCCAnmgAwIBAgIUPdBt/Z2aCedplRF6Qy/8E7lPPY8wDQYJKoZIhvcNAQEL +BQAwGDEWMBQGA1UEAwwNdGVzdC5lZHVpZC5jaDAeFw0yMzA5MTMwNjIyMjNaFw0z +MzA5MTAwNjIyMjNaMBgxFjAUBgNVBAMMDXRlc3QuZWR1aWQuY2gwggGiMA0GCSqG +SIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbv+wTgRUGXCKCpoiJR0cdR5ik8DZtytWi +t06KA9oAgY2bdfMVgRKqUoALQkOoEeZg7pOzuvN6PgKKikKWUbMZozYEpKr6GAxL +VY5ZBieWQkI/wr40YZhU8tnwpU/IIekXzNziG8jkGflbWBpQ2cUN9GQ8/RwkDj0u +HeiivW4pgVHM4i9uFA8ZZnJ1XBBPyCUKTjsg94M8MTrfBe1XPzkjpYtP6czF5oJj +LAyylKWBGnuR4BHdgisKSYQPS4qg8s7S4VQA0Xa0CpBGIyopnhDBUDP23S9pVt1R +SbvV1tB4rnpyPHEmiW/vR4/pFBm+jv6YLCrMOt7UFNeLtaoA5XbXw53bBUh8Lde3 +FP8QvoPE6rj1XY7LluY0Sb4JvHjJaX9kG4Fmjknx7fm0QR2FXVcoU1utxt0R1rlr +ov1GN3P1fhy6ADU9pwd7XXcDaIjJyeTkbld2YJs3iS/ZhD9PoJGJrPwdC9eFL5jf +ao8RwuR1y4Gshou32XG6Os5o0r0wjrECAwEAAaNTMFEwHQYDVR0OBBYEFGAAmC/z +8UQCszlkME/KHZvOgNW5MB8GA1UdIwQYMBaAFGAAmC/z8UQCszlkME/KHZvOgNW5 +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggGBAARtn9tUlkxwxecP +J0zhI58SzpdcoE1+Qo9qF+sWIdzIAZ8ev8LBHfHhpXjOBlQQv8hIhs8U9hTJx1c+ +Jq94ftTSQ96FTZ9BmDaw7iQmorDSZeMxss6RXROF0ORJJ9kdAhTyMJj3zAfPlB4/ +32WbFlS9AtZdEfLdHNEmlxKtWFnaNasYFUh8oMpkaXnVToAp0H0qpZbzfwpI85/4 +S1zwwM62v+JyanUWX8BUSlB7L5cBa+6BRiUMkr0OFk0Vx9nM1GOkEfHttSdq7OnW +0QeMdXYo417/DsCDxrD9A/SH1a+w3odYmbjl9lFr83wS8nApZtWG+7matS4CqI6a +QjPRt0yZKQqc3vA5qvGWjQAITaNXlLVIRtTDP2Qs3cU0UUA0vqYX/CkLnGHCppqd +GHLxP3n9LluZJs+OmArbgdifGm+7xgZMbmuAnoDCsTxNoSYqxHgU9vuETyjXTkFC +2Wy9XopqcmwoW7fP44EShC4kp6sHgl+u2Ms+kjHGVhkvt+fyLw== diff --git a/data/idp_certificates/idp.crt b/data/idp_certificates/idp.crt deleted file mode 100644 index 2d8cab416..000000000 --- a/data/idp_certificates/idp.crt +++ /dev/null @@ -1,17 +0,0 @@ -MIIDIDCCAgigAwIBAgIVANt+in4DKHpdPEdzf7EKsoI+xrUyMA0GCSqGSIb3DQEB -CwUAMBgxFjAUBgNVBAMMDXRlc3QuZWR1aWQuY2gwHhcNMTcxMDMxMDgyNTQzWhcN -MjAxMDMxMDgyNTQzWjAYMRYwFAYDVQQDDA10ZXN0LmVkdWlkLmNoMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAobHkbul8u6SEpc4C3/fkkwGYcthsFQC+ -ILWKrK7D2Mnt/OdZrkFrNVCaDi6dl/T6tN8ESrpCxqG9jUxlgxHr0L2LlN2ilqiB -FkY1zM/OkJa2WarQZ9dkP4dfjLdp2T17MkPZvTCMoHqzyK7WvFCGAa5JxXubKmh+ -KjuNzGA/4Y3Vm+Y8Qs/wbpHeMXVk+Oeke6Qnqs1JfMXIWGD81AYbUjHaBIhF1J5u -8iJe0zTJ8MSuPdMOLEY/4KSWKtT0HM/LRAH4iRKdq+B3Cl+WZGKgd864GQ1HkxHZ -1xx7H1TisWzUIyeeuO1n6MNa9+kcLal2p2C7q+wuC8BWLDXlut+VxwIDAQABo2Ew -XzAdBgNVHQ4EFgQUfGdP74lJsTJldXc4hVRkM5KX6oEwPgYDVR0RBDcwNYINdGVz -dC5lZHVpZC5jaIYkaHR0cHM6Ly90ZXN0LmVkdWlkLmNoL2lkcC9zaGliYm9sZXRo -MA0GCSqGSIb3DQEBCwUAA4IBAQCUBMahAkFjp+9B5fMjqM6ZCj/zC7UgM2DBBxm4 -YQaSSfjNGz1YC0fVDpoH1qVf0m9Ig7tI4QOAipzlwrKgCbB++KTxsXQBtfw2EBbg -hfNphXCU0b855FUirnJWN+T2t3APq68dII3KkRPQ8uSq7JN7Ccm38RzmbQ9gt1RR -4IVqHf/TstovqXC1sf/bDM9IBqvsZyD/043i1uwOQyiuZyOUS5J15BnKmo2N0NLz -9E5IDu+3NpnlwBcqCaXPZhYSoTGZJfPE8MPzlP3tDoElEK07dOzyOF8mlnioqd0q -OKNguza3bi3RRsZUlDAJHSGHm+Fc3QLUPCDLZc0+OKcQbThC diff --git a/docker/haproxy/Dockerfile b/docker/haproxy/Dockerfile index 9f5654595..853431dfc 100644 --- a/docker/haproxy/Dockerfile +++ b/docker/haproxy/Dockerfile @@ -1,7 +1,20 @@ # SPDX-FileCopyrightText: Fondation RERO+ # SPDX-License-Identifier: AGPL-3.0-or-later +# Self signed certificate generated at build time, so that no private key is +# versioned. Built in a separate stage because the haproxy image ships no +# openssl command. In production the external certificate is provided by the +# deployment, through Let's Encrypt. +FROM alpine:3 AS certificate +RUN apk add --no-cache openssl \ + && openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \ + -keyout /cert.key -out /cert.crt \ + -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" \ + && cat /cert.crt /cert.key > /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 +# HAProxy expects the certificate and its key concatenated in a single file. +COPY --from=certificate /cert.pem /usr/local/etc/cert.pem diff --git a/docker/haproxy/haproxy.cfg b/docker/haproxy/haproxy.cfg index 9a5e245dd..dc2f4d47a 100644 --- a/docker/haproxy/haproxy.cfg +++ b/docker/haproxy/haproxy.cfg @@ -41,6 +41,12 @@ frontend in-stats stats uri / stats hide-version +# "ssl verify none" is deliberate on both backends: this configuration only +# serves the local demonstration stack, where the frontend certificate is a self +# signed one generated when its image is built. The link stays inside the docker +# network, so it is encrypted but not authenticated. Any deployment where this +# link crosses a machine boundary should sign the frontend certificate with an +# internal authority and switch to "verify required ca-file ...". backend ssl_app balance leastconn http-check disable-on-404 diff --git a/docker/haproxy/haproxy_cert.pem b/docker/haproxy/haproxy_cert.pem deleted file mode 100644 index c4c2ebd74..000000000 --- a/docker/haproxy/haproxy_cert.pem +++ /dev/null @@ -1,83 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFQjCCAyoCCQCb5O/0dce0ojANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJD -SDEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEKMAgGA1UECgwBLjEKMAgGA1UECwwB -LjESMBAGA1UEAwwJbG9jYWxob3N0MRAwDgYJKoZIhvcNAQkBFgEuMB4XDTE5MDQw -NDE0MzkxOFoXDTIwMDQwMzE0MzkxOFowYzELMAkGA1UEBhMCQ0gxCjAIBgNVBAgM -AS4xCjAIBgNVBAcMAS4xCjAIBgNVBAoMAS4xCjAIBgNVBAsMAS4xEjAQBgNVBAMM -CWxvY2FsaG9zdDEQMA4GCSqGSIb3DQEJARYBLjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAMdOkUXeK1UPD+eyvfJTCCu09U09DeRH0xHso2ZXHzNHAulc -TF8Yx9kkTC6IHTSZX9Of77k/SoH3V2Tii1fBa06N3Xa8PAZ8pBxPsxiH8czdabBU -WYxgZ/n8Y/K5DvJRyspZB/2SxGMRXnRVnUgqXti3MFfHvU+Z9PXcgBOPi0jRfxqO -EznJjyXoLPY1Valdqbr3Ew97tpksxKDXCoKHyq0GoGQZC2iYMl8pxEzVYnGZ3IYY -65bX6/49SuVxU1ECd79o7ozrHiVMNDQRfeQDJknG2HOmDiDvgU/MgCkJhAtg7Oz0 -Z1x7rSlY7zFBDmalDNzmRZ6dKZcvdjJv+JDPZsnOrvPc9fMOzAPHxKSNsQm9+5/I -bhj6iHLntb9cqhsB1sUgX+OcjERZihJDrgIym7cKm1SLOIyn7SUQfuPKRvsA9GOc -NmhMV+uIxFsDXK6yxdplSKvYXRsqGr9oD1txBjKZHAIqHM/2/UWwXgZr98t2iz9G -EZX4PXZVgokrDYryFJIEFttvRdeX4uqy+0kMTzW21ju1BqXIjFHTvR4dXaLgCBZW -cFCinWGp+G5d3b8w/myGYDQNDP7RjzDbV7yJKPMMPdq9/LehoTz9WUWetsPgj+Ye -aar15qanLDuZqUr0gmGMKlj05XwgE1fc3tvl/Nxv7nOmXld5agjfbQ75+GxvAgMB -AAEwDQYJKoZIhvcNAQELBQADggIBADG3AV9WDlZYsRdC+Le2YLwRTM2v2/mZ1Cgq -s8oktgk3upZI/6L09s2rbIHpEurEucwMxlEvZTDthKOafVYXJedLT4IFT9ezaP58 -eBLRS+LUlpKO7IHBEPe6FP5kXWiLpJlGgIMeOhJbb65x7eojbSOhATNo1vPRpg5w -xLKX+s0keAGUyuKvOkE+FSInT87zQeH29p4CqBxPZLJZfCzLb4FIgCz9FIZ7KEIW -h26QSBfboBIJ+H5RPWO8WsaH0jgFkTcmHs5oOVsZv3qQUVPJfEfyG007vcviw1ok -4uwNOv22KJDI/VBJ5UbzJOcD0BW0EMw7/1pcJ3itm17YvmAReTIOwDa0VMxGji1d -6PEni1EKiJyiKuEqluEbcWLV4A7LbqItxOobVVcZJPB/NwlV/vKwAzJJ4Q7kItSn -IpaDarV+P69gUWKGjWQCFLU3LtzUiK/jbGfAWa7V4PZ1M2q35wWQMzmj0jjidFFn -HxOc+aeq63+2TFaCh0D1XzG+dmtpPMO/nax6Ak0QSIBYjTvGjXhJM3vLIJZBdvmM -XVdZUoE6S0ha5LPK9xryKF20wIq0A/rZ/c3u2VnDPQKeMs6tCxhQcGIaGDHWSAFh -dO4oouQYaH5vDH5eESrwM22PJvjd5Lykv2l/oBFpMyrYkDh4Nqm3lqY/kB+W2U7m -ZnihdMEE ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -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 -ox5oXud+Wan4s1Aw/fVhc1akHSDfH2En ------END PRIVATE KEY----- diff --git a/docker/nginx/Dockerfile b/docker/nginx/Dockerfile index 1e53bf526..8a06257f6 100644 --- a/docker/nginx/Dockerfile +++ b/docker/nginx/Dockerfile @@ -5,5 +5,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 certificate generated at build time, so that no private key is +# versioned. It is only used for the connection from the load balancer, which +# does not verify it. +RUN openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \ + -keyout /etc/ssl/private/test.key -out /etc/ssl/certs/test.crt \ + -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,DNS:frontend,IP:127.0.0.1" diff --git a/docker/nginx/sp.key b/docker/nginx/sp.key deleted file mode 100644 index f70fa338e..000000000 --- a/docker/nginx/sp.key +++ /dev/null @@ -1,40 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -zUkmF4YN7sH1IeURI149LUha ------END PRIVATE KEY----- diff --git a/docker/nginx/sp.pem b/docker/nginx/sp.pem deleted file mode 100644 index 1d5062d0d..000000000 --- a/docker/nginx/sp.pem +++ /dev/null @@ -1,93 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 11240779440452688558 (0x9bff45584ab25aae) - Signature Algorithm: sha256WithRSAEncryption - Issuer: CN=sonar.ch - Validity - Not Before: Jul 2 13:27:23 2019 GMT - Not After : Jul 2 13:27:23 2022 GMT - Subject: CN=sonar.ch - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (3072 bit) - Modulus: - 00:a3:44:76:cd:9e:5e:51:64:1f:11:ee:fe:a3:0f: - 19:c8:c7:e6:e9:c1:ec:58:a9:cb:b0:45:42:d7:b3: - 78:f8:6f:23:0f:ab:23:7c:cd:71:14:80:b4:55:0b: - dd:a4:6a:d3:39:a9:bc:95:bd:d9:d5:aa:4c:25:24: - 6b:3e:d4:2a:e8:61:e3:c2:55:a0:e0:7d:fd:b7:40: - 9f:94:4e:52:f1:2d:09:3d:c8:32:4c:f1:7b:d9:5d: - c2:ed:23:a1:a9:f0:57:da:e5:a2:65:87:bf:89:1e: - 44:c7:2b:dc:6f:b7:3c:27:f2:df:85:f3:c9:7b:e7: - 75:2d:ef:85:2e:3c:97:35:17:5a:39:3c:ab:27:b5: - aa:38:7c:7c:88:48:b3:91:e0:fb:88:f0:fe:e0:64: - 16:53:c5:28:fc:b3:a5:9f:70:ed:cd:9e:af:a4:fe: - cb:9e:52:57:53:f0:ca:3a:b5:f3:05:bc:13:e6:e1: - d5:1c:9d:52:74:55:92:60:ad:13:86:af:e6:21:90: - 50:f2:78:57:c2:49:94:dd:1c:12:15:fc:ae:52:c3: - 15:5f:a3:09:f3:79:95:eb:6c:eb:03:a0:60:bd:65: - 86:32:7b:77:ac:3c:c8:ad:ce:2f:7a:14:07:96:58: - 8f:d5:75:28:44:d1:f2:42:4f:84:ea:7d:32:ad:cc: - ff:f7:8e:1a:20:ba:01:e5:2f:b5:7c:66:16:4b:82: - 27:80:c3:8a:81:5f:cc:f1:d2:54:c4:00:8c:14:80: - d9:2e:5b:9c:4a:e7:6f:fb:61:7e:30:ea:25:89:0b: - c5:5c:6c:b6:4e:62:c7:f7:bd:5d:33:d8:2c:a0:e9: - 4f:e4:6a:ad:bc:56:ac:f3:b6:de:c6:99:9d:ad:4d: - 75:d6:fd:ea:38:a2:c3:c0:16:72:8a:ed:08:ae:4a: - 56:ac:2b:51:a1:05:ef:e1:16:57:0c:ce:de:6c:6e: - b4:ce:d2:fe:40:d3:18:10:86:4c:4c:79:84:4a:7d: - f8:d0:03:75:53:67:b3:af:39:f3 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Subject Alternative Name: - DNS:sonar.ch, URI:https://sonar.ch/shibboleth - X509v3 Subject Key Identifier: - 4D:BA:B6:DD:18:94:97:53:4F:5B:C9:D0:35:62:A0:1C:17:09:2A:A5 - Signature Algorithm: sha256WithRSAEncryption - 2e:03:5c:27:12:e9:31:e3:ad:75:8c:18:fb:c2:c5:7f:67:52: - fc:98:e3:59:90:fa:ce:50:4e:ff:0f:5e:0b:4d:e0:fd:b7:19: - e7:15:e7:fa:b4:c5:43:f9:73:57:fa:6e:96:9b:d6:39:a0:ab: - 03:ac:70:42:d8:d7:31:de:00:fc:5a:0b:6f:7e:30:14:3e:91: - e4:70:f5:ca:2c:1e:4c:e7:1d:51:2e:bf:ff:cb:66:dc:a2:e8: - 3f:e1:bc:e1:7c:58:32:62:c9:83:53:b1:ba:01:5d:62:18:ba: - e0:a7:c8:d0:97:70:06:66:b5:68:e9:76:eb:83:04:f2:03:1a: - c1:92:ee:a9:40:4e:73:c2:20:15:03:75:97:ef:1f:cc:e3:58: - ee:04:59:36:fa:48:d9:e2:9a:67:5d:f1:99:8d:78:6d:5c:29: - af:f0:27:fc:63:78:ca:20:13:3e:30:c2:9d:5b:94:73:9d:ec: - f5:17:8c:4c:16:d3:c1:24:6a:0c:78:82:61:d4:ba:1e:88:7c: - 89:5f:35:b8:2a:3e:73:48:ea:ec:cb:a1:af:8f:0e:b8:72:6d: - 09:8d:15:29:82:ec:23:74:1a:31:41:43:f7:5e:e6:ec:ea:ad: - 4c:99:f5:44:07:0a:9d:a3:17:da:c7:9f:38:2e:90:c7:0b:fe: - 4d:74:ef:33:d4:a2:2e:d1:cb:d7:cb:e4:54:3e:cc:f9:63:9f: - f0:29:25:c5:50:f7:31:44:24:ff:73:fe:c9:8b:3e:01:6a:08: - cd:d1:80:81:90:b4:09:30:a9:34:be:77:2d:81:69:b3:7b:3e: - e8:0a:85:76:c4:fa:53:cb:3d:d0:f5:89:a3:de:88:81:9b:cb: - 1d:da:bb:5f:93:e1:34:7c:10:f4:a8:f7:6f:2f:31:22:c0:d8: - bd:5a:67:c0:5e:31:04:ba:5b:07:08:38:9e:53:fc:f4:6b:bd: - 94:64:26:cc:02:c8:ed:d4:a6:d8:0c:96:4a:f0:46:a1:c3:be: - 9c:be:bb:2c:08:a6 ------BEGIN CERTIFICATE----- -MIID/DCCAmSgAwIBAgIJAJv/RVhKslquMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNV -BAMTCHNvbmFyLmNoMB4XDTE5MDcwMjEzMjcyM1oXDTIyMDcwMjEzMjcyM1owEzER -MA8GA1UEAxMIc29uYXIuY2gwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIB -gQCjRHbNnl5RZB8R7v6jDxnIx+bpwexYqcuwRULXs3j4byMPqyN8zXEUgLRVC92k -atM5qbyVvdnVqkwlJGs+1CroYePCVaDgff23QJ+UTlLxLQk9yDJM8XvZXcLtI6Gp -8Ffa5aJlh7+JHkTHK9xvtzwn8t+F88l753Ut74UuPJc1F1o5PKsntao4fHyISLOR -4PuI8P7gZBZTxSj8s6WfcO3Nnq+k/sueUldT8Mo6tfMFvBPm4dUcnVJ0VZJgrROG -r+YhkFDyeFfCSZTdHBIV/K5SwxVfownzeZXrbOsDoGC9ZYYye3esPMitzi96FAeW -WI/VdShE0fJCT4TqfTKtzP/3jhogugHlL7V8ZhZLgieAw4qBX8zx0lTEAIwUgNku -W5xK52/7YX4w6iWJC8VcbLZOYsf3vV0z2Cyg6U/kaq28Vqzztt7GmZ2tTXXW/eo4 -osPAFnKK7QiuSlasK1GhBe/hFlcMzt5sbrTO0v5A0xgQhkxMeYRKffjQA3VTZ7Ov -OfMCAwEAAaNTMFEwMAYDVR0RBCkwJ4IIc29uYXIuY2iGG2h0dHBzOi8vc29uYXIu -Y2gvc2hpYmJvbGV0aDAdBgNVHQ4EFgQUTbq23RiUl1NPW8nQNWKgHBcJKqUwDQYJ -KoZIhvcNAQELBQADggGBAC4DXCcS6THjrXWMGPvCxX9nUvyY41mQ+s5QTv8PXgtN -4P23GecV5/q0xUP5c1f6bpab1jmgqwOscELY1zHeAPxaC29+MBQ+keRw9cosHkzn -HVEuv//LZtyi6D/hvOF8WDJiyYNTsboBXWIYuuCnyNCXcAZmtWjpduuDBPIDGsGS -7qlATnPCIBUDdZfvH8zjWO4EWTb6SNnimmdd8ZmNeG1cKa/wJ/xjeMogEz4wwp1b -lHOd7PUXjEwW08Ekagx4gmHUuh6IfIlfNbgqPnNI6uzLoa+PDrhybQmNFSmC7CN0 -GjFBQ/de5uzqrUyZ9UQHCp2jF9rHnzgukMcL/k107zPUoi7Ry9fL5FQ+zPljn/Ap -JcVQ9zFEJP9z/smLPgFqCM3RgIGQtAkwqTS+dy2BabN7PugKhXbE+lPLPdD1iaPe -iIGbyx3au1+T4TR8EPSo928vMSLA2L1aZ8BeMQS6WwcIOJ5T/PRrvZRkJswCyO3U -ptgMlkrwRqHDvpy+uywIpg== ------END CERTIFICATE----- diff --git a/docker/nginx/test.crt b/docker/nginx/test.crt deleted file mode 100644 index 6fc0b57a7..000000000 --- a/docker/nginx/test.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFQjCCAyoCCQCb5O/0dce0ojANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJD -SDEKMAgGA1UECAwBLjEKMAgGA1UEBwwBLjEKMAgGA1UECgwBLjEKMAgGA1UECwwB -LjESMBAGA1UEAwwJbG9jYWxob3N0MRAwDgYJKoZIhvcNAQkBFgEuMB4XDTE5MDQw -NDE0MzkxOFoXDTIwMDQwMzE0MzkxOFowYzELMAkGA1UEBhMCQ0gxCjAIBgNVBAgM -AS4xCjAIBgNVBAcMAS4xCjAIBgNVBAoMAS4xCjAIBgNVBAsMAS4xEjAQBgNVBAMM -CWxvY2FsaG9zdDEQMA4GCSqGSIb3DQEJARYBLjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAMdOkUXeK1UPD+eyvfJTCCu09U09DeRH0xHso2ZXHzNHAulc -TF8Yx9kkTC6IHTSZX9Of77k/SoH3V2Tii1fBa06N3Xa8PAZ8pBxPsxiH8czdabBU -WYxgZ/n8Y/K5DvJRyspZB/2SxGMRXnRVnUgqXti3MFfHvU+Z9PXcgBOPi0jRfxqO -EznJjyXoLPY1Valdqbr3Ew97tpksxKDXCoKHyq0GoGQZC2iYMl8pxEzVYnGZ3IYY -65bX6/49SuVxU1ECd79o7ozrHiVMNDQRfeQDJknG2HOmDiDvgU/MgCkJhAtg7Oz0 -Z1x7rSlY7zFBDmalDNzmRZ6dKZcvdjJv+JDPZsnOrvPc9fMOzAPHxKSNsQm9+5/I -bhj6iHLntb9cqhsB1sUgX+OcjERZihJDrgIym7cKm1SLOIyn7SUQfuPKRvsA9GOc -NmhMV+uIxFsDXK6yxdplSKvYXRsqGr9oD1txBjKZHAIqHM/2/UWwXgZr98t2iz9G -EZX4PXZVgokrDYryFJIEFttvRdeX4uqy+0kMTzW21ju1BqXIjFHTvR4dXaLgCBZW -cFCinWGp+G5d3b8w/myGYDQNDP7RjzDbV7yJKPMMPdq9/LehoTz9WUWetsPgj+Ye -aar15qanLDuZqUr0gmGMKlj05XwgE1fc3tvl/Nxv7nOmXld5agjfbQ75+GxvAgMB -AAEwDQYJKoZIhvcNAQELBQADggIBADG3AV9WDlZYsRdC+Le2YLwRTM2v2/mZ1Cgq -s8oktgk3upZI/6L09s2rbIHpEurEucwMxlEvZTDthKOafVYXJedLT4IFT9ezaP58 -eBLRS+LUlpKO7IHBEPe6FP5kXWiLpJlGgIMeOhJbb65x7eojbSOhATNo1vPRpg5w -xLKX+s0keAGUyuKvOkE+FSInT87zQeH29p4CqBxPZLJZfCzLb4FIgCz9FIZ7KEIW -h26QSBfboBIJ+H5RPWO8WsaH0jgFkTcmHs5oOVsZv3qQUVPJfEfyG007vcviw1ok -4uwNOv22KJDI/VBJ5UbzJOcD0BW0EMw7/1pcJ3itm17YvmAReTIOwDa0VMxGji1d -6PEni1EKiJyiKuEqluEbcWLV4A7LbqItxOobVVcZJPB/NwlV/vKwAzJJ4Q7kItSn -IpaDarV+P69gUWKGjWQCFLU3LtzUiK/jbGfAWa7V4PZ1M2q35wWQMzmj0jjidFFn -HxOc+aeq63+2TFaCh0D1XzG+dmtpPMO/nax6Ak0QSIBYjTvGjXhJM3vLIJZBdvmM -XVdZUoE6S0ha5LPK9xryKF20wIq0A/rZ/c3u2VnDPQKeMs6tCxhQcGIaGDHWSAFh -dO4oouQYaH5vDH5eESrwM22PJvjd5Lykv2l/oBFpMyrYkDh4Nqm3lqY/kB+W2U7m -ZnihdMEE ------END CERTIFICATE----- diff --git a/docker/nginx/test.key b/docker/nginx/test.key deleted file mode 100644 index f01556b83..000000000 --- a/docker/nginx/test.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -ox5oXud+Wan4s1Aw/fVhc1akHSDfH2En ------END PRIVATE KEY----- diff --git a/pyproject.toml b/pyproject.toml index d71ea1cd6..905647436 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -266,6 +266,7 @@ convention = "pep257" [tool.poe.tasks] # Setup bootstrap = {cmd = "./scripts/bootstrap", help = "Runs bootstrap"} +certs = {cmd = "./scripts/gen-certs", help = "Generates the development certificates"} console = {cmd = "./scripts/console", help = "Opens invenio shell"} server = {cmd = "./scripts/server", help = "Starts the server"} setup = {cmd = "./scripts/setup", help = "Runs setup"} diff --git a/scripts/bootstrap b/scripts/bootstrap index 0610ca2e3..a5725a5c4 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -50,6 +50,7 @@ deploy=false cmd="uv sync --frozen" ci=false tgz_file="" +force_certs=false # uv is a mandatory condition to launch this program! if [[ -z "${VIRTUAL_ENV}" ]]; then @@ -73,6 +74,9 @@ while test $# -gt 0; do --deploy) deploy=true ;; + --force-certs) + force_certs=true + ;; *) error_msg "Option $1 not recognized" ;; esac shift @@ -132,6 +136,16 @@ else fi +# Generate the certificates used by the HTTPS development server. Skipped on +# deploy, where the application image has no use for a development private key. +if ! $deploy ; then + if $force_certs ; then + $(dirname "$0")/gen-certs --force + else + $(dirname "$0")/gen-certs + fi +fi + # Compile translations catalogs info_msg "Compile translations catalogs" uv run pybabel compile -d sonar/translations diff --git a/scripts/gen-certs b/scripts/gen-certs new file mode 100755 index 000000000..cd60c30a8 --- /dev/null +++ b/scripts/gen-certs @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Fondation RERO+ +# SPDX-License-Identifier: AGPL-3.0-or-later + +source $(dirname "$0")/functions + +set -e + +PROGRAM=`basename $0` +CERTS_DIR="$(dirname "$0")/../.certs" +CERT="${CERTS_DIR}/dev.crt" +KEY="${CERTS_DIR}/dev.key" +HOSTS=(sonar.ch localhost 127.0.0.1 ::1) +force=false + +while test $# -gt 0; do + case "$1" in + -f|--force) + force=true ;; + *) message "Option $1 not recognized" "error" ;; + esac + shift +done + +section "Generate development certificates" "info" + +if [[ -f "${CERT}" && -f "${KEY}" ]] && ! $force ; then + message "Certificates already exist, skipping. Use --force to regenerate." "info" + exit 0 +fi + +mkdir -p "${CERTS_DIR}" + +if command -v mkcert &> /dev/null; then + # mkcert signs with a local CA, so browsers accept the certificate without + # warning. This matters for SAML: the identity provider answers with an + # auto-submitted POST, and clicking through a browser interstitial loses it. + message "Generating with mkcert" "info" + mkcert -cert-file "${CERT}" -key-file "${KEY}" "${HOSTS[@]}" + message "Run 'mkcert -install' once if the certificate is not trusted yet." "info" +else + # Fallback used by the CI and any environment without mkcert. The browser + # will warn about the unknown issuer. + message "mkcert not found, generating a self-signed certificate with openssl" "info" + openssl req -x509 -newkey rsa:4096 -sha256 -days 825 -nodes \ + -keyout "${KEY}" -out "${CERT}" \ + -subj "/CN=sonar.ch" \ + -addext "subjectAltName=DNS:sonar.ch,DNS:localhost,IP:127.0.0.1,IP:::1" +fi + +chmod 600 "${KEY}" + +message "Certificates generated in ${CERTS_DIR}" "success" diff --git a/scripts/server b/scripts/server index be6531ef9..efb446304 100755 --- a/scripts/server +++ b/scripts/server @@ -45,10 +45,20 @@ if [[ -z "${FLASK_DEBUG}" ]]; then export FLASK_DEBUG=True fi +cert="$script_path"/../.certs/dev.crt +key="$script_path"/../.certs/dev.key + +# Checked before anything is started, so that a missing certificate does not +# leave a celery worker behind. +if [[ ! -f "${cert}" || ! -f "${key}" ]]; then + message "Development certificates not found, run: uv run poe certs" "error" + exit 1 +fi + # Start Worker and Beat if $worker; then section "Start celery worker" "info" - celery --app sonar.celery worker --loglevel ${CELERY_LOG_LEVEL} --beat & PID_CELERY=$! + celery --app sonar.celery worker --loglevel ${CELERY_LOG_LEVEL} --beat & pid_celery=$! message "Done" "success" fi @@ -56,8 +66,8 @@ fi # Start web server section "Start web server" "info" invenio run \ - --cert "$script_path"/../docker/nginx/test.crt \ - --key "$script_path"/../docker/nginx/test.key \ + --cert "${cert}" \ + --key "${key}" \ -p $PORT & pid_server=$! message "Done" "success" diff --git a/sonar/modules/shibboleth_authenticator/auth.py b/sonar/modules/shibboleth_authenticator/auth.py index eefac0d3b..411ee4dfa 100644 --- a/sonar/modules/shibboleth_authenticator/auth.py +++ b/sonar/modules/shibboleth_authenticator/auth.py @@ -42,7 +42,9 @@ def init_saml_auth(req, remote_app): with open(sp_config["private_key"]) as content_file: private_key = content_file.read() - with open(f"./data/idp_certificates/{remote_app}.crt") as content_file: + idp_certificates_path = current_app.config.get("SHIBBOLETH_IDENTITY_PROVIDERS_CERTIFICATES_PATH") + + with open(f"{idp_certificates_path}/{remote_app}.crt") as content_file: idp_cert = content_file.read() # Create auth object with settings below diff --git a/sonar/modules/shibboleth_authenticator/config.py b/sonar/modules/shibboleth_authenticator/config.py index 6f5ef9cda..506585a93 100644 --- a/sonar/modules/shibboleth_authenticator/config.py +++ b/sonar/modules/shibboleth_authenticator/config.py @@ -14,11 +14,20 @@ ) """ -SHIBBOLETH_SERVICE_PROVIDER_CERTIFICATE = "./docker/nginx/sp.pem" -"""Path to certificate.""" +SHIBBOLETH_SERVICE_PROVIDER_CERTIFICATE = None +"""Path to the service provider certificate. -SHIBBOLETH_SERVICE_PROVIDER_PRIVATE_KEY = "./docker/nginx/sp.key" -"""Path to certificate private key.""" +Left undefined on purpose: the certificate identifies the service provider to +the identity providers, so it must be registered in the SWITCH edu-ID resource +registry. Each environment provides its own pair, outside of this repository. +SAML authentication stays disabled until both this and the private key are set. +""" + +SHIBBOLETH_SERVICE_PROVIDER_PRIVATE_KEY = None +"""Path to the service provider private key.""" + +SHIBBOLETH_IDENTITY_PROVIDERS_CERTIFICATES_PATH = "./data/idp_certificates" +"""Directory storing the identity providers certificates, named .crt.""" SHIBBOLETH_SERVICE_PROVIDER = {} """Configuration of service provider.""" diff --git a/sonar/modules/shibboleth_authenticator/ext.py b/sonar/modules/shibboleth_authenticator/ext.py index 1f05edb0f..d42734bec 100644 --- a/sonar/modules/shibboleth_authenticator/ext.py +++ b/sonar/modules/shibboleth_authenticator/ext.py @@ -21,11 +21,19 @@ def init_app(self, app): def init_config(self, app): """Initialize configuration.""" - # Update service provider configuration with certificate path found - # in environment variables + # Update service provider configuration with the configured certificate + # paths. They are only injected when set, so that an unconfigured + # instance raises the explicit error from ``init_saml_auth`` instead of + # trying to open ``None``. service_provider = app.config.get("SHIBBOLETH_SERVICE_PROVIDER") - service_provider["x509cert"] = app.config.get("SHIBBOLETH_SERVICE_PROVIDER_CERTIFICATE") - service_provider["private_key"] = app.config.get("SHIBBOLETH_SERVICE_PROVIDER_PRIVATE_KEY") + certificate = app.config.get("SHIBBOLETH_SERVICE_PROVIDER_CERTIFICATE") + private_key = app.config.get("SHIBBOLETH_SERVICE_PROVIDER_PRIVATE_KEY") + + if certificate: + service_provider["x509cert"] = certificate + + if private_key: + service_provider["private_key"] = private_key app.config.setdefault("SHIBBOLETH_SERVICE_PROVIDER", service_provider) diff --git a/sonar/modules/utils.py b/sonar/modules/utils.py index 35363af4d..cb8c96d47 100644 --- a/sonar/modules/utils.py +++ b/sonar/modules/utils.py @@ -85,6 +85,12 @@ def send_email(recipients, subject, template, ctx=None, html=True, lang="en"): def get_switch_aai_providers(): """Return the list of available SWITCHaai providers.""" + # Without a service provider key pair no SAML request can be signed, so no + # provider is offered rather than letting the user reach a dead end. + service_provider = current_app.config.get("SHIBBOLETH_SERVICE_PROVIDER", {}) + if not service_provider.get("x509cert") or not service_provider.get("private_key"): + return [] + providers = [] for provider, data in current_app.config.get("SHIBBOLETH_IDENTITY_PROVIDERS").items(): # Don't take providers flagged as dev in production mode diff --git a/sonar/theme/templates/sonar/accounts/login.html b/sonar/theme/templates/sonar/accounts/login.html index deec96063..e3adc07f3 100644 --- a/sonar/theme/templates/sonar/accounts/login.html +++ b/sonar/theme/templates/sonar/accounts/login.html @@ -28,11 +28,13 @@

{{ _('Log in to account') }}

ORCID + {% if aai_providers() %}
{% with label = _('Log in with SWITCHaai') %} {% include 'sonar/partial/switch_aai_dropdown.html' %} {% endwith %}
+ {% endif %} diff --git a/sonar/theme/templates/sonar/accounts/signup.html b/sonar/theme/templates/sonar/accounts/signup.html index 88348df0c..24091faa6 100644 --- a/sonar/theme/templates/sonar/accounts/signup.html +++ b/sonar/theme/templates/sonar/accounts/signup.html @@ -37,9 +37,11 @@

ORCID + {% if aai_providers() %}
{% include 'sonar/partial/switch_aai_dropdown.html' %}
+ {% endif %} diff --git a/tests/conftest.py b/tests/conftest.py index a1ffb080e..181689cf7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,7 +8,7 @@ import shutil import sys import tempfile -from datetime import date +from datetime import UTC, date, datetime, timedelta from io import BytesIO from os.path import dirname, join from types import SimpleNamespace @@ -16,6 +16,10 @@ import pytest import requests_mock +from cryptography import x509 +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID from dotenv import load_dotenv from flask_principal import ActionNeed from flask_security.utils import hash_password @@ -135,8 +139,55 @@ def instance_path(): shutil.rmtree(path) +@pytest.fixture(scope="session") +def saml_certificates(tmp_path_factory): + """Generate the SAML certificates needed by the tests. + + Certificates are only read and formatted, never used to validate a real + signature, so ephemeral ones are enough and no key material has to be + versioned. + """ + path = tmp_path_factory.mktemp("saml") + + def create_certificate(common_name): + """Create a self signed certificate and its private key.""" + key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name)]) + certificate = ( + x509.CertificateBuilder() + .subject_name(name) + .issuer_name(name) + .public_key(key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.now(UTC)) + .not_valid_after(datetime.now(UTC) + timedelta(days=1)) + .sign(key, hashes.SHA256()) + ) + return key, certificate + + key, certificate = create_certificate("sonar.ch") + (path / "sp.pem").write_bytes(certificate.public_bytes(serialization.Encoding.PEM)) + (path / "sp.key").write_bytes( + key.private_bytes( + serialization.Encoding.PEM, + serialization.PrivateFormat.PKCS8, + serialization.NoEncryption(), + ) + ) + + # Named after the "idp" identity provider key configured below. + _, idp_certificate = create_certificate("idp.com") + (path / "idp.crt").write_bytes(idp_certificate.public_bytes(serialization.Encoding.PEM)) + + return SimpleNamespace( + path=str(path), + certificate=str(path / "sp.pem"), + private_key=str(path / "sp.key"), + ) + + @pytest.fixture(scope="module", autouse=True) -def app_config(app_config): +def app_config(app_config, saml_certificates): """Define configuration for module.""" help_test_dir = join(dirname(__file__), "data", "help") @@ -144,9 +195,13 @@ def app_config(app_config): "strict": True, "debug": True, "entity_id": "entity_id", - "x509cert": "./docker/nginx/sp.pem", - "private_key": "./docker/nginx/sp.key", + "x509cert": saml_certificates.certificate, + "private_key": saml_certificates.private_key, } + # The extension overwrites the two keys above with these values. + app_config["SHIBBOLETH_SERVICE_PROVIDER_CERTIFICATE"] = saml_certificates.certificate + app_config["SHIBBOLETH_SERVICE_PROVIDER_PRIVATE_KEY"] = saml_certificates.private_key + app_config["SHIBBOLETH_IDENTITY_PROVIDERS_CERTIFICATES_PATH"] = saml_certificates.path app_config["SHIBBOLETH_IDENTITY_PROVIDERS"] = { "idp": { diff --git a/tests/ui/shibboleth_authenticator/conftest.py b/tests/ui/shibboleth_authenticator/conftest.py index 9ef53ab57..0c105d9e5 100644 --- a/tests/ui/shibboleth_authenticator/conftest.py +++ b/tests/ui/shibboleth_authenticator/conftest.py @@ -17,12 +17,12 @@ def valid_attributes(): @pytest.fixture(scope="module") -def valid_sp_configuration(): +def valid_sp_configuration(saml_certificates): """Fixture for valid service provider configuration.""" return { "strict": True, "debug": True, "entity_id": "entity_id", - "x509cert": "./docker/nginx/sp.pem", - "private_key": "./docker/nginx/sp.key", + "x509cert": saml_certificates.certificate, + "private_key": saml_certificates.private_key, } diff --git a/tests/ui/shibboleth_authenticator/test_shibboleth_auth.py b/tests/ui/shibboleth_authenticator/test_shibboleth_auth.py index 3bef4a940..d9f2545ea 100644 --- a/tests/ui/shibboleth_authenticator/test_shibboleth_auth.py +++ b/tests/ui/shibboleth_authenticator/test_shibboleth_auth.py @@ -20,29 +20,36 @@ def test_get_identity_provider_configuration(app): assert auth.get_identity_provider_configuration("idp").get("entity_id") == "https://idp.com/shibboleth" -def test_init_saml_auth(app, request): +def test_init_saml_auth(app, request, saml_certificates): """Test SAML auth initialization.""" - # Valid init without strict mode + certificate = saml_certificates.certificate + private_key = saml_certificates.private_key + + # Strict mode defaults to True when not configured app.config.update( SHIBBOLETH_SERVICE_PROVIDER={ "debug": True, "entity_id": "entity_id", - "x509cert": "./docker/nginx/sp.pem", - "private_key": "./docker/nginx/sp.key", + "x509cert": certificate, + "private_key": private_key, } ) - assert isinstance(auth.init_saml_auth(request, "idp"), OneLogin_Saml2_Auth) + saml_auth = auth.init_saml_auth(request, "idp") + assert isinstance(saml_auth, OneLogin_Saml2_Auth) + assert saml_auth.get_settings().is_strict() - # Valid init without debug + # Debug mode defaults to False when not configured app.config.update( SHIBBOLETH_SERVICE_PROVIDER={ "strict": True, "entity_id": "entity_id", - "x509cert": "./docker/nginx/sp.pem", - "private_key": "./docker/nginx/sp.key", + "x509cert": certificate, + "private_key": private_key, } ) - assert isinstance(auth.init_saml_auth(request, "idp"), OneLogin_Saml2_Auth) + saml_auth = auth.init_saml_auth(request, "idp") + assert isinstance(saml_auth, OneLogin_Saml2_Auth) + assert not saml_auth.get_settings().is_debug_active() # Init failed caused by certificate lack app.config.update( @@ -50,7 +57,7 @@ def test_init_saml_auth(app, request): "debug": True, "strict": True, "entity_id": "entity_id", - "private_key": "./docker/nginx/sp.key", + "private_key": private_key, } ) with pytest.raises(Exception) as e: @@ -63,7 +70,7 @@ def test_init_saml_auth(app, request): "debug": True, "strict": True, "entity_id": "entity_id", - "x509cert": "./docker/nginx/sp.pem", + "x509cert": certificate, } ) with pytest.raises(Exception) as e: @@ -75,8 +82,8 @@ def test_init_saml_auth(app, request): SHIBBOLETH_SERVICE_PROVIDER={ "debug": True, "strict": True, - "x509cert": "./docker/nginx/sp.pem", - "private_key": "./docker/nginx/sp.key", + "x509cert": certificate, + "private_key": private_key, } ) with pytest.raises(Exception) as e: diff --git a/tests/ui/test_utils.py b/tests/ui/test_utils.py index 3a837202a..7a97e3819 100644 --- a/tests/ui/test_utils.py +++ b/tests/ui/test_utils.py @@ -70,6 +70,10 @@ def test_get_switch_aai_providers(app): app.config["SHIBBOLETH_IDENTITY_PROVIDERS"] = {"idp": {}, "idpdev": {"dev": True}} assert get_switch_aai_providers() == ["idp"] + # No provider is offered when the service provider key pair is missing + app.config["SHIBBOLETH_SERVICE_PROVIDER"] = {"entity_id": "entity_id"} + assert get_switch_aai_providers() == [] + def test_get_current_language(app): """Test getting the current language.""" diff --git a/tests/ui/test_views.py b/tests/ui/test_views.py index dbacd2f30..658e54705 100644 --- a/tests/ui/test_views.py +++ b/tests/ui/test_views.py @@ -256,6 +256,15 @@ def test_schema_deposits(client, moderator, submitter, moderator_dedicated): assert "subdivisions" in res.json["schema"]["properties"]["diffusion"]["propertiesOrder"] +def test_login_switch_aai_button(app, client): + """Test the SWITCHaai button is only displayed when SAML is configured.""" + assert b"SWITCHaai" in client.get(url_for("security.login")).data + + # Without a service provider key pair no provider can be offered + app.config["SHIBBOLETH_SERVICE_PROVIDER"] = {"entity_id": "entity_id"} + assert b"SWITCHaai" not in client.get(url_for("security.login")).data + + def test_profile(client, user): """Test profile page.""" # Not logged