root: make redis settings more consistent (#9335)
* make redis settings more consistent Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add support to go Signed-off-by: Jens Langhammer <jens@goauthentik.io> * rewrite url Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix redis connect in wait_for_db Signed-off-by: Jens Langhammer <jens@goauthentik.io> * censor password when logging error Signed-off-by: Jens Langhammer <jens@goauthentik.io> * update docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * reword docs Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add redis url generation helper Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		@ -14,7 +14,7 @@ from pathlib import Path
 | 
				
			|||||||
from sys import argv, stderr
 | 
					from sys import argv, stderr
 | 
				
			||||||
from time import time
 | 
					from time import time
 | 
				
			||||||
from typing import Any
 | 
					from typing import Any
 | 
				
			||||||
from urllib.parse import urlparse
 | 
					from urllib.parse import quote_plus, urlparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import yaml
 | 
					import yaml
 | 
				
			||||||
from django.conf import ImproperlyConfigured
 | 
					from django.conf import ImproperlyConfigured
 | 
				
			||||||
@ -331,6 +331,26 @@ class ConfigLoader:
 | 
				
			|||||||
CONFIG = ConfigLoader()
 | 
					CONFIG = ConfigLoader()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def redis_url(db: int) -> str:
 | 
				
			||||||
 | 
					    """Helper to create a Redis URL for a specific database"""
 | 
				
			||||||
 | 
					    _redis_protocol_prefix = "redis://"
 | 
				
			||||||
 | 
					    _redis_tls_requirements = ""
 | 
				
			||||||
 | 
					    if CONFIG.get_bool("redis.tls", False):
 | 
				
			||||||
 | 
					        _redis_protocol_prefix = "rediss://"
 | 
				
			||||||
 | 
					        _redis_tls_requirements = f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
 | 
				
			||||||
 | 
					        if _redis_ca := CONFIG.get("redis.tls_ca_cert", None):
 | 
				
			||||||
 | 
					            _redis_tls_requirements += f"&ssl_ca_certs={_redis_ca}"
 | 
				
			||||||
 | 
					    _redis_url = (
 | 
				
			||||||
 | 
					        f"{_redis_protocol_prefix}"
 | 
				
			||||||
 | 
					        f"{quote_plus(CONFIG.get('redis.username'))}:"
 | 
				
			||||||
 | 
					        f"{quote_plus(CONFIG.get('redis.password'))}@"
 | 
				
			||||||
 | 
					        f"{quote_plus(CONFIG.get('redis.host'))}:"
 | 
				
			||||||
 | 
					        f"{CONFIG.get_int('redis.port')}"
 | 
				
			||||||
 | 
					        f"/{db}{_redis_tls_requirements}"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    return _redis_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    if len(argv) < 2:  # noqa: PLR2004
 | 
					    if len(argv) < 2:  # noqa: PLR2004
 | 
				
			||||||
        print(dumps(CONFIG.raw, indent=4, cls=AttrEncoder))
 | 
					        print(dumps(CONFIG.raw, indent=4, cls=AttrEncoder))
 | 
				
			||||||
 | 
				
			|||||||
@ -35,6 +35,7 @@ redis:
 | 
				
			|||||||
  password: ""
 | 
					  password: ""
 | 
				
			||||||
  tls: false
 | 
					  tls: false
 | 
				
			||||||
  tls_reqs: "none"
 | 
					  tls_reqs: "none"
 | 
				
			||||||
 | 
					  tls_ca_cert: null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# broker:
 | 
					# broker:
 | 
				
			||||||
#   url: ""
 | 
					#   url: ""
 | 
				
			||||||
 | 
				
			|||||||
@ -5,13 +5,12 @@ import os
 | 
				
			|||||||
from collections import OrderedDict
 | 
					from collections import OrderedDict
 | 
				
			||||||
from hashlib import sha512
 | 
					from hashlib import sha512
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
from urllib.parse import quote_plus
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from celery.schedules import crontab
 | 
					from celery.schedules import crontab
 | 
				
			||||||
from sentry_sdk import set_tag
 | 
					from sentry_sdk import set_tag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik import ENV_GIT_HASH_KEY, __version__
 | 
					from authentik import ENV_GIT_HASH_KEY, __version__
 | 
				
			||||||
from authentik.lib.config import CONFIG
 | 
					from authentik.lib.config import CONFIG, redis_url
 | 
				
			||||||
from authentik.lib.logging import get_logger_config, structlog_configure
 | 
					from authentik.lib.logging import get_logger_config, structlog_configure
 | 
				
			||||||
from authentik.lib.sentry import sentry_init
 | 
					from authentik.lib.sentry import sentry_init
 | 
				
			||||||
from authentik.lib.utils.reflection import get_env
 | 
					from authentik.lib.utils.reflection import get_env
 | 
				
			||||||
@ -195,25 +194,15 @@ REST_FRAMEWORK = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_redis_protocol_prefix = "redis://"
 | 
					 | 
				
			||||||
_redis_celery_tls_requirements = ""
 | 
					 | 
				
			||||||
if CONFIG.get_bool("redis.tls", False):
 | 
					 | 
				
			||||||
    _redis_protocol_prefix = "rediss://"
 | 
					 | 
				
			||||||
    _redis_celery_tls_requirements = f"?ssl_cert_reqs={CONFIG.get('redis.tls_reqs')}"
 | 
					 | 
				
			||||||
_redis_url = (
 | 
					 | 
				
			||||||
    f"{_redis_protocol_prefix}"
 | 
					 | 
				
			||||||
    f"{quote_plus(CONFIG.get('redis.username'))}:"
 | 
					 | 
				
			||||||
    f"{quote_plus(CONFIG.get('redis.password'))}@"
 | 
					 | 
				
			||||||
    f"{quote_plus(CONFIG.get('redis.host'))}:"
 | 
					 | 
				
			||||||
    f"{CONFIG.get_int('redis.port')}"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
CACHES = {
 | 
					CACHES = {
 | 
				
			||||||
    "default": {
 | 
					    "default": {
 | 
				
			||||||
        "BACKEND": "django_redis.cache.RedisCache",
 | 
					        "BACKEND": "django_redis.cache.RedisCache",
 | 
				
			||||||
        "LOCATION": CONFIG.get("cache.url") or f"{_redis_url}/{CONFIG.get('redis.db')}",
 | 
					        "LOCATION": CONFIG.get("cache.url") or redis_url(CONFIG.get("redis.db")),
 | 
				
			||||||
        "TIMEOUT": CONFIG.get_int("cache.timeout", 300),
 | 
					        "TIMEOUT": CONFIG.get_int("cache.timeout", 300),
 | 
				
			||||||
        "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
 | 
					        "OPTIONS": {
 | 
				
			||||||
 | 
					            "CLIENT_CLASS": "django_redis.client.DefaultClient",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "KEY_PREFIX": "authentik_cache",
 | 
					        "KEY_PREFIX": "authentik_cache",
 | 
				
			||||||
        "KEY_FUNCTION": "django_tenants.cache.make_key",
 | 
					        "KEY_FUNCTION": "django_tenants.cache.make_key",
 | 
				
			||||||
        "REVERSE_KEY_FUNCTION": "django_tenants.cache.reverse_key",
 | 
					        "REVERSE_KEY_FUNCTION": "django_tenants.cache.reverse_key",
 | 
				
			||||||
@ -276,7 +265,7 @@ CHANNEL_LAYERS = {
 | 
				
			|||||||
    "default": {
 | 
					    "default": {
 | 
				
			||||||
        "BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
 | 
					        "BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
 | 
				
			||||||
        "CONFIG": {
 | 
					        "CONFIG": {
 | 
				
			||||||
            "hosts": [CONFIG.get("channel.url", f"{_redis_url}/{CONFIG.get('redis.db')}")],
 | 
					            "hosts": [CONFIG.get("channel.url") or redis_url(CONFIG.get("redis.db"))],
 | 
				
			||||||
            "prefix": "authentik_channels_",
 | 
					            "prefix": "authentik_channels_",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -376,11 +365,9 @@ CELERY = {
 | 
				
			|||||||
    "beat_scheduler": "authentik.tenants.scheduler:TenantAwarePersistentScheduler",
 | 
					    "beat_scheduler": "authentik.tenants.scheduler:TenantAwarePersistentScheduler",
 | 
				
			||||||
    "task_create_missing_queues": True,
 | 
					    "task_create_missing_queues": True,
 | 
				
			||||||
    "task_default_queue": "authentik",
 | 
					    "task_default_queue": "authentik",
 | 
				
			||||||
    "broker_url": CONFIG.get("broker.url")
 | 
					    "broker_url": CONFIG.get("broker.url") or redis_url(CONFIG.get("redis.db")),
 | 
				
			||||||
    or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
 | 
					    "result_backend": CONFIG.get("result_backend.url") or redis_url(CONFIG.get("redis.db")),
 | 
				
			||||||
    "broker_transport_options": CONFIG.get_dict_from_b64_json("broker.transport_options"),
 | 
					    "broker_transport_options": CONFIG.get_dict_from_b64_json("broker.transport_options"),
 | 
				
			||||||
    "result_backend": CONFIG.get("result_backend.url")
 | 
					 | 
				
			||||||
    or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Sentry integration
 | 
					# Sentry integration
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,7 @@ type RedisConfig struct {
 | 
				
			|||||||
	Password  string  `yaml:"password" env:"PASSWORD, overwrite"`
 | 
						Password  string  `yaml:"password" env:"PASSWORD, overwrite"`
 | 
				
			||||||
	TLS       bool    `yaml:"tls" env:"TLS, overwrite"`
 | 
						TLS       bool    `yaml:"tls" env:"TLS, overwrite"`
 | 
				
			||||||
	TLSReqs   string  `yaml:"tls_reqs" env:"TLS_REQS, overwrite"`
 | 
						TLSReqs   string  `yaml:"tls_reqs" env:"TLS_REQS, overwrite"`
 | 
				
			||||||
 | 
						TLSCaCert *string `yaml:"tls_ca_certs" env:"TLS_CA_CERT, overwrite"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ListenConfig struct {
 | 
					type ListenConfig struct {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,8 @@ package application
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"crypto/x509"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
@ -19,6 +21,7 @@ import (
 | 
				
			|||||||
	"goauthentik.io/internal/outpost/proxyv2/codecs"
 | 
						"goauthentik.io/internal/outpost/proxyv2/codecs"
 | 
				
			||||||
	"goauthentik.io/internal/outpost/proxyv2/constants"
 | 
						"goauthentik.io/internal/outpost/proxyv2/constants"
 | 
				
			||||||
	"goauthentik.io/internal/outpost/proxyv2/redisstore"
 | 
						"goauthentik.io/internal/outpost/proxyv2/redisstore"
 | 
				
			||||||
 | 
						"goauthentik.io/internal/utils"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const RedisKeyPrefix = "authentik_proxy_session_"
 | 
					const RedisKeyPrefix = "authentik_proxy_session_"
 | 
				
			||||||
@ -31,11 +34,40 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
 | 
				
			|||||||
		maxAge = int(*t) + 1
 | 
							maxAge = int(*t) + 1
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if a.isEmbedded {
 | 
						if a.isEmbedded {
 | 
				
			||||||
 | 
							var tls *tls.Config
 | 
				
			||||||
 | 
							if config.Get().Redis.TLS {
 | 
				
			||||||
 | 
								tls = utils.GetTLSConfig()
 | 
				
			||||||
 | 
								switch strings.ToLower(config.Get().Redis.TLSReqs) {
 | 
				
			||||||
 | 
								case "none":
 | 
				
			||||||
 | 
								case "false":
 | 
				
			||||||
 | 
									tls.InsecureSkipVerify = true
 | 
				
			||||||
 | 
								case "required":
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								ca := config.Get().Redis.TLSCaCert
 | 
				
			||||||
 | 
								if ca != nil {
 | 
				
			||||||
 | 
									// Get the SystemCertPool, continue with an empty pool on error
 | 
				
			||||||
 | 
									rootCAs, _ := x509.SystemCertPool()
 | 
				
			||||||
 | 
									if rootCAs == nil {
 | 
				
			||||||
 | 
										rootCAs = x509.NewCertPool()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									certs, err := os.ReadFile(*ca)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										a.log.WithError(err).Fatalf("Failed to append %s to RootCAs", *ca)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// Append our cert to the system pool
 | 
				
			||||||
 | 
									if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
 | 
				
			||||||
 | 
										a.log.Println("No certs appended, using system certs only")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									tls.RootCAs = rootCAs
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		client := redis.NewClient(&redis.Options{
 | 
							client := redis.NewClient(&redis.Options{
 | 
				
			||||||
			Addr:      fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
 | 
								Addr:      fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
 | 
				
			||||||
			Username:  config.Get().Redis.Username,
 | 
								Username:  config.Get().Redis.Username,
 | 
				
			||||||
			Password:  config.Get().Redis.Password,
 | 
								Password:  config.Get().Redis.Password,
 | 
				
			||||||
			DB:        config.Get().Redis.DB,
 | 
								DB:        config.Get().Redis.DB,
 | 
				
			||||||
 | 
								TLSConfig: tls,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// New default RedisStore
 | 
							// New default RedisStore
 | 
				
			||||||
 | 
				
			|||||||
@ -9,7 +9,7 @@ from psycopg import OperationalError, connect
 | 
				
			|||||||
from redis import Redis
 | 
					from redis import Redis
 | 
				
			||||||
from redis.exceptions import RedisError
 | 
					from redis.exceptions import RedisError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from authentik.lib.config import CONFIG
 | 
					from authentik.lib.config import CONFIG, redis_url
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_postgres():
 | 
					def check_postgres():
 | 
				
			||||||
@ -35,24 +35,18 @@ def check_postgres():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_redis():
 | 
					def check_redis():
 | 
				
			||||||
    REDIS_PROTOCOL_PREFIX = "redis://"
 | 
					    url = redis_url(CONFIG.get("redis.db"))
 | 
				
			||||||
    if CONFIG.get_bool("redis.tls", False):
 | 
					 | 
				
			||||||
        REDIS_PROTOCOL_PREFIX = "rediss://"
 | 
					 | 
				
			||||||
    REDIS_URL = (
 | 
					 | 
				
			||||||
        f"{REDIS_PROTOCOL_PREFIX}"
 | 
					 | 
				
			||||||
        f"{quote_plus(CONFIG.get('redis.username'))}:"
 | 
					 | 
				
			||||||
        f"{quote_plus(CONFIG.get('redis.password'))}@"
 | 
					 | 
				
			||||||
        f"{quote_plus(CONFIG.get('redis.host'))}:"
 | 
					 | 
				
			||||||
        f"{CONFIG.get_int('redis.port')}/{CONFIG.get('redis.db')}"
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    while True:
 | 
					    while True:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            redis = Redis.from_url(REDIS_URL)
 | 
					            redis = Redis.from_url(url)
 | 
				
			||||||
            redis.ping()
 | 
					            redis.ping()
 | 
				
			||||||
            break
 | 
					            break
 | 
				
			||||||
        except RedisError as exc:
 | 
					        except RedisError as exc:
 | 
				
			||||||
            sleep(1)
 | 
					            sleep(1)
 | 
				
			||||||
            CONFIG.log("info", f"Redis Connection failed, retrying... ({exc})", redis_url=REDIS_URL)
 | 
					            sanitized_url = url.replace(quote_plus(CONFIG.get("redis.password")), "******")
 | 
				
			||||||
 | 
					            CONFIG.log(
 | 
				
			||||||
 | 
					                "info", f"Redis Connection failed, retrying... ({exc})", redis_url=sanitized_url
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
    CONFIG.log("info", "Redis Connection successful")
 | 
					    CONFIG.log("info", "Redis Connection successful")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -72,7 +72,7 @@ To check if your config has been applied correctly, you can run the following co
 | 
				
			|||||||
-   `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
 | 
					-   `AUTHENTIK_POSTGRESQL__PASSWORD`: Database password, defaults to the environment variable `POSTGRES_PASSWORD`
 | 
				
			||||||
-   `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer
 | 
					-   `AUTHENTIK_POSTGRESQL__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer
 | 
				
			||||||
-   `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool
 | 
					-   `AUTHENTIK_POSTGRESQL__USE_PGPOOL`: Adjust configuration to support connection to Pgpool
 | 
				
			||||||
-   `AUTHENTIK_POSTGRESQL__SSLMODE`: Strictness of ssl verification. Defaults to `verify-ca`
 | 
					-   `AUTHENTIK_POSTGRESQL__SSLMODE`: Strictness of ssl verification. Defaults to `"verify-ca"`
 | 
				
			||||||
-   `AUTHENTIK_POSTGRESQL__SSLROOTCERT`: CA root for server ssl verification
 | 
					-   `AUTHENTIK_POSTGRESQL__SSLROOTCERT`: CA root for server ssl verification
 | 
				
			||||||
-   `AUTHENTIK_POSTGRESQL__SSLCERT`: Path to x509 client certificate to authenticate to server
 | 
					-   `AUTHENTIK_POSTGRESQL__SSLCERT`: Path to x509 client certificate to authenticate to server
 | 
				
			||||||
-   `AUTHENTIK_POSTGRESQL__SSLKEY`: Path to private key of `SSLCERT` certificate
 | 
					-   `AUTHENTIK_POSTGRESQL__SSLKEY`: Path to private key of `SSLCERT` certificate
 | 
				
			||||||
@ -85,7 +85,8 @@ To check if your config has been applied correctly, you can run the following co
 | 
				
			|||||||
-   `AUTHENTIK_REDIS__USERNAME`: Redis server username when not using configuration URL
 | 
					-   `AUTHENTIK_REDIS__USERNAME`: Redis server username when not using configuration URL
 | 
				
			||||||
-   `AUTHENTIK_REDIS__PASSWORD`: Redis server password when not using configuration URL
 | 
					-   `AUTHENTIK_REDIS__PASSWORD`: Redis server password when not using configuration URL
 | 
				
			||||||
-   `AUTHENTIK_REDIS__TLS`: Redis server connection using TLS when not using configuration URL
 | 
					-   `AUTHENTIK_REDIS__TLS`: Redis server connection using TLS when not using configuration URL
 | 
				
			||||||
-   `AUTHENTIK_REDIS__TLS_REQS`: Redis server TLS connection requirements when not using configuration URL
 | 
					-   `AUTHENTIK_REDIS__TLS_REQS`: Redis server TLS connection requirements when not using configuration URL. Defaults to `"none"`. Allowed values are `"none"` and `"required"`.
 | 
				
			||||||
 | 
					-   `AUTHENTIK_REDIS__TLS_CA_CERT`: Path to the Redis server TLS CA root when not using configuration URL. Defaults to `null`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Result Backend Settings
 | 
					## Result Backend Settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user