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 time import time
|
||||
from typing import Any
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import quote_plus, urlparse
|
||||
|
||||
import yaml
|
||||
from django.conf import ImproperlyConfigured
|
||||
@ -331,6 +331,26 @@ class 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 len(argv) < 2: # noqa: PLR2004
|
||||
print(dumps(CONFIG.raw, indent=4, cls=AttrEncoder))
|
||||
|
@ -35,6 +35,7 @@ redis:
|
||||
password: ""
|
||||
tls: false
|
||||
tls_reqs: "none"
|
||||
tls_ca_cert: null
|
||||
|
||||
# broker:
|
||||
# url: ""
|
||||
|
@ -5,13 +5,12 @@ import os
|
||||
from collections import OrderedDict
|
||||
from hashlib import sha512
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote_plus
|
||||
|
||||
from celery.schedules import crontab
|
||||
from sentry_sdk import set_tag
|
||||
|
||||
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.sentry import sentry_init
|
||||
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 = {
|
||||
"default": {
|
||||
"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),
|
||||
"OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"},
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
},
|
||||
"KEY_PREFIX": "authentik_cache",
|
||||
"KEY_FUNCTION": "django_tenants.cache.make_key",
|
||||
"REVERSE_KEY_FUNCTION": "django_tenants.cache.reverse_key",
|
||||
@ -276,7 +265,7 @@ CHANNEL_LAYERS = {
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
|
||||
"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_",
|
||||
},
|
||||
},
|
||||
@ -376,11 +365,9 @@ CELERY = {
|
||||
"beat_scheduler": "authentik.tenants.scheduler:TenantAwarePersistentScheduler",
|
||||
"task_create_missing_queues": True,
|
||||
"task_default_queue": "authentik",
|
||||
"broker_url": CONFIG.get("broker.url")
|
||||
or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
|
||||
"broker_url": CONFIG.get("broker.url") or redis_url(CONFIG.get("redis.db")),
|
||||
"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"),
|
||||
"result_backend": CONFIG.get("result_backend.url")
|
||||
or f"{_redis_url}/{CONFIG.get('redis.db')}{_redis_celery_tls_requirements}",
|
||||
}
|
||||
|
||||
# Sentry integration
|
||||
|
@ -25,13 +25,14 @@ type Config struct {
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
Host string `yaml:"host" env:"HOST, overwrite"`
|
||||
Port int `yaml:"port" env:"PORT, overwrite"`
|
||||
DB int `yaml:"db" env:"DB, overwrite"`
|
||||
Username string `yaml:"username" env:"USERNAME, overwrite"`
|
||||
Password string `yaml:"password" env:"PASSWORD, overwrite"`
|
||||
TLS bool `yaml:"tls" env:"TLS, overwrite"`
|
||||
TLSReqs string `yaml:"tls_reqs" env:"TLS_REQS, overwrite"`
|
||||
Host string `yaml:"host" env:"HOST, overwrite"`
|
||||
Port int `yaml:"port" env:"PORT, overwrite"`
|
||||
DB int `yaml:"db" env:"DB, overwrite"`
|
||||
Username string `yaml:"username" env:"USERNAME, overwrite"`
|
||||
Password string `yaml:"password" env:"PASSWORD, overwrite"`
|
||||
TLS bool `yaml:"tls" env:"TLS, overwrite"`
|
||||
TLSReqs string `yaml:"tls_reqs" env:"TLS_REQS, overwrite"`
|
||||
TLSCaCert *string `yaml:"tls_ca_certs" env:"TLS_CA_CERT, overwrite"`
|
||||
}
|
||||
|
||||
type ListenConfig struct {
|
||||
|
@ -2,6 +2,8 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
@ -19,6 +21,7 @@ import (
|
||||
"goauthentik.io/internal/outpost/proxyv2/codecs"
|
||||
"goauthentik.io/internal/outpost/proxyv2/constants"
|
||||
"goauthentik.io/internal/outpost/proxyv2/redisstore"
|
||||
"goauthentik.io/internal/utils"
|
||||
)
|
||||
|
||||
const RedisKeyPrefix = "authentik_proxy_session_"
|
||||
@ -31,11 +34,40 @@ func (a *Application) getStore(p api.ProxyOutpostConfig, externalHost *url.URL)
|
||||
maxAge = int(*t) + 1
|
||||
}
|
||||
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{
|
||||
Addr: fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
|
||||
Username: config.Get().Redis.Username,
|
||||
Password: config.Get().Redis.Password,
|
||||
DB: config.Get().Redis.DB,
|
||||
Addr: fmt.Sprintf("%s:%d", config.Get().Redis.Host, config.Get().Redis.Port),
|
||||
Username: config.Get().Redis.Username,
|
||||
Password: config.Get().Redis.Password,
|
||||
DB: config.Get().Redis.DB,
|
||||
TLSConfig: tls,
|
||||
})
|
||||
|
||||
// New default RedisStore
|
||||
|
@ -9,7 +9,7 @@ from psycopg import OperationalError, connect
|
||||
from redis import Redis
|
||||
from redis.exceptions import RedisError
|
||||
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.config import CONFIG, redis_url
|
||||
|
||||
|
||||
def check_postgres():
|
||||
@ -35,24 +35,18 @@ def check_postgres():
|
||||
|
||||
|
||||
def check_redis():
|
||||
REDIS_PROTOCOL_PREFIX = "redis://"
|
||||
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')}"
|
||||
)
|
||||
url = redis_url(CONFIG.get("redis.db"))
|
||||
while True:
|
||||
try:
|
||||
redis = Redis.from_url(REDIS_URL)
|
||||
redis = Redis.from_url(url)
|
||||
redis.ping()
|
||||
break
|
||||
except RedisError as exc:
|
||||
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")
|
||||
|
||||
|
||||
|
@ -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__USE_PGBOUNCER`: Adjust configuration to support connection to PgBouncer
|
||||
- `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__SSLCERT`: Path to x509 client certificate to authenticate to server
|
||||
- `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__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_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
|
||||
|
||||
|
Reference in New Issue
Block a user