[FEATURE] Add TLS support. (#677)

* [FEATURE] Add TLS support.

Fixes #368.

* [FEATURE] Introduce OnError hook in suites.

This hook allows to perform actions following an erroneous suite
like displaying the logs of Authelia.

* Display Authelia logs of Standalone suite when tests fail.

* Fix Standalone suite.

* Apply suggestions from code review

* Rename ssl_key and ssl_cert into tls_key and tls_cert.
pull/680/head^2
Clément Michaud 2020-03-03 08:18:25 +01:00 committed by GitHub
parent 0c43740a4e
commit faf43de14f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 428 additions and 18 deletions

View File

@ -151,6 +151,14 @@ func runOnSetupTimeout(suite string) error {
return utils.RunCommandWithTimeout(cmd, 15*time.Second)
}
func runOnError(suite string) error {
cmd := utils.CommandWithStdout("go", "run", "cmd/authelia-suites/main.go", "error", suite)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
return utils.RunCommandWithTimeout(cmd, 15*time.Second)
}
func setupSuite(suiteName string) error {
log.Infof("Setup environment for suite %s...", suiteName)
signalChannel := make(chan os.Signal)
@ -261,11 +269,19 @@ func runSuiteTests(suiteName string, withEnv bool) error {
testErr := cmd.Run()
if withEnv {
err := teardownSuite(suiteName)
// If the tests failed, run the error hook.
if testErr != nil {
if err := runOnError(suiteName); err != nil {
// Do not return this error to return the test error instead
// and not skip the teardown phase.
log.Errorf("Error executing OnError callback: %v", err)
}
}
if err != nil {
log.Error(err)
if withEnv {
if err := teardownSuite(suiteName); err != nil {
// Do not return this error to return the test error instead
log.Errorf("Error running teardown: %v", err)
}
}

View File

@ -38,6 +38,12 @@ func main() {
Run: setupTimeoutSuite,
}
errorCmd := &cobra.Command{
Use: "error [suite]",
Short: "Run the OnError callback when some tests fail",
Run: runErrorCallback,
}
stopCmd := &cobra.Command{
Use: "teardown [suite]",
Short: "Teardown the suite environment",
@ -46,8 +52,11 @@ func main() {
rootCmd.AddCommand(startCmd)
rootCmd.AddCommand(setupTimeoutCmd)
rootCmd.AddCommand(errorCmd)
rootCmd.AddCommand(stopCmd)
rootCmd.Execute()
if err := rootCmd.Execute(); err != nil {
log.Fatal(err)
}
}
func createRunningSuiteFile(suite string) error {
@ -120,6 +129,18 @@ func setupTimeoutSuite(cmd *cobra.Command, args []string) {
}
}
func runErrorCallback(cmd *cobra.Command, args []string) {
suiteName := args[0]
s := suites.GlobalRegistry.Get(suiteName)
if s.OnError == nil {
return
}
if err := s.OnError(); err != nil {
log.Fatal(err)
}
}
func teardownSuite(cmd *cobra.Command, args []string) {
if os.Getenv("SKIP_TEARDOWN") != "" {
return

View File

@ -5,6 +5,8 @@
# The host and port to listen on
host: 0.0.0.0
port: 9091
# tls_key: /var/lib/authelia/ssl/key.pem
# tls_cert: /var/lib/authelia/ssl/cert.pem
# Level of verbosity for logs: info, debug, trace
logs_level: debug

View File

@ -10,6 +10,7 @@ nav_order: 5
Here are the main customizable options in Authelia.
## Host & Port
`optional: true`
Defines the address to listen on.
@ -17,6 +18,17 @@ Defines the address to listen on.
host: 0.0.0.0
port: 9091
## TLS
`optional: true`
Authelia can use TLS. Provide the certificate and the key with the
following configuration options:
tls_key: /var/lib/authelia/ssl/key.pem
tls_cert: /var/lib/authelia/ssl/cert.pem
## Logs level
`optional: true`
@ -50,4 +62,4 @@ can redirect her after the authentication process.
However, when a user visits the sign in portal directly, the portal considers
the targeted website is the portal. In that case and if the default redirection URL
is configured, the user is redirected to that URL. If not defined, the user is not
redirected after authentication.
redirected after authentication.

View File

@ -2,8 +2,11 @@ package schema
// Configuration object extracted from YAML configuration file.
type Configuration struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
TLSCert string `mapstructure:"tls_cert"`
TLSKey string `mapstructure:"tls_key"`
LogsLevel string `mapstructure:"logs_level"`
// This secret is used by the identity validation process to forge JWT tokens

View File

@ -24,6 +24,12 @@ func Validate(configuration *schema.Configuration, validator *schema.StructValid
configuration.LogsLevel = defaultLogsLevel
}
if configuration.TLSKey != "" && configuration.TLSCert == "" {
validator.Push(fmt.Errorf("No TLS certificate provided, please check the \"tls_cert\" which has been configured"))
} else if configuration.TLSKey == "" && configuration.TLSCert != "" {
validator.Push(fmt.Errorf("No TLS key provided, please check the \"tls_key\" which has been configured"))
}
if configuration.DefaultRedirectionURL != "" {
_, err := url.ParseRequestURI(configuration.DefaultRedirectionURL)
if err != nil {

View File

@ -99,3 +99,33 @@ func TestShouldAddDefaultAccessControl(t *testing.T) {
assert.NotNil(t, config.AccessControl)
assert.Equal(t, "deny", config.AccessControl.DefaultPolicy)
}
func TestShouldRaiseErrorWhenTLSCertWithoutKeyIsProvided(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultConfig()
config.TLSCert = "/tmp/cert.pem"
Validate(&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")
}
func TestShouldRaiseErrorWhenTLSKeyWithoutCertIsProvided(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultConfig()
config.TLSKey = "/tmp/key.pem"
Validate(&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")
}
func TestShouldNotRaiseErrorWhenBothTLSCertificateAndKeyAreProvided(t *testing.T) {
validator := schema.NewStructValidator()
config := newDefaultConfig()
config.TLSCert = "/tmp/cert.pem"
config.TLSKey = "/tmp/key.pem"
Validate(&config, validator)
require.Len(t, validator.Errors(), 0)
}

View File

@ -101,9 +101,18 @@ func StartServer(configuration schema.Configuration, providers middlewares.Provi
ctx.SendFile(path.Join(publicDir, "index.html"))
}
portPattern := fmt.Sprintf("%s:%d", configuration.Host, configuration.Port)
logging.Logger().Infof("Authelia is listening on %s", portPattern)
addrPattern := fmt.Sprintf("%s:%d", configuration.Host, configuration.Port)
logging.Logger().Fatal(fasthttp.ListenAndServe(portPattern,
middlewares.LogRequestMiddleware(router.Handler)))
if configuration.TLSCert != "" && configuration.TLSKey != "" {
logging.Logger().Infof("Authelia is listening on %s and uses TLS", addrPattern)
logging.Logger().Fatal(fasthttp.ListenAndServeTLS(addrPattern,
configuration.TLSCert, configuration.TLSKey,
middlewares.LogRequestMiddleware(router.Handler)))
} else {
logging.Logger().Infof("Authelia is listening on %s and do not use TLS", addrPattern)
logging.Logger().Fatal(fasthttp.ListenAndServe(addrPattern,
middlewares.LogRequestMiddleware(router.Handler)))
}
}

View File

@ -3,6 +3,8 @@
###############################################################
port: 9091
tls_cert: /var/lib/authelia/ssl/cert.pem
tls_key: /var/lib/authelia/ssl/key.pem
logs_level: debug

View File

@ -7,5 +7,6 @@ services:
volumes:
- './Standalone/configuration.yml:/etc/authelia/configuration.yml:ro'
- './Standalone/users.yml:/var/lib/authelia/users.yml'
- './Standalone/ssl:/var/lib/authelia/ssl'
- '/tmp:/tmp'
user: ${USER_ID}:${GROUP_ID}

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/jCCAeagAwIBAgIRAKF0IRxC55eee6icERVf6fgwDQYJKoZIhvcNAQELBQAw
EjEQMA4GA1UEChMHQWNtZSBDbzAgFw0yMDAzMDExMjMzMzlaGA8yMTIwMDIwNjEy
MzMzOVowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAMi7/oSazFIxP3rHsSLjw5XPnpMKEaVwU1zLRzW6W80BDa/ER5to
I3POGLv8lAhtUwB6WvyilrCZfs/D5lkcCxswafU/2LNppFuODnW+PG9eobgOy6Nv
f+KbnZFPRV7PB2yK6DqMyb+tbTQ7F6rEf4i6n28DI0dNyNvUCk0ld3o93LZBvC/D
/+Ulf3Vtdfsd2TckXvdA8lH4VGQJ+FIxhboTlbW8VJlk1V7FZef7+m867kOnPSaj
zv5yygrIA0XPaMAZC/SZrXHMdhvcs43fgmmTel7JD4Sy/Z/pmFlrZr5Xa8jcWycJ
ILLuPnXhgKstgq5wtDkTMZ6rpgMrKcjMKcMCAwEAAaNNMEswDgYDVR0PAQH/BAQD
AgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwFgYDVR0RBA8w
DYILZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBABdWkbipzPivAvvamMmQ
5iPPeStfdr5MBxJGT9nPbeXdtS/13FJnspLBMMYOw/2AZk7VFrNjxkXc4NHZSlGz
FcGMlSO40fyirdYaQTDtS230ucLB+LzfZx37y9dKpEKVmQ151kKJjJ4hAZ47LmAQ
aFoDLRo7PA2HmnJ60GrI9wVp96uy1sQ6PcToIyMcVEQ/tLEEow+ykSeiZb9+qBKV
K9GUcu2LorhBtUMmEWs0TJElaf6eKUoG6JXM2byulDg24w5b9gC26kAlHWc5WDU5
pAXOjlN/OYHB0sDbYViWIL390376fYIfu2N5EDKY4QjEYsWEs4Wm9HVS9IgHP/Gi
Xbo=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDIu/6EmsxSMT96
x7Ei48OVz56TChGlcFNcy0c1ulvNAQ2vxEebaCNzzhi7/JQIbVMAelr8opawmX7P
w+ZZHAsbMGn1P9izaaRbjg51vjxvXqG4Dsujb3/im52RT0Vezwdsiug6jMm/rW00
OxeqxH+Iup9vAyNHTcjb1ApNJXd6Pdy2Qbwvw//lJX91bXX7Hdk3JF73QPJR+FRk
CfhSMYW6E5W1vFSZZNVexWXn+/pvOu5Dpz0mo87+csoKyANFz2jAGQv0ma1xzHYb
3LON34Jpk3peyQ+Esv2f6ZhZa2a+V2vI3FsnCSCy7j514YCrLYKucLQ5EzGeq6YD
KynIzCnDAgMBAAECggEAC13R0LJvRWwyewJZvm8FQTNreEoGq8aLgeKk2p792cLo
gn5ry5n+/+y4q9RmkX+XRpynEE0omUFn09306jDTVCvOpCuEWsxtmR2XJgWqqGfE
Yoa78zo6FJvZNUQ22mKAuh23frFAL1FjsKRz96B+1EA1DPUxhzUZXZFJMAsiE9LZ
PxqPmnqXbPZsOb1XG33TAdCp6CC3H8KHICC+i4IC8prjKHGH/Q1saoNw8jmgwv0S
DelQUbEtqfmE6BmyTGxdeu4uW2Nv/wcENwySAOPKi5gstlbSKTa4IpKGp7CdquWi
stUW6pnSiEeDrDAzwC8uWdncOvnkAy2lRJkz/F9YoQKBgQDrCCqYdvGshecBBnfQ
fowxak2YBfG2jhAKPMHzrvQn5FIb+11x/jeXPEfOB6FShIzZ97JpFIpH3tcONlj3
OVzGCTD6WdRTcltzXVneJtNog7DliNFY4YmIPmQJ+y+EvJW1rSZTZAZI1Nbijg3n
fSd0PTzvgOGHSl1//RI1mFx7MwKBgQDapIPPSF0yf1UJ6Hhzam5NHGZ9fSqV5Qs0
Gi7uM08iDV5K7xiPglBkbN2EuMlgVnHaa5g8X897uwRSYR6nL4PRvcJiNSvnhWhe
+K3x7iHewIPYVfcghoqzuPKsXH2Zm26usdXHxBBa3IBbKtGaHnAd9h65AOUYAmAx
C2BzN90XMQKBgE2MjEFyPZunMulrsOziVG+Zm7ClhXOuvCwkj/pPp8/hzhXdgp+y
ObV09lxMuDX59l+VExEI7fd414yg8gngq3PMZJS2PxCpkvMlwhlCxk6d5ShXVHv3
LuH9dBS3BJ7PerZPQ24QeuJdF+n45S2UZgg8jHaaF9AEAYXRgsicVSdxAoGAJI0U
K/bg/awjv0BJwqGsRt/Ukm32TJC5ysAF0HRrajnp5YULChKy9dbtQV7S63QIHIeY
L5+kw/6DvnHV+gULeGjMsjZJXK8Ev7u6+JLivqZYZDYa1iknztvAVegwZxmA61t3
bantQgNSwerql2U3QQsAH9Vydw0On6RTP2+7WkECgYBWD3u64hBKmAxPkqPotkgI
w/jdOlv8FLHO79+oH1PtKvkzspcYaecKGDm/RNLIXLYnt0AmZEK4qQ4/zDFaR/rc
AhoxK2cKTRltMrhp1ivtFfLggVGogtYNxEnjnsD4KMvH3SjSNdt06YgtZ92++fOp
UsE8Mpf4/G5X7DmcHJHk+w==
-----END PRIVATE KEY-----

View File

@ -39,4 +39,4 @@ var MX2MailBaseURL = fmt.Sprintf("https://mx2.mail.%s", BaseDomain)
var DuoBaseURL = "https://duo.example.com"
// AutheliaBaseURL the base URL of Authelia service
var AutheliaBaseURL = "http://authelia.example.com:9091"
var AutheliaBaseURL = "https://authelia.example.com:9091"

View File

@ -14,4 +14,14 @@ http {
proxy_pass http://authelia-backend:9091;
}
}
server {
# Reach authelia-backend using TLS.
listen 3001;
location / {
proxy_set_header Host $http_host;
proxy_pass https://authelia-backend:9091;
}
}
}

View File

@ -0,0 +1,16 @@
version: '3'
services:
nginx-portal:
image: nginx:alpine
volumes:
- ./example/compose/nginx/portal/nginx.https.conf:/etc/nginx/nginx.conf
- ./example/compose/nginx/portal/ssl:/etc/ssl
networks:
authelianet:
aliases:
- public.example.com
- secure.example.com
- login.example.com
- duo.example.com
# Set the IP to be able to query on port 443
ipv4_address: 192.168.240.100

View File

@ -0,0 +1,227 @@
#
# You can find a documented example of configuration in ./docs/proxies/nginx.md.
#
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 8080 ssl;
server_name login.example.com;
resolver 127.0.0.11 ipv6=off;
set $frontend_endpoint http://authelia-frontend:3001;
set $backend_endpoint https://authelia-backend:9091;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
# Serve the backend API for the portal.
location /api {
proxy_set_header X-Real-IP $remote_addr;
# Required by Authelia because "trust proxy" option is used.
# See https://expressjs.com/en/guide/behind-proxies.html
proxy_set_header X-Forwarded-Proto $scheme;
# Required by Authelia to build correct links for identity validation.
proxy_set_header X-Forwarded-Host $http_host;
# Needed for network ACLs to work. It appends the IP of the client to the list of IPs
# and allows Authelia to use it to match the network-based ACLs.
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_intercept_errors on;
proxy_pass $backend_endpoint;
}
# Serves the portal application.
location / {
# Allow websockets for webpack to auto-reload.
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host "127.0.0.1";
proxy_pass $frontend_endpoint;
}
}
# Serves the home page.
server {
listen 8080 ssl;
server_name home.example.com;
resolver 127.0.0.11 ipv6=off;
set $upstream_endpoint http://nginx-backend;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
location / {
proxy_set_header Host $http_host;
proxy_pass $upstream_endpoint;
}
}
# Example configuration of domains protected by Authelia.
server {
listen 8080 ssl;
server_name public.example.com
admin.example.com
secure.example.com
dev.example.com
singlefactor.example.com
mx1.mail.example.com mx2.mail.example.com;
resolver 127.0.0.11 ipv6=off;
set $upstream_verify https://authelia-backend:9091/api/verify;
set $upstream_endpoint http://nginx-backend;
set $upstream_headers http://httpbin:8000/headers;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
# Reverse proxy to the backend. It is protected by Authelia by forwarding authorization checks
# to the virtual endpoint introduced by nginx and declared in the next block.
location / {
auth_request /auth_verify;
auth_request_set $user $upstream_http_remote_user;
proxy_set_header X-Forwarded-User $user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Remote-Groups $groups;
# Route the request to the correct virtual host in the backend.
proxy_set_header Host $http_host;
# Authelia relies on Proxy-Authorization header to authenticate in basic auth.
# but for the sake of simplicity (because Authorization in supported in most
# clients) we take Authorization from the frontend and rewrite it to
# Proxy-Authorization before sending it to Authelia.
proxy_set_header Proxy-Authorization $http_authorization;
# mitigate HTTPoxy Vulnerability
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
proxy_set_header Proxy "";
# Set the `target_url` variable based on the request. It will be used to build the portal
# URL with the correct redirection parameter.
set $target_url $scheme://$http_host$request_uri;
error_page 401 =302 https://login.example.com:8080/?rd=$target_url;
proxy_pass $upstream_endpoint;
}
# Virtual endpoint forwarding requests to Authelia server.
location /auth_verify {
internal;
proxy_set_header X-Real-IP $remote_addr;
# Provide either X-Original-URL and X-Forwarded-Proto or
# X-Forwarded-Proto, X-Forwarded-Host and X-Forwarded-URI or both.
# Those headers will be used by Authelia to deduce the target url of the user.
#
# X-Forwarded-Proto is mandatory since Authelia uses the "trust proxy" option.
# See https://expressjs.com/en/guide/behind-proxies.html
proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-URI $request_uri;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Authelia can receive Proxy-Authorization to authenticate however most of the clients
# support Authorization instead. Therefore we rewrite Authorization into Proxy-Authorization.
proxy_set_header Proxy-Authorization $http_authorization;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_pass $upstream_verify;
}
# Used by suites to test the forwarded users and groups headers produced by Authelia.
location /headers {
auth_request /auth_verify;
auth_request_set $user $upstream_http_remote_user;
proxy_set_header Custom-Forwarded-User $user;
auth_request_set $groups $upstream_http_remote_groups;
proxy_set_header Custom-Forwarded-Groups $groups;
set $target_url $scheme://$http_host$request_uri;
error_page 401 =302 https://login.example.com:8080/?rd=$target_url;
proxy_pass $upstream_headers;
}
}
# Fake Web Mail used to receive emails sent by Authelia.
server {
listen 8080 ssl;
server_name mail.example.com;
resolver 127.0.0.11 ipv6=off;
set $upstream_endpoint http://smtp:1080;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
location / {
proxy_set_header Host $http_host;
proxy_pass $upstream_endpoint;
}
}
# Fake API emulating Duo behavior
server {
listen 443 ssl;
server_name duo.example.com;
resolver 127.0.0.11 ipv6=off;
set $upstream_endpoint http://duo-api:3000;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN";
location / {
proxy_set_header Host $http_host;
proxy_pass $upstream_endpoint;
}
}
# Matches all domains. It redirects to the home page.
server {
listen 8080 ssl;
server_name _;
ssl_certificate /etc/ssl/server.cert;
ssl_certificate_key /etc/ssl/server.key;
return 301 https://home.example.com:8080/;
}
}

View File

@ -9,10 +9,15 @@ import (
// Suite the definition of a suite
type Suite struct {
SetUp func(tmpPath string) error
SetUpTimeout time.Duration
SetUp func(tmpPath string) error
SetUpTimeout time.Duration
// Callback called when an error occur during setup phase.
OnSetupTimeout func() error
// Callback called when at least one test fail
OnError func() error
TestTimeout time.Duration
TearDown func(tmpPath string) error

View File

@ -9,6 +9,8 @@ import (
"github.com/stretchr/testify/suite"
)
// WARNING: This scenario is intended to be used with TLS enabled in the authelia backend.
type BackendProtectionScenario struct {
suite.Suite
}

View File

@ -14,7 +14,7 @@ func init() {
"internal/suites/example/compose/authelia/docker-compose.backend.{}.yml",
"internal/suites/example/compose/authelia/docker-compose.frontend.{}.yml",
"internal/suites/example/compose/nginx/backend/docker-compose.yml",
"internal/suites/example/compose/nginx/portal/docker-compose.yml",
"internal/suites/example/compose/nginx/portal/docker-compose.https.yml",
"internal/suites/example/compose/smtp/docker-compose.yml",
})
@ -28,7 +28,7 @@ func init() {
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
}
onSetupTimeout := func() error {
displayAutheliaLogs := func() error {
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
if err != nil {
return err
@ -51,7 +51,8 @@ func init() {
GlobalRegistry.Register(standaloneSuiteName, Suite{
SetUp: setup,
SetUpTimeout: 5 * time.Minute,
OnSetupTimeout: onSetupTimeout,
OnError: displayAutheliaLogs,
OnSetupTimeout: displayAutheliaLogs,
TearDown: teardown,
TestTimeout: 3 * time.Minute,
TearDownTimeout: 2 * time.Minute,