From 9532823a99c93d2ab53624f530742190163418f4 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Fri, 21 Oct 2022 19:41:33 +1100 Subject: [PATCH] feat(configuration): mtls clients (#4221) This implements mTLS support for LDAP, Redis, and SMTP. Specified via the tls.certificate_chain and tls.private_key options. Closes #4044 --- config.template.yml | 76 ++++++++++++++++ .../en/configuration/first-factor/ldap.md | 68 ++++++++++++++ .../en/configuration/notifications/smtp.md | 74 ++++++++++++++- .../en/configuration/prologue/common.md | 35 +++++++- .../content/en/configuration/session/redis.md | 68 ++++++++++++++ docs/data/configkeys.json | 2 +- internal/authentication/ldap_user_provider.go | 2 +- internal/configuration/config.template.yml | 76 ++++++++++++++++ internal/configuration/decode_hooks.go | 70 +++++++++++++++ internal/configuration/provider.go | 2 + .../configuration/schema/authentication.go | 5 +- internal/configuration/schema/const.go | 42 +++++++-- internal/configuration/schema/keys.go | 9 ++ internal/configuration/schema/notifier.go | 3 +- internal/configuration/schema/session.go | 8 ++ internal/configuration/schema/shared.go | 17 +++- internal/configuration/schema/types.go | 90 ++++++++++++++++++- .../configuration/test_resources/config.yml | 29 ++++++ .../validator/access_control_test.go | 26 ++++++ .../configuration/validator/authentication.go | 55 +++++------- .../validator/authentication_test.go | 61 +++++++++++-- .../validator/configuration_test.go | 2 +- internal/configuration/validator/const.go | 14 +-- .../configuration/validator/const_test.go | 4 + .../validator/identity_providers_test.go | 14 +-- internal/configuration/validator/notifier.go | 12 ++- .../configuration/validator/notifier_test.go | 52 +++++++++-- internal/configuration/validator/session.go | 12 +++ .../configuration/validator/session_test.go | 64 ++++++++++++- internal/configuration/validator/shared.go | 42 +++++++++ internal/notification/smtp_notifier.go | 2 +- internal/notification/smtp_notifier_test.go | 2 +- internal/session/provider_config.go | 2 +- internal/session/provider_config_test.go | 2 +- internal/utils/crypto.go | 35 +++----- internal/utils/crypto_test.go | 68 -------------- 36 files changed, 974 insertions(+), 171 deletions(-) create mode 100644 internal/configuration/validator/shared.go diff --git a/config.template.yml b/config.template.yml index 16ea13b17..2572b06fe 100644 --- a/config.template.yml +++ b/config.template.yml @@ -315,6 +315,82 @@ authentication_backend: ## Minimum TLS version for either Secure LDAP or LDAP StartTLS. minimum_version: TLS1.2 + ## Maximum TLS version for either Secure LDAP or LDAP StartTLS. + maximum_version: TLS1.3 + + ## The certificate chain used with the private_key if the server requests TLS Client Authentication + ## i.e. Mutual TLS. + # certificate_chain: | + # -----BEGIN CERTIFICATE----- + # MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw + # EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + # MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + # ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q + # /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6 + # LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY + # 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H + # kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR + # Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD + # AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN + # AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh + # /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4 + # lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq + # wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg + # OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i + # ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE= + # -----END CERTIFICATE----- + # -----BEGIN CERTIFICATE----- + # MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw + # EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + # MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + # ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S + # zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50 + # 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou + # kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7 + # ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi + # Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD + # AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU + # Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/ + # kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf + # 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ + # HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB + # D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj + # 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b + # qocikt3WAdU^invalid DO NOT USE= + # -----END CERTIFICATE----- + + ## The private key used with the certificate_chain if the server requests TLS Client Authentication + ## i.e. Mutual TLS. + # private_key: | + # -----BEGIN RSA PRIVATE KEY----- + # MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1 + # T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ + # KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+ + # +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW + # LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5 + # txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um + # aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc + # Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB + # ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr + # LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG + # jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5 + # BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR + # Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV + # R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i + # tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9 + # ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR + # lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx + # 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos + # fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ + # 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX + # jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe + # rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84 + # n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk + # yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ + # 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid.. + # DO NOT USE== + # -----END RSA PRIVATE KEY----- + ## The distinguished name of the container searched for objects in the directory information tree. ## See also: additional_users_dn, additional_groups_dn. base_dn: dc=example,dc=com diff --git a/docs/content/en/configuration/first-factor/ldap.md b/docs/content/en/configuration/first-factor/ldap.md index d7474d1a9..f1d9604da 100644 --- a/docs/content/en/configuration/first-factor/ldap.md +++ b/docs/content/en/configuration/first-factor/ldap.md @@ -28,6 +28,74 @@ authentication_backend: server_name: ldap.example.com skip_verify: false minimum_version: TLS1.2 + maximum_version: TLS1.3 + certificate_chain: | + -----BEGIN CERTIFICATE----- + MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw + EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q + /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6 + LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY + 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H + kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR + Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD + AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN + AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh + /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4 + lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq + wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg + OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i + ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw + EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S + zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50 + 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou + kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7 + ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi + Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD + AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU + Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/ + kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf + 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ + HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB + D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj + 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b + qocikt3WAdU^invalid DO NOT USE= + -----END CERTIFICATE----- + private_key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1 + T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ + KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+ + +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW + LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5 + txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um + aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc + Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB + ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr + LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG + jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5 + BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR + Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV + R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i + tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9 + ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR + lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx + 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos + fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ + 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX + jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe + rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84 + n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk + yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ + 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid.. + DO NOT USE== + -----END RSA PRIVATE KEY----- base_dn: DC=example,DC=com additional_users_dn: ou=users users_filter: (&({username_attribute}={input})(objectClass=person)) diff --git a/docs/content/en/configuration/notifications/smtp.md b/docs/content/en/configuration/notifications/smtp.md index 20cd93a5d..e2a69d963 100644 --- a/docs/content/en/configuration/notifications/smtp.md +++ b/docs/content/en/configuration/notifications/smtp.md @@ -37,6 +37,74 @@ notifier: server_name: smtp.example.com skip_verify: false minimum_version: TLS1.2 + maximum_version: TLS1.3 + certificate_chain: | + -----BEGIN CERTIFICATE----- + MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw + EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q + /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6 + LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY + 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H + kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR + Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD + AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN + AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh + /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4 + lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq + wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg + OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i + ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw + EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S + zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50 + 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou + kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7 + ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi + Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD + AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU + Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/ + kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf + 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ + HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB + D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj + 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b + qocikt3WAdU^invalid DO NOT USE= + -----END CERTIFICATE----- + private_key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1 + T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ + KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+ + +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW + LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5 + txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um + aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc + Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB + ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr + LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG + jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5 + BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR + Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV + R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i + tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9 + ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR + lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx + 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos + fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ + 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX + jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe + rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84 + n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk + yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ + 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid.. + DO NOT USE== + -----END RSA PRIVATE KEY----- ``` ## Options @@ -139,9 +207,9 @@ for more information. This option disables this measure (not recommended). Some SMTP servers ignore SMTP specifications and claim to support STARTTLS when they in fact do not. For security reasons Authelia refuses to send messages to these servers. -This option disables this measure and is enabled *__AT YOUR OWN RISK__*. It's *__strongly recommended__* -that instead of enabling this option you either fix the issue with the SMTP server's configuration or -have the administrators of the server fix it. If the issue can't be fixed by configuration we recommend +This option disables this measure and is enabled *__AT YOUR OWN RISK__*. It's *__strongly recommended__* +that instead of enabling this option you either fix the issue with the SMTP server's configuration or +have the administrators of the server fix it. If the issue can't be fixed by configuration we recommend lodging an issue with the authors of the SMTP server. See [security] for more information. diff --git a/docs/content/en/configuration/prologue/common.md b/docs/content/en/configuration/prologue/common.md index 4c37a16c5..6222e5448 100644 --- a/docs/content/en/configuration/prologue/common.md +++ b/docs/content/en/configuration/prologue/common.md @@ -129,10 +129,39 @@ instead you should tweak the `server_name` option, and the global option {{< confkey type="string" default="TLS1.2" required="no" >}} -The key `minimum_version` controls the minimum TLS version Authelia will use when opening TLS connections. -The possible values are `TLS1.3`, `TLS1.2`, `TLS1.1`, `TLS1.0`. Anything other than `TLS1.3` or `TLS1.2` +Controls the minimum TLS version Authelia will use when performing TLS handshakes. +The possible values are `TLS1.3`, `TLS1.2`, `TLS1.1`, `TLS1.0`, `SSL3.0`. Anything other than `TLS1.3` or `TLS1.2` are very old and deprecated. You should avoid using these and upgrade your backend service instead of decreasing -this value. +this value. At the time of this writing `SSL3.0` will always produce errors. + +### maximum_version + +{{< confkey type="string" default="TLS1.3" required="no" >}} + +Controls the maximum TLS version Authelia will use when performing TLS handshakes. +The possible values are `TLS1.3`, `TLS1.2`, `TLS1.1`, `TLS1.0`, `SSL3.0`. Anything other than `TLS1.3` or `TLS1.2` +are very old and deprecated. You should avoid using these and upgrade your backend service instead of decreasing +this value. At the time of this writing `SSL3.0` will always produce errors. + +### certificate_chain + +{{< confkey type="string" required="no" >}} + +The certificate chain/bundle to be used with the [private_key](#private_key) to perform mutual TLS authentication with +the server. + +The value must be one or more certificates encoded in the DER base64 ([RFC4648]) encoded PEM format. + +### private_key + +{{< confkey type="string" required="yes" >}} + +*__Important Note:__ This can also be defined using a [secret](../methods/secrets.md) which is __strongly recommended__ +especially for containerized deployments.* + +The private key to be used with the [certificate_chain](#certificate_chain) for mutual TLS authentication. + +The value must be one private key encoded in the DER base64 ([RFC4648]) encoded PEM format. ## Server Buffers diff --git a/docs/content/en/configuration/session/redis.md b/docs/content/en/configuration/session/redis.md index dd9e2bbc5..e8fc23dc3 100644 --- a/docs/content/en/configuration/session/redis.md +++ b/docs/content/en/configuration/session/redis.md @@ -34,6 +34,74 @@ session: server_name: myredis.example.com skip_verify: false minimum_version: TLS1.2 + maximum_version: TLS1.3 + certificate_chain: | + -----BEGIN CERTIFICATE----- + MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw + EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q + /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6 + LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY + 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H + kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR + Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD + AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN + AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh + /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4 + lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq + wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg + OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i + ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE= + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw + EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S + zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50 + 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou + kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7 + ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi + Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD + AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU + Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/ + kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf + 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ + HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB + D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj + 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b + qocikt3WAdU^invalid DO NOT USE= + -----END CERTIFICATE----- + private_key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1 + T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ + KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+ + +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW + LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5 + txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um + aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc + Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB + ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr + LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG + jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5 + BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR + Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV + R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i + tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9 + ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR + lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx + 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos + fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ + 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX + jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe + rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84 + n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk + yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ + 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid.. + DO NOT USE== + -----END RSA PRIVATE KEY----- high_availability: sentinel_name: mysentinel # If `sentinel_username` is supplied, Authelia will connect using ACL-based diff --git a/docs/data/configkeys.json b/docs/data/configkeys.json index 76fc3c6a9..3f0938209 100644 --- a/docs/data/configkeys.json +++ b/docs/data/configkeys.json @@ -1 +1 @@ -[{"path":"theme","secret":false,"env":"AUTHELIA_THEME"},{"path":"certificates_directory","secret":false,"env":"AUTHELIA_CERTIFICATES_DIRECTORY"},{"path":"jwt_secret","secret":true,"env":"AUTHELIA_JWT_SECRET_FILE"},{"path":"default_redirection_url","secret":false,"env":"AUTHELIA_DEFAULT_REDIRECTION_URL"},{"path":"default_2fa_method","secret":false,"env":"AUTHELIA_DEFAULT_2FA_METHOD"},{"path":"log.level","secret":false,"env":"AUTHELIA_LOG_LEVEL"},{"path":"log.format","secret":false,"env":"AUTHELIA_LOG_FORMAT"},{"path":"log.file_path","secret":false,"env":"AUTHELIA_LOG_FILE_PATH"},{"path":"log.keep_stdout","secret":false,"env":"AUTHELIA_LOG_KEEP_STDOUT"},{"path":"identity_providers.oidc.hmac_secret","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE"},{"path":"identity_providers.oidc.issuer_certificate_chain","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN"},{"path":"identity_providers.oidc.issuer_private_key","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE"},{"path":"identity_providers.oidc.access_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ACCESS_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.authorize_code_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_AUTHORIZE_CODE_LIFESPAN"},{"path":"identity_providers.oidc.id_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ID_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.refresh_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_REFRESH_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.enable_client_debug_messages","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_CLIENT_DEBUG_MESSAGES"},{"path":"identity_providers.oidc.minimum_parameter_entropy","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_MINIMUM_PARAMETER_ENTROPY"},{"path":"identity_providers.oidc.enforce_pkce","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENFORCE_PKCE"},{"path":"identity_providers.oidc.enable_pkce_plain_challenge","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_PKCE_PLAIN_CHALLENGE"},{"path":"identity_providers.oidc.cors.endpoints","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ENDPOINTS"},{"path":"identity_providers.oidc.cors.allowed_origins","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS"},{"path":"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS_FROM_CLIENT_REDIRECT_URIS"},{"path":"identity_providers.oidc.clients","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS"},{"path":"authentication_backend.password_reset.disable","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_DISABLE"},{"path":"authentication_backend.password_reset.custom_url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_CUSTOM_URL"},{"path":"authentication_backend.refresh_interval","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_REFRESH_INTERVAL"},{"path":"authentication_backend.file.path","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH"},{"path":"authentication_backend.file.watch","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_WATCH"},{"path":"authentication_backend.file.password.algorithm","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ALGORITHM"},{"path":"authentication_backend.file.password.argon2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_VARIANT"},{"path":"authentication_backend.file.password.argon2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_ITERATIONS"},{"path":"authentication_backend.file.password.argon2.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_MEMORY"},{"path":"authentication_backend.file.password.argon2.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_PARALLELISM"},{"path":"authentication_backend.file.password.argon2.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_KEY_LENGTH"},{"path":"authentication_backend.file.password.argon2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_SALT_LENGTH"},{"path":"authentication_backend.file.password.sha2crypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_VARIANT"},{"path":"authentication_backend.file.password.sha2crypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.sha2crypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.pbkdf2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_VARIANT"},{"path":"authentication_backend.file.password.pbkdf2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_ITERATIONS"},{"path":"authentication_backend.file.password.pbkdf2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_SALT_LENGTH"},{"path":"authentication_backend.file.password.bcrypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_VARIANT"},{"path":"authentication_backend.file.password.bcrypt.cost","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_COST"},{"path":"authentication_backend.file.password.scrypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.scrypt.block_size","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_BLOCK_SIZE"},{"path":"authentication_backend.file.password.scrypt.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_PARALLELISM"},{"path":"authentication_backend.file.password.scrypt.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_KEY_LENGTH"},{"path":"authentication_backend.file.password.scrypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ITERATIONS"},{"path":"authentication_backend.file.password.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_MEMORY"},{"path":"authentication_backend.file.password.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PARALLELISM"},{"path":"authentication_backend.file.password.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_KEY_LENGTH"},{"path":"authentication_backend.file.password.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SALT_LENGTH"},{"path":"authentication_backend.file.search.email","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_EMAIL"},{"path":"authentication_backend.file.search.case_insensitive","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_CASE_INSENSITIVE"},{"path":"authentication_backend.ldap.implementation","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_IMPLEMENTATION"},{"path":"authentication_backend.ldap.url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_URL"},{"path":"authentication_backend.ldap.timeout","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TIMEOUT"},{"path":"authentication_backend.ldap.start_tls","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_START_TLS"},{"path":"authentication_backend.ldap.tls.minimum_version","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MINIMUM_VERSION"},{"path":"authentication_backend.ldap.tls.skip_verify","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SKIP_VERIFY"},{"path":"authentication_backend.ldap.tls.server_name","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SERVER_NAME"},{"path":"authentication_backend.ldap.base_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN"},{"path":"authentication_backend.ldap.additional_users_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_USERS_DN"},{"path":"authentication_backend.ldap.users_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERS_FILTER"},{"path":"authentication_backend.ldap.additional_groups_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_GROUPS_DN"},{"path":"authentication_backend.ldap.groups_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUPS_FILTER"},{"path":"authentication_backend.ldap.group_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUP_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.username_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERNAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.mail_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_MAIL_ATTRIBUTE"},{"path":"authentication_backend.ldap.display_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_DISPLAY_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.permit_referrals","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_REFERRALS"},{"path":"authentication_backend.ldap.permit_unauthenticated_bind","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_UNAUTHENTICATED_BIND"},{"path":"authentication_backend.ldap.permit_feature_detection_failure","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_FEATURE_DETECTION_FAILURE"},{"path":"authentication_backend.ldap.user","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER"},{"path":"authentication_backend.ldap.password","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE"},{"path":"session.name","secret":false,"env":"AUTHELIA_SESSION_NAME"},{"path":"session.domain","secret":false,"env":"AUTHELIA_SESSION_DOMAIN"},{"path":"session.same_site","secret":false,"env":"AUTHELIA_SESSION_SAME_SITE"},{"path":"session.secret","secret":true,"env":"AUTHELIA_SESSION_SECRET_FILE"},{"path":"session.expiration","secret":false,"env":"AUTHELIA_SESSION_EXPIRATION"},{"path":"session.inactivity","secret":false,"env":"AUTHELIA_SESSION_INACTIVITY"},{"path":"session.remember_me_duration","secret":false,"env":"AUTHELIA_SESSION_REMEMBER_ME_DURATION"},{"path":"session.redis.host","secret":false,"env":"AUTHELIA_SESSION_REDIS_HOST"},{"path":"session.redis.port","secret":false,"env":"AUTHELIA_SESSION_REDIS_PORT"},{"path":"session.redis.username","secret":false,"env":"AUTHELIA_SESSION_REDIS_USERNAME"},{"path":"session.redis.password","secret":true,"env":"AUTHELIA_SESSION_REDIS_PASSWORD_FILE"},{"path":"session.redis.database_index","secret":false,"env":"AUTHELIA_SESSION_REDIS_DATABASE_INDEX"},{"path":"session.redis.maximum_active_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MAXIMUM_ACTIVE_CONNECTIONS"},{"path":"session.redis.minimum_idle_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MINIMUM_IDLE_CONNECTIONS"},{"path":"session.redis.tls.minimum_version","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_MINIMUM_VERSION"},{"path":"session.redis.tls.skip_verify","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SKIP_VERIFY"},{"path":"session.redis.tls.server_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SERVER_NAME"},{"path":"session.redis.high_availability.sentinel_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_NAME"},{"path":"session.redis.high_availability.sentinel_username","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_USERNAME"},{"path":"session.redis.high_availability.sentinel_password","secret":true,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE"},{"path":"session.redis.high_availability.nodes","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_NODES"},{"path":"session.redis.high_availability.route_by_latency","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_BY_LATENCY"},{"path":"session.redis.high_availability.route_randomly","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_RANDOMLY"},{"path":"totp.disable","secret":false,"env":"AUTHELIA_TOTP_DISABLE"},{"path":"totp.issuer","secret":false,"env":"AUTHELIA_TOTP_ISSUER"},{"path":"totp.algorithm","secret":false,"env":"AUTHELIA_TOTP_ALGORITHM"},{"path":"totp.digits","secret":false,"env":"AUTHELIA_TOTP_DIGITS"},{"path":"totp.period","secret":false,"env":"AUTHELIA_TOTP_PERIOD"},{"path":"totp.skew","secret":false,"env":"AUTHELIA_TOTP_SKEW"},{"path":"totp.secret_size","secret":false,"env":"AUTHELIA_TOTP_SECRET_SIZE"},{"path":"duo_api.disable","secret":false,"env":"AUTHELIA_DUO_API_DISABLE"},{"path":"duo_api.hostname","secret":false,"env":"AUTHELIA_DUO_API_HOSTNAME"},{"path":"duo_api.integration_key","secret":true,"env":"AUTHELIA_DUO_API_INTEGRATION_KEY_FILE"},{"path":"duo_api.secret_key","secret":true,"env":"AUTHELIA_DUO_API_SECRET_KEY_FILE"},{"path":"duo_api.enable_self_enrollment","secret":false,"env":"AUTHELIA_DUO_API_ENABLE_SELF_ENROLLMENT"},{"path":"access_control.default_policy","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_DEFAULT_POLICY"},{"path":"access_control.networks","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_NETWORKS"},{"path":"access_control.rules","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_RULES"},{"path":"ntp.address","secret":false,"env":"AUTHELIA_NTP_ADDRESS"},{"path":"ntp.version","secret":false,"env":"AUTHELIA_NTP_VERSION"},{"path":"ntp.max_desync","secret":false,"env":"AUTHELIA_NTP_MAX_DESYNC"},{"path":"ntp.disable_startup_check","secret":false,"env":"AUTHELIA_NTP_DISABLE_STARTUP_CHECK"},{"path":"ntp.disable_failure","secret":false,"env":"AUTHELIA_NTP_DISABLE_FAILURE"},{"path":"regulation.max_retries","secret":false,"env":"AUTHELIA_REGULATION_MAX_RETRIES"},{"path":"regulation.find_time","secret":false,"env":"AUTHELIA_REGULATION_FIND_TIME"},{"path":"regulation.ban_time","secret":false,"env":"AUTHELIA_REGULATION_BAN_TIME"},{"path":"storage.local.path","secret":false,"env":"AUTHELIA_STORAGE_LOCAL_PATH"},{"path":"storage.mysql.host","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_HOST"},{"path":"storage.mysql.port","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_PORT"},{"path":"storage.mysql.database","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_DATABASE"},{"path":"storage.mysql.username","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_USERNAME"},{"path":"storage.mysql.password","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE"},{"path":"storage.mysql.timeout","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TIMEOUT"},{"path":"storage.postgres.host","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_HOST"},{"path":"storage.postgres.port","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_PORT"},{"path":"storage.postgres.database","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_DATABASE"},{"path":"storage.postgres.username","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_USERNAME"},{"path":"storage.postgres.password","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE"},{"path":"storage.postgres.timeout","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TIMEOUT"},{"path":"storage.postgres.schema","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SCHEMA"},{"path":"storage.postgres.ssl.mode","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_MODE"},{"path":"storage.postgres.ssl.root_certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_ROOT_CERTIFICATE"},{"path":"storage.postgres.ssl.certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_CERTIFICATE"},{"path":"storage.postgres.ssl.key","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_KEY_FILE"},{"path":"storage.encryption_key","secret":true,"env":"AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE"},{"path":"notifier.disable_startup_check","secret":false,"env":"AUTHELIA_NOTIFIER_DISABLE_STARTUP_CHECK"},{"path":"notifier.filesystem.filename","secret":false,"env":"AUTHELIA_NOTIFIER_FILESYSTEM_FILENAME"},{"path":"notifier.smtp.host","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_HOST"},{"path":"notifier.smtp.port","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_PORT"},{"path":"notifier.smtp.timeout","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TIMEOUT"},{"path":"notifier.smtp.username","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_USERNAME"},{"path":"notifier.smtp.password","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE"},{"path":"notifier.smtp.identifier","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_IDENTIFIER"},{"path":"notifier.smtp.sender","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SENDER"},{"path":"notifier.smtp.subject","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SUBJECT"},{"path":"notifier.smtp.startup_check_address","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_STARTUP_CHECK_ADDRESS"},{"path":"notifier.smtp.disable_require_tls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_REQUIRE_TLS"},{"path":"notifier.smtp.disable_html_emails","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_HTML_EMAILS"},{"path":"notifier.smtp.disable_starttls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_STARTTLS"},{"path":"notifier.smtp.tls.minimum_version","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_MINIMUM_VERSION"},{"path":"notifier.smtp.tls.skip_verify","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SKIP_VERIFY"},{"path":"notifier.smtp.tls.server_name","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SERVER_NAME"},{"path":"notifier.template_path","secret":false,"env":"AUTHELIA_NOTIFIER_TEMPLATE_PATH"},{"path":"server.host","secret":false,"env":"AUTHELIA_SERVER_HOST"},{"path":"server.port","secret":false,"env":"AUTHELIA_SERVER_PORT"},{"path":"server.path","secret":false,"env":"AUTHELIA_SERVER_PATH"},{"path":"server.asset_path","secret":false,"env":"AUTHELIA_SERVER_ASSET_PATH"},{"path":"server.enable_pprof","secret":false,"env":"AUTHELIA_SERVER_ENABLE_PPROF"},{"path":"server.enable_expvars","secret":false,"env":"AUTHELIA_SERVER_ENABLE_EXPVARS"},{"path":"server.disable_healthcheck","secret":false,"env":"AUTHELIA_SERVER_DISABLE_HEALTHCHECK"},{"path":"server.tls.certificate","secret":false,"env":"AUTHELIA_SERVER_TLS_CERTIFICATE"},{"path":"server.tls.key","secret":true,"env":"AUTHELIA_SERVER_TLS_KEY_FILE"},{"path":"server.tls.client_certificates","secret":false,"env":"AUTHELIA_SERVER_TLS_CLIENT_CERTIFICATES"},{"path":"server.headers.csp_template","secret":false,"env":"AUTHELIA_SERVER_HEADERS_CSP_TEMPLATE"},{"path":"server.buffers.read","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_READ"},{"path":"server.buffers.write","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_WRITE"},{"path":"server.timeouts.read","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_READ"},{"path":"server.timeouts.write","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_WRITE"},{"path":"server.timeouts.idle","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_IDLE"},{"path":"telemetry.metrics.enabled","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ENABLED"},{"path":"telemetry.metrics.address","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ADDRESS"},{"path":"telemetry.metrics.buffers.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_READ"},{"path":"telemetry.metrics.buffers.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_WRITE"},{"path":"telemetry.metrics.timeouts.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_READ"},{"path":"telemetry.metrics.timeouts.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_WRITE"},{"path":"telemetry.metrics.timeouts.idle","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_IDLE"},{"path":"webauthn.disable","secret":false,"env":"AUTHELIA_WEBAUTHN_DISABLE"},{"path":"webauthn.display_name","secret":false,"env":"AUTHELIA_WEBAUTHN_DISPLAY_NAME"},{"path":"webauthn.attestation_conveyance_preference","secret":false,"env":"AUTHELIA_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE"},{"path":"webauthn.user_verification","secret":false,"env":"AUTHELIA_WEBAUTHN_USER_VERIFICATION"},{"path":"webauthn.timeout","secret":false,"env":"AUTHELIA_WEBAUTHN_TIMEOUT"},{"path":"password_policy.standard.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_ENABLED"},{"path":"password_policy.standard.min_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MIN_LENGTH"},{"path":"password_policy.standard.max_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MAX_LENGTH"},{"path":"password_policy.standard.require_uppercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_UPPERCASE"},{"path":"password_policy.standard.require_lowercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_LOWERCASE"},{"path":"password_policy.standard.require_number","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_NUMBER"},{"path":"password_policy.standard.require_special","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_SPECIAL"},{"path":"password_policy.zxcvbn.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_ENABLED"},{"path":"password_policy.zxcvbn.min_score","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_MIN_SCORE"}] \ No newline at end of file +[{"path":"theme","secret":false,"env":"AUTHELIA_THEME"},{"path":"certificates_directory","secret":false,"env":"AUTHELIA_CERTIFICATES_DIRECTORY"},{"path":"jwt_secret","secret":true,"env":"AUTHELIA_JWT_SECRET_FILE"},{"path":"default_redirection_url","secret":false,"env":"AUTHELIA_DEFAULT_REDIRECTION_URL"},{"path":"default_2fa_method","secret":false,"env":"AUTHELIA_DEFAULT_2FA_METHOD"},{"path":"log.level","secret":false,"env":"AUTHELIA_LOG_LEVEL"},{"path":"log.format","secret":false,"env":"AUTHELIA_LOG_FORMAT"},{"path":"log.file_path","secret":false,"env":"AUTHELIA_LOG_FILE_PATH"},{"path":"log.keep_stdout","secret":false,"env":"AUTHELIA_LOG_KEEP_STDOUT"},{"path":"identity_providers.oidc.hmac_secret","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE"},{"path":"identity_providers.oidc.issuer_certificate_chain","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN"},{"path":"identity_providers.oidc.issuer_private_key","secret":true,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE"},{"path":"identity_providers.oidc.access_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ACCESS_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.authorize_code_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_AUTHORIZE_CODE_LIFESPAN"},{"path":"identity_providers.oidc.id_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ID_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.refresh_token_lifespan","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_REFRESH_TOKEN_LIFESPAN"},{"path":"identity_providers.oidc.enable_client_debug_messages","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_CLIENT_DEBUG_MESSAGES"},{"path":"identity_providers.oidc.minimum_parameter_entropy","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_MINIMUM_PARAMETER_ENTROPY"},{"path":"identity_providers.oidc.enforce_pkce","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENFORCE_PKCE"},{"path":"identity_providers.oidc.enable_pkce_plain_challenge","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_ENABLE_PKCE_PLAIN_CHALLENGE"},{"path":"identity_providers.oidc.cors.endpoints","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ENDPOINTS"},{"path":"identity_providers.oidc.cors.allowed_origins","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS"},{"path":"identity_providers.oidc.cors.allowed_origins_from_client_redirect_uris","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CORS_ALLOWED_ORIGINS_FROM_CLIENT_REDIRECT_URIS"},{"path":"identity_providers.oidc.clients","secret":false,"env":"AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS"},{"path":"authentication_backend.password_reset.disable","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_DISABLE"},{"path":"authentication_backend.password_reset.custom_url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_PASSWORD_RESET_CUSTOM_URL"},{"path":"authentication_backend.refresh_interval","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_REFRESH_INTERVAL"},{"path":"authentication_backend.file.path","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PATH"},{"path":"authentication_backend.file.watch","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_WATCH"},{"path":"authentication_backend.file.password.algorithm","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ALGORITHM"},{"path":"authentication_backend.file.password.argon2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_VARIANT"},{"path":"authentication_backend.file.password.argon2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_ITERATIONS"},{"path":"authentication_backend.file.password.argon2.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_MEMORY"},{"path":"authentication_backend.file.password.argon2.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_PARALLELISM"},{"path":"authentication_backend.file.password.argon2.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_KEY_LENGTH"},{"path":"authentication_backend.file.password.argon2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ARGON2_SALT_LENGTH"},{"path":"authentication_backend.file.password.sha2crypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_VARIANT"},{"path":"authentication_backend.file.password.sha2crypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.sha2crypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SHA2CRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.pbkdf2.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_VARIANT"},{"path":"authentication_backend.file.password.pbkdf2.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_ITERATIONS"},{"path":"authentication_backend.file.password.pbkdf2.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PBKDF2_SALT_LENGTH"},{"path":"authentication_backend.file.password.bcrypt.variant","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_VARIANT"},{"path":"authentication_backend.file.password.bcrypt.cost","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_BCRYPT_COST"},{"path":"authentication_backend.file.password.scrypt.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_ITERATIONS"},{"path":"authentication_backend.file.password.scrypt.block_size","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_BLOCK_SIZE"},{"path":"authentication_backend.file.password.scrypt.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_PARALLELISM"},{"path":"authentication_backend.file.password.scrypt.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_KEY_LENGTH"},{"path":"authentication_backend.file.password.scrypt.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SCRYPT_SALT_LENGTH"},{"path":"authentication_backend.file.password.iterations","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_ITERATIONS"},{"path":"authentication_backend.file.password.memory","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_MEMORY"},{"path":"authentication_backend.file.password.parallelism","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_PARALLELISM"},{"path":"authentication_backend.file.password.key_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_KEY_LENGTH"},{"path":"authentication_backend.file.password.salt_length","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_PASSWORD_SALT_LENGTH"},{"path":"authentication_backend.file.search.email","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_EMAIL"},{"path":"authentication_backend.file.search.case_insensitive","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_FILE_SEARCH_CASE_INSENSITIVE"},{"path":"authentication_backend.ldap.implementation","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_IMPLEMENTATION"},{"path":"authentication_backend.ldap.url","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_URL"},{"path":"authentication_backend.ldap.timeout","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TIMEOUT"},{"path":"authentication_backend.ldap.start_tls","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_START_TLS"},{"path":"authentication_backend.ldap.tls.minimum_version","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MINIMUM_VERSION"},{"path":"authentication_backend.ldap.tls.maximum_version","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_MAXIMUM_VERSION"},{"path":"authentication_backend.ldap.tls.skip_verify","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SKIP_VERIFY"},{"path":"authentication_backend.ldap.tls.server_name","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_SERVER_NAME"},{"path":"authentication_backend.ldap.tls.private_key","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_PRIVATE_KEY_FILE"},{"path":"authentication_backend.ldap.tls.certificate_chain","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_TLS_CERTIFICATE_CHAIN"},{"path":"authentication_backend.ldap.base_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_BASE_DN"},{"path":"authentication_backend.ldap.additional_users_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_USERS_DN"},{"path":"authentication_backend.ldap.users_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERS_FILTER"},{"path":"authentication_backend.ldap.additional_groups_dn","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_ADDITIONAL_GROUPS_DN"},{"path":"authentication_backend.ldap.groups_filter","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUPS_FILTER"},{"path":"authentication_backend.ldap.group_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_GROUP_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.username_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USERNAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.mail_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_MAIL_ATTRIBUTE"},{"path":"authentication_backend.ldap.display_name_attribute","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_DISPLAY_NAME_ATTRIBUTE"},{"path":"authentication_backend.ldap.permit_referrals","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_REFERRALS"},{"path":"authentication_backend.ldap.permit_unauthenticated_bind","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_UNAUTHENTICATED_BIND"},{"path":"authentication_backend.ldap.permit_feature_detection_failure","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PERMIT_FEATURE_DETECTION_FAILURE"},{"path":"authentication_backend.ldap.user","secret":false,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_USER"},{"path":"authentication_backend.ldap.password","secret":true,"env":"AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE"},{"path":"session.name","secret":false,"env":"AUTHELIA_SESSION_NAME"},{"path":"session.domain","secret":false,"env":"AUTHELIA_SESSION_DOMAIN"},{"path":"session.same_site","secret":false,"env":"AUTHELIA_SESSION_SAME_SITE"},{"path":"session.secret","secret":true,"env":"AUTHELIA_SESSION_SECRET_FILE"},{"path":"session.expiration","secret":false,"env":"AUTHELIA_SESSION_EXPIRATION"},{"path":"session.inactivity","secret":false,"env":"AUTHELIA_SESSION_INACTIVITY"},{"path":"session.remember_me_duration","secret":false,"env":"AUTHELIA_SESSION_REMEMBER_ME_DURATION"},{"path":"session.redis.host","secret":false,"env":"AUTHELIA_SESSION_REDIS_HOST"},{"path":"session.redis.port","secret":false,"env":"AUTHELIA_SESSION_REDIS_PORT"},{"path":"session.redis.username","secret":false,"env":"AUTHELIA_SESSION_REDIS_USERNAME"},{"path":"session.redis.password","secret":true,"env":"AUTHELIA_SESSION_REDIS_PASSWORD_FILE"},{"path":"session.redis.database_index","secret":false,"env":"AUTHELIA_SESSION_REDIS_DATABASE_INDEX"},{"path":"session.redis.maximum_active_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MAXIMUM_ACTIVE_CONNECTIONS"},{"path":"session.redis.minimum_idle_connections","secret":false,"env":"AUTHELIA_SESSION_REDIS_MINIMUM_IDLE_CONNECTIONS"},{"path":"session.redis.tls.minimum_version","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_MINIMUM_VERSION"},{"path":"session.redis.tls.maximum_version","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_MAXIMUM_VERSION"},{"path":"session.redis.tls.skip_verify","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SKIP_VERIFY"},{"path":"session.redis.tls.server_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_SERVER_NAME"},{"path":"session.redis.tls.private_key","secret":true,"env":"AUTHELIA_SESSION_REDIS_TLS_PRIVATE_KEY_FILE"},{"path":"session.redis.tls.certificate_chain","secret":false,"env":"AUTHELIA_SESSION_REDIS_TLS_CERTIFICATE_CHAIN"},{"path":"session.redis.high_availability.sentinel_name","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_NAME"},{"path":"session.redis.high_availability.sentinel_username","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_USERNAME"},{"path":"session.redis.high_availability.sentinel_password","secret":true,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_SENTINEL_PASSWORD_FILE"},{"path":"session.redis.high_availability.nodes","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_NODES"},{"path":"session.redis.high_availability.route_by_latency","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_BY_LATENCY"},{"path":"session.redis.high_availability.route_randomly","secret":false,"env":"AUTHELIA_SESSION_REDIS_HIGH_AVAILABILITY_ROUTE_RANDOMLY"},{"path":"totp.disable","secret":false,"env":"AUTHELIA_TOTP_DISABLE"},{"path":"totp.issuer","secret":false,"env":"AUTHELIA_TOTP_ISSUER"},{"path":"totp.algorithm","secret":false,"env":"AUTHELIA_TOTP_ALGORITHM"},{"path":"totp.digits","secret":false,"env":"AUTHELIA_TOTP_DIGITS"},{"path":"totp.period","secret":false,"env":"AUTHELIA_TOTP_PERIOD"},{"path":"totp.skew","secret":false,"env":"AUTHELIA_TOTP_SKEW"},{"path":"totp.secret_size","secret":false,"env":"AUTHELIA_TOTP_SECRET_SIZE"},{"path":"duo_api.disable","secret":false,"env":"AUTHELIA_DUO_API_DISABLE"},{"path":"duo_api.hostname","secret":false,"env":"AUTHELIA_DUO_API_HOSTNAME"},{"path":"duo_api.integration_key","secret":true,"env":"AUTHELIA_DUO_API_INTEGRATION_KEY_FILE"},{"path":"duo_api.secret_key","secret":true,"env":"AUTHELIA_DUO_API_SECRET_KEY_FILE"},{"path":"duo_api.enable_self_enrollment","secret":false,"env":"AUTHELIA_DUO_API_ENABLE_SELF_ENROLLMENT"},{"path":"access_control.default_policy","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_DEFAULT_POLICY"},{"path":"access_control.networks","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_NETWORKS"},{"path":"access_control.rules","secret":false,"env":"AUTHELIA_ACCESS_CONTROL_RULES"},{"path":"ntp.address","secret":false,"env":"AUTHELIA_NTP_ADDRESS"},{"path":"ntp.version","secret":false,"env":"AUTHELIA_NTP_VERSION"},{"path":"ntp.max_desync","secret":false,"env":"AUTHELIA_NTP_MAX_DESYNC"},{"path":"ntp.disable_startup_check","secret":false,"env":"AUTHELIA_NTP_DISABLE_STARTUP_CHECK"},{"path":"ntp.disable_failure","secret":false,"env":"AUTHELIA_NTP_DISABLE_FAILURE"},{"path":"regulation.max_retries","secret":false,"env":"AUTHELIA_REGULATION_MAX_RETRIES"},{"path":"regulation.find_time","secret":false,"env":"AUTHELIA_REGULATION_FIND_TIME"},{"path":"regulation.ban_time","secret":false,"env":"AUTHELIA_REGULATION_BAN_TIME"},{"path":"storage.local.path","secret":false,"env":"AUTHELIA_STORAGE_LOCAL_PATH"},{"path":"storage.mysql.host","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_HOST"},{"path":"storage.mysql.port","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_PORT"},{"path":"storage.mysql.database","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_DATABASE"},{"path":"storage.mysql.username","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_USERNAME"},{"path":"storage.mysql.password","secret":true,"env":"AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE"},{"path":"storage.mysql.timeout","secret":false,"env":"AUTHELIA_STORAGE_MYSQL_TIMEOUT"},{"path":"storage.postgres.host","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_HOST"},{"path":"storage.postgres.port","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_PORT"},{"path":"storage.postgres.database","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_DATABASE"},{"path":"storage.postgres.username","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_USERNAME"},{"path":"storage.postgres.password","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE"},{"path":"storage.postgres.timeout","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_TIMEOUT"},{"path":"storage.postgres.schema","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SCHEMA"},{"path":"storage.postgres.ssl.mode","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_MODE"},{"path":"storage.postgres.ssl.root_certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_ROOT_CERTIFICATE"},{"path":"storage.postgres.ssl.certificate","secret":false,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_CERTIFICATE"},{"path":"storage.postgres.ssl.key","secret":true,"env":"AUTHELIA_STORAGE_POSTGRES_SSL_KEY_FILE"},{"path":"storage.encryption_key","secret":true,"env":"AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE"},{"path":"notifier.disable_startup_check","secret":false,"env":"AUTHELIA_NOTIFIER_DISABLE_STARTUP_CHECK"},{"path":"notifier.filesystem.filename","secret":false,"env":"AUTHELIA_NOTIFIER_FILESYSTEM_FILENAME"},{"path":"notifier.smtp.host","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_HOST"},{"path":"notifier.smtp.port","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_PORT"},{"path":"notifier.smtp.timeout","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TIMEOUT"},{"path":"notifier.smtp.username","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_USERNAME"},{"path":"notifier.smtp.password","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE"},{"path":"notifier.smtp.identifier","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_IDENTIFIER"},{"path":"notifier.smtp.sender","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SENDER"},{"path":"notifier.smtp.subject","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_SUBJECT"},{"path":"notifier.smtp.startup_check_address","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_STARTUP_CHECK_ADDRESS"},{"path":"notifier.smtp.disable_require_tls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_REQUIRE_TLS"},{"path":"notifier.smtp.disable_html_emails","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_HTML_EMAILS"},{"path":"notifier.smtp.disable_starttls","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_DISABLE_STARTTLS"},{"path":"notifier.smtp.tls.minimum_version","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_MINIMUM_VERSION"},{"path":"notifier.smtp.tls.maximum_version","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_MAXIMUM_VERSION"},{"path":"notifier.smtp.tls.skip_verify","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SKIP_VERIFY"},{"path":"notifier.smtp.tls.server_name","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_SERVER_NAME"},{"path":"notifier.smtp.tls.private_key","secret":true,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_PRIVATE_KEY_FILE"},{"path":"notifier.smtp.tls.certificate_chain","secret":false,"env":"AUTHELIA_NOTIFIER_SMTP_TLS_CERTIFICATE_CHAIN"},{"path":"notifier.template_path","secret":false,"env":"AUTHELIA_NOTIFIER_TEMPLATE_PATH"},{"path":"server.host","secret":false,"env":"AUTHELIA_SERVER_HOST"},{"path":"server.port","secret":false,"env":"AUTHELIA_SERVER_PORT"},{"path":"server.path","secret":false,"env":"AUTHELIA_SERVER_PATH"},{"path":"server.asset_path","secret":false,"env":"AUTHELIA_SERVER_ASSET_PATH"},{"path":"server.enable_pprof","secret":false,"env":"AUTHELIA_SERVER_ENABLE_PPROF"},{"path":"server.enable_expvars","secret":false,"env":"AUTHELIA_SERVER_ENABLE_EXPVARS"},{"path":"server.disable_healthcheck","secret":false,"env":"AUTHELIA_SERVER_DISABLE_HEALTHCHECK"},{"path":"server.tls.certificate","secret":false,"env":"AUTHELIA_SERVER_TLS_CERTIFICATE"},{"path":"server.tls.key","secret":true,"env":"AUTHELIA_SERVER_TLS_KEY_FILE"},{"path":"server.tls.client_certificates","secret":false,"env":"AUTHELIA_SERVER_TLS_CLIENT_CERTIFICATES"},{"path":"server.headers.csp_template","secret":false,"env":"AUTHELIA_SERVER_HEADERS_CSP_TEMPLATE"},{"path":"server.buffers.read","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_READ"},{"path":"server.buffers.write","secret":false,"env":"AUTHELIA_SERVER_BUFFERS_WRITE"},{"path":"server.timeouts.read","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_READ"},{"path":"server.timeouts.write","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_WRITE"},{"path":"server.timeouts.idle","secret":false,"env":"AUTHELIA_SERVER_TIMEOUTS_IDLE"},{"path":"telemetry.metrics.enabled","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ENABLED"},{"path":"telemetry.metrics.address","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_ADDRESS"},{"path":"telemetry.metrics.buffers.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_READ"},{"path":"telemetry.metrics.buffers.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_BUFFERS_WRITE"},{"path":"telemetry.metrics.timeouts.read","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_READ"},{"path":"telemetry.metrics.timeouts.write","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_WRITE"},{"path":"telemetry.metrics.timeouts.idle","secret":false,"env":"AUTHELIA_TELEMETRY_METRICS_TIMEOUTS_IDLE"},{"path":"webauthn.disable","secret":false,"env":"AUTHELIA_WEBAUTHN_DISABLE"},{"path":"webauthn.display_name","secret":false,"env":"AUTHELIA_WEBAUTHN_DISPLAY_NAME"},{"path":"webauthn.attestation_conveyance_preference","secret":false,"env":"AUTHELIA_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE"},{"path":"webauthn.user_verification","secret":false,"env":"AUTHELIA_WEBAUTHN_USER_VERIFICATION"},{"path":"webauthn.timeout","secret":false,"env":"AUTHELIA_WEBAUTHN_TIMEOUT"},{"path":"password_policy.standard.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_ENABLED"},{"path":"password_policy.standard.min_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MIN_LENGTH"},{"path":"password_policy.standard.max_length","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_MAX_LENGTH"},{"path":"password_policy.standard.require_uppercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_UPPERCASE"},{"path":"password_policy.standard.require_lowercase","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_LOWERCASE"},{"path":"password_policy.standard.require_number","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_NUMBER"},{"path":"password_policy.standard.require_special","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_STANDARD_REQUIRE_SPECIAL"},{"path":"password_policy.zxcvbn.enabled","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_ENABLED"},{"path":"password_policy.zxcvbn.min_score","secret":false,"env":"AUTHELIA_PASSWORD_POLICY_ZXCVBN_MIN_SCORE"}] \ No newline at end of file diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go index 46db74610..a4c23a37a 100644 --- a/internal/authentication/ldap_user_provider.go +++ b/internal/authentication/ldap_user_provider.go @@ -53,7 +53,7 @@ func newLDAPUserProvider(config schema.LDAPAuthenticationBackend, disableResetPa config.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS } - tlsConfig := utils.NewTLSConfig(config.TLS, tls.VersionTLS12, certPool) + tlsConfig := utils.NewTLSConfig(config.TLS, certPool) var dialOpts = []ldap.DialOpt{ ldap.DialWithDialer(&net.Dialer{Timeout: config.Timeout}), diff --git a/internal/configuration/config.template.yml b/internal/configuration/config.template.yml index 16ea13b17..2572b06fe 100644 --- a/internal/configuration/config.template.yml +++ b/internal/configuration/config.template.yml @@ -315,6 +315,82 @@ authentication_backend: ## Minimum TLS version for either Secure LDAP or LDAP StartTLS. minimum_version: TLS1.2 + ## Maximum TLS version for either Secure LDAP or LDAP StartTLS. + maximum_version: TLS1.3 + + ## The certificate chain used with the private_key if the server requests TLS Client Authentication + ## i.e. Mutual TLS. + # certificate_chain: | + # -----BEGIN CERTIFICATE----- + # MIIC5jCCAc6gAwIBAgIRAK4Sj7FiN6PXo/urPfO4E7owDQYJKoZIhvcNAQELBQAw + # EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + # MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + # ADCCAQoCggEBAPKv3pSyP4ozGEiVLJ14dIWFCEGEgq7WUMI0SZZqQA2ID0L59U/Q + # /Usyy7uC9gfMUzODTpANtkOjFQcQAsxlR1FOjVBrX5QgjSvXwbQn3DtwMA7XWSl6 + # LuYx2rBYSlMSN5UZQm/RxMtXfLK2b51WgEEYDFi+nECSqKzR4R54eOPkBEWRfvuY + # 91AMjlhpivg8e4JWkq4LVQUKbmiFYwIdK8XQiN4blY9WwXwJFYs5sQ/UYMwBFi0H + # kWOh7GEjfxgoUOPauIueZSMSlQp7zqAH39N0ZSYb6cS0Npj57QoWZSY3ak87ebcR + # Nf4rCvZLby7LoN7qYCKxmCaDD3x2+NYpWH8CAwEAAaM1MDMwDgYDVR0PAQH/BAQD + # AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcN + # AQELBQADggEBAHSITqIQSNzonFl3DzxHPEzr2hp6peo45buAAtu8FZHoA+U7Icfh + # /ZXjPg7Xz+hgFwM/DTNGXkMWacQA/PaNWvZspgRJf2AXvNbMSs2UQODr7Tbv+Fb4 + # lyblmMUNYFMCFVAMU0eIxXAFq2qcwv8UMcQFT0Z/35s6PVOakYnAGGQjTfp5Ljuq + # wsdc/xWmM0cHWube6sdRRUD7SY20KU/kWzl8iFO0VbSSrDf1AlEhnLEkp1SPaxXg + # OdBnl98MeoramNiJ7NT6Jnyb3zZ578fjaWfThiBpagItI8GZmG4s4Ovh2JbheN8i + # ZsjNr9jqHTjhyLVbDRlmJzcqoj4JhbKs6/I^invalid DO NOT USE= + # -----END CERTIFICATE----- + # -----BEGIN CERTIFICATE----- + # MIIDBDCCAeygAwIBAgIRALJsPg21kA0zY4F1wUCIuoMwDQYJKoZIhvcNAQELBQAw + # EzERMA8GA1UEChMIQXV0aGVsaWEwHhcNNzAwMTAxMDAwMDAwWhcNNzEwMTAxMDAw + # MDAwWjATMREwDwYDVQQKEwhBdXRoZWxpYTCCASIwDQYJKoZIhvcNAQEBBQADggEP + # ADCCAQoCggEBAMXHBvVxUzYk0u34/DINMSF+uiOekKOAjOrC6Mi9Ww8ytPVO7t2S + # zfTvM+XnEJqkFQFgimERfG/eGhjF9XIEY6LtnXe8ATvOK4nTwdufzBaoeQu3Gd50 + # 5VXr6OHRo//ErrGvFXwP3g8xLePABsi/fkH3oDN+ztewOBMDzpd+KgTrk8ysv2ou + # kNRMKFZZqASvCgv0LD5KWvUCnL6wgf1oTXG7aztduA4oSkUP321GpOmBC5+5ElU7 + # ysoRzvD12o9QJ/IfEaulIX06w9yVMo60C/h6A3U6GdkT1SiyTIqR7v7KU/IWd/Qi + # Lfftcj91VhCmJ73Meff2e2S2PrpjdXbG5FMCAwEAAaNTMFEwDgYDVR0PAQH/BAQD + # AgKkMA8GA1UdJQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU + # Z7AtA3mzFc0InSBA5fiMfeLXA3owDQYJKoZIhvcNAQELBQADggEBAEE5hm1mtlk/ + # kviCoHH4evbpw7rxPxDftIQlqYTtvMM4eWY/6icFoSZ4fUHEWYyps8SsPu/8f2tf + # 71LGgZn0FdHi1QU2H8m0HHK7TFw+5Q6RLrLdSyk0PItJ71s9en7r8pX820nAFEHZ + # HkOSfJZ7B5hFgUDkMtVM6bardXAhoqcMk4YCU96e9d4PB4eI+xGc+mNuYvov3RbB + # D0s8ICyojeyPVLerz4wHjZu68Z5frAzhZ68YbzNs8j2fIBKKHkHyLG1iQyF+LJVj + # 2PjCP+auJsj6fQQpMGoyGtpLcSDh+ptcTngUD8JsWipzTCjmaNqdPHAOYmcgtf4b + # qocikt3WAdU^invalid DO NOT USE= + # -----END CERTIFICATE----- + + ## The private key used with the certificate_chain if the server requests TLS Client Authentication + ## i.e. Mutual TLS. + # private_key: | + # -----BEGIN RSA PRIVATE KEY----- + # MIIEpAIBAAKCAQEA8q/elLI/ijMYSJUsnXh0hYUIQYSCrtZQwjRJlmpADYgPQvn1 + # T9D9SzLLu4L2B8xTM4NOkA22Q6MVBxACzGVHUU6NUGtflCCNK9fBtCfcO3AwDtdZ + # KXou5jHasFhKUxI3lRlCb9HEy1d8srZvnVaAQRgMWL6cQJKorNHhHnh44+QERZF+ + # +5j3UAyOWGmK+Dx7glaSrgtVBQpuaIVjAh0rxdCI3huVj1bBfAkVizmxD9RgzAEW + # LQeRY6HsYSN/GChQ49q4i55lIxKVCnvOoAff03RlJhvpxLQ2mPntChZlJjdqTzt5 + # txE1/isK9ktvLsug3upgIrGYJoMPfHb41ilYfwIDAQABAoIBAQDTOdFf2JjHH1um + # aPgRAvNf9v7Nj5jytaRKs5nM6iNf46ls4QPreXnMhqSeSwj6lpNgBYxOgzC9Q+cc + # Y4ob/paJJPaIJTxmP8K/gyWcOQlNToL1l+eJ20eQoZm23NGr5fIsunSBwLEpTrdB + # ENqqtcwhW937K8Pxy/Q1nuLyU2bc6Tn/ivLozc8n27dpQWWKh8537VY7ancIaACr + # LJJLYxKqhQpjtBWAyCDvZQirnAOm9KnvIHaGXIswCZ4Xbsu0Y9NL+woARPyRVQvG + # jfxy4EmO9s1s6y7OObSukwKDSNihAKHx/VIbvVWx8g2Lv5fGOa+J2Y7o9Qurs8t5 + # BQwMTt0BAoGBAPUw5Z32EszNepAeV3E2mPFUc5CLiqAxagZJuNDO2pKtyN29ETTR + # Ma4O1cWtGb6RqcNNN/Iukfkdk27Q5nC9VJSUUPYelOLc1WYOoUf6oKRzE72dkMQV + # R4bf6TkjD+OVR17fAfkswkGahZ5XA7j48KIQ+YC4jbnYKSxZTYyKPjH/AoGBAP1i + # tqXt36OVlP+y84wWqZSjMelBIVa9phDVGJmmhz3i1cMni8eLpJzWecA3pfnG6Tm9 + # ze5M4whASleEt+M00gEvNaU9ND+z0wBfi+/DwJYIbv8PQdGrBiZFrPhTPjGQUldR + # lXccV2meeLZv7TagVxSi3DO6dSJfSEHyemd5j9mBAoGAX8Hv+0gOQZQCSOTAq8Nx + # 6dZcp9gHlNaXnMsP9eTDckOSzh636JPGvj6m+GPJSSbkURUIQ3oyokMNwFqvlNos + # fTaLhAOfjBZI9WnDTTQxpugWjphJ4HqbC67JC/qIiw5S6FdaEvGLEEoD4zoChywZ + # 9oGAn+fz2d/0/JAH/FpFPgsCgYEAp/ipZgPzziiZ9ov1wbdAQcWRj7RaWnssPFpX + # jXwEiXT3CgEMO4MJ4+KWIWOChrti3qFBg6i6lDyyS6Qyls7sLFbUdC7HlTcrOEMe + # rBoTcCI1GqZNlqWOVQ65ZIEiaI7o1vPBZo2GMQEZuq8mDKFsOMThvvTrM5cAep84 + # n6HJR4ECgYABWcbsSnr0MKvVth/inxjbKapbZnp2HUCuw87Ie5zK2Of/tbC20wwk + # yKw3vrGoE3O1t1g2m2tn8UGGASeZ842jZWjIODdSi5+icysQGuULKt86h/woz2SQ + # 27GoE2i5mh6Yez6VAYbUuns3FcwIsMyWLq043Tu2DNkx9ijOOAuQzw^invalid.. + # DO NOT USE== + # -----END RSA PRIVATE KEY----- + ## The distinguished name of the container searched for objects in the directory information tree. ## See also: additional_users_dn, additional_groups_dn. base_dn: dc=example,dc=com diff --git a/internal/configuration/decode_hooks.go b/internal/configuration/decode_hooks.go index 3fa27396e..a5872627f 100644 --- a/internal/configuration/decode_hooks.go +++ b/internal/configuration/decode_hooks.go @@ -346,6 +346,76 @@ func StringToX509CertificateChainHookFunc() mapstructure.DecodeHookFuncType { } } +// StringToTLSVersionHookFunc decodes strings to schema.TLSVersion's. +func StringToTLSVersionHookFunc() mapstructure.DecodeHookFuncType { + return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) { + var ptr bool + + if f.Kind() != reflect.String { + return data, nil + } + + prefixType := "" + + if t.Kind() == reflect.Ptr { + ptr = true + prefixType = "*" + } + + expectedType := reflect.TypeOf(schema.TLSVersion{}) + + if ptr && t.Elem() != expectedType { + return data, nil + } else if !ptr && t != expectedType { + return data, nil + } + + dataStr := data.(string) + + var result *schema.TLSVersion + + if result, err = schema.NewTLSVersion(dataStr); err != nil { + return nil, fmt.Errorf(errFmtDecodeHookCouldNotParse, dataStr, prefixType, expectedType, err) + } + + if ptr { + return result, nil + } + + return *result, nil + } +} + +// StringToCryptoPrivateKeyHookFunc decodes strings to schema.CryptographicPrivateKey's. +func StringToCryptoPrivateKeyHookFunc() mapstructure.DecodeHookFuncType { + return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) { + if f.Kind() != reflect.String { + return data, nil + } + + field, _ := reflect.TypeOf(schema.TLSConfig{}).FieldByName("PrivateKey") + expectedType := field.Type + + if t != expectedType { + return data, nil + } + + dataStr := data.(string) + + var i any + + if i, err = utils.ParseX509FromPEM([]byte(dataStr)); err != nil { + return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "", expectedType, err) + } + + if result, ok := i.(schema.CryptographicPrivateKey); !ok { + return nil, fmt.Errorf(errFmtDecodeHookCouldNotParseBasic, "", expectedType, fmt.Errorf("the data is for a %T not a %s", i, expectedType)) + } else { + return result, nil + } + } +} + // StringToPrivateKeyHookFunc decodes strings to rsa.PrivateKey's. func StringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType { return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) { diff --git a/internal/configuration/provider.go b/internal/configuration/provider.go index 91476ff4b..e64a3b661 100644 --- a/internal/configuration/provider.go +++ b/internal/configuration/provider.go @@ -64,6 +64,8 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o any) StringToX509CertificateHookFunc(), StringToX509CertificateChainHookFunc(), StringToPrivateKeyHookFunc(), + StringToCryptoPrivateKeyHookFunc(), + StringToTLSVersionHookFunc(), StringToPasswordDigestHookFunc(true), ToTimeDurationHookFunc(), ), diff --git a/internal/configuration/schema/authentication.go b/internal/configuration/schema/authentication.go index 35466e2e6..e070a503a 100644 --- a/internal/configuration/schema/authentication.go +++ b/internal/configuration/schema/authentication.go @@ -1,6 +1,7 @@ package schema import ( + "crypto/tls" "net/url" "time" ) @@ -180,7 +181,7 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationCustom = LDAPAuth GroupNameAttribute: "cn", Timeout: time.Second * 5, TLS: &TLSConfig{ - MinimumVersion: "TLS1.2", + MinimumVersion: TLSVersion{tls.VersionTLS12}, }, } @@ -194,6 +195,6 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory = GroupNameAttribute: "cn", Timeout: time.Second * 5, TLS: &TLSConfig{ - MinimumVersion: "TLS1.2", + MinimumVersion: TLSVersion{tls.VersionTLS12}, }, } diff --git a/internal/configuration/schema/const.go b/internal/configuration/schema/const.go index 23491b648..c59b8ffc5 100644 --- a/internal/configuration/schema/const.go +++ b/internal/configuration/schema/const.go @@ -1,6 +1,7 @@ package schema import ( + "errors" "regexp" "time" ) @@ -9,20 +10,51 @@ const ( argon2 = "argon2" argon2id = "argon2id" sha512 = "sha512" - sha256 = "sha256" ) -// ProfileRefreshDisabled represents a value for refresh_interval that disables the check entirely. +const ( + // TLSVersion13 is the textual representation of TLS 1.3. + TLSVersion13 = "TLS1.3" + + // TLSVersion12 is the textual representation of TLS 1.2. + TLSVersion12 = "TLS1.2" + + // TLSVersion11 is the textual representation of TLS 1.1. + TLSVersion11 = "TLS1.1" + + // TLSVersion10 is the textual representation of TLS 1.0. + TLSVersion10 = "TLS1.0" + + // SSLVersion30 is the textual representation of SSL 3.0. + SSLVersion30 = "SSL3.0" + + // Version13 is the textual representation of version 1.3. + Version13 = "1.3" + + // Version12 is the textual representation of version 1.2. + Version12 = "1.2" + + // Version11 is the textual representation of version 1.1. + Version11 = "1.1" + + // Version10 is the textual representation of version 1.0. + Version10 = "1.0" +) + +// ErrTLSVersionNotSupported returned when an unknown TLS version supplied. +var ErrTLSVersionNotSupported = errors.New("supplied tls version isn't supported") + +// ProfileRefreshDisabled represents a Value for refresh_interval that disables the check entirely. const ProfileRefreshDisabled = "disable" const ( - // ProfileRefreshAlways represents a value for refresh_interval that's the same as 0ms. + // ProfileRefreshAlways represents a Value for refresh_interval that's the same as 0ms. ProfileRefreshAlways = "always" - // RefreshIntervalDefault represents the default value of refresh_interval. + // RefreshIntervalDefault represents the default Value of refresh_interval. RefreshIntervalDefault = "5m" - // RefreshIntervalAlways represents the duration value refresh interval should have if set to always. + // RefreshIntervalAlways represents the duration Value refresh interval should have if set to always. RefreshIntervalAlways = 0 * time.Millisecond ) diff --git a/internal/configuration/schema/keys.go b/internal/configuration/schema/keys.go index 618a74403..283d4e692 100644 --- a/internal/configuration/schema/keys.go +++ b/internal/configuration/schema/keys.go @@ -84,8 +84,11 @@ var Keys = []string{ "authentication_backend.ldap.timeout", "authentication_backend.ldap.start_tls", "authentication_backend.ldap.tls.minimum_version", + "authentication_backend.ldap.tls.maximum_version", "authentication_backend.ldap.tls.skip_verify", "authentication_backend.ldap.tls.server_name", + "authentication_backend.ldap.tls.private_key", + "authentication_backend.ldap.tls.certificate_chain", "authentication_backend.ldap.base_dn", "authentication_backend.ldap.additional_users_dn", "authentication_backend.ldap.users_filter", @@ -115,8 +118,11 @@ var Keys = []string{ "session.redis.maximum_active_connections", "session.redis.minimum_idle_connections", "session.redis.tls.minimum_version", + "session.redis.tls.maximum_version", "session.redis.tls.skip_verify", "session.redis.tls.server_name", + "session.redis.tls.private_key", + "session.redis.tls.certificate_chain", "session.redis.high_availability.sentinel_name", "session.redis.high_availability.sentinel_username", "session.redis.high_availability.sentinel_password", @@ -195,8 +201,11 @@ var Keys = []string{ "notifier.smtp.disable_html_emails", "notifier.smtp.disable_starttls", "notifier.smtp.tls.minimum_version", + "notifier.smtp.tls.maximum_version", "notifier.smtp.tls.skip_verify", "notifier.smtp.tls.server_name", + "notifier.smtp.tls.private_key", + "notifier.smtp.tls.certificate_chain", "notifier.template_path", "server.host", "server.port", diff --git a/internal/configuration/schema/notifier.go b/internal/configuration/schema/notifier.go index 159a90dea..a95b935f1 100644 --- a/internal/configuration/schema/notifier.go +++ b/internal/configuration/schema/notifier.go @@ -1,6 +1,7 @@ package schema import ( + "crypto/tls" "net/mail" "time" ) @@ -42,6 +43,6 @@ var DefaultSMTPNotifierConfiguration = SMTPNotifierConfiguration{ Identifier: "localhost", StartupCheckAddress: mail.Address{Name: "Authelia Test", Address: "test@authelia.com"}, TLS: &TLSConfig{ - MinimumVersion: "TLS1.2", + MinimumVersion: TLSVersion{tls.VersionTLS12}, }, } diff --git a/internal/configuration/schema/session.go b/internal/configuration/schema/session.go index 6b55e8df3..2a935ee9a 100644 --- a/internal/configuration/schema/session.go +++ b/internal/configuration/schema/session.go @@ -1,6 +1,7 @@ package schema import ( + "crypto/tls" "time" ) @@ -54,3 +55,10 @@ var DefaultSessionConfiguration = SessionConfiguration{ RememberMeDuration: time.Hour * 24 * 30, SameSite: "lax", } + +// DefaultRedisConfiguration is the default redis configuration. +var DefaultRedisConfiguration = RedisSessionConfiguration{ + TLS: &TLSConfig{ + MinimumVersion: TLSVersion{Value: tls.VersionTLS12}, + }, +} diff --git a/internal/configuration/schema/shared.go b/internal/configuration/schema/shared.go index 336121839..13d56b037 100644 --- a/internal/configuration/schema/shared.go +++ b/internal/configuration/schema/shared.go @@ -6,9 +6,20 @@ import ( // TLSConfig is a representation of the TLS configuration. type TLSConfig struct { - MinimumVersion string `koanf:"minimum_version"` - SkipVerify bool `koanf:"skip_verify"` - ServerName string `koanf:"server_name"` + MinimumVersion TLSVersion `koanf:"minimum_version"` + MaximumVersion TLSVersion `koanf:"maximum_version"` + + SkipVerify bool `koanf:"skip_verify"` + ServerName string `koanf:"server_name"` + + PrivateKey CryptographicPrivateKey `koanf:"private_key"` + CertificateChain X509CertificateChain `koanf:"certificate_chain"` +} + +// TLSCertificateConfig is a representation of the TLS Certificate configuration. +type TLSCertificateConfig struct { + Key CryptographicPrivateKey `koanf:"key"` + CertificateChain X509CertificateChain `koanf:"certificate_chain"` } // ServerTimeouts represents server timeout configurations. diff --git a/internal/configuration/schema/types.go b/internal/configuration/schema/types.go index e421d7eb8..ee4ed3934 100644 --- a/internal/configuration/schema/types.go +++ b/internal/configuration/schema/types.go @@ -5,6 +5,7 @@ import ( "crypto/ecdsa" "crypto/ed25519" "crypto/rsa" + "crypto/tls" "crypto/x509" "encoding/pem" "fmt" @@ -174,6 +175,71 @@ func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error) return chain, nil } +// NewTLSVersion returns a new TLSVersion given a string. +func NewTLSVersion(input string) (version *TLSVersion, err error) { + switch strings.ReplaceAll(strings.ToUpper(input), " ", "") { + case TLSVersion13, Version13: + return &TLSVersion{tls.VersionTLS13}, nil + case TLSVersion12, Version12: + return &TLSVersion{tls.VersionTLS12}, nil + case TLSVersion11, Version11: + return &TLSVersion{tls.VersionTLS11}, nil + case TLSVersion10, Version10: + return &TLSVersion{tls.VersionTLS10}, nil + case SSLVersion30: + return &TLSVersion{tls.VersionSSL30}, nil //nolint:staticcheck + } + + return nil, ErrTLSVersionNotSupported +} + +// TLSVersion is a struct which handles tls.Config versions. +type TLSVersion struct { + Value uint16 +} + +// MaxVersion returns the value of this as a MaxVersion value. +func (v *TLSVersion) MaxVersion() uint16 { + if v.Value == 0 { + return tls.VersionTLS13 + } + + return v.Value +} + +// MinVersion returns the value of this as a MinVersion value. +func (v *TLSVersion) MinVersion() uint16 { + if v.Value == 0 { + return tls.VersionTLS12 + } + + return v.Value +} + +// String provides the Stringer. +func (v *TLSVersion) String() string { + switch v.Value { + case tls.VersionTLS10: + return TLSVersion10 + case tls.VersionTLS11: + return TLSVersion11 + case tls.VersionTLS12: + return TLSVersion12 + case tls.VersionTLS13: + return TLSVersion13 + case tls.VersionSSL30: //nolint:staticcheck + return SSLVersion30 + default: + return "" + } +} + +// CryptographicPrivateKey represents the actual crypto.PrivateKey interface. +type CryptographicPrivateKey interface { + Public() crypto.PublicKey + Equal(x crypto.PrivateKey) bool +} + // X509CertificateChain is a helper struct that holds a list of *x509.Certificate's. type X509CertificateChain struct { certs []*x509.Certificate @@ -259,10 +325,32 @@ func (c *X509CertificateChain) EqualKey(other any) (equal bool) { } // Certificates for this X509CertificateChain. -func (c *X509CertificateChain) Certificates() []*x509.Certificate { +func (c *X509CertificateChain) Certificates() (certificates []*x509.Certificate) { return c.certs } +// CertificatesRaw for this X509CertificateChain. +func (c *X509CertificateChain) CertificatesRaw() (certificates [][]byte) { + if !c.HasCertificates() { + return nil + } + + for _, cert := range c.certs { + certificates = append(certificates, cert.Raw) + } + + return certificates +} + +// Leaf returns the first certificate if available for use with tls.Certificate. +func (c *X509CertificateChain) Leaf() (leaf *x509.Certificate) { + if !c.HasCertificates() { + return nil + } + + return c.certs[0] +} + // Validate the X509CertificateChain ensuring the certificates were provided in the correct order // (with nth being signed by the nth+1), and that all of the certificates are valid based on the current time. func (c *X509CertificateChain) Validate() (err error) { diff --git a/internal/configuration/test_resources/config.yml b/internal/configuration/test_resources/config.yml index fbe2eca0a..ea48a847f 100644 --- a/internal/configuration/test_resources/config.yml +++ b/internal/configuration/test_resources/config.yml @@ -18,6 +18,35 @@ duo_api: authentication_backend: ldap: url: ldap://127.0.0.1 + tls: + private_key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA6z1LOg1ZCqb0lytXWZ+MRBpMHEXOoTOLYgfZXt1IYyE3Z758 + cyalk0NYQhY5cZDsXPYWPvAHiPMUxutWkoxFwby56S+AbIMa3/Is+ILrHRJs8Exn + ZkpyrYFxPX12app2kErdmAkHSx0Z5/kuXiz96PHs8S8/ZbyZolLHzdfLtSzjvRm5 + Zue5iFzsf19NJz5CIBfv8g5lRwtE8wNJoRSpn1xq7fqfuA0weDNFPzjlNWRLy6aa + rK7qJexRkmkCs4sLgyl+9NODYJpvmN8E1yhyC27E0joI6rBFVW7Ihv+cSPCdDzGp + EWe81x3AeqAa3mjVqkiq4u4Z2i8JDgBaPboqJwIDAQABAoIBAAFdLZ58jVOefDSU + L8F5R1rtvBs93GDa56f926jNJ6pLewLC+/2+757W+SAI+PRLntM7Kg3bXm/Q2QH+ + Q1Y+MflZmspbWCdI61L5GIGoYKyeers59i+FpvySj5GHtLQRiTZ0+Kv1AXHSDWBm + 9XneUOqU3IbZe0ifu1RRno72/VtjkGXbW8Mkkw+ohyGbIeTx/0/JQ6sSNZTT3Vk7 + 8i4IXptq3HSF0/vqZuah8rShoeNq72pD1YLM9YPdL5by1QkDLnqATDiCpLBTCaNV + I8sqYEun+HYbQzBj8ZACG2JVZpEEidONWQHw5BPWO95DSZYrVnEkuCqeH+u5vYt7 + CHuJ3AECgYEA+W3v5z+j91w1VPHS0VB3SCDMouycAMIUnJPAbt+0LPP0scUFsBGE + hPAKddC54pmMZRQ2KIwBKiyWfCrJ8Xz8Yogn7fJgmwTHidJBr2WQpIEkNGlK3Dzi + jXL2sh0yC7sHvn0DqiQ79l/e7yRbSnv2wrTJEczOOH2haD7/tBRyCYECgYEA8W+q + E9YyGvEltnPFaOxofNZ8LHVcZSsQI5b6fc0iE7fjxFqeXPXEwGSOTwqQLQRiHn9b + CfPmIG4Vhyq0otVmlPvUnfBZ2OK+tl5X2/mQFO3ROMdvpi0KYa994uqfJdSTaqLn + jjoKFB906UFHnDQDLZUNiV1WwnkTglgLc+xrd6cCgYEAqqthyv6NyBTM3Tm2gcio + Ra9Dtntl51LlXZnvwy3IkDXBCd6BHM9vuLKyxZiziGx+Vy90O1xI872cnot8sINQ + Am+dur/tAEVN72zxyv0Y8qb2yfH96iKy9gxi5s75TnOEQgAygLnYWaWR2lorKRUX + bHTdXBOiS58S0UzCFEslGIECgYBqkO4SKWYeTDhoKvuEj2yjRYyzlu28XeCWxOo1 + otiauX0YSyNBRt2cSgYiTzhKFng0m+QUJYp63/wymB/5C5Zmxi0XtWIDADpLhqLj + HmmBQ2Mo26alQ5YkffBju0mZyhVzaQop1eZi8WuKFV1FThPlB7hc3E0SM5zv2Grd + tQnOWwKBgQC40yZY0PcjuILhy+sIc0Wvh7LUA7taSdTye149kRvbvsCDN7Jh75lM + USjhLXY0Nld2zBm9r8wMb81mXH29uvD+tDqqsICvyuKlA/tyzXR+QTr7dCVKVwu0 + 1YjCJ36UpTsLre2f8nOSLtNmRfDPtbOE2mkOoO9dD9UU0XZwnvn9xw== + -----END RSA PRIVATE KEY----- base_dn: dc=example,dc=com username_attribute: uid additional_users_dn: ou=users diff --git a/internal/configuration/validator/access_control_test.go b/internal/configuration/validator/access_control_test.go index 7bf24d52f..ae7dabb18 100644 --- a/internal/configuration/validator/access_control_test.go +++ b/internal/configuration/validator/access_control_test.go @@ -201,6 +201,22 @@ func (suite *AccessControl) TestShouldRaiseErrorInvalidSubject() { suite.Assert().EqualError(suite.validator.Errors()[1], fmt.Sprintf(errAccessControlRuleBypassPolicyInvalidWithSubjects, ruleDescriptor(1, suite.config.AccessControl.Rules[0]))) } +func (suite *AccessControl) TestShouldRaiseErrorBypassWithSubjectDomainRegexGroup() { + suite.config.AccessControl.Rules = []schema.ACLRule{ + { + DomainsRegex: MustCompileRegexps([]string{`^(?P\w+)\.example\.com$`}), + Policy: "bypass", + }, + } + + ValidateRules(suite.config, suite.validator) + + suite.Require().Len(suite.validator.Warnings(), 0) + suite.Require().Len(suite.validator.Errors(), 1) + + suite.Assert().EqualError(suite.validator.Errors()[0], "access control: rule #1: 'policy' option 'bypass' is not supported when 'domain_regex' option contains the user or group named matches. For more information see: https://www.authelia.com/c/acl-match-concept-2") +} + func (suite *AccessControl) TestShouldSetQueryDefaults() { domains := []string{"public.example.com"} suite.config.AccessControl.Rules = []schema.ACLRule{ @@ -359,3 +375,13 @@ func TestShouldReturnCorrectResultsForValidNetworkGroups(t *testing.T) { assert.True(t, validNetwork) assert.False(t, invalidNetwork) } + +func MustCompileRegexps(exps []string) (regexps []regexp.Regexp) { + regexps = make([]regexp.Regexp, len(exps)) + + for i, exp := range exps { + regexps[i] = *regexp.MustCompile(exp) + } + + return regexps +} diff --git a/internal/configuration/validator/authentication.go b/internal/configuration/validator/authentication.go index 280d383ec..62fe289b2 100644 --- a/internal/configuration/validator/authentication.go +++ b/internal/configuration/validator/authentication.go @@ -321,19 +321,35 @@ func validateLDAPAuthenticationBackend(config *schema.AuthenticationBackend, val validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.LDAP.Implementation, strings.Join([]string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory}, "', '"))) } + configDefaultTLS := &schema.TLSConfig{} + if implementation != nil { - setDefaultImplementationLDAPAuthenticationBackendProfileMisc(config.LDAP, implementation) + if config.LDAP.Timeout == 0 { + config.LDAP.Timeout = implementation.Timeout + } + + configDefaultTLS = &schema.TLSConfig{ + MinimumVersion: implementation.TLS.MinimumVersion, + MaximumVersion: implementation.TLS.MaximumVersion, + } + setDefaultImplementationLDAPAuthenticationBackendProfileAttributes(config.LDAP, implementation) } - if config.LDAP.TLS != nil { - if _, err := utils.TLSStringToTLSConfigVersion(config.LDAP.TLS.MinimumVersion); err != nil { - validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSMinVersion, config.LDAP.TLS.MinimumVersion, err)) - } + if config.LDAP.URL == "" { + validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "url")) } else { + configDefaultTLS.ServerName = validateLDAPAuthenticationBackendURL(config.LDAP, validator) + } + + if config.LDAP.TLS == nil { config.LDAP.TLS = &schema.TLSConfig{} } + if err := ValidateTLSConfig(config.LDAP.TLS, configDefaultTLS); err != nil { + validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSConfigInvalid, err)) + } + if strings.Contains(config.LDAP.UsersFilter, "{0}") { validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "users_filter", "{0}", "{input}")) } @@ -346,31 +362,9 @@ func validateLDAPAuthenticationBackend(config *schema.AuthenticationBackend, val validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "groups_filter", "{1}", "{username}")) } - if config.LDAP.URL == "" { - validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "url")) - } else { - validateLDAPAuthenticationBackendURL(config.LDAP, validator) - } - validateLDAPRequiredParameters(config, validator) } -func setDefaultImplementationLDAPAuthenticationBackendProfileMisc(config *schema.LDAPAuthenticationBackend, implementation *schema.LDAPAuthenticationBackend) { - if config.Timeout == 0 { - config.Timeout = implementation.Timeout - } - - if implementation.TLS == nil { - return - } - - if config.TLS == nil { - config.TLS = implementation.TLS - } else if config.TLS.MinimumVersion == "" { - config.TLS.MinimumVersion = implementation.TLS.MinimumVersion - } -} - func ldapImplementationShouldSetStr(config, implementation string) bool { return config == "" && implementation != "" } @@ -401,7 +395,7 @@ func setDefaultImplementationLDAPAuthenticationBackendProfileAttributes(config * } } -func validateLDAPAuthenticationBackendURL(config *schema.LDAPAuthenticationBackend, validator *schema.StructValidator) { +func validateLDAPAuthenticationBackendURL(config *schema.LDAPAuthenticationBackend, validator *schema.StructValidator) (hostname string) { var ( parsedURL *url.URL err error @@ -420,9 +414,8 @@ func validateLDAPAuthenticationBackendURL(config *schema.LDAPAuthenticationBacke } config.URL = parsedURL.String() - if config.TLS.ServerName == "" { - config.TLS.ServerName = parsedURL.Hostname() - } + + return parsedURL.Hostname() } func validateLDAPRequiredParameters(config *schema.AuthenticationBackend, validator *schema.StructValidator) { diff --git a/internal/configuration/validator/authentication_test.go b/internal/configuration/validator/authentication_test.go index 328e90590..5f589063c 100644 --- a/internal/configuration/validator/authentication_test.go +++ b/internal/configuration/validator/authentication_test.go @@ -1,6 +1,7 @@ package validator import ( + "crypto/tls" "net/url" "testing" "time" @@ -625,6 +626,42 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNot suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'password' is required") } +func (suite *LDAPAuthenticationBackendSuite) TestShouldNotRaiseErrorWhenPasswordNotProvidedWithPermitUnauthenticatedBind() { + suite.config.LDAP.Password = "" + suite.config.LDAP.PermitUnauthenticatedBind = true + + ValidateAuthenticationBackend(&suite.config, suite.validator) + + suite.Assert().Len(suite.validator.Warnings(), 0) + suite.Require().Len(suite.validator.Errors(), 1) + + suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'permit_unauthenticated_bind' can't be enabled when password reset is enabled") +} + +func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordProvidedWithPermitUnauthenticatedBind() { + suite.config.LDAP.Password = "test" + suite.config.LDAP.PermitUnauthenticatedBind = true + suite.config.PasswordReset.Disable = true + + ValidateAuthenticationBackend(&suite.config, suite.validator) + + suite.Assert().Len(suite.validator.Warnings(), 0) + suite.Require().Len(suite.validator.Errors(), 1) + + suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'permit_unauthenticated_bind' can't be enabled when a password is specified") +} + +func (suite *LDAPAuthenticationBackendSuite) TestShouldNotRaiseErrorWhenPermitUnauthenticatedBindConfiguredCorrectly() { + suite.config.LDAP.Password = "" + suite.config.LDAP.PermitUnauthenticatedBind = true + suite.config.PasswordReset.Disable = true + + ValidateAuthenticationBackend(&suite.config, suite.validator) + + suite.Assert().Len(suite.validator.Warnings(), 0) + suite.Require().Len(suite.validator.Errors(), 0) +} + func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotProvided() { suite.config.LDAP.BaseDN = "" @@ -783,19 +820,19 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlacehol } func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultTLSMinimumVersion() { - suite.config.LDAP.TLS = &schema.TLSConfig{MinimumVersion: ""} + suite.config.LDAP.TLS = &schema.TLSConfig{MinimumVersion: schema.TLSVersion{}} ValidateAuthenticationBackend(&suite.config, suite.validator) suite.Assert().Len(suite.validator.Warnings(), 0) suite.Assert().Len(suite.validator.Errors(), 0) - suite.Assert().Equal(schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS.MinimumVersion, suite.config.LDAP.TLS.MinimumVersion) + suite.Assert().Equal(schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS.MinimumVersion.Value, suite.config.LDAP.TLS.MinimumVersion.MinVersion()) } -func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue() { +func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowSSL30() { suite.config.LDAP.TLS = &schema.TLSConfig{ - MinimumVersion: "SSL2.0", + MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck } ValidateAuthenticationBackend(&suite.config, suite.validator) @@ -803,7 +840,21 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue() suite.Assert().Len(suite.validator.Warnings(), 0) suite.Require().Len(suite.validator.Errors(), 1) - suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: tls: option 'minimum_tls_version' is invalid: SSL2.0: supplied tls version isn't supported") + suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") +} + +func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowTLSVerMinGreaterThanVerMax() { + suite.config.LDAP.TLS = &schema.TLSConfig{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS12}, + } + + ValidateAuthenticationBackend(&suite.config, suite.validator) + + suite.Assert().Len(suite.validator.Warnings(), 0) + suite.Require().Len(suite.validator.Errors(), 1) + + suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.2") } func TestLdapAuthenticationBackend(t *testing.T) { diff --git a/internal/configuration/validator/configuration_test.go b/internal/configuration/validator/configuration_test.go index e212fbf95..18ff2ae53 100644 --- a/internal/configuration/validator/configuration_test.go +++ b/internal/configuration/validator/configuration_test.go @@ -25,7 +25,7 @@ func newDefaultConfig() schema.Configuration { DefaultPolicy: "two_factor", } config.Session = schema.SessionConfiguration{ - Domain: "example.com", + Domain: examplecom, Name: "authelia_session", Secret: "secret", } diff --git a/internal/configuration/validator/const.go b/internal/configuration/validator/const.go index 30a7ab357..24c8810f5 100644 --- a/internal/configuration/validator/const.go +++ b/internal/configuration/validator/const.go @@ -58,7 +58,11 @@ const ( errFmtNotifierTemplatePathUnknownError = "notifier: option 'template_path' refers to location '%s' which couldn't be opened: %w" errFmtNotifierFileSystemFileNameNotConfigured = "notifier: filesystem: option 'filename' is required" errFmtNotifierSMTPNotConfigured = "notifier: smtp: option '%s' is required" - errFmtNotifierStartTlsDisabled = "Notifier SMTP connection has opportunistic STARTTLS explicitly disabled which means all emails will be sent insecurely over plain text and this setting is only necessary for non-compliant SMTP servers which advertise they support STARTTLS when they actually don't support STARTTLS" + errFmtNotifierSMTPTLSConfigInvalid = "notifier: smtp: tls: %w" + errFmtNotifierStartTlsDisabled = "notifier: smtp: option 'disable_starttls' is enabled: " + + "opportunistic STARTTLS is explicitly disabled which means all emails will be sent insecurely over plaintext " + + "and this setting is only necessary for non-compliant SMTP servers which advertise they support STARTTLS " + + "when they actually don't support STARTTLS" ) const ( @@ -91,10 +95,9 @@ const ( errFmtLDAPAuthBackendUnauthenticatedBindWithPassword = "authentication_backend: ldap: option 'permit_unauthenticated_bind' can't be enabled when a password is specified" errFmtLDAPAuthBackendUnauthenticatedBindWithResetEnabled = "authentication_backend: ldap: option 'permit_unauthenticated_bind' can't be enabled when password reset is enabled" - errFmtLDAPAuthBackendMissingOption = "authentication_backend: ldap: option '%s' is required" - errFmtLDAPAuthBackendTLSMinVersion = "authentication_backend: ldap: tls: option " + - "'minimum_tls_version' is invalid: %s: %w" - errFmtLDAPAuthBackendImplementation = "authentication_backend: ldap: option 'implementation' " + + errFmtLDAPAuthBackendMissingOption = "authentication_backend: ldap: option '%s' is required" + errFmtLDAPAuthBackendTLSConfigInvalid = "authentication_backend: ldap: tls: %w" + errFmtLDAPAuthBackendImplementation = "authentication_backend: ldap: option 'implementation' " + errSuffixMustBeOneOf errFmtLDAPAuthBackendFilterReplacedPlaceholders = "authentication_backend: ldap: option " + "'%s' has an invalid placeholder: '%s' has been removed, please use '%s' instead" @@ -247,6 +250,7 @@ const ( errFmtSessionRedisPortRange = "session: redis: option 'port' must be between 1 and 65535 but is configured as '%d'" errFmtSessionRedisHostRequired = "session: redis: option 'host' is required" errFmtSessionRedisHostOrNodesRequired = "session: redis: option 'host' or the 'high_availability' option 'nodes' is required" + errFmtSessionRedisTLSConfigInvalid = "session: redis: tls: %w" errFmtSessionRedisSentinelMissingName = "session: redis: high_availability: option 'sentinel_name' is required" errFmtSessionRedisSentinelNodeHostMissing = "session: redis: high_availability: option 'nodes': option 'host' is required for each node but one or more nodes are missing this" diff --git a/internal/configuration/validator/const_test.go b/internal/configuration/validator/const_test.go index 4fe7d56f0..ab398a187 100644 --- a/internal/configuration/validator/const_test.go +++ b/internal/configuration/validator/const_test.go @@ -10,3 +10,7 @@ const ( testLDAPUser = "user" testEncryptionKey = "a_not_so_secure_encryption_key" ) + +const ( + examplecom = "example.com" +) diff --git a/internal/configuration/validator/identity_providers_test.go b/internal/configuration/validator/identity_providers_test.go index 23eca844a..727676bb4 100644 --- a/internal/configuration/validator/identity_providers_test.go +++ b/internal/configuration/validator/identity_providers_test.go @@ -257,7 +257,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) { RedirectURIs: []string{ "https://google.com", }, - SectorIdentifier: mustParseURL("example.com"), + SectorIdentifier: mustParseURL(examplecom), }, }, }, @@ -289,12 +289,12 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) { }, }, Errors: []string{ - fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", "example.com", "scheme", "https"), - fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", "example.com", "path", "/path"), - fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", "example.com", "query", "query=abc"), - fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", "example.com", "fragment", "fragment"), - fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", "example.com", "username", "user"), - fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", "example.com", "password"), + fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "scheme", "https"), + fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "path", "/path"), + fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "query", "query=abc"), + fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "fragment", "fragment"), + fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "username", "user"), + fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "password"), }, }, { diff --git a/internal/configuration/validator/notifier.go b/internal/configuration/validator/notifier.go index ea37f972e..b3515188f 100644 --- a/internal/configuration/validator/notifier.go +++ b/internal/configuration/validator/notifier.go @@ -83,11 +83,17 @@ func validateSMTPNotifier(config *schema.SMTPNotifierConfiguration, validator *s } if config.TLS == nil { - config.TLS = schema.DefaultSMTPNotifierConfiguration.TLS + config.TLS = &schema.TLSConfig{} } - if config.TLS.ServerName == "" { - config.TLS.ServerName = config.Host + configDefaultTLS := &schema.TLSConfig{ + ServerName: config.Host, + MinimumVersion: schema.DefaultSMTPNotifierConfiguration.TLS.MinimumVersion, + MaximumVersion: schema.DefaultSMTPNotifierConfiguration.TLS.MaximumVersion, + } + + if err := ValidateTLSConfig(config.TLS, configDefaultTLS); err != nil { + validator.Push(fmt.Errorf(errFmtNotifierSMTPTLSConfigInvalid, err)) } if config.DisableStartTLS { diff --git a/internal/configuration/validator/notifier_test.go b/internal/configuration/validator/notifier_test.go index c22b2494f..7ba756711 100644 --- a/internal/configuration/validator/notifier_test.go +++ b/internal/configuration/validator/notifier_test.go @@ -1,6 +1,7 @@ package validator import ( + "crypto/tls" "fmt" "net/mail" "testing" @@ -22,7 +23,7 @@ func (suite *NotifierSuite) SetupTest() { Username: "john", Password: "password", Sender: mail.Address{Name: "Authelia", Address: "authelia@example.com"}, - Host: "example.com", + Host: examplecom, Port: 25, } suite.config.FileSystem = nil @@ -77,8 +78,8 @@ func (suite *NotifierSuite) TestSMTPShouldSetTLSDefaults() { suite.Assert().Len(suite.validator.Warnings(), 0) suite.Assert().Len(suite.validator.Errors(), 0) - suite.Assert().Equal("example.com", suite.config.SMTP.TLS.ServerName) - suite.Assert().Equal("TLS1.2", suite.config.SMTP.TLS.MinimumVersion) + suite.Assert().Equal(examplecom, suite.config.SMTP.TLS.ServerName) + suite.Assert().Equal(uint16(tls.VersionTLS12), suite.config.SMTP.TLS.MinimumVersion.Value) suite.Assert().False(suite.config.SMTP.TLS.SkipVerify) } @@ -96,7 +97,7 @@ func (suite *NotifierSuite) TestSMTPShouldDefaultStartupCheckAddress() { func (suite *NotifierSuite) TestSMTPShouldDefaultTLSServerNameToHost() { suite.config.SMTP.Host = "google.com" suite.config.SMTP.TLS = &schema.TLSConfig{ - MinimumVersion: "TLS1.1", + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS11}, } ValidateNotifier(&suite.config, suite.validator) @@ -105,10 +106,51 @@ func (suite *NotifierSuite) TestSMTPShouldDefaultTLSServerNameToHost() { suite.Assert().Len(suite.validator.Errors(), 0) suite.Assert().Equal("google.com", suite.config.SMTP.TLS.ServerName) - suite.Assert().Equal("TLS1.1", suite.config.SMTP.TLS.MinimumVersion) + suite.Assert().Equal(uint16(tls.VersionTLS11), suite.config.SMTP.TLS.MinimumVersion.MinVersion()) suite.Assert().False(suite.config.SMTP.TLS.SkipVerify) } +func (suite *NotifierSuite) TestSMTPShouldErrorOnSSL30() { + suite.config.SMTP.Host = examplecom + suite.config.SMTP.TLS = &schema.TLSConfig{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck + } + + ValidateNotifier(&suite.config, suite.validator) + + suite.Assert().Len(suite.validator.Warnings(), 0) + suite.Require().Len(suite.validator.Errors(), 1) + + suite.Assert().EqualError(suite.validator.Errors()[0], "notifier: smtp: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") +} + +func (suite *NotifierSuite) TestSMTPShouldErrorOnTLSMinVerGreaterThanMaxVer() { + suite.config.SMTP.Host = examplecom + suite.config.SMTP.TLS = &schema.TLSConfig{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS10}, + } + + ValidateNotifier(&suite.config, suite.validator) + + suite.Assert().Len(suite.validator.Warnings(), 0) + suite.Require().Len(suite.validator.Errors(), 1) + + suite.Assert().EqualError(suite.validator.Errors()[0], "notifier: smtp: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.0") +} + +func (suite *NotifierSuite) TestSMTPShouldWarnOnDisabledSTARTTLS() { + suite.config.SMTP.Host = examplecom + suite.config.SMTP.DisableStartTLS = true + + ValidateNotifier(&suite.config, suite.validator) + + suite.Require().Len(suite.validator.Warnings(), 1) + suite.Assert().Len(suite.validator.Errors(), 0) + + suite.Assert().EqualError(suite.validator.Warnings()[0], "notifier: smtp: option 'disable_starttls' is enabled: opportunistic STARTTLS is explicitly disabled which means all emails will be sent insecurely over plaintext and this setting is only necessary for non-compliant SMTP servers which advertise they support STARTTLS when they actually don't support STARTTLS") +} + func (suite *NotifierSuite) TestSMTPShouldEnsureHostAndPortAreProvided() { suite.config.FileSystem = nil ValidateNotifier(&suite.config, suite.validator) diff --git a/internal/configuration/validator/session.go b/internal/configuration/validator/session.go index 67517089f..f39ab517b 100644 --- a/internal/configuration/validator/session.go +++ b/internal/configuration/validator/session.go @@ -58,6 +58,18 @@ func validateRedisCommon(config *schema.SessionConfiguration, validator *schema. if config.Secret == "" { validator.Push(fmt.Errorf(errFmtSessionSecretRequired, "redis")) } + + if config.Redis.TLS != nil { + configDefaultTLS := &schema.TLSConfig{ + ServerName: config.Redis.Host, + MinimumVersion: schema.DefaultRedisConfiguration.TLS.MinimumVersion, + MaximumVersion: schema.DefaultRedisConfiguration.TLS.MaximumVersion, + } + + if err := ValidateTLSConfig(config.Redis.TLS, configDefaultTLS); err != nil { + validator.Push(fmt.Errorf(errFmtSessionRedisTLSConfigInvalid, err)) + } + } } func validateRedis(config *schema.SessionConfiguration, validator *schema.StructValidator) { diff --git a/internal/configuration/validator/session_test.go b/internal/configuration/validator/session_test.go index 605a65fa9..6e4336e47 100644 --- a/internal/configuration/validator/session_test.go +++ b/internal/configuration/validator/session_test.go @@ -1,6 +1,7 @@ package validator import ( + "crypto/tls" "fmt" "testing" @@ -13,7 +14,7 @@ import ( func newDefaultSessionConfig() schema.SessionConfiguration { config := schema.SessionConfiguration{} config.Secret = testJWTSecret - config.Domain = "example.com" + config.Domain = examplecom return config } @@ -354,6 +355,67 @@ func TestShouldRaiseErrorsWhenRedisHostNotSet(t *testing.T) { assert.EqualError(t, errors[0], errFmtSessionRedisHostRequired) } +func TestShouldSetDefaultRedisTLSOptions(t *testing.T) { + validator := schema.NewStructValidator() + config := newDefaultSessionConfig() + + config.Redis = &schema.RedisSessionConfiguration{ + Host: "redis.local", + Port: 6379, + TLS: &schema.TLSConfig{}, + } + + ValidateSession(&config, validator) + + assert.Len(t, validator.Warnings(), 0) + assert.Len(t, validator.Errors(), 0) + + assert.Equal(t, uint16(tls.VersionTLS12), config.Redis.TLS.MinimumVersion.Value) + assert.Equal(t, uint16(0), config.Redis.TLS.MaximumVersion.Value) + assert.Equal(t, "redis.local", config.Redis.TLS.ServerName) +} + +func TestShouldRaiseErrorOnBadRedisTLSOptionsSSL30(t *testing.T) { + validator := schema.NewStructValidator() + config := newDefaultSessionConfig() + + config.Redis = &schema.RedisSessionConfiguration{ + Host: "redis.local", + Port: 6379, + TLS: &schema.TLSConfig{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck + }, + } + + ValidateSession(&config, validator) + + assert.Len(t, validator.Warnings(), 0) + require.Len(t, validator.Errors(), 1) + + assert.EqualError(t, validator.Errors()[0], "session: redis: tls: option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") +} + +func TestShouldRaiseErrorOnBadRedisTLSOptionsMinVerGreaterThanMax(t *testing.T) { + validator := schema.NewStructValidator() + config := newDefaultSessionConfig() + + config.Redis = &schema.RedisSessionConfiguration{ + Host: "redis.local", + Port: 6379, + TLS: &schema.TLSConfig{ + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, + MaximumVersion: schema.TLSVersion{Value: tls.VersionTLS10}, + }, + } + + ValidateSession(&config, validator) + + assert.Len(t, validator.Warnings(), 0) + require.Len(t, validator.Errors(), 1) + + assert.EqualError(t, validator.Errors()[0], "session: redis: tls: option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version TLS1.3 is greater than the maximum version TLS1.0") +} + func TestShouldRaiseErrorWhenDomainNotSet(t *testing.T) { validator := schema.NewStructValidator() config := newDefaultSessionConfig() diff --git a/internal/configuration/validator/shared.go b/internal/configuration/validator/shared.go new file mode 100644 index 000000000..804945553 --- /dev/null +++ b/internal/configuration/validator/shared.go @@ -0,0 +1,42 @@ +package validator + +import ( + "crypto/tls" + "errors" + "fmt" + + "github.com/authelia/authelia/v4/internal/configuration/schema" +) + +// ValidateTLSConfig sets the default values and validates a schema.TLSConfig. +func ValidateTLSConfig(config *schema.TLSConfig, configDefault *schema.TLSConfig) (err error) { + if config == nil { + return + } + + if config.ServerName == "" { + config.ServerName = configDefault.ServerName + } + + if config.MinimumVersion.Value == 0 { + config.MinimumVersion.Value = configDefault.MinimumVersion.Value + } + + if config.MaximumVersion.Value == 0 { + config.MaximumVersion.Value = configDefault.MaximumVersion.Value + } + + if config.MinimumVersion.MinVersion() < tls.VersionTLS10 { + return errors.New("option 'minimum_version' is invalid: minimum version is TLS1.0 but SSL3.0 was configured") + } + + if config.MinimumVersion.MinVersion() > config.MaximumVersion.MaxVersion() { + return fmt.Errorf("option combination of 'minimum_version' and 'maximum_version' is invalid: minimum version %s is greater than the maximum version %s", config.MinimumVersion.String(), config.MaximumVersion.String()) + } + + if (config.CertificateChain.HasCertificates() || config.PrivateKey != nil) && !config.CertificateChain.EqualKey(config.PrivateKey) { + return errors.New("option 'certificates' is invalid: provided certificate is not the public key for the private key provided") + } + + return nil +} diff --git a/internal/notification/smtp_notifier.go b/internal/notification/smtp_notifier.go index cd7fa501d..0b4d70786 100644 --- a/internal/notification/smtp_notifier.go +++ b/internal/notification/smtp_notifier.go @@ -27,7 +27,7 @@ import ( func NewSMTPNotifier(config *schema.SMTPNotifierConfiguration, certPool *x509.CertPool, templateProvider *templates.Provider) *SMTPNotifier { notifier := &SMTPNotifier{ config: config, - tlsConfig: utils.NewTLSConfig(config.TLS, tls.VersionTLS12, certPool), + tlsConfig: utils.NewTLSConfig(config.TLS, certPool), log: logging.Logger(), templates: templateProvider, } diff --git a/internal/notification/smtp_notifier_test.go b/internal/notification/smtp_notifier_test.go index efa7f8c09..0ecc3f36d 100644 --- a/internal/notification/smtp_notifier_test.go +++ b/internal/notification/smtp_notifier_test.go @@ -18,7 +18,7 @@ func TestShouldConfigureSMTPNotifierWithTLS11(t *testing.T) { Port: 25, TLS: &schema.TLSConfig{ ServerName: "smtp.example.com", - MinimumVersion: "TLS1.1", + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS11}, }, }, } diff --git a/internal/session/provider_config.go b/internal/session/provider_config.go index 0a1ba6e11..c9e694a28 100644 --- a/internal/session/provider_config.go +++ b/internal/session/provider_config.go @@ -75,7 +75,7 @@ func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPo var tlsConfig *tls.Config if config.Redis.TLS != nil { - tlsConfig = utils.NewTLSConfig(config.Redis.TLS, tls.VersionTLS12, certPool) + tlsConfig = utils.NewTLSConfig(config.Redis.TLS, certPool) } if config.Redis.HighAvailability != nil && config.Redis.HighAvailability.SentinelName != "" { diff --git a/internal/session/provider_config_test.go b/internal/session/provider_config_test.go index 3f7b79dac..23b131c71 100644 --- a/internal/session/provider_config_test.go +++ b/internal/session/provider_config_test.go @@ -42,7 +42,7 @@ func TestShouldCreateRedisSessionProviderTLS(t *testing.T) { Password: "pass", TLS: &schema.TLSConfig{ ServerName: "redis.fqdn.example.com", - MinimumVersion: "TLS1.3", + MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13}, }, } providerConfig := NewProviderConfig(configuration, nil) diff --git a/internal/utils/crypto.go b/internal/utils/crypto.go index bf7ea44ff..e8408cee6 100644 --- a/internal/utils/crypto.go +++ b/internal/utils/crypto.go @@ -235,17 +235,26 @@ func IsX509PrivateKey(i any) bool { } // NewTLSConfig generates a tls.Config from a schema.TLSConfig and a x509.CertPool. -func NewTLSConfig(config *schema.TLSConfig, defaultMinVersion uint16, certPool *x509.CertPool) (tlsConfig *tls.Config) { - minVersion, err := TLSStringToTLSConfigVersion(config.MinimumVersion) - if err != nil { - minVersion = defaultMinVersion +func NewTLSConfig(config *schema.TLSConfig, certPool *x509.CertPool) (tlsConfig *tls.Config) { + var certificates []tls.Certificate + + if config.CertificateChain.HasCertificates() && config.PrivateKey != nil { + certificates = []tls.Certificate{ + { + Certificate: config.CertificateChain.CertificatesRaw(), + Leaf: config.CertificateChain.Leaf(), + PrivateKey: config.PrivateKey, + }, + } } return &tls.Config{ ServerName: config.ServerName, InsecureSkipVerify: config.SkipVerify, //nolint:gosec // Informed choice by user. Off by default. - MinVersion: minVersion, + MinVersion: config.MinimumVersion.MinVersion(), + MaxVersion: config.MinimumVersion.MaxVersion(), RootCAs: certPool, + Certificates: certificates, } } @@ -290,22 +299,6 @@ func NewX509CertPool(directory string) (certPool *x509.CertPool, warnings []erro return certPool, warnings, errors } -// TLSStringToTLSConfigVersion returns a go crypto/tls version for a tls.Config based on string input. -func TLSStringToTLSConfigVersion(input string) (version uint16, err error) { - switch strings.ToUpper(input) { - case "TLS1.3", TLS13: - return tls.VersionTLS13, nil - case "TLS1.2", TLS12: - return tls.VersionTLS12, nil - case "TLS1.1", TLS11: - return tls.VersionTLS11, nil - case "TLS1.0", TLS10: - return tls.VersionTLS10, nil - } - - return 0, ErrTLSVersionNotSupported -} - // WriteCertificateBytesToPEM writes a certificate/csr to a file in the PEM format. func WriteCertificateBytesToPEM(cert []byte, path string, csr bool) (err error) { out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) diff --git a/internal/utils/crypto_test.go b/internal/utils/crypto_test.go index 9d4adb9f5..dcc065721 100644 --- a/internal/utils/crypto_test.go +++ b/internal/utils/crypto_test.go @@ -5,7 +5,6 @@ import ( "crypto/ed25519" "crypto/elliptic" "crypto/rsa" - "crypto/tls" "crypto/x509" "runtime" "strings" @@ -14,75 +13,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/authelia/authelia/v4/internal/configuration/schema" ) -func TestShouldSetupDefaultTLSMinVersionOnErr(t *testing.T) { - schemaTLSConfig := &schema.TLSConfig{ - MinimumVersion: "NotAVersion", - ServerName: "golang.org", - SkipVerify: true, - } - - tlsConfig := NewTLSConfig(schemaTLSConfig, tls.VersionTLS12, nil) - - assert.Equal(t, uint16(tls.VersionTLS12), tlsConfig.MinVersion) - assert.Equal(t, "golang.org", tlsConfig.ServerName) - assert.True(t, tlsConfig.InsecureSkipVerify) -} - -func TestShouldReturnCorrectTLSVersions(t *testing.T) { - tls13 := uint16(tls.VersionTLS13) - tls12 := uint16(tls.VersionTLS12) - tls11 := uint16(tls.VersionTLS11) - tls10 := uint16(tls.VersionTLS10) - - version, err := TLSStringToTLSConfigVersion(TLS13) - assert.Equal(t, tls13, version) - assert.NoError(t, err) - - version, err = TLSStringToTLSConfigVersion("TLS" + TLS13) - assert.Equal(t, tls13, version) - assert.NoError(t, err) - - version, err = TLSStringToTLSConfigVersion(TLS12) - assert.Equal(t, tls12, version) - assert.NoError(t, err) - - version, err = TLSStringToTLSConfigVersion("TLS" + TLS12) - assert.Equal(t, tls12, version) - assert.NoError(t, err) - - version, err = TLSStringToTLSConfigVersion(TLS11) - assert.Equal(t, tls11, version) - assert.NoError(t, err) - - version, err = TLSStringToTLSConfigVersion("TLS" + TLS11) - assert.Equal(t, tls11, version) - assert.NoError(t, err) - - version, err = TLSStringToTLSConfigVersion(TLS10) - assert.Equal(t, tls10, version) - assert.NoError(t, err) - - version, err = TLSStringToTLSConfigVersion("TLS" + TLS10) - assert.Equal(t, tls10, version) - assert.NoError(t, err) -} - -func TestShouldReturnZeroAndErrorOnInvalidTLSVersions(t *testing.T) { - version, err := TLSStringToTLSConfigVersion("TLS1.4") - assert.Error(t, err) - assert.Equal(t, uint16(0), version) - assert.EqualError(t, err, "supplied tls version isn't supported") - - version, err = TLSStringToTLSConfigVersion("SSL3.0") - assert.Error(t, err) - assert.Equal(t, uint16(0), version) - assert.EqualError(t, err, "supplied tls version isn't supported") -} - func TestShouldReturnErrWhenX509DirectoryNotExist(t *testing.T) { pool, warnings, errors := NewX509CertPool("/tmp/asdfzyxabc123/not/a/real/dir") assert.NotNil(t, pool)