Skip to content

Commit

Permalink
improve tls implementation (#11690)
Browse files Browse the repository at this point in the history
* improve tls implementation

* update docs
  • Loading branch information
blakeblackshear committed Jun 2, 2024
1 parent beefc51 commit e431031
Show file tree
Hide file tree
Showing 13 changed files with 92 additions and 37 deletions.
8 changes: 7 additions & 1 deletion docker/main/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ ARG TARGETARCH
WORKDIR /rootfs/usr/local/go2rtc/bin
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


####
#
Expand Down Expand Up @@ -131,6 +136,7 @@ RUN pip3 wheel --wheel-dir=/wheels -r /requirements-wheels.txt
FROM scratch AS deps-rootfs
COPY --from=nginx /usr/local/nginx/ /usr/local/nginx/
COPY --from=go2rtc /rootfs/ /
COPY --from=tempio /rootfs/ /
COPY --from=s6-overlay /rootfs/ /
COPY --from=models /rootfs/ /
COPY docker/main/rootfs/ /
Expand All @@ -148,7 +154,7 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
ENV NVIDIA_VISIBLE_DEVICES=all
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
RUN --mount=type=bind,source=docker/main/install_deps.sh,target=/deps/install_deps.sh \
Expand Down
9 changes: 7 additions & 2 deletions docker/main/rootfs/etc/s6-overlay/s6-rc.d/certsync/run
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@ echo "[INFO] Starting certsync..."

lefile="/etc/letsencrypt/live/frigate/fullchain.pem"

tls_enabled=`python3 /usr/local/nginx/get_tls_settings.py | jq -r .enabled`

while true
do
if [[ "$tls_enabled" == 'false' ]]; then
sleep 9999
continue
fi

if [ ! -e $lefile ]
then
echo "[ERROR] TLS certificate does not exist: $lefile"
fi

leprint=`openssl x509 -in $lefile -fingerprint -noout || echo 'failed'`
leprint=`openssl x509 -in $lefile -fingerprint -noout 2>&1 || echo 'failed'`

case "$leprint" in
*Fingerprint*)
Expand All @@ -29,7 +34,7 @@ do
;;
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
*Fingerprint*)
Expand Down
7 changes: 6 additions & 1 deletion docker/main/rootfs/etc/s6-overlay/s6-rc.d/nginx/run
Original file line number Diff line number Diff line change
Expand Up @@ -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..."
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
-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

# 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
exec 2>&1
exec \
Expand Down
12 changes: 1 addition & 11 deletions docker/main/rootfs/usr/local/nginx/conf/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,10 @@ http {
include go2rtc_upstream.conf;

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
listen [::]:5000 ipv6only=off;

include tls.conf;
include listen.conf;

# vod settings
vod_base_url '';
Expand Down
28 changes: 28 additions & 0 deletions docker/main/rootfs/usr/local/nginx/get_tls_settings.py
Original file line number Diff line number Diff line change
@@ -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))
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
keepalive_timeout 70;
listen [::]:443 ipv6only=off default_server ssl;
{{ if not .enabled }}
# 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_key /etc/letsencrypt/live/frigate/privkey.pem;
Expand All @@ -22,3 +26,5 @@ location /.well-known/acme-challenge/ {
default_type "text/plain";
root /etc/letsencrypt/www;
}
{{ end }}

5 changes: 5 additions & 0 deletions docs/docs/configuration/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ database:
# The path to store the SQLite DB (default: shown below)
path: /config/frigate.db

# Optional: TLS configuration
tls:
# Optional: Enable TLS for port 8080 (default: shown below)
enabled: true

# Optional: Authentication configuration
auth:
# Optional: Authentication mode (default: shown below)
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/configuration/restream.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Frigate uses [go2rtc](https://github.com/AlexxIT/go2rtc/tree/v1.9.2) to provide

:::note

You can access the go2rtc stream info at `http:https://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.

:::

Expand Down
18 changes: 12 additions & 6 deletions docs/docs/configuration/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ title: 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

Expand All @@ -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.

## ACME Challenge
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.

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`.
```yaml
frigate:
...
ports:
- "443:8080"
...
```

## Advanced customization
## ACME Challenge

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.
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`.
1 change: 0 additions & 1 deletion docs/docs/frigate/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ The following ports are used by Frigate and can be mapped via docker as required
| Port | Description |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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. |
| `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. |
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/guides/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ cameras:
- 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

Expand Down
22 changes: 11 additions & 11 deletions docs/docs/integrations/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ Accepts the following query string parameters:
| `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) |

You can access a higher resolution mjpeg stream by appending `h=height-in-pixels` to the endpoint. For example `http:https://localhost:8080/api/back?h=1080`. You can also increase the FPS by appending `fps=frame-rate` to the URL such as `http:https://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]`

Expand Down Expand Up @@ -496,21 +496,21 @@ Delete review items.

Get the motion activity for camera(s) during a specified time period.

| param | Type | Description |
| ---------- | ---- | -------------------------------------------------------------- |
| `before` | int | Epoch time |
| `after` | int | Epoch time |
| `cameras` | str | , separated list of cameras |
| param | Type | Description |
| --------- | ---- | --------------------------- |
| `before` | int | Epoch time |
| `after` | int | Epoch time |
| `cameras` | str | , separated list of cameras |

### `GET /review/activity/audio`

Get the audio activity for camera(s) during a specified time period.

| param | Type | Description |
| ---------- | ---- | -------------------------------------------------------------- |
| `before` | int | Epoch time |
| `after` | int | Epoch time |
| `cameras` | str | , separated list of cameras |
| param | Type | Description |
| --------- | ---- | --------------------------- |
| `before` | int | Epoch time |
| `after` | int | Epoch time |
| `cameras` | str | , separated list of cameras |

## Timeline

Expand Down
5 changes: 5 additions & 0 deletions frigate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
native = "native"
proxy = "proxy"
Expand Down Expand Up @@ -1303,6 +1307,7 @@ class FrigateConfig(FrigateBaseModel):
database: DatabaseConfig = Field(
default_factory=DatabaseConfig, title="Database configuration."
)
tls: TlsConfig = Field(default_factory=TlsConfig, title="TLS configuration.")
auth: AuthConfig = Field(default_factory=AuthConfig, title="Auth configuration.")
environment_vars: Dict[str, str] = Field(
default_factory=dict, title="Frigate environment variables."
Expand Down

0 comments on commit e431031

Please sign in to comment.