fix: WireGuard clients may use the router's DNS resolver, which runs while WireGuard does, and /remote-access names its addresses for their configuration.
Release / release (push) Successful in 14s
Release / release (push) Successful in 14s
This commit is contained in:
1 parent
e14bbe2ba1
commit
770332728e
6 files changed
+38
-15
No files matched your search
@@ -1176,7 +1176,8 @@ Decision: `vpn-router-resolver.service` runs dnsmasq, bound to the addresses of
|
|||||||
only (`bind-interfaces`), forwarding to resolved's stub on 127.0.0.53, without `/etc/hosts` and
|
only (`bind-interfaces`), forwarding to resolved's stub on 127.0.0.53, without `/etc/hosts` and
|
||||||
without forwarding names that have no dot (`domain-needed`). resolved keeps its loopback stubs
|
without forwarding names that have no dot (`domain-needed`). resolved keeps its loopback stubs
|
||||||
only and stays the router's own resolver, with the upstream servers of the platform. Setup writes
|
only and stays the router's own resolver, with the upstream servers of the platform. Setup writes
|
||||||
`/etc/vpn-router/dnsmasq.conf` while IKEv2 or OpenVPN runs and removes the earlier drop-in.
|
`/etc/vpn-router/dnsmasq.conf` while IKEv2, OpenVPN or WireGuard runs, opens port 53 there for
|
||||||
|
their client networks, and removes the earlier drop-in.
|
||||||
|
|
||||||
Consequences: Clients resolve the names resolved resolves, Azure's internal names included, but
|
Consequences: Clients resolve the names resolved resolves, Azure's internal names included, but
|
||||||
not the router's synthesized ones. The package depends on dnsmasq-base. PowerDNS and the clients'
|
not the router's synthesized ones. The package depends on dnsmasq-base. PowerDNS and the clients'
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ must have the type shown below; an unknown section or setting is an error.
|
|||||||
| `site_to_site` | | object | | The site-to-site connections, each a name and an object of the settings below |
|
| `site_to_site` | | object | | The site-to-site connections, each a name and an object of the settings below |
|
||||||
| `p2s` | `enabled` | boolean | `false` | Road-warrior access |
|
| `p2s` | `enabled` | boolean | `false` | Road-warrior access |
|
||||||
| `p2s` | `address_pool` | string | | CIDR assigned to road-warrior clients |
|
| `p2s` | `address_pool` | string | | CIDR assigned to road-warrior clients |
|
||||||
| `p2s` | `dns_listen` | string | `internal` | The addresses of the resolver the road-warrior and OpenVPN clients are given: `internal`, `external` or `both` |
|
| `p2s` | `dns_listen` | string | `internal` | The addresses of the resolver the road-warrior, OpenVPN and WireGuard clients are given: `internal`, `external` or `both` |
|
||||||
| `wireguard` | `enabled` | boolean | `false` | WireGuard endpoint |
|
| `wireguard` | `enabled` | boolean | `false` | WireGuard endpoint |
|
||||||
| `wireguard` | `address` | string | | The router's own address on `wg0` with the prefix of the peers' network, for example `192.168.200.1/24`; the network and the broadcast address of a network larger than /31 are refused |
|
| `wireguard` | `address` | string | | The router's own address on `wg0` with the prefix of the peers' network, for example `192.168.200.1/24`; the network and the broadcast address of a network larger than /31 are refused |
|
||||||
| `wireguard` | `listen_port` | integer | `51820` | UDP port |
|
| `wireguard` | `listen_port` | integer | `51820` | UDP port |
|
||||||
@@ -872,10 +872,11 @@ deploying.
|
|||||||
which is how a deferred install is expressed. `generic` is the module to choose when the
|
which is how a deferred install is expressed. `generic` is the module to choose when the
|
||||||
router should be configured but the platform adds nothing.
|
router should be configured but the platform adds nothing.
|
||||||
|
|
||||||
The shipped modules currently add nothing. Road-warrior and OpenVPN clients are given the
|
The shipped modules currently add nothing. Road-warrior, OpenVPN and WireGuard clients are given the
|
||||||
router's addresses of `p2s.dns_listen` as their DNS servers, by default the internal address,
|
router's addresses of `p2s.dns_listen` as their DNS servers, by default the internal address,
|
||||||
|
the WireGuard clients in the configuration the Router Manager makes for a device (`DNS =`),
|
||||||
where `vpn-router-resolver.service`, dnsmasq with `/etc/vpn-router/dnsmasq.conf`, answers and
|
where `vpn-router-resolver.service`, dnsmasq with `/etc/vpn-router/dnsmasq.conf`, answers and
|
||||||
the firewall opens port 53 for the client pools, so no provider resolver has to be advertised or
|
the firewall opens port 53 for the client pools and the WireGuard network, so no provider resolver has to be advertised or
|
||||||
routed through the tunnel on any platform. dnsmasq forwards to `systemd-resolved` on
|
routed through the tunnel on any platform. dnsmasq forwards to `systemd-resolved` on
|
||||||
127.0.0.53, which keeps only its loopback stubs, and forwards no name without a dot, so the
|
127.0.0.53, which keeps only its loopback stubs, and forwards no name without a dot, so the
|
||||||
router's own host name, `_gateway` and `localhost` are not answered to the clients. The clients reach
|
router's own host name, `_gateway` and `localhost` are not answered to the clients. The clients reach
|
||||||
@@ -1310,7 +1311,7 @@ OpenVPN (`openvpn`) and WireGuard (`wireguard`), with the same token as `/users`
|
|||||||
|
|
||||||
| Request | Body | Answer |
|
| Request | Body | Answer |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `GET /remote-access` | | `{"p2s", "openvpn", "wireguard", "running", "local_fqdn", "wireguard_server"}`; `wireguard_server` is `{"public_key", "endpoint", "allowed_ips"}`: the router's WireGuard public key (`null` before setup made it), `<wan.local_fqdn>:<wireguard.listen_port>` (`null` without a name) and the networks a peer reaches through the tunnel, the router's local CIDRs and the WireGuard network |
|
| `GET /remote-access` | | `{"p2s", "openvpn", "wireguard", "running", "local_fqdn", "wireguard_server"}`; `wireguard_server` is `{"public_key", "endpoint", "allowed_ips", "dns"}`: the router's WireGuard public key (`null` before setup made it), `<wan.local_fqdn>:<wireguard.listen_port>` (`null` without a name), the networks a peer reaches through the tunnel, the router's local CIDRs and the WireGuard network, and the addresses of the remote access clients' resolver (`[]` while it does not run) |
|
||||||
| `PUT /remote-access/<system>` | any of the settings of the section `p2s`, `openvpn` or `wireguard` | the section |
|
| `PUT /remote-access/<system>` | any of the settings of the section `p2s`, `openvpn` or `wireguard` | the section |
|
||||||
|
|
||||||
`running` tells for each system whether setup runs it. IKEv2 and OpenVPN run once enabled with an
|
`running` tells for each system whether setup runs it. IKEv2 and OpenVPN run once enabled with an
|
||||||
@@ -1575,7 +1576,7 @@ missing, without the recommended bind backend.
|
|||||||
|
|
||||||
Records added with `/dns` or the Router Manager (A, AAAA, CNAME, TXT, MX, SRV, CAA) are served
|
Records added with `/dns` or the Router Manager (A, AAAA, CNAME, TXT, MX, SRV, CAA) are served
|
||||||
too, but not those of a name and type the zone's own records have. Setup writes the zone at each
|
too, but not those of a name and type the zone's own records have. Setup writes the zone at each
|
||||||
run; the SOA's serial grows only when the records change. While P2S or OpenVPN is enabled,
|
run; the SOA's serial grows only when the records change. While P2S, OpenVPN or WireGuard is enabled,
|
||||||
dnsmasq answers their clients on port 53 of the addresses of `p2s.dns_listen`, by default the
|
dnsmasq answers their clients on port 53 of the addresses of `p2s.dns_listen`, by default the
|
||||||
internal address, so `dns.listen` must not share an interface with it. The parent zone must delegate `dns.zone` to `dns.nameserver`, with glue records for
|
internal address, so `dns.listen` must not share an interface with it. The parent zone must delegate `dns.zone` to `dns.nameserver`, with glue records for
|
||||||
a name server inside the zone.
|
a name server inside the zone.
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
vpn-router (0.31.3-1) unstable; urgency=medium
|
||||||
|
|
||||||
|
fix: WireGuard clients may use the router's DNS resolver, which runs while WireGuard does, and /remote-access names its addresses for their configuration.
|
||||||
|
|
||||||
|
* The firewall opens port 53 of the remote access clients' resolver for
|
||||||
|
the WireGuard network, as it does for the IKEv2 and OpenVPN pools.
|
||||||
|
* The resolver runs while WireGuard is the only remote access enabled.
|
||||||
|
* GET /remote-access answers the resolver's addresses as
|
||||||
|
wireguard_server.dns.
|
||||||
|
* dns.listen may not share an interface with p2s.dns_listen while
|
||||||
|
WireGuard is enabled either.
|
||||||
|
|
||||||
|
-- Slawomir Koszewski <slawek@koszewscy.waw.pl> Tue, 27 Oct 2026 12:00:00 +0100
|
||||||
|
|
||||||
vpn-router (0.31.2-1) unstable; urgency=medium
|
vpn-router (0.31.2-1) unstable; urgency=medium
|
||||||
|
|
||||||
fix: dnsmasq answers the remote access clients' DNS queries, so they no longer get the router's own host name with all its addresses from systemd-resolved.
|
fix: dnsmasq answers the remote access clients' DNS queries, so they no longer get the router's own host name with all its addresses from systemd-resolved.
|
||||||
|
|||||||
@@ -243,7 +243,8 @@ def filter_block(ctx):
|
|||||||
lines += ['# OPENVPN RULES END']
|
lines += ['# OPENVPN RULES END']
|
||||||
|
|
||||||
pools = [pool for ready, pool in ((vpnrouter.p2s_ready(ctx), ctx['p2s_address_pool']),
|
pools = [pool for ready, pool in ((vpnrouter.p2s_ready(ctx), ctx['p2s_address_pool']),
|
||||||
(vpnrouter.openvpn_ready(ctx), ctx['openvpn_pool'])) if ready]
|
(vpnrouter.openvpn_ready(ctx), ctx['openvpn_pool']),
|
||||||
|
(vpnrouter.wg_ready(ctx), ctx['wg_network'])) if ready]
|
||||||
# The remote access clients' resolver answers on the addresses of p2s.dns_listen.
|
# The remote access clients' resolver answers on the addresses of p2s.dns_listen.
|
||||||
if pools and ctx['p2s_resolver_addrs']:
|
if pools and ctx['p2s_resolver_addrs']:
|
||||||
lines += ['', '# P2S DNS RULES START']
|
lines += ['', '# P2S DNS RULES START']
|
||||||
@@ -953,6 +954,7 @@ def refresh_web(cfg, ctx):
|
|||||||
'internal_address': ctx['int_addr'],
|
'internal_address': ctx['int_addr'],
|
||||||
'internal_gateway': ctx['int_gateway_ip'],
|
'internal_gateway': ctx['int_gateway_ip'],
|
||||||
'local_cidrs': ctx['local_cidr_list'],
|
'local_cidrs': ctx['local_cidr_list'],
|
||||||
|
'client_dns': ctx['p2s_resolver_addrs'] if vpnrouter.RESOLVER_CONF.exists() else [],
|
||||||
'site_to_site': [{
|
'site_to_site': [{
|
||||||
'name': connection['name'],
|
'name': connection['name'],
|
||||||
'local_identity': connection['local_id'],
|
'local_identity': connection['local_id'],
|
||||||
|
|||||||
@@ -588,11 +588,11 @@ def validate(cfg):
|
|||||||
if resolver_listen not in LISTEN_CHOICES:
|
if resolver_listen not in LISTEN_CHOICES:
|
||||||
raise ConfigError(f'p2s.dns_listen: {resolver_listen!r} is not one of {", ".join(LISTEN_CHOICES)}')
|
raise ConfigError(f'p2s.dns_listen: {resolver_listen!r} is not one of {", ".join(LISTEN_CHOICES)}')
|
||||||
sides = {'internal': {'internal'}, 'external': {'external'}, 'both': {'internal', 'external'}}
|
sides = {'internal': {'internal'}, 'external': {'external'}, 'both': {'internal', 'external'}}
|
||||||
if dns['enabled'] and (_get(cfg, 'p2s', 'enabled') or _get(cfg, 'openvpn', 'enabled')) and \
|
remote_access = any(_get(cfg, system, 'enabled') for system in ('p2s', 'openvpn', 'wireguard'))
|
||||||
sides[dns['listen']] & sides[resolver_listen]:
|
if dns['enabled'] and remote_access and sides[dns['listen']] & sides[resolver_listen]:
|
||||||
raise ConfigError(f'dns.listen: port 53 of the {" and ".join(sorted(sides[dns["listen"]] & sides[resolver_listen]))} '
|
raise ConfigError(f'dns.listen: port 53 of the {" and ".join(sorted(sides[dns["listen"]] & sides[resolver_listen]))} '
|
||||||
f'addresses is the resolver of the remote access clients (p2s.dns_listen {resolver_listen}) '
|
f'addresses is the resolver of the remote access clients (p2s.dns_listen {resolver_listen}) '
|
||||||
'while p2s.enabled or openvpn.enabled is true')
|
'while p2s.enabled, openvpn.enabled or wireguard.enabled is true')
|
||||||
|
|
||||||
for option in ('root_name', 'p2s_name', 's2s_name'):
|
for option in ('root_name', 'p2s_name', 's2s_name'):
|
||||||
value = _get(cfg, 'pki', option)
|
value = _get(cfg, 'pki', option)
|
||||||
@@ -993,6 +993,8 @@ def build_context(cfg, public_ip=None):
|
|||||||
'p2s_dns': _join(resolver_addrs, _split_list(extras['EXTRA_P2S_DNS'])),
|
'p2s_dns': _join(resolver_addrs, _split_list(extras['EXTRA_P2S_DNS'])),
|
||||||
'wg_enabled': _get(cfg, 'wireguard', 'enabled'),
|
'wg_enabled': _get(cfg, 'wireguard', 'enabled'),
|
||||||
'wg_address': _get(cfg, 'wireguard', 'address'),
|
'wg_address': _get(cfg, 'wireguard', 'address'),
|
||||||
|
'wg_network': str(ipaddress.ip_interface(_get(cfg, 'wireguard', 'address')).network)
|
||||||
|
if _get(cfg, 'wireguard', 'address') else '',
|
||||||
'wg_listen_port': _get(cfg, 'wireguard', 'listen_port'),
|
'wg_listen_port': _get(cfg, 'wireguard', 'listen_port'),
|
||||||
'wg_peers_begin': WG_PEERS_BEGIN,
|
'wg_peers_begin': WG_PEERS_BEGIN,
|
||||||
'wg_peers_end': WG_PEERS_END,
|
'wg_peers_end': WG_PEERS_END,
|
||||||
@@ -1236,7 +1238,7 @@ def configure(cfg=None, public_ip=None):
|
|||||||
# its loopback stub only, so its records of the router's own name stay on the router.
|
# its loopback stub only, so its records of the router's own name stay on the router.
|
||||||
if remove(P2S_FORWARDER):
|
if remove(P2S_FORWARDER):
|
||||||
changed.add('resolved')
|
changed.add('resolved')
|
||||||
if (p2s_ready(ctx) or openvpn_ready(ctx)) and ctx['p2s_resolver_addrs']:
|
if (p2s_ready(ctx) or openvpn_ready(ctx) or wg_ready(ctx)) and ctx['p2s_resolver_addrs']:
|
||||||
if render(env, ctx, 'dnsmasq.conf.j2', RESOLVER_CONF, 0o644):
|
if render(env, ctx, 'dnsmasq.conf.j2', RESOLVER_CONF, 0o644):
|
||||||
changed.add('resolver')
|
changed.add('resolver')
|
||||||
elif remove(RESOLVER_CONF):
|
elif remove(RESOLVER_CONF):
|
||||||
|
|||||||
@@ -888,9 +888,10 @@ def backup(method, path, body):
|
|||||||
|
|
||||||
|
|
||||||
def wireguard_server(cfg, state):
|
def wireguard_server(cfg, state):
|
||||||
"""What a WireGuard peer's configuration needs of the router: {public_key, endpoint, allowed_ips}; public_key is
|
"""What a WireGuard peer's configuration needs of the router: {public_key, endpoint, allowed_ips, dns}; public_key is
|
||||||
null before setup made the key pair, endpoint, <wan.local_fqdn>:<wireguard.listen_port>, null without a name, and
|
null before setup made the key pair, endpoint, <wan.local_fqdn>:<wireguard.listen_port>, null without a name,
|
||||||
allowed_ips the networks reached through the tunnel, the router's local CIDRs and the WireGuard network."""
|
allowed_ips the networks reached through the tunnel, the router's local CIDRs and the WireGuard network, and dns the
|
||||||
|
addresses of the remote access clients' resolver, [] while it does not run."""
|
||||||
try:
|
try:
|
||||||
public_key = vpnrouter.WG_PUBLIC_KEY.read_text(encoding='utf-8').strip() or None
|
public_key = vpnrouter.WG_PUBLIC_KEY.read_text(encoding='utf-8').strip() or None
|
||||||
except OSError:
|
except OSError:
|
||||||
@@ -901,8 +902,10 @@ def wireguard_server(cfg, state):
|
|||||||
allowed = [cidr for cidr in networks if isinstance(cidr, str)] if isinstance(networks, list) else []
|
allowed = [cidr for cidr in networks if isinstance(cidr, str)] if isinstance(networks, list) else []
|
||||||
if address:
|
if address:
|
||||||
allowed.append(str(ipaddress.ip_interface(address).network))
|
allowed.append(str(ipaddress.ip_interface(address).network))
|
||||||
|
dns = section(state, 'network').get('client_dns')
|
||||||
return {'public_key': public_key, 'endpoint': f'{fqdn}:{cfg["wireguard"]["listen_port"]}' if fqdn else None,
|
return {'public_key': public_key, 'endpoint': f'{fqdn}:{cfg["wireguard"]["listen_port"]}' if fqdn else None,
|
||||||
'allowed_ips': list(dict.fromkeys(allowed))}
|
'allowed_ips': list(dict.fromkeys(allowed)),
|
||||||
|
'dns': [address for address in dns if isinstance(address, str)] if isinstance(dns, list) else []}
|
||||||
|
|
||||||
|
|
||||||
def remote_access(state, method, path, body):
|
def remote_access(state, method, path, body):
|
||||||
|
|||||||
Reference in new issue
Block a user