Adopt nanoDNS-style INI config for the guide gateway

Replace the gateway's env-var DNS policy with docker/nanodns.ini, modelled on
https://github.com/drakmor/nanoDNS#config: [general] (redirect_url,
allowed_clients), [upstream] (ordered resolver list + timeout_ms), [overrides]
(mask = IPv4 spoof) and [exceptions] (masks forwarded upstream). Masks use
shell-style wildcards and match case-insensitively; resolution order is
exceptions -> overrides -> NXDOMAIN.

The unmatched-name behaviour is deliberately inverted from upstream nanoDNS:
names matching no rule return NXDOMAIN (default-deny) instead of being forwarded,
so PSN, updates and telemetry stay blocked. The source-client allowlist now lives
in [general] allowed_clients. The INI is baked into the image; CONFIG_FILE can
point at a mounted file for per-host overrides.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
2026-10-11 13:38:29 +01:00
co-authored by Claude Opus 4.8
parent 9f28b8e2a1
commit 50dd2d8266
7 changed files with 237 additions and 72 deletions
+2
View File
@@ -22,6 +22,8 @@ before including them in an image.
- `compose.yaml`: hardened local deployment. - `compose.yaml`: hardened local deployment.
- `docker/server.py`: static HTTP server and security headers. - `docker/server.py`: static HTTP server and security headers.
- `docker/guide_gateway.py`: restricted DNS plus HTTP/HTTPS User Guide redirect. - `docker/guide_gateway.py`: restricted DNS plus HTTP/HTTPS User Guide redirect.
- `docker/nanodns.ini`: nanoDNS-style DNS policy (overrides, exceptions, upstreams,
allowed client VLANs) read by `guide_gateway.py`; default-deny for unmatched names.
- `payloads.sha256`: approved payload digests for the pinned submodule commit. - `payloads.sha256`: approved payload digests for the pinned submodule commit.
- `docs/PS5_SETUP.md`: user and operator guide. - `docs/PS5_SETUP.md`: user and operator guide.
- `docs/HANDOFF.md`: current infrastructure state and durable session handoff. - `docs/HANDOFF.md`: current infrastructure state and durable session handoff.
+1
View File
@@ -25,6 +25,7 @@ COPY app/offsets /srv/public/offsets
COPY app/payloads /srv/public/payloads COPY app/payloads /srv/public/payloads
COPY docker/server.py /srv/server.py COPY docker/server.py /srv/server.py
COPY docker/guide_gateway.py /srv/guide_gateway.py COPY docker/guide_gateway.py /srv/guide_gateway.py
COPY docker/nanodns.ini /srv/nanodns.ini
COPY --from=certificates /certificates /srv/certificates COPY --from=certificates /certificates /srv/certificates
RUN cd /srv/public && sha256sum -c /tmp/payloads.sha256 \ RUN cd /srv/public && sha256sum -c /tmp/payloads.sha256 \
+3 -6
View File
@@ -17,12 +17,9 @@ services:
ps5-guide-gateway: ps5-guide-gateway:
image: relapse-exploit:local image: relapse-exploit:local
command: ["python", "/srv/guide_gateway.py"] command: ["python", "/srv/guide_gateway.py"]
environment: # DNS policy (overrides, exceptions, upstreams, allowed client VLANs) lives in
GATEWAY_IP: "${GATEWAY_IP:-192.168.20.30}" # docker/nanodns.ini, baked into the image. Set CONFIG_FILE to point at a
REDIRECT_URL: "${GUIDE_REDIRECT_URL:-http://192.168.20.30:8000/}" # different mounted file for per-host customisation.
FORWARDED_DOMAINS: "${FORWARDED_DOMAINS:-homebrew.page,itsplk.github.io,store.spectrumlibrary.online,api.github.com,github.com,raw.githubusercontent.com,release-assets.githubusercontent.com,prosperopatches.com,cdn.prosperopatches.com,image.api.playstation.com,gmedia.playstation.com}"
UPSTREAM_DNS: "${UPSTREAM_DNS:-1.1.1.1}"
ALLOWED_CLIENTS: "${ALLOWED_CLIENTS:-192.168.40.0/28,192.168.20.0/24,192.168.10.0/27}"
ports: ports:
- "${GATEWAY_BIND_IP:-192.168.20.30}:53:8053/udp" - "${GATEWAY_BIND_IP:-192.168.20.30}:53:8053/udp"
- "${GATEWAY_BIND_IP:-192.168.20.30}:53:8053/tcp" - "${GATEWAY_BIND_IP:-192.168.20.30}:53:8053/tcp"
+123 -34
View File
@@ -1,3 +1,5 @@
import configparser
import fnmatch
import http.server import http.server
import ipaddress import ipaddress
import os import os
@@ -9,38 +11,96 @@ import struct
import threading import threading
CONFIG_FILE = os.environ.get("CONFIG_FILE", "/srv/nanodns.ini")
DNS_PORT = int(os.environ.get("DNS_PORT", "8053")) DNS_PORT = int(os.environ.get("DNS_PORT", "8053"))
HTTP_PORT = int(os.environ.get("HTTP_PORT", "8081")) HTTP_PORT = int(os.environ.get("HTTP_PORT", "8081"))
HTTPS_PORT = int(os.environ.get("HTTPS_PORT", "8443")) HTTPS_PORT = int(os.environ.get("HTTPS_PORT", "8443"))
GATEWAY_IP = ipaddress.IPv4Address(os.environ.get("GATEWAY_IP", "192.168.20.30"))
REDIRECT_URL = os.environ.get("REDIRECT_URL", f"http://{GATEWAY_IP}:8000/")
TARGET_DOMAIN = "manuals.playstation.net"
# Exact names forwarded to the upstream resolver (homebrew store: catalog + downloads).
FORWARDED_DOMAINS = frozenset(
name.strip().lower()
for name in os.environ.get(
"FORWARDED_DOMAINS",
"homebrew.page,itsplk.github.io,store.spectrumlibrary.online,api.github.com,github.com,raw.githubusercontent.com,release-assets.githubusercontent.com,prosperopatches.com,cdn.prosperopatches.com,image.api.playstation.com,gmedia.playstation.com",
).split(",")
if name.strip()
)
UPSTREAM_DNS = str(ipaddress.ip_address(os.environ.get("UPSTREAM_DNS", "1.1.1.1")))
UPSTREAM_TIMEOUT = 3.0
# Source networks allowed to query the DNS service. Defaults to the console VLAN
# plus the server and trusted-management VLANs. Accepts a comma-separated mix of
# individual addresses and CIDR ranges; empty disables the filter and serves
# every client.
ALLOWED_CLIENTS = tuple(
ipaddress.ip_network(entry.strip(), strict=False)
for entry in os.environ.get(
"ALLOWED_CLIENTS",
"192.168.40.0/28,192.168.20.0/24,192.168.10.0/27",
).split(",")
if entry.strip()
)
CERTIFICATE = "/srv/certificates/manuals.crt" CERTIFICATE = "/srv/certificates/manuals.crt"
PRIVATE_KEY = "/srv/certificates/manuals.key" PRIVATE_KEY = "/srv/certificates/manuals.key"
# Built-in defaults, used when the config file (or a given key) is absent. They
# mirror the reviewed production policy: the User Guide domain is spoofed to the
# local gateway, the reviewed store/update hosts are forwarded upstream, and
# every other name returns NXDOMAIN (default-deny).
DEFAULT_REDIRECT_URL = "http://192.168.20.30:8000/"
DEFAULT_ALLOWED_CLIENTS = "192.168.40.0/28, 192.168.20.0/24, 192.168.10.0/27"
DEFAULT_UPSTREAMS = "1.1.1.1"
DEFAULT_TIMEOUT_MS = "3000"
DEFAULT_OVERRIDES = (("manuals.playstation.net", "192.168.20.30"),)
DEFAULT_EXCEPTIONS = (
"homebrew.page",
"itsplk.github.io",
"store.spectrumlibrary.online",
"api.github.com",
"github.com",
"raw.githubusercontent.com",
"release-assets.githubusercontent.com",
"prosperopatches.com",
"cdn.prosperopatches.com",
"image.api.playstation.com",
"gmedia.playstation.com",
)
def _split(value):
return [item for item in value.replace(",", " ").split() if item]
def load_config(path):
parser = configparser.ConfigParser(
allow_no_value=True,
inline_comment_prefixes=("#", ";"),
)
# Keep mask/address case verbatim; query names are lowercased before lookup.
parser.optionxform = str
parser.read(path)
general = parser["general"] if parser.has_section("general") else {}
redirect_url = general.get("redirect_url") or DEFAULT_REDIRECT_URL
allowed_raw = general.get("allowed_clients") or DEFAULT_ALLOWED_CLIENTS
allowed_clients = tuple(
ipaddress.ip_network(entry, strict=False) for entry in _split(allowed_raw)
)
upstream = parser["upstream"] if parser.has_section("upstream") else {}
servers_raw = (
upstream.get("server") or upstream.get("servers") or DEFAULT_UPSTREAMS
)
upstreams = tuple(
str(ipaddress.ip_address(server)) for server in _split(servers_raw)
)
timeout = max(0.1, int(upstream.get("timeout_ms") or DEFAULT_TIMEOUT_MS) / 1000.0)
override_items = (
parser.items("overrides")
if parser.has_section("overrides")
else DEFAULT_OVERRIDES
)
overrides = tuple(
(mask.lower(), ipaddress.IPv4Address(address))
for mask, address in override_items
if address is not None
)
exception_items = (
parser.items("exceptions")
if parser.has_section("exceptions")
else ((mask, None) for mask in DEFAULT_EXCEPTIONS)
)
exceptions = tuple(mask.lower() for mask, _ in exception_items)
return redirect_url, allowed_clients, upstreams, timeout, overrides, exceptions
(
REDIRECT_URL,
ALLOWED_CLIENTS,
UPSTREAMS,
UPSTREAM_TIMEOUT,
OVERRIDES,
EXCEPTIONS,
) = load_config(CONFIG_FILE)
def client_allowed(address): def client_allowed(address):
if not ALLOWED_CLIENTS: if not ALLOWED_CLIENTS:
@@ -52,6 +112,17 @@ def client_allowed(address):
return any(client in network for network in ALLOWED_CLIENTS) return any(client in network for network in ALLOWED_CLIENTS)
def match_any(name, masks):
return any(fnmatch.fnmatchcase(name, mask) for mask in masks)
def override_for(name):
for mask, address in OVERRIDES:
if fnmatch.fnmatchcase(name, mask):
return address
return None
def parse_question(packet): def parse_question(packet):
if len(packet) < 17: if len(packet) < 17:
raise ValueError("short DNS query") raise ValueError("short DNS query")
@@ -104,12 +175,12 @@ def receive_exactly(connection, wanted):
return b"".join(chunks) return b"".join(chunks)
def forward_query(packet, use_tcp=False): def _forward_one(packet, server, use_tcp):
family = socket.AF_INET6 if ":" in UPSTREAM_DNS else socket.AF_INET family = socket.AF_INET6 if ":" in server else socket.AF_INET
socket_type = socket.SOCK_STREAM if use_tcp else socket.SOCK_DGRAM socket_type = socket.SOCK_STREAM if use_tcp else socket.SOCK_DGRAM
with socket.socket(family, socket_type) as upstream: with socket.socket(family, socket_type) as upstream:
upstream.settimeout(UPSTREAM_TIMEOUT) upstream.settimeout(UPSTREAM_TIMEOUT)
upstream.connect((UPSTREAM_DNS, 53)) upstream.connect((server, 53))
if use_tcp: if use_tcp:
upstream.sendall(struct.pack("!H", len(packet)) + packet) upstream.sendall(struct.pack("!H", len(packet)) + packet)
response_length = struct.unpack("!H", receive_exactly(upstream, 2))[0] response_length = struct.unpack("!H", receive_exactly(upstream, 2))[0]
@@ -123,6 +194,16 @@ def forward_query(packet, use_tcp=False):
return response return response
def forward_query(packet, use_tcp=False):
last_error = OSError("no upstream resolver configured")
for server in UPSTREAMS:
try:
return _forward_one(packet, server, use_tcp)
except (OSError, ValueError) as error:
last_error = error
raise last_error
def dns_response(packet, use_tcp=False): def dns_response(packet, use_tcp=False):
if len(packet) < 12: if len(packet) < 12:
return b"" return b""
@@ -137,21 +218,25 @@ def dns_response(packet, use_tcp=False):
return struct.pack("!HHHHHH", transaction_id, response_flags | 1, 0, 0, 0, 0) return struct.pack("!HHHHHH", transaction_id, response_flags | 1, 0, 0, 0, 0)
question = packet[12:question_end] question = packet[12:question_end]
if name in FORWARDED_DOMAINS:
# Exceptions bypass overrides and are forwarded upstream (nanoDNS semantics).
if match_any(name, EXCEPTIONS):
try: try:
return forward_query(packet, use_tcp=use_tcp) return forward_query(packet, use_tcp=use_tcp)
except (OSError, ValueError) as error: except (OSError, ValueError) as error:
print(f"DNS upstream failure for {name}: {error}", flush=True) print(f"DNS upstream failure for {name}: {error}", flush=True)
return error_response(packet, 2) # SERVFAIL return error_response(packet, 2) # SERVFAIL
if name != TARGET_DOMAIN: address = override_for(name)
if address is None:
# Default-deny: a name matching no rule returns NXDOMAIN.
return struct.pack("!HHHHHH", transaction_id, response_flags | 3, 1, 0, 0, 0) + question return struct.pack("!HHHHHH", transaction_id, response_flags | 3, 1, 0, 0, 0) + question
if qclass == 1 and qtype in (1, 255): if qclass == 1 and qtype in (1, 255):
answer = ( answer = (
b"\xc0\x0c" b"\xc0\x0c"
+ struct.pack("!HHIH", 1, 1, 60, 4) + struct.pack("!HHIH", 1, 1, 60, 4)
+ GATEWAY_IP.packed + address.packed
) )
return struct.pack("!HHHHHH", transaction_id, response_flags, 1, 1, 0, 0) + question + answer return struct.pack("!HHHHHH", transaction_id, response_flags, 1, 1, 0, 0) + question + answer
@@ -274,9 +359,13 @@ if __name__ == "__main__":
serve(http, "http-redirect") serve(http, "http-redirect")
serve(https, "https-redirect") serve(https, "https-redirect")
overrides_summary = (
", ".join(f"{mask}->{address}" for mask, address in OVERRIDES) or "none"
)
print( print(
f"PS5 guide gateway: {TARGET_DOMAIN} -> {GATEWAY_IP}; redirect -> {REDIRECT_URL}; " "PS5 guide gateway (nanoDNS-style, default-deny): "
f"forward {','.join(sorted(FORWARDED_DOMAINS))} -> {UPSTREAM_DNS}; " f"redirect -> {REDIRECT_URL}; overrides {overrides_summary}; "
f"forward {','.join(EXCEPTIONS) or 'none'} -> {','.join(UPSTREAMS) or 'none'}; "
f"clients {','.join(str(network) for network in ALLOWED_CLIENTS) or 'any'}", f"clients {','.join(str(network) for network in ALLOWED_CLIENTS) or 'any'}",
flush=True, flush=True,
) )
+47
View File
@@ -0,0 +1,47 @@
# PS5 User Guide DNS gateway -- nanoDNS-style policy.
#
# Config model adapted from https://github.com/drakmor/nanoDNS#config, but the
# unmatched-name behaviour is deliberately inverted to default-DENY.
#
# Resolution order for each query:
# 1. the source address must fall inside [general] allowed_clients, otherwise
# the query is dropped and logged as "denied";
# 2. a name matching an [exceptions] mask is forwarded to [upstream];
# 3. otherwise a name matching an [overrides] mask is answered with its IPv4;
# 4. otherwise NXDOMAIN. Unlike upstream nanoDNS, names that match no rule are
# NOT forwarded, which keeps PSN, updates and telemetry blocked by default.
#
# Masks use shell-style wildcards: * ? [abc] [a-z] [!0-9]
# Names are matched case-insensitively.
[general]
redirect_url = http://192.168.20.30:8000/
# Source networks allowed to query this resolver (comma/space/newline separated).
# Consoles VLAN /28, server VLAN /24, trusted management VLAN /27.
allowed_clients = 192.168.40.0/28, 192.168.20.0/24, 192.168.10.0/27
[upstream]
# Resolvers tried in order; the first valid answer wins. Several may be listed,
# separated by commas, spaces or newlines. Must be IP addresses, not hostnames.
server = 1.1.1.1
timeout_ms = 3000
[overrides]
# mask = IPv4 -- matched names are answered with this address (A records).
manuals.playstation.net = 192.168.20.30
[exceptions]
# One mask per line (no address) -- matched names are forwarded upstream. These
# are the reviewed homebrew-store, PS5 Payload Manager, Spectrum Library and
# BlackBox endpoints. Everything not listed here or in [overrides] is NXDOMAIN.
homebrew.page
itsplk.github.io
store.spectrumlibrary.online
api.github.com
github.com
raw.githubusercontent.com
release-assets.githubusercontent.com
prosperopatches.com
cdn.prosperopatches.com
image.api.playstation.com
gmedia.playstation.com
+35 -19
View File
@@ -182,26 +182,42 @@ the local container. The local gateway was therefore added. Configure the PS5 wi
primary DNS `192.168.20.30` and secondary DNS `0.0.0.0`, then restart the console and primary DNS `192.168.20.30` and secondary DNS `0.0.0.0`, then restart the console and
accept the self-signed certificate warning in the User Guide. accept the self-signed certificate warning in the User Guide.
The gateway provides allowlisted recursive DNS forwarding only for `homebrew.page`, The gateway's DNS policy follows the nanoDNS config model
`itsplk.github.io`, `store.spectrumlibrary.online`, `api.github.com`, `github.com` (<https://github.com/drakmor/nanoDNS#config>), with the unmatched-name behaviour
`raw.githubusercontent.com`, `release-assets.githubusercontent.com`, deliberately inverted to default-deny. The policy lives in `docker/nanodns.ini`,
`prosperopatches.com`, `cdn.prosperopatches.com`, `image.api.playstation.com` and baked into the image, with these sections:
`gmedia.playstation.com`. Only those names and
`manuals.playstation.net` resolve; all other names receive NXDOMAIN. This permits
the reviewed fixed endpoints of the homebrew store, PS5 Payload Manager, Spectrum
Library and BlackBox while continuing to block PSN and update resolution. The PS5
sends DNS only to `net-utils01`; that host must be allowed to reach the configured
upstream resolver on UDP/TCP 53.
The gateway additionally restricts which source clients it answers. `ALLOWED_CLIENTS` - `[general]` — `redirect_url` and `allowed_clients`.
defaults to the console VLAN `192.168.40.0/28`, the server VLAN `192.168.20.0/24`, - `[upstream]` — one or more resolver IPs (`server`, comma/space/newline
and the trusted management VLAN `192.168.10.0/27`; queries from any other source separated, tried in order) and a `timeout_ms` budget.
address are dropped and logged as `denied`, on both UDP and TCP. The server and - `[overrides]` — `mask = IPv4`; matched names are answered with that A record.
trusted VLANs are included so operators can test and administer the resolver; the `manuals.playstation.net` maps to `192.168.20.30`.
consoles themselves only need the `/28`. The value accepts a comma-separated mix of - `[exceptions]` — one mask per line; matched names are forwarded upstream. These
individual addresses and CIDR ranges, and an empty value disables the filter. This are the reviewed homebrew-store, PS5 Payload Manager, Spectrum Library and
keeps the service off the rest of the LAN and prevents arbitrary hosts from using it BlackBox endpoints (`homebrew.page`, `itsplk.github.io`,
as an open resolver for the allowlisted names. `store.spectrumlibrary.online`, `api.github.com`, `github.com`,
`raw.githubusercontent.com`, `release-assets.githubusercontent.com`,
`prosperopatches.com`, `cdn.prosperopatches.com`, `image.api.playstation.com`,
`gmedia.playstation.com`).
Masks use shell-style wildcards (`*`, `?`, `[abc]`, `[a-z]`, `[!0-9]`) and match
case-insensitively. Resolution order per query is: exceptions (forward) →
overrides (spoof) → NXDOMAIN. Unlike upstream nanoDNS, a name matching no rule is
**not** forwarded; it returns NXDOMAIN, which keeps PSN, updates and telemetry
blocked by default. The PS5 sends DNS only to `net-utils01`; that host must be
allowed to reach the configured upstream resolver on UDP/TCP 53.
The gateway additionally restricts which source clients it answers, via
`[general] allowed_clients`. It defaults to the console VLAN `192.168.40.0/28`,
the server VLAN `192.168.20.0/24`, and the trusted management VLAN
`192.168.10.0/27`; queries from any other source address are dropped and logged
as `denied`, on both UDP and TCP. The server and trusted VLANs are included so
operators can test and administer the resolver; the consoles themselves only need
the `/28`. The value accepts a comma/space/newline mix of individual addresses and
CIDR ranges, and an empty value disables the filter. This keeps the service off the
rest of the LAN and prevents arbitrary hosts from using it as an open resolver for
the allowlisted names. Editing the policy requires an image rebuild, or pointing
`CONFIG_FILE` at a mounted file.
The Payload Manager review used upstream commit The Payload Manager review used upstream commit
`e4e72dc9d831dd09ee8fabc222ad1e7f5139050e` and the official catalog current on `e4e72dc9d831dd09ee8fabc222ad1e7f5139050e` and the official catalog current on
+26 -13
View File
@@ -154,6 +154,17 @@ O Compose inclui um DNS controlado no `net-utils01` que:
- apresente um gateway HTTPS local e redirecione o browser para o exploit em - apresente um gateway HTTPS local e redirecione o browser para o exploit em
`http://192.168.20.30:8000/`. `http://192.168.20.30:8000/`.
A política de DNS segue o modelo de configuração do nanoDNS
(<https://github.com/drakmor/nanoDNS#config>), mas com o comportamento para nomes
não correspondidos invertido para **default-deny** (NXDOMAIN em vez de reencaminhar).
Vive no ficheiro `docker/nanodns.ini`, incluído na imagem, com as secções
`[general]` (`redirect_url`, `allowed_clients`), `[upstream]` (`server`,
`timeout_ms`), `[overrides]` (`máscara = IPv4`, faz spoof) e `[exceptions]` (uma
máscara por linha, reencaminha para o upstream). As máscaras aceitam wildcards
shell (`*`, `?`, `[abc]`, `[a-z]`, `[!0-9]`) e a ordem de resolução é
exceptions → overrides → NXDOMAIN. Para alterar a política, editar o INI e
reconstruir a imagem (ou apontar `CONFIG_FILE` para um ficheiro montado).
Nesse cenário, a PS5 usaria: Nesse cenário, a PS5 usaria:
```text ```text
@@ -171,20 +182,22 @@ definir `GATEWAY_BIND_IP`, `GATEWAY_IP` e `GUIDE_REDIRECT_URL` antes de executar
Compose. Também é possível bloquear o acesso WAN da PS5 no router, mantendo apenas Compose. Também é possível bloquear o acesso WAN da PS5 no router, mantendo apenas
a comunicação LAN; esta continua a ser uma camada adicional mais forte do que DNS. a comunicação LAN; esta continua a ser uma camada adicional mais forte do que DNS.
O resolvedor upstream predefinido é `1.1.1.1`. Pode ser substituído por um endereço O resolvedor upstream predefinido é `1.1.1.1`, definido em `[upstream] server` no
DNS controlado na rede através de `UPSTREAM_DNS`; o valor deve ser um endereço IP, `docker/nanodns.ini`. Podem listar-se vários (separados por vírgulas/espaços/linhas),
não um hostname. A firewall deve permitir UDP e TCP `53` de `net-utils01` para esse tentados por ordem; os valores devem ser endereços IP, não hostnames. A firewall deve
resolvedor. A consola continua a precisar apenas de UDP/TCP `53` para permitir UDP e TCP `53` de `net-utils01` para esse resolvedor. A consola continua a
`192.168.20.30`; não deve consultar diretamente um DNS público. precisar apenas de UDP/TCP `53` para `192.168.20.30`; não deve consultar diretamente
um DNS público.
O gateway só responde a consultas DNS vindas das redes listadas em `ALLOWED_CLIENTS`. O gateway só responde a consultas DNS vindas das redes listadas em
Por defeito inclui a VLAN das consolas `192.168.40.0/28`, a VLAN de servers `[general] allowed_clients`. Por defeito inclui a VLAN das consolas
`192.168.20.0/24` e a VLAN de gestão/confiança `192.168.10.0/27` (estas duas últimas `192.168.40.0/28`, a VLAN de servers `192.168.20.0/24` e a VLAN de gestão/confiança
para o operador poder testar e administrar). O valor aceita uma lista separada por `192.168.10.0/27` (estas duas últimas para o operador poder testar e administrar). O
vírgulas de endereços individuais e/ou intervalos CIDR — por exemplo, valor aceita uma lista separada por vírgulas/espaços de endereços individuais e/ou
`192.168.40.13,192.168.40.14` para trancar apenas às duas PS5 autorizadas (PS5FAT e intervalos CIDR — por exemplo, `192.168.40.13 192.168.40.14` para trancar apenas às
PS5PRO). Um valor vazio desativa o filtro e serve todos os clientes. Consultas de duas PS5 autorizadas (PS5FAT e PS5PRO). Um valor vazio desativa o filtro e serve todos
origens não permitidas são descartadas e registadas como `denied`. os clientes. Consultas de origens não permitidas são descartadas e registadas como
`denied`.
O acesso WAN da consola pode ficar limitado a TCP `443` para os domínios O acesso WAN da consola pode ficar limitado a TCP `443` para os domínios
permitidos. Não são necessárias regras WAN de entrada. Se faltarem ícones, consultar permitidos. Não são necessárias regras WAN de entrada. Se faltarem ícones, consultar