add support for user passthru from upstream proxies

This commit is contained in:
Blake Blackshear 2024-05-17 18:52:33 -05:00
parent 46d60e4a39
commit dd730bf6c7
6 changed files with 63 additions and 8 deletions

View File

@ -22,6 +22,9 @@ location /auth {
proxy_set_header Cookie $http_cookie;
proxy_set_header X-CSRF-TOKEN "1";
# include headers from common auth proxies
include proxy_trusted_headers.conf;
## Basic Proxy Configuration
proxy_pass_request_body off;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503; # Timeout if the real server is dead

View File

@ -0,0 +1,22 @@
# these headers will be copied to the /auth request and are available
# to be mapped in the config to Frigate's remote-user header
# List of headers sent by common authentication proxies:
# - Authelia
# - Traefik forward auth
# - oauth2_proxy
# - Authentik
proxy_set_header Remote-User $http_remote_user;
proxy_set_header Remote-Groups $http_remote_groups;
proxy_set_header Remote-Email $http_remote_email;
proxy_set_header Remote-Name $http_remote_name;
proxy_set_header X-Forwarded-User $http_x_forwarded_user;
proxy_set_header X-Forwarded-Groups $http_x_forwarded_groups;
proxy_set_header X-Forwarded-Email $http_x_forwarded_email;
proxy_set_header X-Forwarded-Preferred-Username $http_x_forwarded_preferred_username;
proxy_set_header X-authentik-username $http_x_authentik_username;
proxy_set_header X-authentik-groups $http_x_authentik_groups;
proxy_set_header X-authentik-email $http_x_authentik_email;
proxy_set_header X-authentik-name $http_x_authentik_name;
proxy_set_header X-authentik-uid $http_x_authentik_uid;

View File

@ -21,7 +21,7 @@ from frigate.api.export import ExportBp
from frigate.api.media import MediaBp
from frigate.api.preview import PreviewBp
from frigate.api.review import ReviewBp
from frigate.config import FrigateConfig
from frigate.config import AuthModeEnum, FrigateConfig
from frigate.const import CONFIG_DIR
from frigate.events.external import ExternalEventProcessor
from frigate.models import Event, Timeline
@ -86,7 +86,9 @@ def create_app(
app.plus_api = plus_api
app.camera_error_image = None
app.stats_emitter = stats_emitter
app.jwt_token = get_jwt_secret() if frigate_config.auth.enabled else None
app.jwt_token = (
get_jwt_secret() if frigate_config.auth.mode == AuthModeEnum.native else None
)
# update the request_address with the x-forwarded-for header from nginx
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)
# initialize the rate limiter for the login endpoint

View File

@ -17,6 +17,7 @@ from flask_limiter import Limiter
from joserfc import jwt
from peewee import DoesNotExist
from frigate.config import AuthModeEnum
from frigate.const import CONFIG_DIR, JWT_SECRET_ENV_VAR, PASSWORD_HASH_ALGORITHM
from frigate.models import User
@ -175,12 +176,24 @@ def auth():
if request.headers.get("x-server-port", 0, type=int) == 5000:
return success_response
# if proxy auth mode
if current_app.frigate_config.auth.mode == AuthModeEnum.proxy:
# pass the user header value from the upstream proxy if a mapping is specified
# or use anonymous if none are specified
if current_app.frigate_config.auth.header_map.user is not None:
upstream_user_header_value = request.headers.get(
current_app.frigate_config.auth.header_map.user,
type=str,
default="anonymous",
)
success_response.headers["remote-user"] = upstream_user_header_value
else:
success_response.headers["remote-user"] = "anonymous"
return success_response
fail_response = make_response({}, 401)
fail_response.headers["location"] = "/login"
if not current_app.frigate_config.auth.enabled:
return success_response
JWT_COOKIE_NAME = current_app.frigate_config.auth.cookie_name
JWT_REFRESH = current_app.frigate_config.auth.refresh_time
JWT_SESSION_LENGTH = current_app.frigate_config.auth.session_length

View File

@ -27,7 +27,7 @@ from frigate.comms.dispatcher import Communicator, Dispatcher
from frigate.comms.inter_process import InterProcessCommunicator
from frigate.comms.mqtt import MqttClient
from frigate.comms.ws import WebSocketClient
from frigate.config import FrigateConfig
from frigate.config import AuthModeEnum, FrigateConfig
from frigate.const import (
CACHE_DIR,
CLIPS_DIR,
@ -592,7 +592,7 @@ class FrigateApp:
)
def init_auth(self) -> None:
if self.config.auth.enabled:
if self.config.auth.mode == AuthModeEnum.native:
if User.select().count() == 0:
password = secrets.token_hex(16)
password_hash = hash_password(

View File

@ -116,8 +116,19 @@ class UIConfig(FrigateBaseModel):
)
class AuthModeEnum(str, Enum):
native = "native"
proxy = "proxy"
class HeaderMappingConfig(FrigateBaseModel):
user: str = Field(
default=None, title="Header name from upstream proxy to identify user."
)
class AuthConfig(FrigateBaseModel):
enabled: bool = Field(default=False, title="Enable authentication")
mode: AuthModeEnum = Field(default=AuthModeEnum.native, title="Authentication mode")
reset_admin_password: bool = Field(
default=False, title="Reset the admin password on startup"
)
@ -132,6 +143,10 @@ class AuthConfig(FrigateBaseModel):
title="Refresh the session if it is going to expire in this many seconds",
ge=30,
)
header_map: Optional[HeaderMappingConfig] = Field(
default_factory=HeaderMappingConfig,
title="Header mapping definitions for proxy auth mode.",
)
failed_login_rate_limit: Optional[str] = Field(
default="1/second;5/minute;20/hour",
title="Rate limits for failed login attempts.",