diff --git a/.github/secret_scanning.yml b/.github/secret_scanning.yml new file mode 100644 index 000000000..3d036d4d4 --- /dev/null +++ b/.github/secret_scanning.yml @@ -0,0 +1,4 @@ +paths-ignore: + - "quickstart/exampledata/config/credentials.yml" + - "quickstart/exampledata/config/nginx/mtls.conf.d/*.key" + - "quickstart/exampledata/config/nginx/mtls.conf.d/*.crt" diff --git a/CHANGELOG.md b/CHANGELOG.md index 45caac297..b16bb2bd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,9 @@ ### Breaking ### Features -* retrieve oauth token automatically from different oauth endpoints by introducing an additional file to - define the credentials for every configuration source +* introducing an additional file to define the credentials for every configuration source +* retrieve oauth token automatically from different oauth endpoints +* retrieve configruation with mTLS authentication #### Improvements ### Bugfix diff --git a/doc/source/getting_started.rst b/doc/source/getting_started.rst index 0f12a9852..43ef46cf3 100644 --- a/doc/source/getting_started.rst +++ b/doc/source/getting_started.rst @@ -124,6 +124,23 @@ Run with getting config from http server with basic authentication logprep run http://localhost:8081/config/pipeline.yml +Run with getting config from http server with mTLS authentication +----------------------------------------------------------------- + + * Run from within the `quickstart` directory: + + .. code-block:: bash + + docker compose --profile mtls up -d + + * Run within the project root directory: + + .. code-block:: bash + + export LOGPREP_CREDENTIALS_FILE="quickstart/exampledata/config/credentials.yml" + logprep run https://localhost:8082/config/pipeline.yml + + Run with getting config from FDA with oauth2 authentication ----------------------------------------------------------- @@ -159,7 +176,8 @@ Opensearch: `localhost:9200` / / Opensearch Dashboards: `localhost:5601` / / Grafana Dashboards: `localhost:3000` admin admin Prometheus: `localhost:9090` / / -Nginx: `localhost:8081` user password +Nginx Basic Auth: `localhost:8081` user password +Nginx mTLS: `localhost:8082` Keycloak: `localhost:8080` admin admin Keycloak Postgres: `localhost:5432` keycloak bitnami FDA: `localhost:8002` logprep logprep diff --git a/logprep/util/credentials.py b/logprep/util/credentials.py index 597b7f357..838d96e3e 100644 --- a/logprep/util/credentials.py +++ b/logprep/util/credentials.py @@ -53,6 +53,15 @@ # example for Basic Authentication with inline password username: password: # will be overwritten if 'password_file' is given + "http://target.url": + # example for mTLS authentication + client_key: <path/to/client/key/file> + cert: <path/to/certificate/file> + "http://target.url": + # example for mTLS authentication with ca cert given + client_key: <path/to/client/key/file> + cert: <path/to/certificate/file> + ca_cert: <path/to/ca/cert> Options for the credentials file are: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,6 +75,9 @@ .. autoclass:: logprep.util.credentials.OAuth2PasswordFlowCredentials :members: endpoint, client_id, client_secret, username, password :no-index: +.. autoclass:: logprep.util.credentials.MTLSCredentials + :members: client_key, cert, ca_cert + :no-index: Authentication Process: ^^^^^^^^^^^^^^^^^^^^^^^ @@ -197,7 +209,8 @@ def _resolve_secret_content(credential_mapping: dict): @classmethod def from_dict(cls, credential_mapping: dict) -> "Credentials": - """matches the given credentials of the credentials mapping with the expected credential object""" + """matches the given credentials of the credentials mapping + with the expected credential object""" if credential_mapping: cls._resolve_secret_content(credential_mapping) try: @@ -229,6 +242,29 @@ def _match_credentials(cls, credential_mapping: dict) -> "Credentials": extra_params.keys(), ) return OAuth2TokenCredentials(token=token) + case { + "client_key": client_key, + "cert": cert, + "ca_cert": ca_cert, + **extra_params, + }: + if extra_params: + cls._logger.warning( + "Other parameters were given: %s but OAuth token authorization was chosen", + extra_params.keys(), + ) + return MTLSCredentials(client_key=client_key, cert=cert, ca_cert=ca_cert) + case { + "client_key": client_key, + "cert": cert, + **extra_params, + }: + if extra_params: + cls._logger.warning( + "Other parameters were given: %s but OAuth token authorization was chosen", + extra_params.keys(), + ) + return MTLSCredentials(client_key=client_key, cert=cert) case { "endpoint": endpoint, "client_id": client_id, @@ -333,6 +369,7 @@ class Credentials: _session: Session = field(validator=validators.instance_of((Session, type(None))), default=None) def get_session(self): + """returns session with retry configuration""" if self._session is None: self._session = Session() max_retries = 3 @@ -438,7 +475,8 @@ class OAuth2PasswordFlowCredentials(Credentials): client_secret: str = field( validator=validators.instance_of((str, type(None))), default=None, repr=False ) - """The client secret for the token request. This is used to authenticate the client. (Optional)""" + """The client secret for the token request. + This is used to authenticate the client. (Optional)""" _token: AccessToken = field( validator=validators.instance_of((AccessToken, type(None))), init=False, @@ -574,3 +612,25 @@ def _get_token(self) -> AccessToken: expires_in = token_response.get("expires_in") self._token = AccessToken(token=access_token, expires_in=expires_in) return self._token + + +@define(kw_only=True) +class MTLSCredentials(Credentials): + """class for mTLS authentification""" + + client_key: str = field(validator=validators.instance_of(str)) + """path to the client key""" + cert: str = field(validator=validators.instance_of(str)) + """path to the client cretificate""" + ca_cert: str = field(validator=validators.instance_of((str, type(None))), default=None) + """path to a certification authority certificate""" + + def get_session(self): + session = super().get_session() + if session.cert is None: + cert = (self.cert, self.client_key) + session.cert = cert + if self.ca_cert: + session.verify = self.ca_cert + + return session diff --git a/quickstart/docker-compose.yml b/quickstart/docker-compose.yml index d94a2ea51..b5cd9ebe8 100644 --- a/quickstart/docker-compose.yml +++ b/quickstart/docker-compose.yml @@ -122,6 +122,18 @@ services: - ../quickstart/exampledata:/usr/share/nginx/html:ro - ../quickstart/exampledata/config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ../quickstart/exampledata/config/nginx/conf.d:/etc/nginx/conf.d:ro + mtls-config: + image: nginx:latest + container_name: mtls-config + profiles: + - mtls + network_mode: host + expose: + - 8082 + volumes: + - ../quickstart/exampledata:/usr/share/nginx/html:ro + - ../quickstart/exampledata/config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - ../quickstart/exampledata/config/nginx/mtls.conf.d:/etc/nginx/conf.d:ro keycloak: image: bitnami/keycloak:latest container_name: keycloak diff --git a/quickstart/exampledata/config/credentials.yml b/quickstart/exampledata/config/credentials.yml index 8e99890d7..95440238c 100644 --- a/quickstart/exampledata/config/credentials.yml +++ b/quickstart/exampledata/config/credentials.yml @@ -7,3 +7,7 @@ "http://localhost:8081": username: user password: password +"https://localhost:8082": + client_key: quickstart/exampledata/config/nginx/mtls.conf.d/client.key + cert: quickstart/exampledata/config/nginx/mtls.conf.d/client.crt + ca_cert: quickstart/exampledata/config/nginx/mtls.conf.d/ca.crt diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/ca.crt b/quickstart/exampledata/config/nginx/mtls.conf.d/ca.crt new file mode 100644 index 000000000..d38cb9e6f --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/ca.crt @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDmzCCAoOgAwIBAgIUCyRRwwUOd0iw1In+yHn9aoqozVcwDQYJKoZIhvcNAQEL +BQAwXDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEVMBMGA1UEAwwMY2EubG9jYWxob3N0 +MCAXDTI0MDMyODA4NDIwOVoYDzIwNTEwODEzMDg0MjA5WjBcMQswCQYDVQQGEwJB +VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMRUwEwYDVQQDDAxjYS5sb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCQAh2nl9w6qFngBVkpZixI2u2IPHuqxaSnm2AkBqcy +4OyDqEC463czZPQUS0Yv0RpzDVFYjq0ii21q0ywCiWxt4DwobqyDXTn68lcwvNTY +qLLlviqC0xuSv2/eqWJZ1QJ8yJhx3QzdpXbLb7MnkVFaiLucebUK205Gs+i1wKw0 +tniC3+BpUCynowZSge095HwGINvwSKrpweGDoYI5woRjsl8Ksn7/0OXn6ueKKJYu +R1dXT5JgbOaY22WBe96b5HUpLxamqbO+M6TJJdVv0H3G/rAgnxrhrYHgJBs/IF8/ +mBgahH2EazGw2BI2SrmOYmayKXs4rmWAX34P/zq3rNLHAgMBAAGjUzBRMB0GA1Ud +DgQWBBRQqo42nJN4uerAY9k2IdECI0922TAfBgNVHSMEGDAWgBRQqo42nJN4uerA +Y9k2IdECI0922TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB5 +JX7Q05yueUtWZK1Gx41jxojj8fi3nq0VUbNLATEJT1OFMSRGrBFlm+ayJw7GhnvO +wPuy3fxA+Ixhw68rzB8JAl9gtLm1Rv4biBbTrlrbCq4S4Ne2qHSZJTzeLLFgpkZ1 +VwneCEjjXNNnIOMvxFT4TequlEzGAUQjIFKxOj0AjeLucnZnktCge2oxNaUr/s4+ +b7LqIvEZUZPup+XTk79rCIfhNN7ML/9yeNZbxneoz2GKn5KU5NoBOLmyHEd6HEGC +/6PAOFNLO9OR2b4zsIEorSTXCP5NkRDuA8DAAKoaYkK8P4pYuFbmvSvQixSLVcVu +gXNBmnx+ej6lKVn5yuo2 +-----END CERTIFICATE----- diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/ca.key b/quickstart/exampledata/config/nginx/mtls.conf.d/ca.key new file mode 100644 index 000000000..62845962b --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/ca.key @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQI+VMpvN/v7YkCAggA +MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECApOwJBfoRwqBIIEyDAZ5NMr2h3x +eOPsu4rmjiICNQ2GyRpo8bGOSgn4SDwEGzw7NfMwx6n1tSLzfD8lKkI54QQ9LS8i +Tviki02htW57lpd09yNv17x0Lt8e5A1iN40UK7c6UWo5khcvoMOvjv+JPNgSheXt +dm0AF0co49j3PmqNjJO37uNmrzcq32S925d02gQ4kBDYri6VNMh4s46s5Wli6tpP +/yvj07x1a0nNVI+EtOyYswtJCh/OGtpidM1wdkvBjIivzcBBbeVjQkgCaa/P/+Wu +qZKJpAarpJgW/HIxGinzFmN6cAerpnG7ARb/+CBNUV3GT2EB8mNh1qRxTK590aN4 +7nhL4x48I8TCBT9xcZ/SE/Zgj3qAnoAa4U/tYJazMG7q69v3Cs/I+Tu5TQoaPH9W +ccFxbwiooBiHOQrYclciDUF6nL3hN1v5XxBzVhmE4qsLzswYN82wwDb1J3cmR2Gb +0orDRoI++jBqOGPwSZ5glYZyE6+epcQSEhaT27F4lzlUk3nrN1b43JED0UPtFgb5 +wpcl4cld8e3Ofl2D+BlKQ1/JlkZ/K55JBb9Khc7HTjtpV3MwPefHs5Zy8zoNgiae +8utHfYegImEFFo1sdcU/cooA1b6c1mWMevM+8Yz32rfLcnmyWK+pCIgU48/sLPmu +Q5oSg+uLhcQaKUj3fpl4AuYz/Q3Owm11zXqSjQLMJX15DPVahv3vIRvpUQN73bAk +TquZ1hoFxDxwmFCX8EnkmK+g7amvFNzIJkX5xJ9KW4U2lHelGNImt6+XfOJmqoxQ +x1Yz6f1I7cv9la9hLHp7SYozEeEjrgDRXEULPYhXCfTHn20do1WDn/VK7HN3ESpm +Rd8cfBsSFvHjBSk1MWW/kFKMPfQRh7+AZY6qvWTZ78pivE6JJDEHcT93gL2X3/Xn +jgyAbtFvBGiIAj46U316tT4/vmCk1xaC8X7UDX+YpgGXMPMJ7QGKRKpG4OCBHYfd +R+Hw7xMxBCtZKI8tq8ghOo9OAs0UulGffv0Wh+1EzBQFpFBlFzVLm5u11XJL4Rs7 +HgoDC7rDX3Nv2JtEsK47WHLWCIw6fswjKbjbUgECm7y0Z/rWjozaooXa6WUm3GzB +d4uvPG/AesjqX4zdFebz0joLYvYvZt03gjGfhjxpcVGGRkv60glNxpK3oAUGaYhf +TKrVOMTL9NKfuOrvuPBSgKVeT6jTFoTC9MfGgfPwrPkAeRdU0fnO/2oAby94B2hB +4mOn69SCBImivPmJSTfytJJ81AzmDKQitEgiKeMS4nyXcWDHGRG4Wj5JavvKlm9Q +rxI3HzwZGfMOaO9i7NN/ML2z6uXjHO/ufupxy63ukkoaAcTbYAOwxk4dte9WXTeG +JrDryj4WDYqdLuET99o+LFqfjUX+YngSwnSZxpDWLs2iLWopkV7s+iIsI/UViqXb +rPVD8kR0FPbMqbtNSm9lySMLdI5hXYw8msflRUAlp9RZnDp6psLNheqJTCRbzw5d +ChKbUx9u/MpZbci9GXdgbE9pq9nFJV4hJWILSQs47ADtuVS6jNoTCY21IMTDp5Ux +aia2Gi1qKAtF1aX5Cfk83F0RTDDV4sQTqCS0quE6qffPjB2Jbq4eoabVRlPp30r4 +gyijUXilVZPCyVTEyPzF8A== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/client.crt b/quickstart/exampledata/config/nginx/mtls.conf.d/client.crt new file mode 100644 index 000000000..dedca6590 --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/client.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEPDCCAyQCFA9Zylday8XeecLLAXbNHTvh2x4dMA0GCSqGSIb3DQEBCwUAMFwx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQxFTATBgNVBAMMDGNhLmxvY2FsaG9zdDAgFw0y +NDAzMjgwODQ2MjZaGA8yMDUxMDgxMzA4NDYyNlowVzELMAkGA1UEBhMCQVUxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDEQMA4GA1UEAwwHbG9ncHJlcDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC +AgoCggIBAMSlbR3LnON+6gszAoyBVCdKc98sbSvzKfQfYeKPj0Pklvo5t59UN9cg ++EpYH+GyAr/OxhywzD/K1Q0G/VZQpAglibwmRoZHCd2D9uqYKb1cNL7rJ/9MtyJs +0w8c7efV6S9gCbu/tbnH93yhPfWotjTxTciHK87SkG/NCcJ0D4pSt2m4KIEotp71 +DeRh9Le+D4JLCpDtTd41jp86tBFOtQUOzHzCJNSqijXuJqPBzNHjV7Jks/PR6Y4f +DymxGcqPne3O1j6LRAvPgkYuw9ikcuUVKcZOUM7gt2qOOlEImGbeYB0u7cxRyyvZ +ZNgscokm9X3cAVcVhlPJwTXiOsBv+dWJK9DMvl37RtpCUj9KqoER06xHvHz0prxq +Tl+1Xqqlk2uEG3ykqrUhDgj4WcY/ahY/oUAVH8scYA+dva5LzChHZOl24eOHF0D2 +Rbn7Ja9Vuw6yMCBaH5GIMwqQep0hmJbVfWi4Gife0+nuYOko9Wi+caU2znKb2HC9 ++wE93lWpcgJ/fVsMEDQxB1KggKeM7iHPw8QQ5UiDwThTgQ1nbTGJ11MKoM21lngZ +JpBDn50dPAkF3UbKhKtepf1vTbnJJZBdPdF99pHNB49O5xUPOVnrog8dAL71RlNu +HNxxCYH3APpuLZflqf1sE4+ZpqQeYLdYEk8qGFe/cu/YQcQcDLdjAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBAFzodmqOES3FZAZdpKNjB55VOtNCKHChqQtMnMmV7RcB +2knn0501ML1/mpryR+cIZqBCkkKkj39WzJn8M9uSiSoJ+16woi3gUKA/5x0p1EOB +v7xLy78EvDnADYMJfsbdTPzejdIoMvJpHKpvv1Jnw7ZkgUThiXbCWRSwYeVi9HPy +U3wxtTovr2aPfzPC2BEp1u8fHFafEdSXOACqo1fRnnmFkFHTNVcstkxprmr0sPBB +o5I0VGElWbTY1B/OUhCCGFwmpv5P+L/3L7D3NSRSqGrlK4iWYuUgQGNvqAvPF2PJ +NSi//I2aCJuKV0cNyV6CX8sTQgxg53MGZLWZTSyLxHM= +-----END CERTIFICATE----- diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/client.csr b/quickstart/exampledata/config/nginx/mtls.conf.d/client.csr new file mode 100644 index 000000000..aaf67924b --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/client.csr @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEnDCCAoQCAQAwVzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEQMA4GA1UEAwwHbG9n +cHJlcDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMSlbR3LnON+6gsz +AoyBVCdKc98sbSvzKfQfYeKPj0Pklvo5t59UN9cg+EpYH+GyAr/OxhywzD/K1Q0G +/VZQpAglibwmRoZHCd2D9uqYKb1cNL7rJ/9MtyJs0w8c7efV6S9gCbu/tbnH93yh +PfWotjTxTciHK87SkG/NCcJ0D4pSt2m4KIEotp71DeRh9Le+D4JLCpDtTd41jp86 +tBFOtQUOzHzCJNSqijXuJqPBzNHjV7Jks/PR6Y4fDymxGcqPne3O1j6LRAvPgkYu +w9ikcuUVKcZOUM7gt2qOOlEImGbeYB0u7cxRyyvZZNgscokm9X3cAVcVhlPJwTXi +OsBv+dWJK9DMvl37RtpCUj9KqoER06xHvHz0prxqTl+1Xqqlk2uEG3ykqrUhDgj4 +WcY/ahY/oUAVH8scYA+dva5LzChHZOl24eOHF0D2Rbn7Ja9Vuw6yMCBaH5GIMwqQ +ep0hmJbVfWi4Gife0+nuYOko9Wi+caU2znKb2HC9+wE93lWpcgJ/fVsMEDQxB1Kg +gKeM7iHPw8QQ5UiDwThTgQ1nbTGJ11MKoM21lngZJpBDn50dPAkF3UbKhKtepf1v +TbnJJZBdPdF99pHNB49O5xUPOVnrog8dAL71RlNuHNxxCYH3APpuLZflqf1sE4+Z +pqQeYLdYEk8qGFe/cu/YQcQcDLdjAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAgEA +dwTgDIU9fPfvsEYmljDWJpRlfvnmqgwaRT+2GC8VhdjAJcsKqtRSjfXZlIagcAfw +JyqR2vgxRlISBXNhcnBzubgAyu//RCIV5g72QZZADj87p9IWBGJTRs1qPd3BhFRF +gTVtGlUmLp2HYFHBRCKngJgMthkftwooErrWo6FbNXoGs0TKh3fcjrbBqSu94yjl +Z4yr+Lgtk1y6DTfNyFhyC43qHAsz2CL9jXuVvMCPo+PZGnTDfJnbeqCGp3jVE497 +A4MFa/tJu5rQbJ1CY5/XprB7XH2GkyYMlmIg30gSDyFdAbaHuC832xN05SP14FA+ +YDJ+zDkGr7IvaNNX6CnOVEaYL8K24tRFpvcXCFlunOu55pr+JkUp23oSQnPFTCfu +FIKY4mn7oVQ3HgckRSGAQsdVbchMPq5p2Qfvr/MuLuV1CAa5CJZKhIORYKGvlyoQ +cOD0jDaxImg9jl08+YWa7QYqodhGoaxK+IE6Xo45Xf4Rt9oGB6SnI24W6P1oG70+ +JULLND75A7lDqUBr1eSOt1TXnzuu4b80b3a/wzMpz202rrrGNuiXGwNKc+b52PJY +XrMo8+CcwbrDPGzOlYM2d0tbLPpKSgM3UGWcm0VwkX2O+9NyHMd1zN6mkcGmfELQ +BwzP5I66kCtEecYEnS3f0d/b4Wl57QE00NrGxq96cxo= +-----END CERTIFICATE REQUEST----- diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/client.key b/quickstart/exampledata/config/nginx/mtls.conf.d/client.key new file mode 100644 index 000000000..5b9bf234d --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/client.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +N0a9jiPNbuK+xhk0g8ptwOO6kYIsDg== +-----END PRIVATE KEY----- diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/default.conf b/quickstart/exampledata/config/nginx/mtls.conf.d/default.conf new file mode 100644 index 000000000..5da259a1a --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/default.conf @@ -0,0 +1,21 @@ +server { + listen 8082 ssl; + listen [::]:8082 ssl; + server_name localhost; + ssl_certificate conf.d/server.crt; + ssl_certificate_key conf.d/server.key; + ssl_client_certificate conf.d/ca.crt; + ssl_verify_client on; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; + ssl_ciphers HIGH:!aNULL:!MD5; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + } + # redirect server error pages to the static page /50x.html + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} + diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/server.crt b/quickstart/exampledata/config/nginx/mtls.conf.d/server.crt new file mode 100644 index 000000000..bb17b5e1b --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/server.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEPjCCAyYCFD1YFblwn/66le9qhiRXER5pWz5MMA0GCSqGSIb3DQEBCwUAMFwx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQxFTATBgNVBAMMDGNhLmxvY2FsaG9zdDAgFw0y +NDAzMjgwODQ0MDFaGA8yMDUxMDgxMzA4NDQwMVowWTELMAkGA1UEBhMCQVUxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAoPgys8nAUnml9hLj62i72tFBmVLUslP8piIc16gvhbi9TkCt9yH7 +65Ch2Oxgjp+WmTC+XggMsW3AA9vAgl+Y7XxThrKP9Zu0eF74OkFKwqQQk+FJQ1iu +NYYXrbf7L7Ud2CtY1wp1zN4uEKvvyBlRhQS0BhwTy2AvcmsBVRr/OAkaQUOwPUFm +ZH5siKRscc9sC/JSDlO6/3qVxC7UGozqtvzAhTyc3++liLu2zdvmk/v731Cs8fAi +2KaXxaxFOOuX7wT0GGjZDJLcYm908CIH57lC5CCo+eunz+Zu7WCtUh5T4edGAzjA +PN0EWvUh12itklcNnTECR3ztC76C4KpMbrtFmsbnO0ERz1XGVpJiUf39O952TfsH +0r6Hifpb6s260qm8JVEZHYK1w22PABkClhlxKkyr2NRum4K5dpuQGno1dSo/kCsa +HDDVDNN9bNC0SIQ60stuojbRurpO564X/Ah7dTopL65wLkclKNlpy7zHODJJqXX1 +tZuFT6lDXJB1Aqea635cBIqvHS6Ee1HBlxY/ko0sdFCZhyUu2K3NFg/SALqhN4dC +oNUiwqxPIK8j9BgBAXTGSJEg7MrUdKIxXctIRh4EzD6xT1ooEOrJqTOF+j+7YM0d +5OZ6RHsDGqZnfT/EJpfqBvuj5Y/7STzi/33a31vKrFpKE3ooQ19hH7kCAwEAATAN +BgkqhkiG9w0BAQsFAAOCAQEAD47grs4Mi+5X0Ry+5zMYwK5f5enn9olbXhs+VcHG +KY9DzVbyGkb2mR5fk9btafhe+Xc1X15nrz/uzX4s6BiklLWG72KMaQlGAObY4d2U +9HITI+bZ3kyvKLbwjNic10IOlVhwzB4WTBCa/gzHuHcCKUf9UJhyjDYqeHZmu2yU +NfXxP1B/pp2qK96K9xrhhXeBrhxKzRCCRY3EriAtDOBtjGPPzfzbgUuKILNk/o/t +Z0tqwzU6Uv95MciGJtHErzNzlgLzoVOHBAP/TNPpAwh2oAE3PH5HSy88DjXRfO9Z +TbTsFpzPNRbhbb5NPDmVhuenhjHOHjoIS+hoUuWcc7yWhg== +-----END CERTIFICATE----- diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/server.csr b/quickstart/exampledata/config/nginx/mtls.conf.d/server.csr new file mode 100644 index 000000000..f9d213e06 --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/server.csr @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEnjCCAoYCAQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9j +YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoPgys8nAUnml +9hLj62i72tFBmVLUslP8piIc16gvhbi9TkCt9yH765Ch2Oxgjp+WmTC+XggMsW3A +A9vAgl+Y7XxThrKP9Zu0eF74OkFKwqQQk+FJQ1iuNYYXrbf7L7Ud2CtY1wp1zN4u +EKvvyBlRhQS0BhwTy2AvcmsBVRr/OAkaQUOwPUFmZH5siKRscc9sC/JSDlO6/3qV +xC7UGozqtvzAhTyc3++liLu2zdvmk/v731Cs8fAi2KaXxaxFOOuX7wT0GGjZDJLc +Ym908CIH57lC5CCo+eunz+Zu7WCtUh5T4edGAzjAPN0EWvUh12itklcNnTECR3zt +C76C4KpMbrtFmsbnO0ERz1XGVpJiUf39O952TfsH0r6Hifpb6s260qm8JVEZHYK1 +w22PABkClhlxKkyr2NRum4K5dpuQGno1dSo/kCsaHDDVDNN9bNC0SIQ60stuojbR +urpO564X/Ah7dTopL65wLkclKNlpy7zHODJJqXX1tZuFT6lDXJB1Aqea635cBIqv +HS6Ee1HBlxY/ko0sdFCZhyUu2K3NFg/SALqhN4dCoNUiwqxPIK8j9BgBAXTGSJEg +7MrUdKIxXctIRh4EzD6xT1ooEOrJqTOF+j+7YM0d5OZ6RHsDGqZnfT/EJpfqBvuj +5Y/7STzi/33a31vKrFpKE3ooQ19hH7kCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IC +AQAP2fJO3ap3tpT10LfQGOzIdRBJuOk7yE2A3aSObgly3SQ2sJ7CzBrfGvoWb/Tc +0uBrBU9cTVgY7mcGbYzx59vHcoq0X0EKGEBLlfAuTh3ZdaDoDHz5769WORCb3IKz ++io1+U/KscGLDzV9DyRFtbpuCb78UZPI/Bjvy8M2AtSRaMCsN/gd20euQCC+da7N +9XOOBDU0GHEMD302jgGZ6n2jcul2rvm2dppuq4bBlusSfDcuhnsqerRukhkJ/n4I +FfB+8ZdaZ0DfJC9OMC0gb3Se8ojYymypMq9zQC/c1cmFjuF1V1scW6QyVjLYEZQG +fm2PjVc6mldRTMUKSSVzfWww/07igvNm0WhbJwT8baAL3Mr/N2GwPbXnoBCydpba +9ZwtLARogxGmd4PYYEef3Bf7GJyNLuJwuGSEbhZBCLigGfA6u1zDsoxJKDZ/D2ab +0bovMJzRSGRH97CO9vmoHW72WaRCv4G7bYoSjT0kS4Mqi2baqOAzTo4sFwar/2cr +/6WEFjv98yy/3xGedCfeA5vgpPNDBgHZ49/2GvSpD6qwWH1lJLwgIZHsXItAYmxZ +CR0KO9+kqowzsyYJzIWMVTqmX0LgkVLgglxXBHEEJjTjPdNNPuzgQPh0r+dunlIt +9B9snQgXptrVoFyFOEB7xcadX8RujuYkJn2exQjN8GH00A== +-----END CERTIFICATE REQUEST----- diff --git a/quickstart/exampledata/config/nginx/mtls.conf.d/server.key b/quickstart/exampledata/config/nginx/mtls.conf.d/server.key new file mode 100644 index 000000000..c0c47c268 --- /dev/null +++ b/quickstart/exampledata/config/nginx/mtls.conf.d/server.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +aeya/ASFEgd5zg+s6KSzP0pWcpio6w== +-----END PRIVATE KEY----- diff --git a/tests/unit/util/test_credentials.py b/tests/unit/util/test_credentials.py index bd61733c9..3a614df44 100644 --- a/tests/unit/util/test_credentials.py +++ b/tests/unit/util/test_credentials.py @@ -17,6 +17,7 @@ Credentials, CredentialsBadRequestError, CredentialsFactory, + MTLSCredentials, OAuth2ClientFlowCredentials, OAuth2PasswordFlowCredentials, OAuth2TokenCredentials, @@ -800,7 +801,7 @@ class TestCredentialsFactory: None, ), ( - "Return OAuthTokenCredential object when username, passowrd, client_id and client_secret are also given", + "Return OAuthTokenCredential object when other params are given", """--- "https://some.url": endpoint: https://endpoint.end @@ -862,6 +863,74 @@ class TestCredentialsFactory: type(None), InvalidConfigurationError, ), + ( + "Return MTLSCredentials object if certificate and key are given", + """--- +"https://some.url": + client_key: "path/to/client/key" + cert: "path/to/cert" +""", + MTLSCredentials, + None, + ), + ( + "Return MTLSCredentials object if certificate key and ca cert are given", + """--- +"https://some.url": + client_key: "path/to/client/key" + cert: "path/to/cert" + ca_cert: "path/to/ca/cert" + endpoint: https://endpoint.end + client_id: test + username: test + password: test + client_secret: test +""", + MTLSCredentials, + None, + ), + ( + "Return MTLSCredentials object if certificate key and ca cert are given with extra params", + """--- +"https://some.url": + client_key: "path/to/client/key" + cert: "path/to/cert" + ca_cert: "path/to/ca/cert" +""", + MTLSCredentials, + None, + ), + ( + "Return MTLSCredentials object if certificate and key are given with extra parameters", + """--- +"https://some.url": + client_key: "path/to/client/key" + cert: "path/to/cert" + endpoint: https://endpoint.end + username: test +""", + MTLSCredentials, + None, + ), + ( + "Return None if certificate is missing", + """--- +"https://some.url": + client_key: "path/to/client/key" +""", + type(None), + None, + ), + ( + "Return InvalidConfigurationError object if certificate is empty", + """--- +"https://some.url": + client_key: "path/to/client/key" + cert: +""", + None, + InvalidConfigurationError, + ), ], ) def test_credentials_returns_expected_credential_object( @@ -996,3 +1065,24 @@ def test_warning_logged_when_extra_params_given(self, mock_logger): r"OAuth password authorization for confidential clients", mock_logger.mock_calls[0][1][0], ) + + +class TestMTLSCredentials: + def test_get_session_returns_session_and_cert_is_set(self): + test = MTLSCredentials( + cert="path/to/cert", + client_key="path/to/key", + ) + assert test.get_session() is not None + assert test._session.cert is not None + + def test_get_session_sets_ca_cert_for_verification(self): + test = MTLSCredentials( + cert="path/to/cert", + client_key="path/to/key", + ca_cert="path/to/ca/cert", + ) + assert test.get_session() is not None + assert "path/to/cert" in test._session.cert + assert "path/to/key" in test._session.cert + assert "path/to/ca/cert" in test._session.verify diff --git a/tests/unit/util/test_getter.py b/tests/unit/util/test_getter.py index 2241fab94..3dd444a55 100644 --- a/tests/unit/util/test_getter.py +++ b/tests/unit/util/test_getter.py @@ -545,3 +545,120 @@ def test_get_raw_raises_if_credential_file_env_set_and_unauthorizes(self): ): http_getter.get_json() assert error.value.response.status_code == 401 + + @responses.activate + def test_get_raw_uses_mtls_and_session_cert_is_set_and_used_in_request(self, tmp_path): + domain = str(uuid.uuid4()) + with responses.RequestsMock(assert_all_requests_are_fired=False): + req_kwargs = { + "cert": ("path/to/cert", "path/to/key"), + "verify": True, + } + responses.add( + responses.GET, + url=f"https://{domain}/bar", + match=[matchers.request_kwargs_matcher(req_kwargs)], + ) + credentials_file_content = { + f"https://{domain}": { + "client_key": "path/to/key", + "cert": "path/to/cert", + } + } + credentials_file: Path = tmp_path / "credentials.json" + credentials_file.write_text(json.dumps(credentials_file_content)) + mock_env = {"LOGPREP_CREDENTIALS_FILE": str(credentials_file)} + with mock.patch.dict("os.environ", mock_env): + http_getter: HttpGetter = GetterFactory.from_string(f"https://{domain}/bar") + http_getter.get_raw() + assert isinstance(http_getter.credentials, Credentials) + assert ( + "path/to/cert" + in http_getter._credentials_registry.get(f"https://{domain}")._session.cert + ) + + @responses.activate + def test_get_raw_uses_mtls_with_session_cert_and_ca_cert(self, tmp_path): + domain = str(uuid.uuid4()) + req_kwargs = { + "cert": ("path/to/cert", "path/to/key"), + "verify": "path/to/ca/cert", + } + responses.add( + responses.GET, + url=f"https://{domain}/bar", + match=[matchers.request_kwargs_matcher(req_kwargs)], + ) + credentials_file_content = { + f"https://{domain}": { + "client_key": "path/to/key", + "cert": "path/to/cert", + "ca_cert": "path/to/ca/cert", + } + } + credentials_file: Path = tmp_path / "credentials.json" + credentials_file.write_text(json.dumps(credentials_file_content)) + mock_env = {"LOGPREP_CREDENTIALS_FILE": str(credentials_file)} + with mock.patch.dict("os.environ", mock_env): + http_getter: HttpGetter = GetterFactory.from_string(f"https://{domain}/bar") + http_getter.get_raw() + assert isinstance(http_getter.credentials, Credentials) + assert ( + "path/to/cert" + in http_getter._credentials_registry.get(f"https://{domain}")._session.cert + ) + assert ( + "path/to/ca/cert" + in http_getter._credentials_registry.get(f"https://{domain}")._session.verify + ) + session = http_getter._credentials_registry.get(f"https://{domain}")._session + assert session.cert == ("path/to/cert", "path/to/key") + assert session.verify == "path/to/ca/cert" + + @responses.activate + def test_get_raw_reuses_mtls_session_and_ca_cert_is_not_updated(self, tmp_path): + domain = str(uuid.uuid4()) + req_kwargs = { + "cert": ("path/to/cert", "path/to/key"), + "verify": "path/to/ca/cert", + } + responses.add( + responses.GET, + url=f"https://{domain}/bar", + match=[matchers.request_kwargs_matcher(req_kwargs)], + ) + credentials_file_content = { + f"https://{domain}": { + "client_key": "path/to/key", + "cert": "path/to/cert", + "ca_cert": "path/to/ca/cert", + } + } + credentials_file: Path = tmp_path / "credentials.json" + credentials_file.write_text(json.dumps(credentials_file_content)) + mock_env = {"LOGPREP_CREDENTIALS_FILE": str(credentials_file)} + with mock.patch.dict("os.environ", mock_env): + http_getter: HttpGetter = GetterFactory.from_string(f"https://{domain}/bar") + http_getter.get_raw() + credentials_file_content.popitem() + credentials_file_content.update( + { + f"https://{domain}": { + "cert": "path/to/other/cert", + "client_key": "path/to/client/key", + "ca_cert": "path/to/ca/cert/the/second", + } + } + ) + http_getter.get_raw() + assert ( + "path/to/cert" + in http_getter._credentials_registry.get(f"https://{domain}")._session.cert + ) + assert ( + "path/to/ca/cert" + in http_getter._credentials_registry.get(f"https://{domain}")._session.verify + ) + session = http_getter._credentials_registry.get(f"https://{domain}")._session + assert session.cert == ("path/to/cert", "path/to/key") + assert session.verify == "path/to/ca/cert"