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
pull/4222/head
James Elliott 2022-10-21 19:41:33 +11:00 committed by GitHub
parent 6e835bd8f8
commit 9532823a99
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 974 additions and 171 deletions

View File

@ -315,6 +315,82 @@ authentication_backend:
## Minimum TLS version for either Secure LDAP or LDAP StartTLS. ## Minimum TLS version for either Secure LDAP or LDAP StartTLS.
minimum_version: TLS1.2 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. ## The distinguished name of the container searched for objects in the directory information tree.
## See also: additional_users_dn, additional_groups_dn. ## See also: additional_users_dn, additional_groups_dn.
base_dn: dc=example,dc=com base_dn: dc=example,dc=com

View File

@ -28,6 +28,74 @@ authentication_backend:
server_name: ldap.example.com server_name: ldap.example.com
skip_verify: false skip_verify: false
minimum_version: TLS1.2 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 base_dn: DC=example,DC=com
additional_users_dn: ou=users additional_users_dn: ou=users
users_filter: (&({username_attribute}={input})(objectClass=person)) users_filter: (&({username_attribute}={input})(objectClass=person))

View File

@ -37,6 +37,74 @@ notifier:
server_name: smtp.example.com server_name: smtp.example.com
skip_verify: false skip_verify: false
minimum_version: TLS1.2 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 ## Options

View File

@ -129,10 +129,39 @@ instead you should tweak the `server_name` option, and the global option
{{< confkey type="string" default="TLS1.2" required="no" >}} {{< confkey type="string" default="TLS1.2" required="no" >}}
The key `minimum_version` controls the minimum TLS version Authelia will use when opening TLS connections. Controls the minimum TLS version Authelia will use when performing TLS handshakes.
The possible values are `TLS1.3`, `TLS1.2`, `TLS1.1`, `TLS1.0`. Anything other than `TLS1.3` or `TLS1.2` 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 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 ## Server Buffers

View File

@ -34,6 +34,74 @@ session:
server_name: myredis.example.com server_name: myredis.example.com
skip_verify: false skip_verify: false
minimum_version: TLS1.2 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: high_availability:
sentinel_name: mysentinel sentinel_name: mysentinel
# If `sentinel_username` is supplied, Authelia will connect using ACL-based # If `sentinel_username` is supplied, Authelia will connect using ACL-based

File diff suppressed because one or more lines are too long

View File

@ -53,7 +53,7 @@ func newLDAPUserProvider(config schema.LDAPAuthenticationBackend, disableResetPa
config.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS config.TLS = schema.DefaultLDAPAuthenticationBackendConfigurationImplementationCustom.TLS
} }
tlsConfig := utils.NewTLSConfig(config.TLS, tls.VersionTLS12, certPool) tlsConfig := utils.NewTLSConfig(config.TLS, certPool)
var dialOpts = []ldap.DialOpt{ var dialOpts = []ldap.DialOpt{
ldap.DialWithDialer(&net.Dialer{Timeout: config.Timeout}), ldap.DialWithDialer(&net.Dialer{Timeout: config.Timeout}),

View File

@ -315,6 +315,82 @@ authentication_backend:
## Minimum TLS version for either Secure LDAP or LDAP StartTLS. ## Minimum TLS version for either Secure LDAP or LDAP StartTLS.
minimum_version: TLS1.2 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. ## The distinguished name of the container searched for objects in the directory information tree.
## See also: additional_users_dn, additional_groups_dn. ## See also: additional_users_dn, additional_groups_dn.
base_dn: dc=example,dc=com base_dn: dc=example,dc=com

View File

@ -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. // StringToPrivateKeyHookFunc decodes strings to rsa.PrivateKey's.
func StringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType { func StringToPrivateKeyHookFunc() mapstructure.DecodeHookFuncType {
return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) { return func(f reflect.Type, t reflect.Type, data interface{}) (value interface{}, err error) {

View File

@ -64,6 +64,8 @@ func unmarshal(ko *koanf.Koanf, val *schema.StructValidator, path string, o any)
StringToX509CertificateHookFunc(), StringToX509CertificateHookFunc(),
StringToX509CertificateChainHookFunc(), StringToX509CertificateChainHookFunc(),
StringToPrivateKeyHookFunc(), StringToPrivateKeyHookFunc(),
StringToCryptoPrivateKeyHookFunc(),
StringToTLSVersionHookFunc(),
StringToPasswordDigestHookFunc(true), StringToPasswordDigestHookFunc(true),
ToTimeDurationHookFunc(), ToTimeDurationHookFunc(),
), ),

View File

@ -1,6 +1,7 @@
package schema package schema
import ( import (
"crypto/tls"
"net/url" "net/url"
"time" "time"
) )
@ -180,7 +181,7 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationCustom = LDAPAuth
GroupNameAttribute: "cn", GroupNameAttribute: "cn",
Timeout: time.Second * 5, Timeout: time.Second * 5,
TLS: &TLSConfig{ TLS: &TLSConfig{
MinimumVersion: "TLS1.2", MinimumVersion: TLSVersion{tls.VersionTLS12},
}, },
} }
@ -194,6 +195,6 @@ var DefaultLDAPAuthenticationBackendConfigurationImplementationActiveDirectory =
GroupNameAttribute: "cn", GroupNameAttribute: "cn",
Timeout: time.Second * 5, Timeout: time.Second * 5,
TLS: &TLSConfig{ TLS: &TLSConfig{
MinimumVersion: "TLS1.2", MinimumVersion: TLSVersion{tls.VersionTLS12},
}, },
} }

