improve tls implementation (#11690)

* improve tls implementation

* update docs
This commit is contained in:
Blake Blackshear 2024-06-02 07:48:28 -05:00 committed by GitHub
parent beefc51361
commit e431031112
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 92 additions and 37 deletions

View File

@ -35,6 +35,11 @@ ARG TARGETARCH
WORKDIR /rootfs/usr/local/go2rtc/bin WORKDIR /rootfs/usr/local/go2rtc/bin
ADD --link --chmod=755 "https://github.com/AlexxIT/go2rtc/releases/download/v1.9.2/go2rtc_linux_${TARGETARCH}" go2rtc ADD --link --chmod=755 "https://github.com/AlexxIT/go2rtc/releases/download/v1.9.2/go2rtc_linux_${TARGETARCH}" go2rtc
FROM scratch AS tempio
ARG TARGETARCH
WORKDIR /rootfs/usr/local/tempio/bin
ADD --link --chmod=755 "https://github.com/home-assistant/tempio/releases/download/2021.09.0/tempio_${TARGETARCH}" tempio
#### ####
# #
@ -131,6 +136,7 @@ RUN pip3 wheel --wheel-dir=/wheels -r /requirements-wheels.txt
FROM scratch AS deps-rootfs FROM scratch AS deps-rootfs
COPY --from=nginx /usr/local/nginx/ /usr/local/nginx/ COPY --from=nginx /usr/local/nginx/ /usr/local/nginx/
COPY --from=go2rtc /rootfs/ / COPY --from=go2rtc /rootfs/ /
COPY --from=tempio /rootfs/ /
COPY --from=s6-overlay /rootfs/ / COPY --from=s6-overlay /rootfs/ /
COPY --from=models /rootfs/ / COPY --from=models /rootfs/ /
COPY docker/main/rootfs/ / COPY docker/main/rootfs/ /
@ -148,7 +154,7 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
ENV NVIDIA_VISIBLE_DEVICES=all ENV NVIDIA_VISIBLE_DEVICES=all
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
ENV PATH="/usr/lib/btbn-ffmpeg/bin:/usr/local/go2rtc/bin:/usr/local/nginx/sbin:${PATH}" ENV PATH="/usr/lib/btbn-ffmpeg/bin:/usr/local/go2rtc/bin:/usr/local/tempio/bin:/usr/local/nginx/sbin:${PATH}"
# Install dependencies # Install dependencies
RUN --mount=type=bind,source=docker/main/install_deps.sh,target=/deps/install_deps.sh \ RUN --mount=type=bind,source=docker/main/install_deps.sh,target=/deps/install_deps.sh \

View File

@ -10,16 +10,21 @@ echo "[INFO] Starting certsync..."
lefile="/etc/letsencrypt/live/frigate/fullchain.pem" lefile="/etc/letsencrypt/live/frigate/fullchain.pem"
tls_enabled=`python3 /usr/local/nginx/get_tls_settings.py | jq -r .enabled`
while true while true
do do
if [[ "$tls_enabled" == 'false' ]]; then
sleep 9999
continue
fi
if [ ! -e $lefile ] if [ ! -e $lefile ]
then then
echo "[ERROR] TLS certificate does not exist: $lefile" echo "[ERROR] TLS certificate does not exist: $lefile"
fi fi
leprint=`openssl x509 -in $lefile -fingerprint -noout || echo 'failed'` leprint=`openssl x509 -in $lefile -fingerprint -noout 2>&1 || echo 'failed'`
case "$leprint" in case "$leprint" in
*Fingerprint*) *Fingerprint*)
@ -29,7 +34,7 @@ do
;; ;;
esac esac
liveprint=`echo | openssl s_client -showcerts -connect 127.0.0.1:443 2>&1 | openssl x509 -fingerprint | grep -i fingerprint || echo 'failed'` liveprint=`echo | openssl s_client -showcerts -connect 127.0.0.1:8080 2>&1 | openssl x509 -fingerprint 2>&1 | grep -i fingerprint || echo 'failed'`
case "$liveprint" in case "$liveprint" in
*Fingerprint*) *Fingerprint*)

