mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-12 22:25:24 +03:00
split out proxy from auth
This commit is contained in:
parent
b49cda274d
commit
2f89f5226e
@ -66,13 +66,28 @@ database:
|
|||||||
# Optional: TLS configuration
|
# Optional: TLS configuration
|
||||||
tls:
|
tls:
|
||||||
# Optional: Enable TLS for port 8080 (default: shown below)
|
# Optional: Enable TLS for port 8080 (default: shown below)
|
||||||
enabled: true
|
enabled: True
|
||||||
|
|
||||||
|
# Optional: Proxy configuration
|
||||||
|
proxy:
|
||||||
|
# Optional: Mapping for headers from upstream proxies. Only used if Frigate's auth
|
||||||
|
# is disabled.
|
||||||
|
# NOTE: Many authentication proxies pass a header downstream with the authenticated
|
||||||
|
# user name. Not all values are supported. It must be a whitelisted header.
|
||||||
|
# See the docs for more info.
|
||||||
|
header_map:
|
||||||
|
user: x-forwarded-user
|
||||||
|
# Optional: Url for logging out a user. This sets the location of the logout url in
|
||||||
|
# the UI.
|
||||||
|
logout_url: /api/logout
|
||||||
|
# Optional: Auth secret that is checked against the X-Proxy-Secret header sent from
|
||||||
|
# the proxy. If not set, all requests are trusted regardless of origin.
|
||||||
|
auth_secret: None
|
||||||
|
|
||||||
# Optional: Authentication configuration
|
# Optional: Authentication configuration
|
||||||
auth:
|
auth:
|
||||||
# Optional: Authentication mode (default: shown below)
|
# Optional: Enable authentication
|
||||||
# Valid values are: native, proxy
|
enabled: True
|
||||||
mode: native
|
|
||||||
# Optional: Reset the admin user password on startup (default: shown below)
|
# Optional: Reset the admin user password on startup (default: shown below)
|
||||||
# New password is printed in the logs
|
# New password is printed in the logs
|
||||||
reset_admin_password: False
|
reset_admin_password: False
|
||||||
@ -87,23 +102,14 @@ auth:
|
|||||||
# When the session is going to expire in less time than this setting,
|
# When the session is going to expire in less time than this setting,
|
||||||
# it will be refreshed back to the session_length.
|
# it will be refreshed back to the session_length.
|
||||||
refresh_time: 43200 # 12 hours
|
refresh_time: 43200 # 12 hours
|
||||||
# Optional: Mapping for headers from upstream proxies. Only used in proxy auth mode.
|
|
||||||
# NOTE: Many authentication proxies pass a header downstream with the authenticated
|
|
||||||
# user name. Not all values are supported. It must be a whitelisted header.
|
|
||||||
# See the docs for more info.
|
|
||||||
header_map:
|
|
||||||
user: x-forwarded-user
|
|
||||||
# Optional: Rate limiting for login failures to help prevent brute force
|
# Optional: Rate limiting for login failures to help prevent brute force
|
||||||
# login attacks (default: shown below)
|
# login attacks (default: shown below)
|
||||||
# See the docs for more information on valid values
|
# See the docs for more information on valid values
|
||||||
failed_login_rate_limit: None
|
failed_login_rate_limit: None
|
||||||
# Optional: Trusted proxies for determining IP address to rate limit
|
# Optional: Trusted proxies for determining IP address to rate limit
|
||||||
# NOTE: This is only used for rate limiting login attempts and does not bypass
|
# NOTE: This is only used for rate limiting login attempts and does not bypass
|
||||||
# authentication in any way
|
# authentication. See the authentication docs for more details.
|
||||||
trusted_proxies: []
|
trusted_proxies: []
|
||||||
# Optional: Url for logging out a user. This only needs to be set if you are using
|
|
||||||
# proxy mode.
|
|
||||||
logout_url: /api/logout
|
|
||||||
# Optional: Number of hashing iterations for user passwords
|
# Optional: Number of hashing iterations for user passwords
|
||||||
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
|
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
|
||||||
# NOTE: changing this value will not automatically update password hashes, you
|
# NOTE: changing this value will not automatically update password hashes, you
|
||||||
|
|||||||
@ -176,6 +176,9 @@ def config():
|
|||||||
# remove the mqtt password
|
# remove the mqtt password
|
||||||
config["mqtt"].pop("password", None)
|
config["mqtt"].pop("password", None)
|
||||||
|
|
||||||
|
# remove the proxy secret
|
||||||
|
config["proxy"].pop("auth_secret", None)
|
||||||
|
|
||||||
for camera_name, camera in current_app.frigate_config.cameras.items():
|
for camera_name, camera in current_app.frigate_config.cameras.items():
|
||||||
camera_dict = config["cameras"][camera_name]
|
camera_dict = config["cameras"][camera_name]
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ from flask_limiter import Limiter
|
|||||||
from joserfc import jwt
|
from joserfc import jwt
|
||||||
from peewee import DoesNotExist
|
from peewee import DoesNotExist
|
||||||
|
|
||||||
from frigate.config import AuthConfig, AuthModeEnum
|
from frigate.config import AuthConfig, ProxyConfig
|
||||||
from frigate.const import CONFIG_DIR, JWT_SECRET_ENV_VAR, PASSWORD_HASH_ALGORITHM
|
from frigate.const import CONFIG_DIR, JWT_SECRET_ENV_VAR, PASSWORD_HASH_ALGORITHM
|
||||||
from frigate.models import User
|
from frigate.models import User
|
||||||
|
|
||||||
@ -166,6 +166,9 @@ def set_jwt_cookie(response, cookie_name, encoded_jwt, expiration, secure):
|
|||||||
# Endpoint for use with nginx auth_request
|
# Endpoint for use with nginx auth_request
|
||||||
@AuthBp.route("/auth")
|
@AuthBp.route("/auth")
|
||||||
def auth():
|
def auth():
|
||||||
|
auth_config: AuthConfig = current_app.frigate_config.auth
|
||||||
|
proxy_config: ProxyConfig = current_app.frigate_config.proxy
|
||||||
|
|
||||||
success_response = make_response({}, 202)
|
success_response = make_response({}, 202)
|
||||||
|
|
||||||
# dont require auth if the request is on the internal port
|
# dont require auth if the request is on the internal port
|
||||||
@ -173,11 +176,22 @@ def auth():
|
|||||||
if request.headers.get("x-server-port", 0, type=int) == 5000:
|
if request.headers.get("x-server-port", 0, type=int) == 5000:
|
||||||
return success_response
|
return success_response
|
||||||
|
|
||||||
# if proxy auth mode
|
fail_response = make_response({}, 401)
|
||||||
if current_app.frigate_config.auth.mode == AuthModeEnum.proxy:
|
|
||||||
|
# ensure the proxy secret matches if configured
|
||||||
|
if (
|
||||||
|
proxy_config.auth_secret is not None
|
||||||
|
and request.headers.get("x-proxy-secret", "", type=str)
|
||||||
|
!= proxy_config.auth_secret
|
||||||
|
):
|
||||||
|
logger.debug("X-Proxy-Secret header does not match configured secret value")
|
||||||
|
return fail_response
|
||||||
|
|
||||||
|
# if auth is disabled, just apply the proxy header map and return success
|
||||||
|
if not auth_config.enabled:
|
||||||
# pass the user header value from the upstream proxy if a mapping is specified
|
# pass the user header value from the upstream proxy if a mapping is specified
|
||||||
# or use anonymous if none are specified
|
# or use anonymous if none are specified
|
||||||
if current_app.frigate_config.auth.header_map.user is not None:
|
if proxy_config.header_map.user is not None:
|
||||||
upstream_user_header_value = request.headers.get(
|
upstream_user_header_value = request.headers.get(
|
||||||
current_app.frigate_config.auth.header_map.user,
|
current_app.frigate_config.auth.header_map.user,
|
||||||
type=str,
|
type=str,
|
||||||
@ -188,7 +202,7 @@ def auth():
|
|||||||
success_response.headers["remote-user"] = "anonymous"
|
success_response.headers["remote-user"] = "anonymous"
|
||||||
return success_response
|
return success_response
|
||||||
|
|
||||||
fail_response = make_response({}, 401)
|
# now apply authentication
|
||||||
fail_response.headers["location"] = "/login"
|
fail_response.headers["location"] = "/login"
|
||||||
|
|
||||||
JWT_COOKIE_NAME = current_app.frigate_config.auth.cookie_name
|
JWT_COOKIE_NAME = current_app.frigate_config.auth.cookie_name
|
||||||
|
|||||||
@ -119,19 +119,28 @@ class TlsConfig(FrigateBaseModel):
|
|||||||
enabled: bool = Field(default=True, title="Enable TLS for port 8080")
|
enabled: bool = Field(default=True, title="Enable TLS for port 8080")
|
||||||
|
|
||||||
|
|
||||||
class AuthModeEnum(str, Enum):
|
|
||||||
native = "native"
|
|
||||||
proxy = "proxy"
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderMappingConfig(FrigateBaseModel):
|
class HeaderMappingConfig(FrigateBaseModel):
|
||||||
user: str = Field(
|
user: str = Field(
|
||||||
default=None, title="Header name from upstream proxy to identify user."
|
default=None, title="Header name from upstream proxy to identify user."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ProxyConfig(FrigateBaseModel):
|
||||||
|
header_map: HeaderMappingConfig = Field(
|
||||||
|
default_factory=HeaderMappingConfig,
|
||||||
|
title="Header mapping definitions for proxy user passing.",
|
||||||
|
)
|
||||||
|
logout_url: Optional[str] = Field(
|
||||||
|
default=None, title="Redirect url for logging out with proxy."
|
||||||
|
)
|
||||||
|
auth_secret: Optional[str] = Field(
|
||||||
|
default=None,
|
||||||
|
title="Secret value for proxy authentication.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AuthConfig(FrigateBaseModel):
|
class AuthConfig(FrigateBaseModel):
|
||||||
mode: AuthModeEnum = Field(default=AuthModeEnum.native, title="Authentication mode")
|
enabled: bool = Field(default=True, title="Enable authentication")
|
||||||
reset_admin_password: bool = Field(
|
reset_admin_password: bool = Field(
|
||||||
default=False, title="Reset the admin password on startup"
|
default=False, title="Reset the admin password on startup"
|
||||||
)
|
)
|
||||||
@ -147,10 +156,6 @@ class AuthConfig(FrigateBaseModel):
|
|||||||
title="Refresh the session if it is going to expire in this many seconds",
|
title="Refresh the session if it is going to expire in this many seconds",
|
||||||
ge=30,
|
ge=30,
|
||||||
)
|
)
|
||||||
header_map: HeaderMappingConfig = Field(
|
|
||||||
default_factory=HeaderMappingConfig,
|
|
||||||
title="Header mapping definitions for proxy auth mode.",
|
|
||||||
)
|
|
||||||
failed_login_rate_limit: Optional[str] = Field(
|
failed_login_rate_limit: Optional[str] = Field(
|
||||||
default=None,
|
default=None,
|
||||||
title="Rate limits for failed login attempts.",
|
title="Rate limits for failed login attempts.",
|
||||||
@ -159,9 +164,6 @@ class AuthConfig(FrigateBaseModel):
|
|||||||
default=[],
|
default=[],
|
||||||
title="Trusted proxies for determining IP address to rate limit",
|
title="Trusted proxies for determining IP address to rate limit",
|
||||||
)
|
)
|
||||||
logout_url: Optional[str] = Field(
|
|
||||||
default=None, title="Redirect url for logging out in proxy mode."
|
|
||||||
)
|
|
||||||
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
|
# As of Feb 2023, OWASP recommends 600000 iterations for PBKDF2-SHA256
|
||||||
hash_iterations: int = Field(default=600000, title="Password hash iterations")
|
hash_iterations: int = Field(default=600000, title="Password hash iterations")
|
||||||
|
|
||||||
@ -1308,6 +1310,9 @@ class FrigateConfig(FrigateBaseModel):
|
|||||||
default_factory=DatabaseConfig, title="Database configuration."
|
default_factory=DatabaseConfig, title="Database configuration."
|
||||||
)
|
)
|
||||||
tls: TlsConfig = Field(default_factory=TlsConfig, title="TLS configuration.")
|
tls: TlsConfig = Field(default_factory=TlsConfig, title="TLS configuration.")
|
||||||
|
proxy: ProxyConfig = Field(
|
||||||
|
default_factory=ProxyConfig, title="Proxy configuration."
|
||||||
|
)
|
||||||
auth: AuthConfig = Field(default_factory=AuthConfig, title="Auth configuration.")
|
auth: AuthConfig = Field(default_factory=AuthConfig, title="Auth configuration.")
|
||||||
environment_vars: Dict[str, str] = Field(
|
environment_vars: Dict[str, str] = Field(
|
||||||
default_factory=dict, title="Frigate environment variables."
|
default_factory=dict, title="Frigate environment variables."
|
||||||
@ -1373,6 +1378,12 @@ class FrigateConfig(FrigateBaseModel):
|
|||||||
"""Merge camera config with globals."""
|
"""Merge camera config with globals."""
|
||||||
config = self.model_copy(deep=True)
|
config = self.model_copy(deep=True)
|
||||||
|
|
||||||
|
# Proxy secret substitution
|
||||||
|
if config.proxy.auth_secret:
|
||||||
|
config.proxy.auth_secret = config.proxy.auth_secret.format(
|
||||||
|
**FRIGATE_ENV_VARS
|
||||||
|
)
|
||||||
|
|
||||||
# MQTT user/password substitutions
|
# MQTT user/password substitutions
|
||||||
if config.mqtt.user or config.mqtt.password:
|
if config.mqtt.user or config.mqtt.password:
|
||||||
config.mqtt.user = config.mqtt.user.format(**FRIGATE_ENV_VARS)
|
config.mqtt.user = config.mqtt.user.format(**FRIGATE_ENV_VARS)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ type AccountSettingsProps = {
|
|||||||
export default function AccountSettings({ className }: AccountSettingsProps) {
|
export default function AccountSettings({ className }: AccountSettingsProps) {
|
||||||
const { data: profile } = useSWR("profile");
|
const { data: profile } = useSWR("profile");
|
||||||
const { data: config } = useSWR("config");
|
const { data: config } = useSWR("config");
|
||||||
const logoutUrl = config?.auth.logout_url || "/api/logout";
|
const logoutUrl = config?.proxy.logout_url || "/api/logout";
|
||||||
|
|
||||||
const Container = isDesktop ? DropdownMenu : Drawer;
|
const Container = isDesktop ? DropdownMenu : Drawer;
|
||||||
const Trigger = isDesktop ? DropdownMenuTrigger : DrawerTrigger;
|
const Trigger = isDesktop ? DropdownMenuTrigger : DrawerTrigger;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user