[FEATURE] File Secrets (#896)
* [FEATURE] File Secret Loading * add a validator for secrets * run the secrets validator before the main config validator * only allow a secret to be defined in one of: config, env, file env * remove LF if found in file * update configuration before main config validation * fix unit tests * implement secret testing * refactor the secrets validator * make check os agnostic * update docs * add warning when user attempts to use ENV instead of ENV file * discourage ENV in docs * update config template * oxford comma * apply suggestions from code review * rename Validate to ValidateConfiguration * add k8s example * add deprecation notice in docs and warning * style changespull/901/head
parent
0ec3f18b44
commit
b9fb33d806
|
@ -15,7 +15,7 @@ log_level: debug
|
|||
|
||||
# The secret used to generate JWT tokens when validating user identity by
|
||||
# email confirmation.
|
||||
# This secret can also be set using the env variables AUTHELIA_JWT_SECRET
|
||||
# JWT Secret can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
jwt_secret: a_very_important_secret
|
||||
|
||||
# Default redirection URL
|
||||
|
@ -58,7 +58,7 @@ totp:
|
|||
duo_api:
|
||||
hostname: api-123456789.example.com
|
||||
integration_key: ABCDEF
|
||||
# This secret can also be set using the env variables AUTHELIA_DUO_API_SECRET_KEY
|
||||
# Secret can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
secret_key: 1234567890abcdefghifjkl
|
||||
|
||||
# The authentication backend to use for verifying user passwords
|
||||
|
@ -138,7 +138,7 @@ authentication_backend:
|
|||
|
||||
# The username and password of the admin user.
|
||||
user: cn=admin,dc=example,dc=com
|
||||
# This secret can also be set using the env variables AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: password
|
||||
|
||||
# File backend configuration.
|
||||
|
@ -271,7 +271,7 @@ session:
|
|||
name: authelia_session
|
||||
|
||||
# The secret to encrypt the session data. This is only used with Redis.
|
||||
# This secret can also be set using the env variables AUTHELIA_SESSION_SECRET
|
||||
# Secret can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
secret: insecure_session_secret
|
||||
|
||||
# The time in seconds before the cookie expires and session is reset.
|
||||
|
@ -296,7 +296,7 @@ session:
|
|||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
# This secret can also be set using the env variables AUTHELIA_SESSION_REDIS_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: authelia
|
||||
# This is the Redis DB Index https://redis.io/commands/select (sometimes referred to as database number, DB, etc).
|
||||
database_index: 0
|
||||
|
@ -334,7 +334,7 @@ storage:
|
|||
port: 3306
|
||||
database: authelia
|
||||
username: authelia
|
||||
# This secret can also be set using the env variables AUTHELIA_STORAGE_MYSQL_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: mypassword
|
||||
|
||||
# Settings to connect to PostgreSQL server
|
||||
|
@ -343,8 +343,9 @@ storage:
|
|||
# port: 5432
|
||||
# database: authelia
|
||||
# username: authelia
|
||||
# # This secret can also be set using the env variables AUTHELIA_STORAGE_POSTGRES_PASSWORD
|
||||
# # Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
# password: mypassword
|
||||
# sslmode: disable
|
||||
|
||||
# Configuration of the notification system.
|
||||
#
|
||||
|
@ -372,7 +373,7 @@ notifier:
|
|||
# - use the disable_verify_cert boolean value to disable the validation (prefer the trusted_cert option as it's more secure)
|
||||
smtp:
|
||||
username: test
|
||||
# This secret can also be set using the env variables AUTHELIA_NOTIFIER_SMTP_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: password
|
||||
host: 127.0.0.1
|
||||
port: 1025
|
||||
|
@ -390,7 +391,7 @@ notifier:
|
|||
# You need to create an app password by following: https://support.google.com/accounts/answer/185833?hl=en
|
||||
## smtp:
|
||||
## username: myaccount@gmail.com
|
||||
## # This secret can also be set using the env variables AUTHELIA_NOTIFIER_SMTP_PASSWORD
|
||||
## # Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
## password: yourapppassword
|
||||
## sender: admin@example.com
|
||||
## host: smtp.gmail.com
|
||||
|
|
|
@ -81,13 +81,13 @@ authentication_backend:
|
|||
# one returned by the LDAP server is used.
|
||||
user: cn=admin,dc=example,dc=com
|
||||
|
||||
# This secret can also be set using the env variables AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: password
|
||||
```
|
||||
|
||||
The user must have an email address in order for Authelia to perform
|
||||
identity verification when password reset request is initiated or
|
||||
when a second factor device is registered.
|
||||
identity verification when a user attempts to reset their password or
|
||||
register a second factor device.
|
||||
|
||||
## Important notes
|
||||
|
||||
|
@ -99,3 +99,7 @@ In order to avoid such problems, we highly recommended you follow https://www.ie
|
|||
`sAMAccountName` for Microsoft Active Directory and `uid` for other implementations as the attribute holding the
|
||||
unique identifier for your users.
|
||||
|
||||
## Loading a password from a secret instead of inside the configuration
|
||||
|
||||
Password can also be defined using a [secret](../secrets.md).
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ log_file_path: /var/log/authelia.log
|
|||
`optional: false`
|
||||
|
||||
Defines the secret used to craft JWT tokens leveraged by the identity
|
||||
verification process
|
||||
verification process. This can also be defined using a [secret](./secrets.md).
|
||||
|
||||
```yaml
|
||||
jwt_secret: v3ry_important_s3cr3t
|
||||
|
|
|
@ -38,7 +38,7 @@ notifier:
|
|||
# - use the disable_verify_cert boolean value to disable the validation (prefer the trusted_cert option as it's more secure)
|
||||
smtp:
|
||||
username: test
|
||||
# This secret can also be set using the env variables AUTHELIA_NOTIFIER_SMTP_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: password
|
||||
host: 127.0.0.1
|
||||
port: 1025
|
||||
|
@ -62,9 +62,13 @@ described [here](https://support.google.com/accounts/answer/185833?hl=en)
|
|||
notifier:
|
||||
smtp:
|
||||
username: myaccount@gmail.com
|
||||
# This secret can also be set using the env variables AUTHELIA_NOTIFIER_SMTP_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: yourapppassword
|
||||
sender: admin@example.com
|
||||
host: smtp.gmail.com
|
||||
port: 587
|
||||
```
|
||||
|
||||
## Loading a password from a secret instead of inside the configuration
|
||||
|
||||
Password can also be defined using a [secret](../secrets.md).
|
|
@ -16,25 +16,47 @@ below.
|
|||
|
||||
A secret can be configured using an environment variable with the
|
||||
prefix AUTHELIA_ followed by the path of the option capitalized
|
||||
and with dots replaced by underscores.
|
||||
and with dots replaced by underscores followed by the suffix _FILE.
|
||||
|
||||
For instance the LDAP password is identified by the path
|
||||
**authentication_backend.ldap.password**, so this password could
|
||||
alternatively be set using the environment variable called
|
||||
**AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD**.
|
||||
The contents of the environment variable must be a path to a file
|
||||
containing the secret data. This file must be readable by the
|
||||
user the Authelia daemon is running as.
|
||||
|
||||
For instance the LDAP password can be defined in the configuration
|
||||
at the path **authentication_backend.ldap.password**, so this password
|
||||
could alternatively be set using the environment variable called
|
||||
**AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE**.
|
||||
|
||||
Here is the list of the environment variables which are considered
|
||||
secrets and can be defined. Any other option defined using an
|
||||
environment variable will not be replaced.
|
||||
|
||||
* AUTHELIA_JWT_SECRET
|
||||
* AUTHELIA_DUO_API_SECRET_KEY
|
||||
* AUTHELIA_SESSION_SECRET
|
||||
* AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD
|
||||
* AUTHELIA_NOTIFIER_SMTP_PASSWORD
|
||||
* AUTHELIA_SESSION_REDIS_PASSWORD
|
||||
* AUTHELIA_STORAGE_MYSQL_PASSWORD
|
||||
* AUTHELIA_STORAGE_POSTGRES_PASSWORD
|
||||
|Configuration Key |Environment Variable |
|
||||
|:----------------------------------:|:------------------------------------------------:|
|
||||
|jwt_secret |AUTHELIA_JWT_SECRET_FILE |
|
||||
|duo_api.secret_key |AUTHELIA_DUO_API_SECRET_KEY_FILE |
|
||||
|session.secret |AUTHELIA_SESSION_SECRET_FILE |
|
||||
|session.redis.password |AUTHELIA_SESSION_REDIS_PASSWORD_FILE |
|
||||
|storage.mysql.password |AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE |
|
||||
|storage.postgres.password |AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE |
|
||||
|notifier.smtp.password |AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE |
|
||||
|authentication_backend.ldap.password|AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE|
|
||||
|
||||
|
||||
## Secrets exposed in an environment variable
|
||||
|
||||
Prior to implementing file secrets you were able to define the
|
||||
values of secrets in the environment variables themselves
|
||||
in plain text instead of referencing a file. This is still
|
||||
supported but discouraged. If you still want to do this
|
||||
just remove _FILE from the environment variable name
|
||||
and define the value in insecure plain text. See
|
||||
[this article](https://diogomonica.com/2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/)
|
||||
for reasons why this is considered insecure and is discouraged.
|
||||
|
||||
**DEPRECATION NOTICE:** This backwards compatibility feature will be
|
||||
**removed** in 4.18.0+.
|
||||
|
||||
|
||||
## Secrets in configuration file
|
||||
|
||||
|
@ -42,3 +64,151 @@ If for some reason you prefer keeping the secrets in the configuration
|
|||
file, be sure to apply the right permissions to the file in order to
|
||||
prevent secret leaks if an another application gets compromised on your
|
||||
server. The UNIX permissions should probably be something like 600.
|
||||
|
||||
|
||||
## Kubernetes
|
||||
|
||||
Secrets can be mounted as files using the following sample manifests.
|
||||
|
||||
|
||||
### Kustomization
|
||||
|
||||
- **Filename:** ./kustomization.yaml
|
||||
- **Command:** kubectl apply -k
|
||||
- **Notes:** this kustomization expects the Authelia configuration.yml in
|
||||
the same directory. You will need to edit the kustomization.yaml with your
|
||||
desired secrets after the equal signs. If you change the value before the
|
||||
equal sign you'll have to adjust the volumes section of the daemonset
|
||||
template (or deployment template if you're using it).
|
||||
|
||||
```yaml
|
||||
#filename: ./kustomization.yaml
|
||||
generatorOptions:
|
||||
disableNameSuffixHash: true
|
||||
labels:
|
||||
type: generated
|
||||
app: authelia
|
||||
configMapGenerator:
|
||||
- name: authelia
|
||||
files:
|
||||
- configuration.yml
|
||||
secretGenerator:
|
||||
- name: authelia
|
||||
literals:
|
||||
- jwt_secret=myverysecuresecret
|
||||
- session_secret=mysessionsecret
|
||||
- redis_password=myredispassword
|
||||
- sql_password=mysqlpassword
|
||||
- ldap_password=myldappassword
|
||||
- duo_secret=myduosecretkey
|
||||
- smtp_password=mysmtppassword
|
||||
```
|
||||
|
||||
### DaemonSet
|
||||
|
||||
- **Filename:** ./daemonset.yaml
|
||||
- **Command:** kubectl apply -f ./daemonset.yaml
|
||||
- **Notes:** assumes Kubernetes API 1.16 or greater
|
||||
```yaml
|
||||
#filename: daemonset.yaml
|
||||
#command: kubectl apply -f daemonset.yaml
|
||||
#notes: assumes kubernetes api 1.16+
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: authelia
|
||||
labels:
|
||||
app: authelia
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: authelia
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: authelia
|
||||
spec:
|
||||
containers:
|
||||
- name: authelia
|
||||
image: authelia/authelia:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: AUTHELIA_JWT_SECRET_FILE
|
||||
value: /usr/app/secrets/jwt
|
||||
- name: AUTHELIA_DUO_API_SECRET_KEY_FILE
|
||||
value: /usr/app/secrets/duo
|
||||
- name: AUTHELIA_SESSION_SECRET_FILE
|
||||
value: /usr/app/secrets/session
|
||||
- name: AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE
|
||||
value: /usr/app/secrets/ldap_password
|
||||
- name: AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE
|
||||
value: /usr/app/secrets/smtp_password
|
||||
- name: AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE
|
||||
value: /usr/app/secrets/sql_password
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /api/configuration
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 5
|
||||
periodSeconds: 5
|
||||
failureThreshold: 4
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/configuration
|
||||
port: http
|
||||
initialDelaySeconds: 60
|
||||
timeoutSeconds: 5
|
||||
periodSeconds: 30
|
||||
failureThreshold: 2
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/configuration
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
timeoutSeconds: 5
|
||||
periodSeconds: 5
|
||||
failureThreshold: 5
|
||||
volumeMounts:
|
||||
- mountPath: /etc/authelia
|
||||
name: config-volume
|
||||
- mountPath: /usr/app/secrets
|
||||
name: secrets
|
||||
readOnly: true
|
||||
- mountPath: /etc/localtime
|
||||
name: localtime
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: authelia
|
||||
items:
|
||||
- key: configuration.yml
|
||||
path: configuration.yml
|
||||
- name: secrets
|
||||
secret:
|
||||
secretName: authelia
|
||||
items:
|
||||
- key: jwt_secret
|
||||
path: jwt
|
||||
- key: duo_secret
|
||||
path: duo
|
||||
- key: session_secret
|
||||
path: session
|
||||
- key: redis_password
|
||||
path: redis_password
|
||||
- key: sql_password
|
||||
path: sql_password
|
||||
- key: ldap_password
|
||||
path: ldap_password
|
||||
- key: smtp_password
|
||||
path: smtp_password
|
||||
- name: localtime
|
||||
hostPath:
|
||||
path: /etc/localtime
|
||||
```
|
|
@ -23,7 +23,7 @@ session:
|
|||
name: authelia_session
|
||||
|
||||
# The secret to encrypt the session cookie.
|
||||
# This secret can also be set using the env variables AUTHELIA_SESSION_SECRET
|
||||
# Secret can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
secret: unsecure_session_secret
|
||||
|
||||
# The time in seconds before the cookie expires and session is reset.
|
||||
|
@ -48,7 +48,7 @@ session:
|
|||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
# This secret can also be set using the env variables AUTHELIA_SESSION_REDIS_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: authelia
|
||||
```
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@ storage:
|
|||
port: 3306
|
||||
database: authelia
|
||||
username: authelia
|
||||
# This secret can also be set using the env variables AUTHELIA_STORAGE_MYSQL_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: mypassword
|
||||
```
|
||||
|
||||
## Loading a password from a secret instead of inside the configuration
|
||||
|
||||
Password can also be defined using a [secret](../secrets.md).
|
||||
|
|
|
@ -15,6 +15,10 @@ storage:
|
|||
port: 3306
|
||||
database: authelia
|
||||
username: authelia
|
||||
# This secret can also be set using the env variables AUTHELIA_STORAGE_MYSQL_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: mypassword
|
||||
```
|
||||
|
||||
## Loading a password from a secret instead of inside the configuration
|
||||
|
||||
Password can also be defined using a [secret](../secrets.md).
|
|
@ -15,6 +15,19 @@ storage:
|
|||
port: 5432
|
||||
database: authelia
|
||||
username: authelia
|
||||
# This secret can also be set using the env variables AUTHELIA_STORAGE_POSTGRES_PASSWORD
|
||||
# Password can also be set using a secret: https://docs.authelia.com/configuration/secrets.html
|
||||
password: mypassword
|
||||
sslmode: disable
|
||||
```
|
||||
|
||||
## SSL Mode
|
||||
|
||||
SSL mode configures how to handle SSL connections with Postgres.
|
||||
Valid options are 'disable', 'require', 'verify-ca', or 'verify-full'.
|
||||
See the [PostgreSQL Documentation](https://www.postgresql.org/docs/12/libpq-ssl.html)
|
||||
or [Pure Go Postgres driver Documentation](https://godoc.org/github.com/lib/pq)
|
||||
for more information.
|
||||
|
||||
## Loading a password from a secret instead of inside the configuration
|
||||
|
||||
Password can also be defined using a [secret](../secrets.md).
|
|
@ -12,19 +12,27 @@ import (
|
|||
|
||||
// Read a YAML configuration and create a Configuration object out of it.
|
||||
func Read(configPath string) (*schema.Configuration, []error) {
|
||||
viper.SetEnvPrefix("AUTHELIA")
|
||||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
|
||||
// we need to bind all env variables as long as https://github.com/spf13/viper/issues/761
|
||||
// is not resolved.
|
||||
viper.BindEnv("jwt_secret") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("duo_api.secret_key") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("session.secret") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authentication_backend.ldap.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("notifier.smtp.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("session.redis.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("storage.mysql.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("storage.postgres.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.jwt_secret") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.duo_api.secret_key") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.session.secret") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.authentication_backend.ldap.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.notifier.smtp.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.session.redis.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.storage.mysql.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.storage.postgres.password") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
|
||||
viper.BindEnv("authelia.jwt_secret.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.duo_api.secret_key.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.session.secret.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.authentication_backend.ldap.password.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.notifier.smtp.password.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.session.redis.password.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.storage.mysql.password.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
viper.BindEnv("authelia.storage.postgres.password.file") //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
|
||||
viper.SetConfigFile(configPath)
|
||||
|
||||
|
@ -38,7 +46,8 @@ func Read(configPath string) (*schema.Configuration, []error) {
|
|||
viper.Unmarshal(&configuration) //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
||||
|
||||
val := schema.NewStructValidator()
|
||||
validator.Validate(&configuration, val)
|
||||
validator.ValidateSecrets(&configuration, val, viper.GetViper())
|
||||
validator.ValidateConfiguration(&configuration, val)
|
||||
|
||||
if val.HasErrors() {
|
||||
return nil, val.Errors()
|
||||
|
|
|
@ -2,12 +2,25 @@ package configuration
|
|||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func resetEnv() {
|
||||
_ = os.Unsetenv("AUTHELIA_JWT_SECRET")
|
||||
_ = os.Unsetenv("AUTHELIA_DUO_API_SECRET_KEY")
|
||||
_ = os.Unsetenv("AUTHELIA_SESSION_SECRET")
|
||||
_ = os.Unsetenv("AUTHELIA_SESSION_SECRET")
|
||||
_ = os.Unsetenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD")
|
||||
_ = os.Unsetenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD")
|
||||
_ = os.Unsetenv("AUTHELIA_SESSION_REDIS_PASSWORD")
|
||||
_ = os.Unsetenv("AUTHELIA_STORAGE_MYSQL_PASSWORD")
|
||||
_ = os.Unsetenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD")
|
||||
}
|
||||
|
||||
func TestShouldParseConfigFile(t *testing.T) {
|
||||
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET", "secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_DUO_API_SECRET_KEY", "duo_secret_from_env"))
|
||||
|
@ -16,7 +29,6 @@ func TestShouldParseConfigFile(t *testing.T) {
|
|||
require.NoError(t, os.Setenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD", "smtp_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_PASSWORD", "redis_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_MYSQL_PASSWORD", "mysql_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD", "postgres_secret_from_env"))
|
||||
|
||||
config, errors := Read("./test_resources/config.yml")
|
||||
|
||||
|
@ -37,8 +49,58 @@ func TestShouldParseConfigFile(t *testing.T) {
|
|||
assert.Equal(t, "smtp_secret_from_env", config.Notifier.SMTP.Password)
|
||||
assert.Equal(t, "redis_secret_from_env", config.Session.Redis.Password)
|
||||
assert.Equal(t, "mysql_secret_from_env", config.Storage.MySQL.Password)
|
||||
|
||||
assert.Equal(t, "deny", config.AccessControl.DefaultPolicy)
|
||||
assert.Len(t, config.AccessControl.Rules, 12)
|
||||
}
|
||||
|
||||
func TestShouldParseAltConfigFile(t *testing.T) {
|
||||
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD", "postgres_secret_from_env"))
|
||||
config, errors := Read("./test_resources/config_alt.yml")
|
||||
require.Len(t, errors, 0)
|
||||
|
||||
assert.Equal(t, 9091, config.Port)
|
||||
assert.Equal(t, "debug", config.LogLevel)
|
||||
assert.Equal(t, "https://home.example.com:8080/", config.DefaultRedirectionURL)
|
||||
assert.Equal(t, "authelia.com", config.TOTP.Issuer)
|
||||
assert.Equal(t, "secret_from_env", config.JWTSecret)
|
||||
|
||||
assert.Equal(t, "api-123456789.example.com", config.DuoAPI.Hostname)
|
||||
assert.Equal(t, "ABCDEF", config.DuoAPI.IntegrationKey)
|
||||
assert.Equal(t, "postgres_secret_from_env", config.Storage.PostgreSQL.Password)
|
||||
|
||||
assert.Equal(t, "deny", config.AccessControl.DefaultPolicy)
|
||||
assert.Len(t, config.AccessControl.Rules, 12)
|
||||
}
|
||||
|
||||
func TestShouldOnlyAllowOneEnvType(t *testing.T) {
|
||||
resetEnv()
|
||||
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD", "postgres_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_POSTGRES_PASSWORD_FILE", "/tmp/postgres_secret"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET", "secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_DUO_API_SECRET_KEY", "duo_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_SESSION_SECRET", "session_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD", "ldap_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD", "smtp_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_PASSWORD", "redis_secret_from_env"))
|
||||
_, errors := Read("./test_resources/config_alt.yml")
|
||||
|
||||
require.Len(t, errors, 2)
|
||||
assert.EqualError(t, errors[0], "secret is defined in multiple areas: storage.postgres.password")
|
||||
assert.True(t, strings.HasPrefix(errors[1].Error(), "error loading secret file (storage.postgres.password): open /tmp/postgres_secret: "))
|
||||
}
|
||||
|
||||
func TestShouldOnlyAllowEnvOrConfig(t *testing.T) {
|
||||
resetEnv()
|
||||
require.NoError(t, os.Setenv("AUTHELIA_STORAGE_MYSQL_PASSWORD", "mysql_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_JWT_SECRET", "secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_DUO_API_SECRET_KEY", "duo_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_SESSION_SECRET", "session_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD", "ldap_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_NOTIFIER_SMTP_PASSWORD", "smtp_secret_from_env"))
|
||||
require.NoError(t, os.Setenv("AUTHELIA_SESSION_REDIS_PASSWORD", "redis_secret_from_env"))
|
||||
_, errors := Read("./test_resources/config_with_secret.yml")
|
||||
|
||||
require.Len(t, errors, 1)
|
||||
require.EqualError(t, errors[0], "error loading secret (jwt_secret): it's already defined in the config file")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
###############################################################
|
||||
# Authelia configuration #
|
||||
###############################################################
|
||||
|
||||
host: 127.0.0.1
|
||||
port: 9091
|
||||
|
||||
log_level: debug
|
||||
default_redirection_url: https://home.example.com:8080/
|
||||
|
||||
totp:
|
||||
issuer: authelia.com
|
||||
|
||||
duo_api:
|
||||
hostname: api-123456789.example.com
|
||||
integration_key: ABCDEF
|
||||
|
||||
authentication_backend:
|
||||
ldap:
|
||||
url: ldap://127.0.0.1
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectclass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
user: cn=admin,dc=example,dc=com
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
||||
rules:
|
||||
# Rules applied to everyone
|
||||
- domain: public.example.com
|
||||
policy: bypass
|
||||
|
||||
- domain: secure.example.com
|
||||
policy: one_factor
|
||||
# Network based rule, if not provided any network matches.
|
||||
networks:
|
||||
- 192.168.1.0/24
|
||||
- domain: secure.example.com
|
||||
policy: two_factor
|
||||
|
||||
- domain: [singlefactor.example.com, onefactor.example.com]
|
||||
policy: one_factor
|
||||
|
||||
# Rules applied to 'admins' group
|
||||
- domain: "mx2.mail.example.com"
|
||||
subject: "group:admins"
|
||||
policy: deny
|
||||
- domain: "*.example.com"
|
||||
subject: "group:admins"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to 'dev' group
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/groups/dev/.*$"
|
||||
subject: "group:dev"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to user 'john'
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/users/john/.*$"
|
||||
subject: "user:john"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to 'dev' group and user 'john'
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/deny-all.*$"
|
||||
subject: ["group:dev", "user:john"]
|
||||
policy: denied
|
||||
|
||||
# Rules applied to user 'harry'
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/users/harry/.*$"
|
||||
subject: "user:harry"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to user 'bob'
|
||||
- domain: "*.mail.example.com"
|
||||
subject: "user:bob"
|
||||
policy: two_factor
|
||||
- domain: "dev.example.com"
|
||||
resources:
|
||||
- "^/users/bob/.*$"
|
||||
subject: "user:bob"
|
||||
policy: two_factor
|
||||
|
||||
session:
|
||||
name: authelia_session
|
||||
expiration: 3600000 # 1 hour
|
||||
inactivity: 300000 # 5 minutes
|
||||
domain: example.com
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
|
||||
regulation:
|
||||
max_retries: 3
|
||||
find_time: 120
|
||||
ban_time: 300
|
||||
|
||||
storage:
|
||||
postgres:
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
database: authelia
|
||||
username: authelia
|
||||
|
||||
notifier:
|
||||
smtp:
|
||||
username: test
|
||||
host: 127.0.0.1
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -0,0 +1,124 @@
|
|||
###############################################################
|
||||
# Authelia configuration #
|
||||
###############################################################
|
||||
|
||||
host: 127.0.0.1
|
||||
port: 9091
|
||||
jwt_secret: secret_from_config
|
||||
|
||||
log_level: debug
|
||||
default_redirection_url: https://home.example.com:8080/
|
||||
|
||||
totp:
|
||||
issuer: authelia.com
|
||||
|
||||
duo_api:
|
||||
hostname: api-123456789.example.com
|
||||
integration_key: ABCDEF
|
||||
|
||||
authentication_backend:
|
||||
ldap:
|
||||
url: ldap://127.0.0.1
|
||||
base_dn: dc=example,dc=com
|
||||
username_attribute: uid
|
||||
additional_users_dn: ou=users
|
||||
users_filter: (&({username_attribute}={input})(objectCategory=person)(objectClass=user))
|
||||
additional_groups_dn: ou=groups
|
||||
groups_filter: (&(member={dn})(objectclass=groupOfNames))
|
||||
group_name_attribute: cn
|
||||
mail_attribute: mail
|
||||
user: cn=admin,dc=example,dc=com
|
||||
|
||||
access_control:
|
||||
default_policy: deny
|
||||
|
||||
rules:
|
||||
# Rules applied to everyone
|
||||
- domain: public.example.com
|
||||
policy: bypass
|
||||
|
||||
- domain: secure.example.com
|
||||
policy: one_factor
|
||||
# Network based rule, if not provided any network matches.
|
||||
networks:
|
||||
- 192.168.1.0/24
|
||||
- domain: secure.example.com
|
||||
policy: two_factor
|
||||
|
||||
- domain: [singlefactor.example.com, onefactor.example.com]
|
||||
policy: one_factor
|
||||
|
||||
# Rules applied to 'admins' group
|
||||
- domain: "mx2.mail.example.com"
|
||||
subject: "group:admins"
|
||||
policy: deny
|
||||
- domain: "*.example.com"
|
||||
subject: "group:admins"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to 'dev' group
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/groups/dev/.*$"
|
||||
subject: "group:dev"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to user 'john'
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/users/john/.*$"
|
||||
subject: "user:john"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to 'dev' group and user 'john'
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/deny-all.*$"
|
||||
subject: ["group:dev", "user:john"]
|
||||
policy: denied
|
||||
|
||||
# Rules applied to user 'harry'
|
||||
- domain: dev.example.com
|
||||
resources:
|
||||
- "^/users/harry/.*$"
|
||||
subject: "user:harry"
|
||||
policy: two_factor
|
||||
|
||||
# Rules applied to user 'bob'
|
||||
- domain: "*.mail.example.com"
|
||||
subject: "user:bob"
|
||||
policy: two_factor
|
||||
- domain: "dev.example.com"
|
||||
resources:
|
||||
- "^/users/bob/.*$"
|
||||
subject: "user:bob"
|
||||
policy: two_factor
|
||||
|
||||
session:
|
||||
name: authelia_session
|
||||
expiration: 3600000 # 1 hour
|
||||
inactivity: 300000 # 5 minutes
|
||||
domain: example.com
|
||||
redis:
|
||||
host: 127.0.0.1
|
||||
port: 6379
|
||||
|
||||
regulation:
|
||||
max_retries: 3
|
||||
find_time: 120
|
||||
ban_time: 300
|
||||
|
||||
storage:
|
||||
mysql:
|
||||
host: 127.0.0.1
|
||||
port: 3306
|
||||
database: authelia
|
||||
username: authelia
|
||||
|
||||
notifier:
|
||||
smtp:
|
||||
username: test
|
||||
host: 127.0.0.1
|
||||
port: 1025
|
||||
sender: admin@example.com
|
||||
disable_require_tls: true
|
|
@ -10,8 +10,8 @@ import (
|
|||
var defaultPort = 8080
|
||||
var defaultLogLevel = "info"
|
||||
|
||||
// Validate and adapt the configuration read from file.
|
||||
func Validate(configuration *schema.Configuration, validator *schema.StructValidator) {
|
||||
// ValidateConfiguration and adapt the configuration read from file.
|
||||
func ValidateConfiguration(configuration *schema.Configuration, validator *schema.StructValidator) {
|
||||
if configuration.Host == "" {
|
||||
configuration.Host = "0.0.0.0"
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ func TestShouldNotUpdateConfig(t *testing.T) {
|
|||
validator := schema.NewStructValidator()
|
||||
config := newDefaultConfig()
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
|
||||
require.Len(t, validator.Errors(), 0)
|
||||
assert.Equal(t, 9090, config.Port)
|
||||
|
@ -49,7 +49,7 @@ func TestShouldValidateAndUpdatePort(t *testing.T) {
|
|||
config := newDefaultConfig()
|
||||
config.Port = 0
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
|
||||
require.Len(t, validator.Errors(), 0)
|
||||
assert.Equal(t, 8080, config.Port)
|
||||
|
@ -60,7 +60,7 @@ func TestShouldValidateAndUpdateHost(t *testing.T) {
|
|||
config := newDefaultConfig()
|
||||
config.Host = ""
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
|
||||
require.Len(t, validator.Errors(), 0)
|
||||
assert.Equal(t, "0.0.0.0", config.Host)
|
||||
|
@ -71,7 +71,7 @@ func TestShouldValidateAndUpdateLogsLevel(t *testing.T) {
|
|||
config := newDefaultConfig()
|
||||
config.LogLevel = ""
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
|
||||
require.Len(t, validator.Errors(), 0)
|
||||
assert.Equal(t, "info", config.LogLevel)
|
||||
|
@ -81,12 +81,12 @@ func TestShouldEnsureNotifierConfigIsProvided(t *testing.T) {
|
|||
validator := schema.NewStructValidator()
|
||||
config := newDefaultConfig()
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 0)
|
||||
|
||||
config.Notifier = nil
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 1)
|
||||
assert.EqualError(t, validator.Errors()[0], "A notifier configuration must be provided")
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ func TestShouldAddDefaultAccessControl(t *testing.T) {
|
|||
validator := schema.NewStructValidator()
|
||||
config := newDefaultConfig()
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 0)
|
||||
assert.NotNil(t, config.AccessControl)
|
||||
assert.Equal(t, "deny", config.AccessControl.DefaultPolicy)
|
||||
|
@ -106,7 +106,7 @@ func TestShouldRaiseErrorWhenTLSCertWithoutKeyIsProvided(t *testing.T) {
|
|||
config := newDefaultConfig()
|
||||
config.TLSCert = "/tmp/cert.pem"
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 1)
|
||||
assert.EqualError(t, validator.Errors()[0], "No TLS key provided, please check the \"tls_key\" which has been configured")
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func TestShouldRaiseErrorWhenTLSKeyWithoutCertIsProvided(t *testing.T) {
|
|||
config := newDefaultConfig()
|
||||
config.TLSKey = "/tmp/key.pem"
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 1)
|
||||
assert.EqualError(t, validator.Errors()[0], "No TLS certificate provided, please check the \"tls_cert\" which has been configured")
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ func TestShouldNotRaiseErrorWhenBothTLSCertificateAndKeyAreProvided(t *testing.T
|
|||
config.TLSCert = "/tmp/cert.pem"
|
||||
config.TLSKey = "/tmp/key.pem"
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 0)
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ func TestShouldRaiseErrorWithUndefinedJWTSecretKey(t *testing.T) {
|
|||
config := newDefaultConfig()
|
||||
config.JWTSecret = ""
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 1)
|
||||
assert.EqualError(t, validator.Errors()[0], "Provide a JWT secret using \"jwt_secret\" key")
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ func TestShouldRaiseErrorWithBadDefaultRedirectionURL(t *testing.T) {
|
|||
config := newDefaultConfig()
|
||||
config.DefaultRedirectionURL = "abc"
|
||||
|
||||
Validate(&config, validator)
|
||||
ValidateConfiguration(&config, validator)
|
||||
require.Len(t, validator.Errors(), 1)
|
||||
assert.EqualError(t, validator.Errors()[0], "Unable to parse default redirection url")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/authelia/authelia/internal/configuration/schema"
|
||||
"github.com/authelia/authelia/internal/logging"
|
||||
)
|
||||
|
||||
// ValidateSecrets checks that secrets are either specified by config file/env or by file references.
|
||||
func ValidateSecrets(configuration *schema.Configuration, validator *schema.StructValidator, viper *viper.Viper) {
|
||||
configuration.JWTSecret = getSecretValue("jwt_secret", validator, viper)
|
||||
configuration.Session.Secret = getSecretValue("session.secret", validator, viper)
|
||||
|
||||
if configuration.DuoAPI != nil {
|
||||
configuration.DuoAPI.SecretKey = getSecretValue("duo_api.secret_key", validator, viper)
|
||||
}
|
||||
|
||||
if configuration.Session.Redis != nil {
|
||||
configuration.Session.Redis.Password = getSecretValue("session.redis.password", validator, viper)
|
||||
}
|
||||
|
||||
if configuration.AuthenticationBackend.Ldap != nil {
|
||||
configuration.AuthenticationBackend.Ldap.Password = getSecretValue("authentication_backend.ldap.password", validator, viper)
|
||||
}
|
||||
|
||||
if configuration.Notifier != nil && configuration.Notifier.SMTP != nil {
|
||||
configuration.Notifier.SMTP.Password = getSecretValue("notifier.smtp.password", validator, viper)
|
||||
}
|
||||
|
||||
if configuration.Storage.MySQL != nil {
|
||||
configuration.Storage.MySQL.Password = getSecretValue("storage.mysql.password", validator, viper)
|
||||
}
|
||||
|
||||
if configuration.Storage.PostgreSQL != nil {
|
||||
configuration.Storage.PostgreSQL.Password = getSecretValue("storage.postgres.password", validator, viper)
|
||||
}
|
||||
}
|
||||
|
||||
func getSecretValue(name string, validator *schema.StructValidator, viper *viper.Viper) string {
|
||||
configValue := viper.GetString(name)
|
||||
envValue := viper.GetString("authelia." + name)
|
||||
fileEnvValue := viper.GetString("authelia." + name + ".file")
|
||||
|
||||
// Error Checking.
|
||||
if envValue != "" && fileEnvValue != "" {
|
||||
validator.Push(fmt.Errorf("secret is defined in multiple areas: %s", name))
|
||||
}
|
||||
if (envValue != "" || fileEnvValue != "") && configValue != "" {
|
||||
validator.Push(fmt.Errorf("error loading secret (%s): it's already defined in the config file", name))
|
||||
}
|
||||
|
||||
// Derive Secret.
|
||||
if fileEnvValue != "" {
|
||||
content, err := ioutil.ReadFile(fileEnvValue)
|
||||
if err != nil {
|
||||
validator.Push(fmt.Errorf("error loading secret file (%s): %s", name, err))
|
||||
} else {
|
||||
return strings.Replace(string(content), "\n", "", -1)
|
||||
}
|
||||
}
|
||||
if envValue != "" {
|
||||
logging.Logger().Warnf("The following secret is defined as an environment variable, this is insecure and being removed in 4.18.0+, it's recommended to use the file secrets instead (https://docs.authelia.com/configuration/secrets.html): %s", name)
|
||||
return envValue
|
||||
}
|
||||
return configValue
|
||||
}
|
|
@ -40,7 +40,7 @@ func validatePostgreSQLConfiguration(configuration *schema.PostgreSQLStorageConf
|
|||
|
||||
if !(configuration.SSLMode == "disable" || configuration.SSLMode == "require" ||
|
||||
configuration.SSLMode == "verify-ca" || configuration.SSLMode == "verify-full") {
|
||||
validator.Push(errors.New("SSL mode must be 'disable', 'require', 'verify-ca' or 'verify-full'"))
|
||||
validator.Push(errors.New("SSL mode must be 'disable', 'require', 'verify-ca', or 'verify-full'"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ func (s *StorageSuite) TestShouldValidatePostgresSSLModeMustBeValid() {
|
|||
ValidateStorage(s.configuration, validator)
|
||||
|
||||
s.Require().Len(validator.Errors(), 1)
|
||||
s.Assert().EqualError(validator.Errors()[0], "SSL mode must be 'disable', 'require', 'verify-ca' or 'verify-full'")
|
||||
s.Assert().EqualError(validator.Errors()[0], "SSL mode must be 'disable', 'require', 'verify-ca', or 'verify-full'")
|
||||
}
|
||||
|
||||
func TestShouldRunStorageSuite(t *testing.T) {
|
||||
|
|
Loading…
Reference in New Issue