View File

@ -33,9 +33,14 @@ if [ ! \( -f "$letsencrypt_path/privkey.pem" -a -f "$letsencrypt_path/fullchain.
echo "[INFO] No TLS certificate found. Generating a self signed certificate..." echo "[INFO] No TLS certificate found. Generating a self signed certificate..."
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \ openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
-subj "/O=FRIGATE DEFAULT CERT/CN=*" \ -subj "/O=FRIGATE DEFAULT CERT/CN=*" \
-keyout "$letsencrypt_path/privkey.pem" -out "$letsencrypt_path/fullchain.pem" -keyout "$letsencrypt_path/privkey.pem" -out "$letsencrypt_path/fullchain.pem" 2>/dev/null
fi fi
# build templates for optional TLS support
python3 /usr/local/nginx/get_tls_settings.py | \
tempio -template /usr/local/nginx/templates/listen.gotmpl \
-out /usr/local/nginx/conf/listen.conf
# Replace the bash process with the NGINX process, redirecting stderr to stdout # Replace the bash process with the NGINX process, redirecting stderr to stdout
exec 2>&1 exec 2>&1
exec \ exec \

View File

@ -59,20 +59,10 @@ http {
include go2rtc_upstream.conf; include go2rtc_upstream.conf;
server { server {
listen [::]:80 ipv6only=off default_server;
location / {
return 301 https://$host$request_uri;
}
}
server {
# intended for external traffic, protected by auth
listen [::]:8080 ipv6only=off;
# intended for internal traffic, not protected by auth # intended for internal traffic, not protected by auth
listen [::]:5000 ipv6only=off; listen [::]:5000 ipv6only=off;
include tls.conf; include listen.conf;
# vod settings # vod settings
vod_base_url ''; vod_base_url '';

View File

@ -0,0 +1,28 @@
"""Prints the tls config as json to stdout."""
import json
import os
import yaml
config_file = os.environ.get("CONFIG_FILE", "/config/config.yml")
# Check if we can use .yaml instead of .yml
config_file_yaml = config_file.replace(".yml", ".yaml")
if os.path.isfile(config_file_yaml):
config_file = config_file_yaml
try:
with open(config_file) as f:
raw_config = f.read()
if config_file.endswith((".yaml", ".yml")):
config: dict[str, any] = yaml.safe_load(raw_config)
elif config_file.endswith(".json"):
config: dict[str, any] = json.loads(raw_config)
except FileNotFoundError:
config: dict[str, any] = {}
tls_config: dict[str, any] = config.get("tls", {})
print(json.dumps(tls_config))

View File

@ -1,5 +1,9 @@
keepalive_timeout 70; {{ if not .enabled }}
listen [::]:443 ipv6only=off default_server ssl; # intended for external traffic, protected by auth
listen [::]:8080 ipv6only=off;
{{ else }}
# intended for external traffic, protected by auth
listen [::]:8080 ipv6only=off ssl;
ssl_certificate /etc/letsencrypt/live/frigate/fullchain.pem; ssl_certificate /etc/letsencrypt/live/frigate/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/frigate/privkey.pem; ssl_certificate_key /etc/letsencrypt/live/frigate/privkey.pem;
@ -22,3 +26,5 @@ location /.well-known/acme-challenge/ {
default_type "text/plain"; default_type "text/plain";
root /etc/letsencrypt/www; root /etc/letsencrypt/www;
} }
{{ end }}

View File

@ -63,6 +63,11 @@ database:
# The path to store the SQLite DB (default: shown below) # The path to store the SQLite DB (default: shown below)
path: /config/frigate.db path: /config/frigate.db
# Optional: TLS configuration
tls:
# Optional: Enable TLS for port 8080 (default: shown below)
enabled: true
# Optional: Authentication configuration # Optional: Authentication configuration
auth: auth:
# Optional: Authentication mode (default: shown below) # Optional: Authentication mode (default: shown below)

View File

@ -11,7 +11,7 @@ Frigate uses [go2rtc](https://github.com/AlexxIT/go2rtc/tree/v1.9.2) to provide
:::note :::note
You can access the go2rtc stream info at `http://frigate_ip:8080/api/go2rtc/streams` which can be helpful to debug as well as provide useful information about your camera streams. You can access the go2rtc stream info at `/api/go2rtc/streams` which can be helpful to debug as well as provide useful information about your camera streams.
::: :::

View File

@ -5,9 +5,9 @@ title: TLS
# TLS # TLS
Frigate's integrated NGINX server supports TLS certificates. By default Frigate will generate a self signed certificate that will be used for port 443. Frigate is designed to make it easy to use whatever tool you prefer to manage certificates. Frigate's integrated NGINX server supports TLS certificates. By default Frigate will generate a self signed certificate that will be used for port 8080. Frigate is designed to make it easy to use whatever tool you prefer to manage certificates.
Frigate is often running behind a reverse proxy that manages TLS certificates for multiple services. However, if you are running on a device that's separate from your proxy or if you expose Frigate directly to the internet, you may want to configure TLS. Frigate is often running behind a reverse proxy that manages TLS certificates for multiple services. You will likely need to set your reverse proxy to allow self signed certificates or you can disable TLS in Frigate's config. However, if you are running on a dedicated device that's separate from your proxy or if you expose Frigate directly to the internet, you may want to configure TLS with valid certificates.
## Certificates ## Certificates
@ -25,10 +25,16 @@ Within the folder, the private key is expected to be named `privkey.pem` and the
Frigate automatically compares the fingerprint of the certificate at `/etc/letsencrypt/live/frigate/fullchain.pem` against the fingerprint of the TLS cert in NGINX every minute. If these differ, the NGINX config is reloaded to pick up the updated certificate. Frigate automatically compares the fingerprint of the certificate at `/etc/letsencrypt/live/frigate/fullchain.pem` against the fingerprint of the TLS cert in NGINX every minute. If these differ, the NGINX config is reloaded to pick up the updated certificate.
If you issue Frigate valid certificates you will likely want to configure it to run on port 443 so you can access it without a port number like `https://your-frigate-domain.com` by mapping 8080 to 443.
```yaml
frigate:
...
ports:
- "443:8080"
...
```
## ACME Challenge ## ACME Challenge
Frigate also supports hosting the acme challenge files for the HTTP challenge method if needed. The challenge files should be mounted at `/etc/letsencrypt/www`. Frigate also supports hosting the acme challenge files for the HTTP challenge method if needed. The challenge files should be mounted at `/etc/letsencrypt/www`.
## Advanced customization
If you would like to customize the TLS configuration, you can do so by using a bind mount to override `/usr/local/nginx/conf/tls.conf`. Check the source code for the default configuration and modify from there.

View File

@ -35,7 +35,6 @@ The following ports are used by Frigate and can be mapped via docker as required
| Port | Description | | Port | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `8080` | Authenticated UI and API access without TLS. Reverse proxies should use this port. | | `8080` | Authenticated UI and API access without TLS. Reverse proxies should use this port. |
| `443` | Authenticated UI and API access with TLS. See the [TLS configuration](/configuration/tls) for more details. |
| `5000` | Internal unauthenticated UI and API access. Access to this port should be limited. Intended to be used within the docker network for services that integrate with Frigate. | | `5000` | Internal unauthenticated UI and API access. Access to this port should be limited. Intended to be used within the docker network for services that integrate with Frigate. |
| `8554` | RTSP restreaming. By default, these streams are unauthenticated. Authentication can be configured in go2rtc section of config. | | `8554` | RTSP restreaming. By default, these streams are unauthenticated. Authentication can be configured in go2rtc section of config. |
| `8555` | WebRTC connections for low latency live views. | | `8555` | WebRTC connections for low latency live views. |

View File

@ -137,7 +137,7 @@ cameras:
- detect - detect
``` ```
Now you should be able to start Frigate by running `docker compose up -d` from within the folder containing `docker-compose.yml`. On startup, an admin user and password will be created and outputted in the logs. You can see this by running `docker logs frigate`. Frigate should now be accessible at `server_ip:8080` where you can login with the `admin` user and finish the configuration using the built-in configuration editor. Now you should be able to start Frigate by running `docker compose up -d` from within the folder containing `docker-compose.yml`. On startup, an admin user and password will be created and outputted in the logs. You can see this by running `docker logs frigate`. Frigate should now be accessible at `https://server_ip:8080` where you can login with the `admin` user and finish the configuration using the built-in configuration editor.
## Configuring Frigate ## Configuring Frigate

View File

@ -164,7 +164,7 @@ Accepts the following query string parameters:
| `motion` | int | Draw blue boxes for areas with detected motion (0 or 1) | | `motion` | int | Draw blue boxes for areas with detected motion (0 or 1) |
| `regions` | int | Draw green boxes for areas where object detection was run (0 or 1) | | `regions` | int | Draw green boxes for areas where object detection was run (0 or 1) |
You can access a higher resolution mjpeg stream by appending `h=height-in-pixels` to the endpoint. For example `http://localhost:8080/api/back?h=1080`. You can also increase the FPS by appending `fps=frame-rate` to the URL such as `http://localhost:8080/api/back?fps=10` or both with `?fps=10&h=1000`. You can access a higher resolution mjpeg stream by appending `h=height-in-pixels` to the endpoint. For example `/api/back?h=1080`. You can also increase the FPS by appending `fps=frame-rate` to the URL such as `/api/back?fps=10` or both with `?fps=10&h=1000`.
### `GET /api/<camera_name>/latest.jpg[?h=300]` ### `GET /api/<camera_name>/latest.jpg[?h=300]`
@ -497,7 +497,7 @@ Delete review items.
Get the motion activity for camera(s) during a specified time period. Get the motion activity for camera(s) during a specified time period.
| param | Type | Description | | param | Type | Description |
| ---------- | ---- | -------------------------------------------------------------- | | --------- | ---- | --------------------------- |
| `before` | int | Epoch time | | `before` | int | Epoch time |
| `after` | int | Epoch time | | `after` | int | Epoch time |
| `cameras` | str | , separated list of cameras | | `cameras` | str | , separated list of cameras |
@ -507,7 +507,7 @@ Get the motion activity for camera(s) during a specified time period.
Get the audio activity for camera(s) during a specified time period. Get the audio activity for camera(s) during a specified time period.
| param | Type | Description | | param | Type | Description |
| ---------- | ---- | -------------------------------------------------------------- | | --------- | ---- | --------------------------- |
| `before` | int | Epoch time | | `before` | int | Epoch time |
| `after` | int | Epoch time | | `after` | int | Epoch time |
| `cameras` | str | , separated list of cameras | | `cameras` | str | , separated list of cameras |

View File

@ -115,6 +115,10 @@ class UIConfig(FrigateBaseModel):
) )
class TlsConfig(FrigateBaseModel):
enabled: bool = Field(default=True, title="Enable TLS for port 8080")
class AuthModeEnum(str, Enum): class AuthModeEnum(str, Enum):
native = "native" native = "native"
proxy = "proxy" proxy = "proxy"
@ -1303,6 +1307,7 @@ class FrigateConfig(FrigateBaseModel):
database: DatabaseConfig = Field( database: DatabaseConfig = Field(
default_factory=DatabaseConfig, title="Database configuration." default_factory=DatabaseConfig, title="Database configuration."
) )
tls: TlsConfig = Field(default_factory=TlsConfig, title="TLS 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."