feat: added support for running the VPN router on a host with Docker Community Edition.
Release / release (push) Successful in 1m8s
Release / release (push) Successful in 1m8s
This commit is contained in:
1 parent
66fcd69d40
commit
2e0cee10ac
10 files changed
+279
-1
No files matched your search
@@ -0,0 +1,28 @@
|
|||||||
|
# Architecture Decision Records
|
||||||
|
|
||||||
|
## ADR-1: Docker's published ports are filtered in DOCKER-USER by the package
|
||||||
|
|
||||||
|
Status: Accepted
|
||||||
|
|
||||||
|
Context: Docker adds its own iptables rules for published ports. They are evaluated before
|
||||||
|
UFW's, so a published port is reachable from the external interface whatever UFW says. Docker
|
||||||
|
reserves the `DOCKER-USER` chain, evaluated first in `FORWARD`, for rules of the operator. The
|
||||||
|
router is an internet-facing host, and Docker is installed on it for services run alongside the
|
||||||
|
router.
|
||||||
|
|
||||||
|
Decision: The package secures Docker itself. A `docker` configuration section has `mode`
|
||||||
|
(`auto`, `on`, `off`) and `exposed_ports`. In `auto` mode the rules are applied when
|
||||||
|
`docker.service` is installed, which is what the package looks up; `on` requires Docker and
|
||||||
|
`off` removes the rules. The rules are in a chain of their own, `VPN-ROUTER-DOCKER`, entered by
|
||||||
|
one jump at the top of `DOCKER-USER`. For traffic from the external interface they accept IPsec
|
||||||
|
protected traffic, TCP to `exposed_ports` and established connections, and drop the rest. The
|
||||||
|
`vpn-router-docker` unit applies them after `docker.service` starts and removes them when it
|
||||||
|
stops; `vpn-router-setup` restarts it while Docker runs.
|
||||||
|
|
||||||
|
Consequences: Installing Docker first is recommended; `auto` mode then finds it as soon as the
|
||||||
|
package is configured. When Docker is installed after the package, the rules are applied by
|
||||||
|
restarting `vpn-router-setup`, a reboot or a restart of Docker, not by Docker's first start,
|
||||||
|
because `vpn-router-docker` is already active. A published
|
||||||
|
port is reachable from the external interface only when it is in `exposed_ports`. Other rules
|
||||||
|
in `DOCKER-USER` are not touched, because the package owns only its own chain. Restarting Docker
|
||||||
|
restores the rules. The rules cover IPv4 only.
|
||||||
@@ -6,6 +6,7 @@ A Debian package that configures a Linux host or virtual machine as a VPN router
|
|||||||
- road-warrior (P2S) access using IKEv2 EAP-TLS, with a bundled certificate authority
|
- road-warrior (P2S) access using IKEv2 EAP-TLS, with a bundled certificate authority
|
||||||
- optional WireGuard endpoint
|
- optional WireGuard endpoint
|
||||||
- routing and NAT for a protected subnet, TCP MSS clamping, and UFW firewall rules
|
- routing and NAT for a protected subnet, TCP MSS clamping, and UFW firewall rules
|
||||||
|
- firewall rules for the ports Docker publishes, when Docker is installed
|
||||||
|
|
||||||
Nothing in the package is specific to a cloud provider. It is built on the external and
|
Nothing in the package is specific to a cloud provider. It is built on the external and
|
||||||
internal NIC names, and a `mode` setting says how much of the rest you are supplying and
|
internal NIC names, and a `mode` setting says how much of the rest you are supplying and
|
||||||
@@ -28,6 +29,10 @@ service that applies it.
|
|||||||
systemctl restart vpn-router-setup apply it
|
systemctl restart vpn-router-setup apply it
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If the host runs Docker, the recommended order is Docker first and the package second. The
|
||||||
|
package secures the ports Docker publishes when it finds the Docker service; the steps for a
|
||||||
|
host that already runs Docker, and for installing Docker later, are under [Docker](#docker).
|
||||||
|
|
||||||
### By hand
|
### By hand
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -108,6 +113,8 @@ must have the type shown below; an unknown section or setting is an error.
|
|||||||
| `wireguard` | `enabled` | boolean | `false` | WireGuard endpoint |
|
| `wireguard` | `enabled` | boolean | `false` | WireGuard endpoint |
|
||||||
| `wireguard` | `address` | string | | Address and prefix for `wg0`, for example `192.168.200.1/24` |
|
| `wireguard` | `address` | string | | Address and prefix for `wg0`, for example `192.168.200.1/24` |
|
||||||
| `wireguard` | `listen_port` | integer | `51820` | UDP port |
|
| `wireguard` | `listen_port` | integer | `51820` | UDP port |
|
||||||
|
| `docker` | `mode` | string | `auto` | `auto` secures Docker when its service is installed, `on` requires it, `off` removes the rules |
|
||||||
|
| `docker` | `exposed_ports` | list | | TCP ports and port ranges (`8000:8100`) reachable from the external interface; at most 15, a range counting as two |
|
||||||
|
|
||||||
A minimal site-to-site configuration in `auto` mode:
|
A minimal site-to-site configuration in `auto` mode:
|
||||||
|
|
||||||
@@ -153,6 +160,56 @@ interface does not exist the service stops and says so. In `interfaces` and `aut
|
|||||||
the internal gateway is taken from the routing table, falling back to the first host
|
the internal gateway is taken from the routing table, falling back to the first host
|
||||||
address of the connected subnet with a warning when the routing table has nothing to say.
|
address of the connected subnet with a warning when the routing table has nothing to say.
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
Docker publishes container ports in its own firewall rules, which bypass UFW and can expose
|
||||||
|
ports on the external interface. Docker evaluates the `DOCKER-USER` chain before its own
|
||||||
|
rules, so the package filters there. With `docker.mode` set to `auto`, the package secures
|
||||||
|
Docker when `docker.service` is installed, and does nothing otherwise.
|
||||||
|
|
||||||
|
### On a host that already runs Docker
|
||||||
|
|
||||||
|
Installing Docker first is the recommended order. After installing the package:
|
||||||
|
|
||||||
|
1. Set `general.platform` in `/etc/vpn-router/vpn-router.json`. While it is `none`, nothing is
|
||||||
|
configured, the Docker rules included.
|
||||||
|
2. List the TCP ports the running containers must expose to the internet, for example
|
||||||
|
`"docker": { "exposed_ports": ["443"] }`. The default is none: once the rules are applied,
|
||||||
|
every published port that is not listed is unreachable from the external interface, and
|
||||||
|
remains reachable through the IPsec tunnel.
|
||||||
|
3. Apply the configuration with `systemctl restart vpn-router-setup`. Docker is running, so
|
||||||
|
this also restarts `vpn-router-docker`, which installs the rules.
|
||||||
|
4. Check the result:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
iptables -S DOCKER-USER # "-j VPN-ROUTER-DOCKER" is the first rule
|
||||||
|
iptables -S VPN-ROUTER-DOCKER # the rules of the external interface
|
||||||
|
```
|
||||||
|
|
||||||
|
### When Docker is installed after the package
|
||||||
|
|
||||||
|
The Docker rules are not applied when Docker starts for the first time, so run
|
||||||
|
`systemctl restart vpn-router-setup` after installing Docker, then check as above. A reboot or a
|
||||||
|
restart of Docker applies them as well.
|
||||||
|
|
||||||
|
The rules sit in their own chain, `VPN-ROUTER-DOCKER`, entered by one jump at the top of
|
||||||
|
`DOCKER-USER`; other rules in `DOCKER-USER` are left alone. For traffic that arrives on the
|
||||||
|
external interface, in this order:
|
||||||
|
|
||||||
|
1. IPsec protected traffic is accepted.
|
||||||
|
2. TCP to the ports in `docker.exposed_ports` is accepted; none by default.
|
||||||
|
3. Packets of established and related connections are accepted.
|
||||||
|
4. Everything else is dropped.
|
||||||
|
|
||||||
|
Traffic from every other interface, including the internal one and the Docker bridges, is
|
||||||
|
not affected. The rules cover IPv4 only.
|
||||||
|
|
||||||
|
The `vpn-router-docker` service applies the rules after `docker.service` starts and removes
|
||||||
|
them when Docker stops, so restarting Docker restores them. `systemctl restart
|
||||||
|
vpn-router-setup` applies a changed configuration while Docker runs. With `docker.mode` set to
|
||||||
|
`on`, the service fails when Docker is not installed or its daemon is not running; with
|
||||||
|
`off` it removes the rules.
|
||||||
|
|
||||||
## Applying changes
|
## Applying changes
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -256,7 +313,8 @@ modules shipped with the package; the configuration file accepts any module inst
|
|||||||
|
|
||||||
## Removal
|
## Removal
|
||||||
|
|
||||||
`apt-get remove` terminates the tunnels and takes `wg-quick@wg0` down, but leaves the
|
`apt-get remove` stops `vpn-router-docker`, which removes the Docker rules. It terminates the
|
||||||
|
tunnels and takes `wg-quick@wg0` down, but leaves the
|
||||||
configuration in place. `strongswan` is left enabled, since it is a shared service that
|
configuration in place. `strongswan` is left enabled, since it is a shared service that
|
||||||
may be in use by something else.
|
may be in use by something else.
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,22 @@
|
|||||||
|
vpn-router (0.3.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Secure the ports Docker publishes. Rules in a chain of their own,
|
||||||
|
VPN-ROUTER-DOCKER, entered by a jump at the top of DOCKER-USER, accept
|
||||||
|
IPsec protected traffic, TCP to docker.exposed_ports and established
|
||||||
|
connections from the external interface and drop the rest of it. Other
|
||||||
|
rules in DOCKER-USER are left alone. The rules cover IPv4 only.
|
||||||
|
* Add the docker configuration section. docker.mode is auto (the default,
|
||||||
|
secures Docker when docker.service is installed), on (requires Docker) or
|
||||||
|
off (removes the rules); docker.exposed_ports lists the TCP ports and port
|
||||||
|
ranges reachable from the external interface, none by default.
|
||||||
|
* Add the vpn-router-docker service, which applies the Docker rules after
|
||||||
|
docker.service starts and removes them when it stops. vpn-router-setup
|
||||||
|
restarts it while Docker runs, so a changed configuration takes effect.
|
||||||
|
* Installing Docker before this package is recommended. When Docker is
|
||||||
|
installed later, restart vpn-router-setup to apply the rules.
|
||||||
|
|
||||||
|
-- Sławomir Koszewski <slawek@koszewscy.waw.pl> Fri, 23 Oct 2026 12:44:45 +0200
|
||||||
|
|
||||||
vpn-router (0.2.0-1) unstable; urgency=medium
|
vpn-router (0.2.0-1) unstable; urgency=medium
|
||||||
|
|
||||||
* Change the configuration file to JSON, /etc/vpn-router/vpn-router.json.
|
* Change the configuration file to JSON, /etc/vpn-router/vpn-router.json.
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ src/etc/sysctl.d/99-vpn-router.conf etc/sysctl.d/
|
|||||||
src/usr/lib/vpn-router/vpnrouter.py usr/lib/vpn-router/
|
src/usr/lib/vpn-router/vpnrouter.py usr/lib/vpn-router/
|
||||||
src/usr/lib/vpn-router/simple-ca usr/lib/vpn-router/
|
src/usr/lib/vpn-router/simple-ca usr/lib/vpn-router/
|
||||||
src/usr/lib/vpn-router/configure usr/lib/vpn-router/
|
src/usr/lib/vpn-router/configure usr/lib/vpn-router/
|
||||||
|
src/usr/lib/vpn-router/docker-rules usr/lib/vpn-router/
|
||||||
src/usr/lib/vpn-router/setup usr/lib/vpn-router/
|
src/usr/lib/vpn-router/setup usr/lib/vpn-router/
|
||||||
src/usr/lib/vpn-router/generate-config usr/lib/vpn-router/
|
src/usr/lib/vpn-router/generate-config usr/lib/vpn-router/
|
||||||
src/usr/lib/vpn-router/vpnrouter_platforms/*.py usr/lib/vpn-router/vpnrouter_platforms/
|
src/usr/lib/vpn-router/vpnrouter_platforms/*.py usr/lib/vpn-router/vpnrouter_platforms/
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
|
|
||||||
override_dh_installsystemd:
|
override_dh_installsystemd:
|
||||||
dh_installsystemd --name=vpn-router-setup
|
dh_installsystemd --name=vpn-router-setup
|
||||||
|
dh_installsystemd --name=vpn-router-docker
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=VPN Router rules for Docker published ports
|
||||||
|
Documentation=file:/usr/share/doc/vpn-router/README.md
|
||||||
|
After=docker.service vpn-router-setup.service
|
||||||
|
PartOf=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/usr/lib/vpn-router/docker-rules apply
|
||||||
|
ExecStop=/usr/lib/vpn-router/docker-rules remove
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=docker.service
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
"""Secure the ports Docker publishes, with rules in the DOCKER-USER chain.
|
||||||
|
|
||||||
|
Usage: docker-rules apply|remove
|
||||||
|
|
||||||
|
Docker evaluates DOCKER-USER in FORWARD before its own rules, which makes it the
|
||||||
|
place to filter traffic to containers. The rules live in their own chain,
|
||||||
|
VPN-ROUTER-DOCKER, entered by one jump at the top of DOCKER-USER, so anything
|
||||||
|
else in DOCKER-USER is left alone. Traffic arriving on the external interface is
|
||||||
|
accepted when it is IPsec protected, when it is TCP to docker.exposed_ports, or
|
||||||
|
when it belongs to an established connection; all other traffic from the
|
||||||
|
external interface is dropped. Traffic from other interfaces is not affected.
|
||||||
|
|
||||||
|
docker.mode decides whether this runs: auto applies the rules when the Docker
|
||||||
|
service is installed, on requires it, off removes the rules. The vpn-router-docker
|
||||||
|
unit runs apply after docker.service starts and remove when it stops.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
|
import vpnrouter
|
||||||
|
from vpnrouter import fail, warn
|
||||||
|
|
||||||
|
CHAIN = 'VPN-ROUTER-DOCKER'
|
||||||
|
USER_CHAIN = 'DOCKER-USER'
|
||||||
|
|
||||||
|
|
||||||
|
def iptables(*args, check=True):
|
||||||
|
result = subprocess.run(['iptables', *args], capture_output=True, text=True)
|
||||||
|
if check and result.returncode != 0:
|
||||||
|
fail(f'iptables {" ".join(args)} failed: {result.stderr.strip()}')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def docker_installed():
|
||||||
|
"""True when systemd knows the docker.service unit."""
|
||||||
|
result = subprocess.run(
|
||||||
|
['systemctl', 'list-unit-files', '--no-legend', 'docker.service'],
|
||||||
|
capture_output=True, text=True)
|
||||||
|
return result.returncode == 0 and result.stdout.strip() != ''
|
||||||
|
|
||||||
|
|
||||||
|
def remove():
|
||||||
|
"""Take the jump and the chain out; nothing happens when they are absent."""
|
||||||
|
while iptables('-D', USER_CHAIN, '-j', CHAIN, check=False).returncode == 0:
|
||||||
|
pass
|
||||||
|
iptables('-F', CHAIN, check=False)
|
||||||
|
iptables('-X', CHAIN, check=False)
|
||||||
|
|
||||||
|
|
||||||
|
def apply(cfg):
|
||||||
|
mode = cfg['docker']['mode']
|
||||||
|
if mode == 'off':
|
||||||
|
remove()
|
||||||
|
return
|
||||||
|
|
||||||
|
if not docker_installed():
|
||||||
|
if mode == 'on':
|
||||||
|
fail('docker.mode is on but the docker service is not installed; '
|
||||||
|
'install Docker before vpn-router')
|
||||||
|
return
|
||||||
|
|
||||||
|
if iptables('-S', USER_CHAIN, check=False).returncode != 0:
|
||||||
|
if mode == 'on':
|
||||||
|
fail(f'docker.mode is on but there is no {USER_CHAIN} chain; '
|
||||||
|
'is the Docker daemon running?')
|
||||||
|
warn(f'no {USER_CHAIN} chain, the Docker daemon is not running; '
|
||||||
|
'the rules are applied when it starts')
|
||||||
|
return
|
||||||
|
|
||||||
|
ctx = vpnrouter.build_context(cfg)
|
||||||
|
wan = ctx['wan_iface']
|
||||||
|
if not wan:
|
||||||
|
fail('the external interface is not known; set interfaces.external '
|
||||||
|
'or use mode auto or interfaces')
|
||||||
|
|
||||||
|
ports = ','.join(item.strip() for item in cfg['docker']['exposed_ports'])
|
||||||
|
|
||||||
|
remove()
|
||||||
|
iptables('-N', CHAIN)
|
||||||
|
iptables('-A', CHAIN, '-i', wan, '-m', 'policy', '--dir', 'in', '--pol', 'ipsec', '-j', 'ACCEPT')
|
||||||
|
if ports:
|
||||||
|
iptables('-A', CHAIN, '-i', wan, '-p', 'tcp', '-m', 'multiport', '--dports', ports, '-j', 'ACCEPT')
|
||||||
|
iptables('-A', CHAIN, '-i', wan, '-m', 'conntrack', '--ctstate', 'ESTABLISHED,RELATED', '-j', 'ACCEPT')
|
||||||
|
iptables('-A', CHAIN, '-i', wan, '-j', 'DROP')
|
||||||
|
iptables('-I', USER_CHAIN, '1', '-j', CHAIN)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 2 or sys.argv[1] not in ('apply', 'remove'):
|
||||||
|
print('usage: docker-rules apply|remove', file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
if sys.argv[1] == 'remove':
|
||||||
|
remove()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
cfg = vpnrouter.load_config()
|
||||||
|
if vpnrouter.deferred(cfg):
|
||||||
|
return 0
|
||||||
|
try:
|
||||||
|
vpnrouter.validate(cfg)
|
||||||
|
apply(cfg)
|
||||||
|
except vpnrouter.ConfigError as exc:
|
||||||
|
fail(str(exc))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
@@ -299,6 +299,16 @@ def run_platform_setup(cfg, ctx):
|
|||||||
fail(f'platform module {module.__name__} failed: {exc}')
|
fail(f'platform module {module.__name__} failed: {exc}')
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_docker_rules():
|
||||||
|
"""Apply the new settings to the Docker rules while Docker is running.
|
||||||
|
|
||||||
|
When Docker is not running, vpn-router-docker applies them after
|
||||||
|
docker.service starts.
|
||||||
|
"""
|
||||||
|
if unit_is_active('docker.service'):
|
||||||
|
run(['systemctl', 'restart', 'vpn-router-docker.service'], check=False)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
cfg = vpnrouter.load_config()
|
cfg = vpnrouter.load_config()
|
||||||
ctx, changed = vpnrouter.configure(cfg)
|
ctx, changed = vpnrouter.configure(cfg)
|
||||||
@@ -337,6 +347,7 @@ def main():
|
|||||||
run(['systemctl', 'restart', 'systemd-resolved'], check=False)
|
run(['systemctl', 'restart', 'systemd-resolved'], check=False)
|
||||||
|
|
||||||
run_platform_setup(cfg, ctx)
|
run_platform_setup(cfg, ctx)
|
||||||
|
refresh_docker_rules()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import ipaddress
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
@@ -46,8 +47,17 @@ SCHEMA = {
|
|||||||
'remote': {'addrs': [], 'id': '', 'cidrs': [], 'psk': '', 'psk_file': ''},
|
'remote': {'addrs': [], 'id': '', 'cidrs': [], 'psk': '', 'psk_file': ''},
|
||||||
'p2s': {'enabled': False, 'address_pool': '', 'ca_name': 'VPN Router CA'},
|
'p2s': {'enabled': False, 'address_pool': '', 'ca_name': 'VPN Router CA'},
|
||||||
'wireguard': {'enabled': False, 'address': '', 'listen_port': 51820},
|
'wireguard': {'enabled': False, 'address': '', 'listen_port': 51820},
|
||||||
|
'docker': {'mode': 'auto', 'exposed_ports': []},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DOCKER_MODES = ('auto', 'on', 'off')
|
||||||
|
|
||||||
|
#: A port or a port range as iptables multiport takes it: 443 or 8000:8100.
|
||||||
|
DOCKER_PORT = re.compile(r'^\d{1,5}(:\d{1,5})?$')
|
||||||
|
|
||||||
|
#: The multiport match takes at most 15 ports, and a range counts as two.
|
||||||
|
DOCKER_PORT_LIMIT = 15
|
||||||
|
|
||||||
TYPE_NAMES = {str: 'a string', list: 'a list of strings', bool: 'a boolean',
|
TYPE_NAMES = {str: 'a string', list: 'a list of strings', bool: 'a boolean',
|
||||||
int: 'an integer'}
|
int: 'an integer'}
|
||||||
|
|
||||||
@@ -212,6 +222,24 @@ def validate(cfg):
|
|||||||
if not 1 <= port <= 65535:
|
if not 1 <= port <= 65535:
|
||||||
raise ConfigError(f'wireguard.listen_port: {port} is not a valid port')
|
raise ConfigError(f'wireguard.listen_port: {port} is not a valid port')
|
||||||
|
|
||||||
|
docker_mode = _get(cfg, 'docker', 'mode')
|
||||||
|
if docker_mode not in DOCKER_MODES:
|
||||||
|
raise ConfigError(
|
||||||
|
f'docker.mode: {docker_mode!r} is not one of {", ".join(DOCKER_MODES)}')
|
||||||
|
|
||||||
|
ports = _get_list(cfg, 'docker', 'exposed_ports')
|
||||||
|
weight = 0
|
||||||
|
for item in ports:
|
||||||
|
if not DOCKER_PORT.match(item) or any(
|
||||||
|
not 1 <= int(part) <= 65535 for part in item.split(':')):
|
||||||
|
raise ConfigError(
|
||||||
|
f'docker.exposed_ports: {item!r} is not a port or a port range')
|
||||||
|
weight += 2 if ':' in item else 1
|
||||||
|
if weight > DOCKER_PORT_LIMIT:
|
||||||
|
raise ConfigError(
|
||||||
|
f'docker.exposed_ports: at most {DOCKER_PORT_LIMIT} ports are allowed, '
|
||||||
|
'a range counting as two')
|
||||||
|
|
||||||
|
|
||||||
def resolve_psk(cfg):
|
def resolve_psk(cfg):
|
||||||
"""Return the pre-shared key, or '' when none is configured."""
|
"""Return the pre-shared key, or '' when none is configured."""
|
||||||
|
|||||||
@@ -32,5 +32,9 @@
|
|||||||
"enabled": false,
|
"enabled": false,
|
||||||
"address": "192.168.200.1/24",
|
"address": "192.168.200.1/24",
|
||||||
"listen_port": 51820
|
"listen_port": 51820
|
||||||
|
},
|
||||||
|
"docker": {
|
||||||
|
"mode": "auto",
|
||||||
|
"exposed_ports": ["443"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user