View File

@ -1,6 +1,7 @@
package schema package schema
import ( import (
"errors"
"regexp" "regexp"
"time" "time"
) )
@ -9,20 +10,51 @@ const (
argon2 = "argon2" argon2 = "argon2"
argon2id = "argon2id" argon2id = "argon2id"
sha512 = "sha512" 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 ProfileRefreshDisabled = "disable"
const ( 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" ProfileRefreshAlways = "always"
// RefreshIntervalDefault represents the default value of refresh_interval. // RefreshIntervalDefault represents the default Value of refresh_interval.
RefreshIntervalDefault = "5m" 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 RefreshIntervalAlways = 0 * time.Millisecond
) )

View File

@ -84,8 +84,11 @@ var Keys = []string{
"authentication_backend.ldap.timeout", "authentication_backend.ldap.timeout",
"authentication_backend.ldap.start_tls", "authentication_backend.ldap.start_tls",
"authentication_backend.ldap.tls.minimum_version", "authentication_backend.ldap.tls.minimum_version",
"authentication_backend.ldap.tls.maximum_version",
"authentication_backend.ldap.tls.skip_verify", "authentication_backend.ldap.tls.skip_verify",
"authentication_backend.ldap.tls.server_name", "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.base_dn",
"authentication_backend.ldap.additional_users_dn", "authentication_backend.ldap.additional_users_dn",
"authentication_backend.ldap.users_filter", "authentication_backend.ldap.users_filter",
@ -115,8 +118,11 @@ var Keys = []string{
"session.redis.maximum_active_connections", "session.redis.maximum_active_connections",
"session.redis.minimum_idle_connections", "session.redis.minimum_idle_connections",
"session.redis.tls.minimum_version", "session.redis.tls.minimum_version",
"session.redis.tls.maximum_version",
"session.redis.tls.skip_verify", "session.redis.tls.skip_verify",
"session.redis.tls.server_name", "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_name",
"session.redis.high_availability.sentinel_username", "session.redis.high_availability.sentinel_username",
"session.redis.high_availability.sentinel_password", "session.redis.high_availability.sentinel_password",
@ -195,8 +201,11 @@ var Keys = []string{
"notifier.smtp.disable_html_emails", "notifier.smtp.disable_html_emails",
"notifier.smtp.disable_starttls", "notifier.smtp.disable_starttls",
"notifier.smtp.tls.minimum_version", "notifier.smtp.tls.minimum_version",
"notifier.smtp.tls.maximum_version",
"notifier.smtp.tls.skip_verify", "notifier.smtp.tls.skip_verify",
"notifier.smtp.tls.server_name", "notifier.smtp.tls.server_name",
"notifier.smtp.tls.private_key",
"notifier.smtp.tls.certificate_chain",
"notifier.template_path", "notifier.template_path",
"server.host", "server.host",
"server.port", "server.port",

View File

@ -1,6 +1,7 @@
package schema package schema
import ( import (
"crypto/tls"
"net/mail" "net/mail"
"time" "time"
) )
@ -42,6 +43,6 @@ var DefaultSMTPNotifierConfiguration = SMTPNotifierConfiguration{
Identifier: "localhost", Identifier: "localhost",
StartupCheckAddress: mail.Address{Name: "Authelia Test", Address: "test@authelia.com"}, StartupCheckAddress: mail.Address{Name: "Authelia Test", Address: "test@authelia.com"},
TLS: &TLSConfig{ TLS: &TLSConfig{
MinimumVersion: "TLS1.2", MinimumVersion: TLSVersion{tls.VersionTLS12},
}, },
} }

View File

@ -1,6 +1,7 @@
package schema package schema
import ( import (
"crypto/tls"
"time" "time"
) )
@ -54,3 +55,10 @@ var DefaultSessionConfiguration = SessionConfiguration{
RememberMeDuration: time.Hour * 24 * 30, RememberMeDuration: time.Hour * 24 * 30,
SameSite: "lax", SameSite: "lax",
} }
// DefaultRedisConfiguration is the default redis configuration.
var DefaultRedisConfiguration = RedisSessionConfiguration{
TLS: &TLSConfig{
MinimumVersion: TLSVersion{Value: tls.VersionTLS12},
},
}

View File

@ -6,9 +6,20 @@ import (
// TLSConfig is a representation of the TLS configuration. // TLSConfig is a representation of the TLS configuration.
type TLSConfig struct { type TLSConfig struct {
MinimumVersion string `koanf:"minimum_version"` MinimumVersion TLSVersion `koanf:"minimum_version"`
SkipVerify bool `koanf:"skip_verify"` MaximumVersion TLSVersion `koanf:"maximum_version"`
ServerName string `koanf:"server_name"`
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. // ServerTimeouts represents server timeout configurations.

View File

@ -5,6 +5,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/ed25519" "crypto/ed25519"
"crypto/rsa" "crypto/rsa"
"crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"fmt" "fmt"
@ -174,6 +175,71 @@ func NewX509CertificateChain(in string) (chain *X509CertificateChain, err error)
return chain, nil 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. // X509CertificateChain is a helper struct that holds a list of *x509.Certificate's.
type X509CertificateChain struct { type X509CertificateChain struct {
certs []*x509.Certificate certs []*x509.Certificate
@ -259,10 +325,32 @@ func (c *X509CertificateChain) EqualKey(other any) (equal bool) {
} }
// Certificates for this X509CertificateChain. // Certificates for this X509CertificateChain.
func (c *X509CertificateChain) Certificates() []*x509.Certificate { func (c *X509CertificateChain) Certificates() (certificates []*x509.Certificate) {
return c.certs 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 // 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. // (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) { func (c *X509CertificateChain) Validate() (err error) {

View File

@ -18,6 +18,35 @@ duo_api:
authentication_backend: authentication_backend:
ldap: ldap:
url: ldap://127.0.0.1 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 base_dn: dc=example,dc=com
username_attribute: uid username_attribute: uid
additional_users_dn: ou=users additional_users_dn: ou=users

View File

@ -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]))) 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<User>\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() { func (suite *AccessControl) TestShouldSetQueryDefaults() {
domains := []string{"public.example.com"} domains := []string{"public.example.com"}
suite.config.AccessControl.Rules = []schema.ACLRule{ suite.config.AccessControl.Rules = []schema.ACLRule{
@ -359,3 +375,13 @@ func TestShouldReturnCorrectResultsForValidNetworkGroups(t *testing.T) {
assert.True(t, validNetwork) assert.True(t, validNetwork)
assert.False(t, invalidNetwork) 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
}

View File

@ -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}, "', '"))) validator.Push(fmt.Errorf(errFmtLDAPAuthBackendImplementation, config.LDAP.Implementation, strings.Join([]string{schema.LDAPImplementationCustom, schema.LDAPImplementationActiveDirectory}, "', '")))
} }
configDefaultTLS := &schema.TLSConfig{}
if implementation != nil { 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) setDefaultImplementationLDAPAuthenticationBackendProfileAttributes(config.LDAP, implementation)
} }
if config.LDAP.TLS != nil { if config.LDAP.URL == "" {
if _, err := utils.TLSStringToTLSConfigVersion(config.LDAP.TLS.MinimumVersion); err != nil { validator.Push(fmt.Errorf(errFmtLDAPAuthBackendMissingOption, "url"))
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendTLSMinVersion, config.LDAP.TLS.MinimumVersion, err))
}
} else { } else {
configDefaultTLS.ServerName = validateLDAPAuthenticationBackendURL(config.LDAP, validator)
}
if config.LDAP.TLS == nil {
config.LDAP.TLS = &schema.TLSConfig{} 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}") { if strings.Contains(config.LDAP.UsersFilter, "{0}") {
validator.Push(fmt.Errorf(errFmtLDAPAuthBackendFilterReplacedPlaceholders, "users_filter", "{0}", "{input}")) 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}")) 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) 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 { func ldapImplementationShouldSetStr(config, implementation string) bool {
return config == "" && implementation != "" 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 ( var (
parsedURL *url.URL parsedURL *url.URL
err error err error
@ -420,9 +414,8 @@ func validateLDAPAuthenticationBackendURL(config *schema.LDAPAuthenticationBacke
} }
config.URL = parsedURL.String() config.URL = parsedURL.String()
if config.TLS.ServerName == "" {
config.TLS.ServerName = parsedURL.Hostname() return parsedURL.Hostname()
}
} }
func validateLDAPRequiredParameters(config *schema.AuthenticationBackend, validator *schema.StructValidator) { func validateLDAPRequiredParameters(config *schema.AuthenticationBackend, validator *schema.StructValidator) {

View File

@ -1,6 +1,7 @@
package validator package validator
import ( import (
"crypto/tls"
"net/url" "net/url"
"testing" "testing"
"time" "time"
@ -625,6 +626,42 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenPasswordNot
suite.Assert().EqualError(suite.validator.Errors()[0], "authentication_backend: ldap: option 'password' is required") 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() { func (suite *LDAPAuthenticationBackendSuite) TestShouldRaiseErrorWhenBaseDNNotProvided() {
suite.config.LDAP.BaseDN = "" suite.config.LDAP.BaseDN = ""
@ -783,19 +820,19 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldHelpDetectNoInputPlacehol
} }
func (suite *LDAPAuthenticationBackendSuite) TestShouldSetDefaultTLSMinimumVersion() { 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) ValidateAuthenticationBackend(&suite.config, suite.validator)
suite.Assert().Len(suite.validator.Warnings(), 0) suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Assert().Len(suite.validator.Errors(), 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{ suite.config.LDAP.TLS = &schema.TLSConfig{
MinimumVersion: "SSL2.0", MinimumVersion: schema.TLSVersion{Value: tls.VersionSSL30}, //nolint:staticcheck
} }
ValidateAuthenticationBackend(&suite.config, suite.validator) ValidateAuthenticationBackend(&suite.config, suite.validator)
@ -803,7 +840,21 @@ func (suite *LDAPAuthenticationBackendSuite) TestShouldNotAllowInvalidTLSValue()
suite.Assert().Len(suite.validator.Warnings(), 0) suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Require().Len(suite.validator.Errors(), 1) 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) { func TestLdapAuthenticationBackend(t *testing.T) {

View File

@ -25,7 +25,7 @@ func newDefaultConfig() schema.Configuration {
DefaultPolicy: "two_factor", DefaultPolicy: "two_factor",
} }
config.Session = schema.SessionConfiguration{ config.Session = schema.SessionConfiguration{
Domain: "example.com", Domain: examplecom,
Name: "authelia_session", Name: "authelia_session",
Secret: "secret", Secret: "secret",
} }

View File

@ -58,7 +58,11 @@ const (
errFmtNotifierTemplatePathUnknownError = "notifier: option 'template_path' refers to location '%s' which couldn't be opened: %w" errFmtNotifierTemplatePathUnknownError = "notifier: option 'template_path' refers to location '%s' which couldn't be opened: %w"
errFmtNotifierFileSystemFileNameNotConfigured = "notifier: filesystem: option 'filename' is required" errFmtNotifierFileSystemFileNameNotConfigured = "notifier: filesystem: option 'filename' is required"
errFmtNotifierSMTPNotConfigured = "notifier: smtp: option '%s' 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 ( const (
@ -91,10 +95,9 @@ const (
errFmtLDAPAuthBackendUnauthenticatedBindWithPassword = "authentication_backend: ldap: option 'permit_unauthenticated_bind' can't be enabled when a password is specified" 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" 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" errFmtLDAPAuthBackendMissingOption = "authentication_backend: ldap: option '%s' is required"
errFmtLDAPAuthBackendTLSMinVersion = "authentication_backend: ldap: tls: option " + errFmtLDAPAuthBackendTLSConfigInvalid = "authentication_backend: ldap: tls: %w"
"'minimum_tls_version' is invalid: %s: %w" errFmtLDAPAuthBackendImplementation = "authentication_backend: ldap: option 'implementation' " +
errFmtLDAPAuthBackendImplementation = "authentication_backend: ldap: option 'implementation' " +
errSuffixMustBeOneOf errSuffixMustBeOneOf
errFmtLDAPAuthBackendFilterReplacedPlaceholders = "authentication_backend: ldap: option " + errFmtLDAPAuthBackendFilterReplacedPlaceholders = "authentication_backend: ldap: option " +
"'%s' has an invalid placeholder: '%s' has been removed, please use '%s' instead" "'%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'" errFmtSessionRedisPortRange = "session: redis: option 'port' must be between 1 and 65535 but is configured as '%d'"
errFmtSessionRedisHostRequired = "session: redis: option 'host' is required" errFmtSessionRedisHostRequired = "session: redis: option 'host' is required"
errFmtSessionRedisHostOrNodesRequired = "session: redis: option 'host' or the 'high_availability' option 'nodes' 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" 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" errFmtSessionRedisSentinelNodeHostMissing = "session: redis: high_availability: option 'nodes': option 'host' is required for each node but one or more nodes are missing this"

View File

@ -10,3 +10,7 @@ const (
testLDAPUser = "user" testLDAPUser = "user"
testEncryptionKey = "a_not_so_secure_encryption_key" testEncryptionKey = "a_not_so_secure_encryption_key"
) )
const (
examplecom = "example.com"
)

View File

@ -257,7 +257,7 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
RedirectURIs: []string{ RedirectURIs: []string{
"https://google.com", "https://google.com",
}, },
SectorIdentifier: mustParseURL("example.com"), SectorIdentifier: mustParseURL(examplecom),
}, },
}, },
}, },
@ -289,12 +289,12 @@ func TestShouldRaiseErrorWhenOIDCServerClientBadValues(t *testing.T) {
}, },
}, },
Errors: []string{ 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", examplecom, "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", examplecom, "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", examplecom, "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", examplecom, "fragment", "fragment"),
fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifier, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", "example.com", "username", "user"), 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", "example.com", "password"), fmt.Sprintf(errFmtOIDCClientInvalidSectorIdentifierWithoutValue, "client-invalid-sector", "https://user:pass@example.com/path?query=abc#fragment", examplecom, "password"),
}, },
}, },
{ {

View File

@ -83,11 +83,17 @@ func validateSMTPNotifier(config *schema.SMTPNotifierConfiguration, validator *s
} }
if config.TLS == nil { if config.TLS == nil {
config.TLS = schema.DefaultSMTPNotifierConfiguration.TLS config.TLS = &schema.TLSConfig{}
} }
if config.TLS.ServerName == "" { configDefaultTLS := &schema.TLSConfig{
config.TLS.ServerName = config.Host 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 { if config.DisableStartTLS {

View File

@ -1,6 +1,7 @@
package validator package validator
import ( import (
"crypto/tls"
"fmt" "fmt"
"net/mail" "net/mail"
"testing" "testing"
@ -22,7 +23,7 @@ func (suite *NotifierSuite) SetupTest() {
Username: "john", Username: "john",
Password: "password", Password: "password",
Sender: mail.Address{Name: "Authelia", Address: "authelia@example.com"}, Sender: mail.Address{Name: "Authelia", Address: "authelia@example.com"},
Host: "example.com", Host: examplecom,
Port: 25, Port: 25,
} }
suite.config.FileSystem = nil suite.config.FileSystem = nil
@ -77,8 +78,8 @@ func (suite *NotifierSuite) TestSMTPShouldSetTLSDefaults() {
suite.Assert().Len(suite.validator.Warnings(), 0) suite.Assert().Len(suite.validator.Warnings(), 0)
suite.Assert().Len(suite.validator.Errors(), 0) suite.Assert().Len(suite.validator.Errors(), 0)
suite.Assert().Equal("example.com", suite.config.SMTP.TLS.ServerName) suite.Assert().Equal(examplecom, suite.config.SMTP.TLS.ServerName)
suite.Assert().Equal("TLS1.2", suite.config.SMTP.TLS.MinimumVersion) suite.Assert().Equal(uint16(tls.VersionTLS12), suite.config.SMTP.TLS.MinimumVersion.Value)
suite.Assert().False(suite.config.SMTP.TLS.SkipVerify) suite.Assert().False(suite.config.SMTP.TLS.SkipVerify)
} }
@ -96,7 +97,7 @@ func (suite *NotifierSuite) TestSMTPShouldDefaultStartupCheckAddress() {
func (suite *NotifierSuite) TestSMTPShouldDefaultTLSServerNameToHost() { func (suite *NotifierSuite) TestSMTPShouldDefaultTLSServerNameToHost() {
suite.config.SMTP.Host = "google.com" suite.config.SMTP.Host = "google.com"
suite.config.SMTP.TLS = &schema.TLSConfig{ suite.config.SMTP.TLS = &schema.TLSConfig{
MinimumVersion: "TLS1.1", MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS11},
} }
ValidateNotifier(&suite.config, suite.validator) ValidateNotifier(&suite.config, suite.validator)
@ -105,10 +106,51 @@ func (suite *NotifierSuite) TestSMTPShouldDefaultTLSServerNameToHost() {
suite.Assert().Len(suite.validator.Errors(), 0) suite.Assert().Len(suite.validator.Errors(), 0)
suite.Assert().Equal("google.com", suite.config.SMTP.TLS.ServerName) 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) 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() { func (suite *NotifierSuite) TestSMTPShouldEnsureHostAndPortAreProvided() {
suite.config.FileSystem = nil suite.config.FileSystem = nil
ValidateNotifier(&suite.config, suite.validator) ValidateNotifier(&suite.config, suite.validator)

View File

@ -58,6 +58,18 @@ func validateRedisCommon(config *schema.SessionConfiguration, validator *schema.
if config.Secret == "" { if config.Secret == "" {
validator.Push(fmt.Errorf(errFmtSessionSecretRequired, "redis")) 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) { func validateRedis(config *schema.SessionConfiguration, validator *schema.StructValidator) {

View File

@ -1,6 +1,7 @@
package validator package validator
import ( import (
"crypto/tls"
"fmt" "fmt"
"testing" "testing"
@ -13,7 +14,7 @@ import (
func newDefaultSessionConfig() schema.SessionConfiguration { func newDefaultSessionConfig() schema.SessionConfiguration {
config := schema.SessionConfiguration{} config := schema.SessionConfiguration{}
config.Secret = testJWTSecret config.Secret = testJWTSecret
config.Domain = "example.com" config.Domain = examplecom
return config return config
} }
@ -354,6 +355,67 @@ func TestShouldRaiseErrorsWhenRedisHostNotSet(t *testing.T) {
assert.EqualError(t, errors[0], errFmtSessionRedisHostRequired) 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) { func TestShouldRaiseErrorWhenDomainNotSet(t *testing.T) {
validator := schema.NewStructValidator() validator := schema.NewStructValidator()
config := newDefaultSessionConfig() config := newDefaultSessionConfig()

View File

@ -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
}

View File

@ -27,7 +27,7 @@ import (
func NewSMTPNotifier(config *schema.SMTPNotifierConfiguration, certPool *x509.CertPool, templateProvider *templates.Provider) *SMTPNotifier { func NewSMTPNotifier(config *schema.SMTPNotifierConfiguration, certPool *x509.CertPool, templateProvider *templates.Provider) *SMTPNotifier {
notifier := &SMTPNotifier{ notifier := &SMTPNotifier{
config: config, config: config,
tlsConfig: utils.NewTLSConfig(config.TLS, tls.VersionTLS12, certPool), tlsConfig: utils.NewTLSConfig(config.TLS, certPool),
log: logging.Logger(), log: logging.Logger(),
templates: templateProvider, templates: templateProvider,
} }

View File

@ -18,7 +18,7 @@ func TestShouldConfigureSMTPNotifierWithTLS11(t *testing.T) {
Port: 25, Port: 25,
TLS: &schema.TLSConfig{ TLS: &schema.TLSConfig{
ServerName: "smtp.example.com", ServerName: "smtp.example.com",
MinimumVersion: "TLS1.1", MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS11},
}, },
}, },
} }

View File

@ -75,7 +75,7 @@ func NewProviderConfig(config schema.SessionConfiguration, certPool *x509.CertPo
var tlsConfig *tls.Config var tlsConfig *tls.Config
if config.Redis.TLS != nil { 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 != "" { if config.Redis.HighAvailability != nil && config.Redis.HighAvailability.SentinelName != "" {

View File

@ -42,7 +42,7 @@ func TestShouldCreateRedisSessionProviderTLS(t *testing.T) {
Password: "pass", Password: "pass",
TLS: &schema.TLSConfig{ TLS: &schema.TLSConfig{
ServerName: "redis.fqdn.example.com", ServerName: "redis.fqdn.example.com",
MinimumVersion: "TLS1.3", MinimumVersion: schema.TLSVersion{Value: tls.VersionTLS13},
}, },
} }
providerConfig := NewProviderConfig(configuration, nil) providerConfig := NewProviderConfig(configuration, nil)

View File

@ -235,17 +235,26 @@ func IsX509PrivateKey(i any) bool {
} }
// NewTLSConfig generates a tls.Config from a schema.TLSConfig and a x509.CertPool. // 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) { func NewTLSConfig(config *schema.TLSConfig, certPool *x509.CertPool) (tlsConfig *tls.Config) {
minVersion, err := TLSStringToTLSConfigVersion(config.MinimumVersion) var certificates []tls.Certificate
if err != nil {
minVersion = defaultMinVersion if config.CertificateChain.HasCertificates() && config.PrivateKey != nil {
certificates = []tls.Certificate{
{
Certificate: config.CertificateChain.CertificatesRaw(),
Leaf: config.CertificateChain.Leaf(),
PrivateKey: config.PrivateKey,
},
}
} }
return &tls.Config{ return &tls.Config{
ServerName: config.ServerName, ServerName: config.ServerName,
InsecureSkipVerify: config.SkipVerify, //nolint:gosec // Informed choice by user. Off by default. InsecureSkipVerify: config.SkipVerify, //nolint:gosec // Informed choice by user. Off by default.
MinVersion: minVersion, MinVersion: config.MinimumVersion.MinVersion(),
MaxVersion: config.MinimumVersion.MaxVersion(),
RootCAs: certPool, RootCAs: certPool,
Certificates: certificates,
} }
} }
@ -290,22 +299,6 @@ func NewX509CertPool(directory string) (certPool *x509.CertPool, warnings []erro
return certPool, warnings, errors 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. // WriteCertificateBytesToPEM writes a certificate/csr to a file in the PEM format.
func WriteCertificateBytesToPEM(cert []byte, path string, csr bool) (err error) { 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) out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)

View File

@ -5,7 +5,6 @@ import (
"crypto/ed25519" "crypto/ed25519"
"crypto/elliptic" "crypto/elliptic"
"crypto/rsa" "crypto/rsa"
"crypto/tls"
"crypto/x509" "crypto/x509"
"runtime" "runtime"
"strings" "strings"
@ -14,75 +13,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "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) { func TestShouldReturnErrWhenX509DirectoryNotExist(t *testing.T) {
pool, warnings, errors := NewX509CertPool("/tmp/asdfzyxabc123/not/a/real/dir") pool, warnings, errors := NewX509CertPool("/tmp/asdfzyxabc123/not/a/real/dir")
assert.NotNil(t, pool) assert.NotNil(t, pool)