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
|
||||||
|
@ -25,13 +25,14 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RedisConfig struct {
|
type RedisConfig struct {
|
||||||
Host string `yaml:"host" env:"HOST, overwrite"`
|
Host string `yaml:"host" env:"HOST, overwrite"`
|
||||||
Port int `yaml:"port" env:"PORT, overwrite"`
|
Port int `yaml:"port" env:"PORT, overwrite"`
|
||||||
DB int `yaml:"db" env:"DB, overwrite"`
|
DB int `yaml:"db" env:"DB, overwrite"`
|
||||||
Username string `yaml:"username" env:"USERNAME, overwrite"`
|
Username string `yaml:"username" env:"USERNAME, overwrite"`
|
||||||
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