summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuriy Andamasov <yuriy@vyos.io>2026-07-11 10:34:18 +0300
committerYuriy Andamasov <yuriy@vyos.io>2026-07-11 10:34:18 +0300
commit028cba2b881ba1beeaa21f6d0fd5eccf7fdc2f25 (patch)
treefeb0c9d7ee29cca29fcbe309e6b7242862d272e9
parent9e38b7190a285c7e2e3590d0381ad7892d5b0e8c (diff)
parentfa98eb5d526599021235c9cec929e99260f61d97 (diff)
downloadvyos-documentation-028cba2b881ba1beeaa21f6d0fd5eccf7fdc2f25.tar.gz
vyos-documentation-028cba2b881ba1beeaa21f6d0fd5eccf7fdc2f25.zip
Merge rolling into claude/pdf-image-conversion (combine constrained apt line: imagemagick, librsvg2-bin, poppler-utils)
🤖 Generated by [robots](https://vyos.io)
-rw-r--r--.github/workflows/docs-build.yml44
-rw-r--r--docker/Dockerfile12
-rw-r--r--docs/configexamples/dmvpn-dualhub-dualcloud.md59
-rw-r--r--docs/configuration/vpn/dmvpn.md95
4 files changed, 169 insertions, 41 deletions
diff --git a/.github/workflows/docs-build.yml b/.github/workflows/docs-build.yml
index fc9a2239..bdb9e41d 100644
--- a/.github/workflows/docs-build.yml
+++ b/.github/workflows/docs-build.yml
@@ -56,15 +56,57 @@ jobs:
cache-to: type=gha,mode=max
- name: Build HTML + PDF in in-workflow-built container
+ env:
+ # pdflatex hard-errors ("! LaTeX Error: Unicode character ... not
+ # set up for use with LaTeX") on the Devanagari etymology text in
+ # docs/introducing/history.md, and separately cannot embed some
+ # pre-existing .webp images (no BoundingBox). latexmk's force mode
+ # (-f) plus non-interactive pdflatex (-interaction=nonstopmode)
+ # makes it skip both classes of per-glyph/per-image failure and
+ # finish the document instead of halting — this is how ReadTheDocs
+ # has always built this project's PDF (verified against the live
+ # docs.vyos.io PDF: same Devanagari glyphs blanked, same ~4 images
+ # embedded out of 2000+ pages, rather than a failed build).
+ # latexmk still exits non-zero in force mode even when it produces
+ # a complete PDF, so success below is verified by checking the
+ # produced artifact directly (exact filename + page count), not the
+ # command's exit code. A bare existence/size check would let a
+ # truncated-but-large PDF (LaTeX aborting partway through) pass, so
+ # the completeness signal is pdfinfo's page count instead of file
+ # size. The §14 retry-once is gated on artifact ABSENCE: latexmk -f
+ # exits non-zero even on a fully-produced PDF, so an unconditional
+ # `|| make latexpdf` would double the ~10-min LaTeX step on every
+ # forced success. Each `|| true` only swallows that expected
+ # non-zero exit; the validation below is the real success signal.
+ LATEXMKOPTS: -f
+ LATEXOPTS: -interaction=nonstopmode
run: |
set -eu
docker run --rm -v "$PWD:/src" -w /src \
-e DOCS_VERSION_SLUG="${{ steps.matrix.outputs.slug }}" \
-e DOCS_VERSION_BRANCH="${{ github.ref_name }}" \
+ -e LATEXMKOPTS \
+ -e LATEXOPTS \
docs-build:local bash -c '
+ set -e
cd docs && make html
if [ "${{ inputs.skip_pdf }}" != "true" ]; then
- make latexpdf || make latexpdf # retry once (§14 LaTeX flakiness)
+ pdf=_build/latex/VyOS.pdf
+ make latexpdf || true
+ # retry ONLY when no artifact was produced (§14 LaTeX flakiness) —
+ # latexmk -f exits non-zero even on success, so exit code cannot gate this
+ [ -f "$pdf" ] || make latexpdf || true
+ [ -f "$pdf" ] || { echo "$pdf not produced — build genuinely failed"; exit 1; }
+ # Page-count floor as a completeness signal (a truncated forced-mode
+ # run can still leave behind a file that exists and is large). rolling
+ # is verified ~2000+ pages; 1.4/1.5 page counts have not been
+ # individually verified, so 1000 is a conservative floor intended to
+ # clear all three release branches without masking a real truncation.
+ pages=$(pdfinfo "$pdf" 2>/dev/null | awk "/^Pages:/{print \$2}")
+ case "$pages" in
+ ""|*[!0-9]*) pages=0 ;;
+ esac
+ [ "$pages" -ge 1000 ] || { echo "$pdf has $pages pages (<1000) — build genuinely failed"; exit 1; }
fi'
- name: Assemble artifact (nest under en/<slug>/, §7.1)
diff --git a/docker/Dockerfile b/docker/Dockerfile
index e0947ac7..8598b808 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -27,13 +27,15 @@ RUN apt-get update && apt-get install -y \
curl \
dos2unix
-# Image-conversion toolchain for the PDF build (sphinx.ext.imgconverter via
-# docker/im-convert.sh). Installed with --no-install-recommends (Trivy
-# DS-0029) on a separate line: the texlive line above deliberately keeps
-# recommends, which carry font packages LaTeX needs.
+# PDF-build toolchain extras, installed with --no-install-recommends (Trivy
+# DS-0029) on a separate line — the texlive line above deliberately keeps
+# recommends, which carry font packages LaTeX needs:
+# - imagemagick + librsvg2-bin: sphinx.ext.imgconverter via docker/im-convert.sh
+# - poppler-utils: pdfinfo for the docs-build workflow's page-count validation
RUN apt-get update && apt-get install -y --no-install-recommends \
imagemagick \
- librsvg2-bin
+ librsvg2-bin \
+ poppler-utils
RUN pip3 install --break-system-packages \
Sphinx \
diff --git a/docs/configexamples/dmvpn-dualhub-dualcloud.md b/docs/configexamples/dmvpn-dualhub-dualcloud.md
index 20c1a064..d98f45d9 100644
--- a/docs/configexamples/dmvpn-dualhub-dualcloud.md
+++ b/docs/configexamples/dmvpn-dualhub-dualcloud.md
@@ -6,11 +6,12 @@ lastproofread: '2024-02-21'
# DMVPN Dual HUB Dual Cloud
-This document is to describe a basic setup to build DMVPN network with two Hubs and two clouds using DMVPN Phase3.
+This document is to describe a basic setup to build DMVPN network with two Hubs
+and two clouds using DMVPN Phase3.
OSPF is used as routing protocol inside DMVPN.
-In this example we use VyOS 1.5 as HUBs and Spokes (HUB-1, HUB-2, SPOKE-2, SPOKE-3) and Cisco IOSv 15.5(3)M (SPOKE-1)
-as a Spoke.
+In this example we use VyOS 1.5 as HUBs and Spokes (HUB-1, HUB-2, SPOKE-2,
+SPOKE-3) and Cisco IOSv 15.5(3)M (SPOKE-1) as a Spoke.
## Network Topology
@@ -79,10 +80,12 @@ set protocols static route 0.0.0.0/0 next-hop 10.0.13.1
### NHRP configuration
-The next step is to configure the NHRP protocol. In a Dual cloud network, every HUB has to be configured with one GRE
-multipoint tunnel interface and every spoke has to be configured with two tunnel interfaces, one tunnel to each hub.
-In this example tunnel networks are 10.100.100.0/24 for the first cloud and 10.100.101.0/24 for the second cloud.
-But VyOS uses FRR for NHRP, that is why the tunnel address mask must be /32.
+The next step is to configure the NHRP protocol. In a Dual cloud network, every
+HUB has to be configured with one GRE multipoint tunnel interface and every
+spoke has to be configured with two tunnel interfaces, one tunnel to each hub.
+In this example tunnel networks are 10.100.100.0/24 for the first cloud
+and 10.100.101.0/24 for the second cloud. But VyOS uses FRR for NHRP, that is
+why the tunnel address mask must be /32.
HUB-1
@@ -209,8 +212,10 @@ set protocols nhrp tunnel tun101 shortcut
### Overlay configuration
-The last step is to configure the routing protocol. In this scenario, OSPF was chosen as the dynamic routing protocol.
-But you can use iBGP or eBGP. To form fast convergence it is possible to use BFD protocol.
+The last step is to configure the routing protocol. In this scenario, OSPF was
+chosen as the dynamic routing protocol.
+But you can use iBGP or eBGP. To form fast convergence it is possible to use
+BFD protocol.
HUB-1
@@ -380,10 +385,31 @@ SPOKE-1
tunnel protection ipsec profile gre_protection shared
```
+Because GRE forwarding in DMVPN is independent of IPSec, there can be conditions
+when traffic is routed over the tunnel but there is no active IPsec SA for
+a peer that would get that packets encrypted at that moment.
+That may result in unencrypted GRE leaving the router.
+
+To prevent this, drop any GRE that is not protected by an outbound IPSec policy.
+Add the following rule on the VyOS nodes (HUB-1, HUB-2, SPOKE-2 and SPOKE-3),
+and make sure it comes before any rule that permits GRE.
+
+Note that this disables unencrypted GRE on the node entirely,
+so any plain GRE tunnels without IPSec will stop working.
+If your setup requires unencrypted GRE tunnels together with DMVPN,
+you have to find a way to exempt their traffic from that filter.
+See {ref}`vpn-dmvpn` for the full explanation.
+
+```none
+set firewall ipv4 output filter rule 10 action 'drop'
+set firewall ipv4 output filter rule 10 protocol 'gre'
+set firewall ipv4 output filter rule 10 ipsec match-none-out
+```
## Monitoring
-All spokes created IPSec tunnels to Hubs, are registered on Hubs using NHRP protocol and formed adjacency in OSPF.
+All spokes created IPSec tunnels to Hubs, are registered on Hubs using NHRP
+protocol and formed adjacency in OSPF.
```none
vyos@HUB-1:~$ show vpn ipsec sa
@@ -472,7 +498,8 @@ trace to 192.168.11.2, 8 hops max, press Ctrl+C to stop
```
First trace goes via HUB but the second goes directly from SPOKE-1 to SPOKE-2.
-Now routing tables are changed. LAN networks 192.168.12.0/24 and 192.168.11.0/24 available directly via SPOKES.
+Now routing tables are changed. LAN networks 192.168.12.0/24
+and 192.168.11.0/24 available directly via SPOKES.
```none
vyos@SPOKE-2:~$ show ip route
@@ -545,8 +572,10 @@ dmvpn-NHRPVPN-tun101-child up 5m58s 5K/4K 62/51
## Summary
-If one of the Hubs loses connectivity to the Internet, the other Hub will be available and take the main role.
-This is a simple example where only one internet connection is used. But in the real world, there can be two
-connections to the Internet. In this case, there is a recommendation to build each tunnel via each Internet connection,
-choose the main cloud, and manipulate traffic via a routing protocol. It allows the creation failover on link-level
+If one of the Hubs loses connectivity to the Internet, the other Hub will be
+available and take the main role. This is a simple example where only one
+internet connection is used. But in the real world, there can be two
+connections to the Internet. In this case, there is a recommendation to build
+each tunnel via each Internet connection, choose the main cloud, and manipulate
+traffic via a routing protocol. It allows the creation failover on link-level
connections too.
diff --git a/docs/configuration/vpn/dmvpn.md b/docs/configuration/vpn/dmvpn.md
index 4dc2c85f..dc0cd4f4 100644
--- a/docs/configuration/vpn/dmvpn.md
+++ b/docs/configuration/vpn/dmvpn.md
@@ -63,8 +63,8 @@ set interfaces tunnel tun100 source-interface 'eth0'
:::{note}
The IP-address is assigned as host prefix to tunnel interface.
- NHRP will automatically create additional host routes pointing to tunnel interface
- when a connection with these hosts is established.
+ NHRP will automatically create additional host routes pointing to tunnel
+ interface when a connection with these hosts is established.
:::
The tunnel interface subnet prefix should be announced by routing protocol
@@ -114,12 +114,13 @@ then destination NBMA address (or addresses) are learnt dynamically.
* **network-id** - NHRP network id <1-4294967295>
-Enable NHRP on this interface and set the interface’s network ID. The network ID
-is used to allow creating multiple nhrp domains on a router when multiple interfaces
-are configured on the router. Interfaces configured with the same ID are part of the
-same logical NBMA network. The ID is a local only parameter and is not sent to other
-NHRP nodes and so IDs on different nodes do not need to match. When NHRP packets are
-received on an interface they are assigned to the local NHRP domain for that interface.
+Enable NHRP on this interface and set the interface’s network ID.
+The network ID is used to allow creating multiple nhrp domains on a router when
+multiple interfaces are configured on the router. Interfaces configured with
+the same ID are part of the same logical NBMA network. The ID is a local only
+parameter and is not sent to other NHRP nodes and so IDs on different nodes
+do not need to match. When NHRP packets are received on an interface they
+are assigned to the local NHRP domain for that interface.
```
```{cfgcmd} set protocols nhrp tunnel \<tunnel\> nhs tunnel-ip \<tunnel-ip\> nbma \<nbma-ip\>
@@ -127,39 +128,40 @@ received on an interface they are assigned to the local NHRP domain for that int
* **tunnel-ip** - Tunnel ip address in format **x.x.x.x** or **dynamic**
* **nbma-ip** - NBMA ip address in format **x.x.x.x**
-Configure the Next Hop Server address and its NBMA address. If dynamic is specified
-then Next Hop Server can have dynamic address which maps to its NBMA address.
+Configure the Next Hop Server address and its NBMA address. If dynamic is
+specified then Next Hop Server can have dynamic address which maps to
+its NBMA address.
```
```{cfgcmd} set protocols nhrp tunnel \<tunnel\> redirect
This enable redirect replies on the NHS similar to ICMP redirects except this is
-managed by the nhrp protocol. This setting allows spokes to communicate with each
-others directly.
+managed by the nhrp protocol. This setting allows spokes to communicate with
+each others directly.
```
```{cfgcmd} set protocols nhrp tunnel \<tunnel\> registration-no-unique
-Allow the client to not set the unique flag in the NHRP packets. This is useful when
-a station has a dynamic IP address that could change over time.
+Allow the client to not set the unique flag in the NHRP packets. This is useful
+when a station has a dynamic IP address that could change over time.
```
```{cfgcmd} set protocols nhrp tunnel \<tunnel\> shortcut
-Enable shortcut (spoke-to-spoke) tunnels to allow NHC to talk to each others directly
-after establishing a connection without going through the hub.
+Enable shortcut (spoke-to-spoke) tunnels to allow NHC to talk to each others
+directly after establishing a connection without going through the hub.
```
### IPSEC configuration
-- Please refer to the {ref}`ipsec_general` documentation for the individual IPSec
- related options.
+- Please refer to the {ref}`ipsec_general` documentation for the individual
+ IPSec related options.
:::{note}
NHRP daemon based on FRR nhrpd. It controls IPSEC. That's why 'close-action'
-parameter in IKE configuration always is set to 'close' and 'dead-peer-detection action'
-always is set to 'clear'.
+parameter in IKE configuration always is set to 'close'
+and 'dead-peer-detection action' always is set to 'clear'.
:::
```{cfgcmd} set vpn ipsec profile \<profile-name\> authentication mode pre-shared-secret
@@ -188,6 +190,59 @@ Map IKE group to IPSEC profile
```
+### Protecting against unencrypted traffic leaks
+
+In DMVPN, the mGRE tunnel and the IPSec SA that protects it are handled
+independently: GRE forwarding follows the DMVPN/NHRP routing decisions on its
+own and does not depend on IPSec SAs being established.
+
+Because peers are discovered and IPSec SAs are negotiated on demand,
+there are conditions when traffic is routed over the tunnel while there is
+no active IPSec security association for a given peer—for example, while
+the SA for a newly discovered spoke is still being negotiated, or after an
+existing SA has expired.
+
+Such conditions can be short-lived, but they can also persist for a long time
+depending on the state of IPSec.
+Whenever they occur, the affected packets may leave the router as
+unencrypted GRE. This is an inherent property of running GRE and IPSec
+independently and is common to DMVPN implementations in general.
+
+To close this gap you can add a firewall rule that drops any GRE traffic that is
+not protected by an outbound IPSec policy. The `match-none-out` matcher matches
+packets leaving the router that did not match any outbound IPSec policy, so
+combined with `protocol gre` and `action drop` it discards GRE that would
+otherwise leave the router in cleartext:
+
+```none
+set firewall ipv4 output filter rule 10 action 'drop'
+set firewall ipv4 output filter rule 10 protocol 'gre'
+set firewall ipv4 output filter rule 10 ipsec match-none-out
+```
+
+:::{note}
+This rule must be evaluated before any rule that permits GRE. Give it a low rule
+number (here `rule 10`) so that it is placed ahead of any GRE-permitting rules
+in the `output` filter. Only GRE that is already protected by IPSec
+(i.e., matches an outbound IPSec policy) will then be allowed out.
+:::
+
+:::{note}
+Because this rule drops all GRE that is not protected by IPSec, it disables
+In that case, refine the rule so that it only matches the DMVPN traffic you want
+to protect (for example, by also matching on the tunnel source).
+Alternatively, you can explicitly allow traffic of known unencrypted tunnels
+by their source or destination addresses, or other criteria.
+to protect (for example, by also matching on the tunnel source).
+Alternatively, you can explicitly allow traffic of known unencrypted tunnels
+by their source or destination addresses, or other criteria.
+:::
+
+- Please refer to the {ref}`firewall-ipv4-configuration` documentation for
+details on the `set firewall ipv4 output filter rule <N> ipsec match-none-out`
+matcher and other firewall options.
+
+
## Monitoring
```{opcmd} show ip nhrp cache