Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package nebula for openSUSE:Factory checked 
in at 2026-01-22 15:13:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/nebula (Old)
 and      /work/SRC/openSUSE:Factory/.nebula.new.1928 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "nebula"

Thu Jan 22 15:13:38 2026 rev:12 rq:1328570 version:1.10.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/nebula/nebula.changes    2025-12-08 
11:55:22.273049268 +0100
+++ /work/SRC/openSUSE:Factory/.nebula.new.1928/nebula.changes  2026-01-22 
15:13:40.756988280 +0100
@@ -1,0 +2,17 @@
+Wed Jan 21 20:34:05 UTC 2026 - Richard Rahl <[email protected]>
+
+- Update to version 1.10.2:
+  * Fix panic when using use_system_route_table
+
+-------------------------------------------------------------------
+Tue Jan 20 23:44:43 UTC 2026 - Richard Rahl <[email protected]>
+
+- Update to version 1.10.1:
+  * Fix a bug where an unsafe route derived from the system route table could
+    be lost on a config reload
+  * Fix the PEM banner for ECDSA P256 public keys
+  * Fix a bug in handshake processing when a peer sends an unexpected public 
key
+  * Add a config option to control accepting recv_error packets which defaults
+    to always
+
+-------------------------------------------------------------------

Old:
----
  nebula-1.10.0.tar.gz

New:
----
  nebula-1.10.2.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ nebula.spec ++++++
--- /var/tmp/diff_new_pack.m7m864/_old  2026-01-22 15:13:41.597023287 +0100
+++ /var/tmp/diff_new_pack.m7m864/_new  2026-01-22 15:13:41.597023287 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package nebula
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           nebula
-Version:        1.10.0
+Version:        1.10.2
 Release:        0
 Summary:        A scalable overlay networking tool
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.m7m864/_old  2026-01-22 15:13:41.637024954 +0100
+++ /var/tmp/diff_new_pack.m7m864/_new  2026-01-22 15:13:41.641025121 +0100
@@ -3,7 +3,7 @@
   <service name="tar_scm" mode="manual">
     <param name="url">https://github.com/slackhq/nebula.git</param>
     <param name="scm">git</param>
-    <param name="revision">refs/tags/v1.10.0</param>
+    <param name="revision">refs/tags/v1.10.2</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="package-meta">yes</param>

++++++ nebula-1.10.0.tar.gz -> nebula-1.10.2.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.git/HEAD new/nebula-1.10.2/.git/HEAD
--- old/nebula-1.10.0/.git/HEAD 2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.git/HEAD 2026-01-21 18:42:34.000000000 +0100
@@ -1 +1 @@
-59e24b98bd4726179750071bec34b9288ba90565
+0b02d982b256dffc9c215306a2e550d8a1bd16ab
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.git/ORIG_HEAD 
new/nebula-1.10.2/.git/ORIG_HEAD
--- old/nebula-1.10.0/.git/ORIG_HEAD    2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.git/ORIG_HEAD    2026-01-21 18:42:34.000000000 +0100
@@ -1 +1 @@
-59e24b98bd4726179750071bec34b9288ba90565
+0b02d982b256dffc9c215306a2e550d8a1bd16ab
Binary files old/nebula-1.10.0/.git/index and new/nebula-1.10.2/.git/index 
differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.git/logs/HEAD 
new/nebula-1.10.2/.git/logs/HEAD
--- old/nebula-1.10.0/.git/logs/HEAD    2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.git/logs/HEAD    2026-01-21 18:42:34.000000000 +0100
@@ -1,2 +1,2 @@
-0000000000000000000000000000000000000000 
59e24b98bd4726179750071bec34b9288ba90565 user <[email protected]> 
1765020549 +0000       clone: from https://github.com/slackhq/nebula.git
-59e24b98bd4726179750071bec34b9288ba90565 
59e24b98bd4726179750071bec34b9288ba90565 user <[email protected]> 
1765020549 +0000       checkout: moving from master to refs/tags/v1.10.0
+0000000000000000000000000000000000000000 
0b02d982b256dffc9c215306a2e550d8a1bd16ab Richard Rahl 
<[email protected]> 1769027621 +0100   clone: from 
https://github.com/slackhq/nebula.git
+0b02d982b256dffc9c215306a2e550d8a1bd16ab 
0b02d982b256dffc9c215306a2e550d8a1bd16ab Richard Rahl 
<[email protected]> 1769027621 +0100   checkout: moving from 
master to refs/tags/v1.10.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.git/logs/refs/heads/master 
new/nebula-1.10.2/.git/logs/refs/heads/master
--- old/nebula-1.10.0/.git/logs/refs/heads/master       2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.git/logs/refs/heads/master       2026-01-21 
18:42:34.000000000 +0100
@@ -1 +1 @@
-0000000000000000000000000000000000000000 
59e24b98bd4726179750071bec34b9288ba90565 user <[email protected]> 
1765020549 +0000       clone: from https://github.com/slackhq/nebula.git
+0000000000000000000000000000000000000000 
0b02d982b256dffc9c215306a2e550d8a1bd16ab Richard Rahl 
<[email protected]> 1769027621 +0100   clone: from 
https://github.com/slackhq/nebula.git
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.git/logs/refs/remotes/origin/HEAD 
new/nebula-1.10.2/.git/logs/refs/remotes/origin/HEAD
--- old/nebula-1.10.0/.git/logs/refs/remotes/origin/HEAD        2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.git/logs/refs/remotes/origin/HEAD        2026-01-21 
18:42:34.000000000 +0100
@@ -1 +1 @@
-0000000000000000000000000000000000000000 
59e24b98bd4726179750071bec34b9288ba90565 user <[email protected]> 
1765020549 +0000       clone: from https://github.com/slackhq/nebula.git
+0000000000000000000000000000000000000000 
0b02d982b256dffc9c215306a2e550d8a1bd16ab Richard Rahl 
<[email protected]> 1769027621 +0100   clone: from 
https://github.com/slackhq/nebula.git
Binary files 
old/nebula-1.10.0/.git/objects/pack/pack-35317a581769d2f0ac26c1f3118761db585cb646.idx
 and 
new/nebula-1.10.2/.git/objects/pack/pack-35317a581769d2f0ac26c1f3118761db585cb646.idx
 differ
Binary files 
old/nebula-1.10.0/.git/objects/pack/pack-35317a581769d2f0ac26c1f3118761db585cb646.pack
 and 
new/nebula-1.10.2/.git/objects/pack/pack-35317a581769d2f0ac26c1f3118761db585cb646.pack
 differ
Binary files 
old/nebula-1.10.0/.git/objects/pack/pack-35317a581769d2f0ac26c1f3118761db585cb646.rev
 and 
new/nebula-1.10.2/.git/objects/pack/pack-35317a581769d2f0ac26c1f3118761db585cb646.rev
 differ
Binary files 
old/nebula-1.10.0/.git/objects/pack/pack-b033e16290a23d4f10a6c31b294a618aa79862e3.idx
 and 
new/nebula-1.10.2/.git/objects/pack/pack-b033e16290a23d4f10a6c31b294a618aa79862e3.idx
 differ
Binary files 
old/nebula-1.10.0/.git/objects/pack/pack-b033e16290a23d4f10a6c31b294a618aa79862e3.pack
 and 
new/nebula-1.10.2/.git/objects/pack/pack-b033e16290a23d4f10a6c31b294a618aa79862e3.pack
 differ
Binary files 
old/nebula-1.10.0/.git/objects/pack/pack-b033e16290a23d4f10a6c31b294a618aa79862e3.rev
 and 
new/nebula-1.10.2/.git/objects/pack/pack-b033e16290a23d4f10a6c31b294a618aa79862e3.rev
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.git/packed-refs 
new/nebula-1.10.2/.git/packed-refs
--- old/nebula-1.10.0/.git/packed-refs  2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.git/packed-refs  2026-01-21 18:42:34.000000000 +0100
@@ -2,6 +2,7 @@
 0af7e6a1ddadd96dfe967ba49e456f2c67969b1e refs/remotes/origin/add-http-pprof
 2d128a3254cef202a81c88d5d7b6922f632bcc2c refs/remotes/origin/batched-path
 e7423d39f936f801697f70516c4f5c778d912f9a refs/remotes/origin/botched-path
+43bdf9066ec7758afee3a33cbb8cf5efaa109d64 
refs/remotes/origin/cert-v2-mixed-af-unsafe-nets
 f8fe454972d740556600cf2ec9e69caf7a18ff75 refs/remotes/origin/cert-v2-more-todo
 5fa386bb703fec75a7a59ac6de9350013a52293d 
refs/remotes/origin/cert-v2-reloads-with-relay-stuff
 824cd3f0d6bc2c36a6515bc93a2c56c3fa8b90b5 refs/remotes/origin/changelog-v1.9.7
@@ -13,23 +14,23 @@
 a0f8cb209854b140410f0832559622c16d20ec5a 
refs/remotes/origin/channels_and_minimal_gsogro
 9101b62162b2f55a21e37e800cc84a18be9b5278 refs/remotes/origin/cross-stack-relay
 f597aa71e325eb52f57d2ac404862ecc79822f79 
refs/remotes/origin/cross-stack-relay-overlaps
-8e09f26165f36363862ff2c1168b973165ba6a76 
refs/remotes/origin/dependabot/github_actions/actions/checkout-6
-662a3358ee572429e0c1257712adbe71b9352b05 refs/remotes/origin/e2e-bench-update
 b418a081a8feebe5740574a43a3364578c9e1c61 refs/remotes/origin/fips140
 e25016a94692b668d4c37833e2f8a25c2fa6b874 
refs/remotes/origin/firewall-forward-table
+12bf7a154d476af976e7f6b11f9d859853e6df4a 
refs/remotes/origin/github-release-upload
 db11e2f1af19017d21ef5912a5613d48b9db3d02 refs/remotes/origin/interface-hooks
 a4b7f624da48761fff4122ab4d0aaec1deb2ef92 
refs/remotes/origin/io-uring-gso-gro-offtherails
 5ceac2b078bcb816d9d738c570a35a0a3bc33c78 refs/remotes/origin/jay.wren-dns-ctx
 2400e2392be3fcb87bf6ef3badc30ee6689ba848 refs/remotes/origin/jay.wren-lint
+97977982cb56f334db00bb67c801359c1a671188 
refs/remotes/origin/jay.wren-maybe-ipv4-6-fix
 8d4dd2648445c1482c7b35d93261d94b2132df63 
refs/remotes/origin/jay.wren-wireguard-tun
 be90e4aa0511a43f11b419eb1707eb41c3744b29 
refs/remotes/origin/jay.wren-wireguard-tun-2
+1cdc7b41494836ff21a3569a704f38e45eed5977 
refs/remotes/origin/jay.wren-wireguard-tun-3
 9c6fb08a6dd15e950731a0440691c234e4227b30 
refs/remotes/origin/make-boringcrypto-checklinkname
-59e24b98bd4726179750071bec34b9288ba90565 refs/remotes/origin/master
+0b02d982b256dffc9c215306a2e550d8a1bd16ab refs/remotes/origin/master
 d6c5c00ef75fe0b4dcac7bc57c28a009cc2bd7fe refs/remotes/origin/master-1-9-tag
-510a8912a931d578d28a7dddcb2ad5aa3bfb9f82 refs/remotes/origin/multiport
+0824035906474c6f815e4b1388b008f1ec57ea3d refs/remotes/origin/multiport
 d4aea03dd1236a032fe5508e29cf0494768ba71c refs/remotes/origin/mutex-debug
 064831cf21020d42200a9399fcf0253e4efcfa6a refs/remotes/origin/no-exit
-9cef6752c928bfcaa9bb544ad0e708c7195a635b 
refs/remotes/origin/one-nine-bart-update
 06372e12f1a2fd763c2a8a9d465f5b80511de614 
refs/remotes/origin/prometheus-static-labels
 6d5299715ebb720197f0095b8c287ff0b94928f5 refs/remotes/origin/psk
 0681195da3f68b6bcda375a2c8f31fca105fec61 refs/remotes/origin/psk-v2
@@ -38,16 +39,22 @@
 4c745e8cfe032899e6188f12e241d1adb13a3856 
refs/remotes/origin/remove-olddead-tunnels
 29157f413c727e937b73acb12035be13520f8b7f refs/remotes/origin/stinkier
 703ac81fa6cf5a8e4c2d68c91ffa87c40301425e refs/remotes/origin/stinky
+886141decfc087ffed71b22eb3a7a515634edf14 refs/remotes/origin/stream-cas-scanner
 03ab9a1208a32cb59d1f4a917ad66b1ed618d803 refs/remotes/origin/synctrace
 3583a3f7aba2cccc6f8d1a1e16338430771c5b64 refs/remotes/origin/tun-name-template
 9642afa14989ee490cda2f6e8c6ceef1ae5b11bd 
refs/remotes/origin/update-lh-on-netlink-addr
-bdfc2f5809d955d9e952e7845e3ecc615a4d48e2 refs/remotes/origin/vhost
+2c30c2edb93e9d116c3a4832684b85fc9bcde4eb refs/remotes/origin/v6hax
+71bf3744b14c0c11338b603b130e4c9f175e4f30 refs/remotes/origin/vhost
 b6c6b96c79a5c64d98fd77162c4427811e69c2e1 
refs/remotes/origin/windows_udp_buffer_setting
 f9d3d521b62aa52463d008ef4e2f2133daaa0b4c refs/tags/v1.0.0
 0312a3e4408836e2a58a08171c31d7b13ae917e2 refs/tags/v1.1.0
 ^13941aa7232765efc671c6d0d6e1a8c0d4704603
 b7e608f0d744dac34e171a3af21cc50a776f977e refs/tags/v1.10.0
 ^59e24b98bd4726179750071bec34b9288ba90565
+63208c0ab6f0879b8f1c10774582b6a2fd8f4671 refs/tags/v1.10.1
+^72a40007ea873d914a099b77916e4933befc85b6
+f16df96a63328ddfb82b03de853139b5c0ab66b2 refs/tags/v1.10.2
+^0b02d982b256dffc9c215306a2e550d8a1bd16ab
 019d573fd023224454e7dce0d68fcf4155f77b91 refs/tags/v1.2.0
 ^fb252db4a153ba7467d1d7320b3821d897640791
 14bd5487d818474fcd39a12b12789a6daeaa210a refs/tags/v1.3.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.git/refs/heads/master 
new/nebula-1.10.2/.git/refs/heads/master
--- old/nebula-1.10.0/.git/refs/heads/master    2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/.git/refs/heads/master    2026-01-21 18:42:34.000000000 
+0100
@@ -1 +1 @@
-59e24b98bd4726179750071bec34b9288ba90565
+0b02d982b256dffc9c215306a2e550d8a1bd16ab
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.github/workflows/gofmt.yml 
new/nebula-1.10.2/.github/workflows/gofmt.yml
--- old/nebula-1.10.0/.github/workflows/gofmt.yml       2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.github/workflows/gofmt.yml       2026-01-21 
18:42:34.000000000 +0100
@@ -14,7 +14,7 @@
     runs-on: ubuntu-latest
     steps:
 
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
 
     - uses: actions/setup-go@v6
       with:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.github/workflows/release.yml 
new/nebula-1.10.2/.github/workflows/release.yml
--- old/nebula-1.10.0/.github/workflows/release.yml     2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.github/workflows/release.yml     2026-01-21 
18:42:34.000000000 +0100
@@ -10,7 +10,7 @@
     name: Build Linux/BSD All
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v5
+      - uses: actions/checkout@v6
 
       - uses: actions/setup-go@v6
         with:
@@ -24,7 +24,7 @@
           mv build/*.tar.gz release
 
       - name: Upload artifacts
-        uses: actions/upload-artifact@v5
+        uses: actions/upload-artifact@v6
         with:
           name: linux-latest
           path: release
@@ -33,7 +33,7 @@
     name: Build Windows
     runs-on: windows-latest
     steps:
-      - uses: actions/checkout@v5
+      - uses: actions/checkout@v6
 
       - uses: actions/setup-go@v6
         with:
@@ -55,7 +55,7 @@
           mv dist\windows\wintun build\dist\windows\
 
       - name: Upload artifacts
-        uses: actions/upload-artifact@v5
+        uses: actions/upload-artifact@v6
         with:
           name: windows-latest
           path: build
@@ -66,7 +66,7 @@
       HAS_SIGNING_CREDS: ${{ secrets.AC_USERNAME != '' }}
     runs-on: macos-latest
     steps:
-      - uses: actions/checkout@v5
+      - uses: actions/checkout@v6
 
       - uses: actions/setup-go@v6
         with:
@@ -75,7 +75,7 @@
 
       - name: Import certificates
         if: env.HAS_SIGNING_CREDS == 'true'
-        uses: Apple-Actions/import-codesign-certs@v5
+        uses: Apple-Actions/import-codesign-certs@v6
         with:
           p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 
}}
           p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
@@ -104,7 +104,7 @@
           fi
 
       - name: Upload artifacts
-        uses: actions/upload-artifact@v5
+        uses: actions/upload-artifact@v6
         with:
           name: darwin-latest
           path: ./release/*
@@ -124,11 +124,11 @@
       # be overwritten
       - name: Checkout code
         if: ${{ env.HAS_DOCKER_CREDS == 'true' }}
-        uses: actions/checkout@v5
+        uses: actions/checkout@v6
 
       - name: Download artifacts
         if: ${{ env.HAS_DOCKER_CREDS == 'true' }}
-        uses: actions/download-artifact@v6
+        uses: actions/download-artifact@v7
         with:
           name: linux-latest
           path: artifacts
@@ -160,10 +160,10 @@
     needs: [build-linux, build-darwin, build-windows]
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v5
+      - uses: actions/checkout@v6
 
       - name: Download artifacts
-        uses: actions/download-artifact@v6
+        uses: actions/download-artifact@v7
         with:
           path: artifacts
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.github/workflows/smoke-extra.yml 
new/nebula-1.10.2/.github/workflows/smoke-extra.yml
--- old/nebula-1.10.0/.github/workflows/smoke-extra.yml 2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.github/workflows/smoke-extra.yml 2026-01-21 
18:42:34.000000000 +0100
@@ -20,7 +20,7 @@
     runs-on: ubuntu-latest
     steps:
 
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
 
     - uses: actions/setup-go@v6
       with:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.github/workflows/smoke.yml 
new/nebula-1.10.2/.github/workflows/smoke.yml
--- old/nebula-1.10.0/.github/workflows/smoke.yml       2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.github/workflows/smoke.yml       2026-01-21 
18:42:34.000000000 +0100
@@ -18,7 +18,7 @@
     runs-on: ubuntu-latest
     steps:
 
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
 
     - uses: actions/setup-go@v6
       with:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/.github/workflows/test.yml 
new/nebula-1.10.2/.github/workflows/test.yml
--- old/nebula-1.10.0/.github/workflows/test.yml        2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/.github/workflows/test.yml        2026-01-21 
18:42:34.000000000 +0100
@@ -18,7 +18,7 @@
     runs-on: ubuntu-latest
     steps:
 
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
 
     - uses: actions/setup-go@v6
       with:
@@ -45,7 +45,7 @@
     - name: Build test mobile
       run: make build-test-mobile
 
-    - uses: actions/upload-artifact@v5
+    - uses: actions/upload-artifact@v6
       with:
         name: e2e packet flow linux-latest
         path: e2e/mermaid/linux-latest
@@ -56,7 +56,7 @@
     runs-on: ubuntu-latest
     steps:
 
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
 
     - uses: actions/setup-go@v6
       with:
@@ -77,7 +77,7 @@
     runs-on: ubuntu-latest
     steps:
 
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
 
     - uses: actions/setup-go@v6
       with:
@@ -98,7 +98,7 @@
         os: [windows-latest, macos-latest]
     steps:
 
-    - uses: actions/checkout@v5
+    - uses: actions/checkout@v6
 
     - uses: actions/setup-go@v6
       with:
@@ -125,7 +125,7 @@
     - name: End 2 end
       run: make e2evv
 
-    - uses: actions/upload-artifact@v5
+    - uses: actions/upload-artifact@v6
       with:
         name: e2e packet flow ${{ matrix.os }}
         path: e2e/mermaid/${{ matrix.os }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/CHANGELOG.md 
new/nebula-1.10.2/CHANGELOG.md
--- old/nebula-1.10.0/CHANGELOG.md      2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/CHANGELOG.md      2026-01-21 18:42:34.000000000 +0100
@@ -7,6 +7,37 @@
 
 ## [Unreleased]
 
+## [1.10.2] - 2026-01-21
+
+### Fixed
+
+- Fix panic when using `use_system_route_table` that was introduced in 
v1.10.1. (#1580)
+
+### Changed
+
+- Fix some typos in comments. (#1582)
+- Dependency updates. (#1581)
+
+## [1.10.1] - 2026-01-16
+
+See the [v1.10.1](https://github.com/slackhq/nebula/milestone/26?closed=1) 
milestone for a complete list of changes.
+
+### Fixed
+
+- Fix a bug where an unsafe route derived from the system route table could be 
lost on a config reload. (#1573)
+- Fix the PEM banner for ECDSA P256 public keys. (#1552)
+- Fix a regression on Windows from 1.9.x where nebula could fall back to a 
less performant UDP listener if 
+  non-critical ioctls failed. (#1568)
+- Fix a bug in handshake processing when a peer sends an unexpected public 
key. (#1566)
+
+### Added
+
+- Add a config option to control accepting `recv_error` packets which defaults 
to `always`. (#1569)
+
+### Changed
+
+- Various dependency updates. (#1541, #1549, #1550, #1557, #1558, #1560, 
#1561, #1570, #1571)
+
 ## [1.10.0] - 2025-12-04
 
 See the [v1.10.0](https://github.com/slackhq/nebula/milestone/16?closed=1) 
milestone for a complete list of changes.
@@ -744,7 +775,9 @@
 
 - Initial public release.
 
-[Unreleased]: https://github.com/slackhq/nebula/compare/v1.10.0...HEAD
+[Unreleased]: https://github.com/slackhq/nebula/compare/v1.10.2...HEAD
+[1.10.2]: https://github.com/slackhq/nebula/releases/tag/v1.10.2
+[1.10.1]: https://github.com/slackhq/nebula/releases/tag/v1.10.1
 [1.10.0]: https://github.com/slackhq/nebula/releases/tag/v1.10.0
 [1.9.7]: https://github.com/slackhq/nebula/releases/tag/v1.9.7
 [1.9.6]: https://github.com/slackhq/nebula/releases/tag/v1.9.6
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/cert.go 
new/nebula-1.10.2/cert/cert.go
--- old/nebula-1.10.0/cert/cert.go      2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/cert/cert.go      2026-01-21 18:42:34.000000000 +0100
@@ -119,6 +119,7 @@
 // Recombine will attempt to unmarshal a certificate received in a handshake.
 // Handshakes save space by placing the peers public key in a different part 
of the packet, we have to
 // reassemble the actual certificate structure with that in mind.
+// Implementations MUST assert the public key is not in the raw certificate 
bytes if the passed in public key is not empty.
 func Recombine(v Version, rawCertBytes, publicKey []byte, curve Curve) 
(Certificate, error) {
        if publicKey == nil {
                return nil, ErrNoPeerStaticKey
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/cert_v1.go 
new/nebula-1.10.2/cert/cert_v1.go
--- old/nebula-1.10.0/cert/cert_v1.go   2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/cert/cert_v1.go   2026-01-21 18:42:34.000000000 +0100
@@ -426,7 +426,7 @@
                        unsafeNetworks: make([]netip.Prefix, 
len(rc.Details.Subnets)/2),
                        notBefore:      time.Unix(rc.Details.NotBefore, 0),
                        notAfter:       time.Unix(rc.Details.NotAfter, 0),
-                       publicKey:      make([]byte, len(rc.Details.PublicKey)),
+                       publicKey:      nil,
                        isCA:           rc.Details.IsCA,
                        curve:          rc.Details.Curve,
                },
@@ -437,12 +437,19 @@
        copy(nc.details.groups, rc.Details.Groups)
        nc.details.issuer = hex.EncodeToString(rc.Details.Issuer)
 
+       // If a public key is passed in as an argument, the certificate pubkey 
must be empty
+       // and the passed-in pubkey copied into the cert.
        if len(publicKey) > 0 {
-               nc.details.publicKey = publicKey
+               if len(rc.Details.PublicKey) != 0 {
+                       return nil, ErrCertPubkeyPresent
+               }
+               nc.details.publicKey = make([]byte, len(publicKey))
+               copy(nc.details.publicKey, publicKey)
+       } else {
+               nc.details.publicKey = make([]byte, len(rc.Details.PublicKey))
+               copy(nc.details.publicKey, rc.Details.PublicKey)
        }
 
-       copy(nc.details.publicKey, rc.Details.PublicKey)
-
        var ip netip.Addr
        for i, rawIp := range rc.Details.Ips {
                if i%2 == 0 {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/cert_v1_test.go 
new/nebula-1.10.2/cert/cert_v1_test.go
--- old/nebula-1.10.0/cert/cert_v1_test.go      2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/cert/cert_v1_test.go      2026-01-21 18:42:34.000000000 
+0100
@@ -62,6 +62,62 @@
        assert.Equal(t, nc.Groups(), nc2.Groups())
 }
 
+func TestCertificateV1_Unmarshal(t *testing.T) {
+       t.Parallel()
+       before := time.Now().Add(time.Second * -60).Round(time.Second)
+       after := time.Now().Add(time.Second * 60).Round(time.Second)
+       pubKey := []byte("1234567890abcedfghij1234567890ab")
+       invalidPubkey := []byte("00000000000000000000000000000000")
+
+       nc := certificateV1{
+               details: detailsV1{
+                       name: "testing",
+                       networks: []netip.Prefix{
+                               mustParsePrefixUnmapped("10.1.1.1/24"),
+                               mustParsePrefixUnmapped("10.1.1.2/16"),
+                       },
+                       unsafeNetworks: []netip.Prefix{
+                               mustParsePrefixUnmapped("9.1.1.2/24"),
+                               mustParsePrefixUnmapped("9.1.1.3/16"),
+                       },
+                       groups:    []string{"test-group1", "test-group2", 
"test-group3"},
+                       notBefore: before,
+                       notAfter:  after,
+                       publicKey: pubKey,
+                       isCA:      false,
+                       issuer:    "1234567890abcedfghij1234567890ab",
+               },
+               signature: []byte("1234567890abcedfghij1234567890ab"),
+       }
+
+       // This certificate has a pubkey included
+       certWithPubkey, err := nc.Marshal()
+       require.NoError(t, err)
+
+       // This certificate is missing the pubkey section
+       certWithoutPubkey, err := nc.MarshalForHandshakes()
+       require.NoError(t, err)
+
+       // Cert has no pubkey and no pubkey passed in must fail to validate
+       isNil, err := unmarshalCertificateV1(certWithoutPubkey, nil)
+       require.Error(t, err)
+
+       // Cert has different pubkey than one passed in must fail
+       isNil, err = unmarshalCertificateV1(certWithPubkey, invalidPubkey)
+       require.Nil(t, isNil)
+       require.Error(t, err)
+
+       // Cert has pubkey and no pubkey argument works ok
+       _, err = unmarshalCertificateV1(certWithPubkey, nil)
+       require.NoError(t, err)
+
+       // Cert has no pubkey and valid, correctly signed pubkey passed in
+       nc2, err := unmarshalCertificateV1(certWithoutPubkey, pubKey)
+       require.NoError(t, err)
+
+       assert.Equal(t, pubKey, nc2.PublicKey())
+}
+
 func TestCertificateV1_PublicKeyPem(t *testing.T) {
        t.Parallel()
        before := time.Now().Add(time.Second * -60).Round(time.Second)
@@ -100,12 +156,18 @@
 AAAAAAAAAAAAAAAAAAAAAAA=
 -----END NEBULA P256 PUBLIC KEY-----
 `)
+
+       pubP256KeyPemCA := []byte(`-----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAA=
+-----END NEBULA ECDSA P256 PUBLIC KEY-----
+`)
        pubP256Key, _, _, err := UnmarshalPublicKeyFromPEM(pubP256KeyPem)
        require.NoError(t, err)
        nc.details.curve = Curve_P256
        nc.details.publicKey = pubP256Key
        assert.Equal(t, Curve_P256, nc.Curve())
-       assert.Equal(t, string(nc.MarshalPublicKeyPEM()), string(pubP256KeyPem))
+       assert.Equal(t, string(nc.MarshalPublicKeyPEM()), 
string(pubP256KeyPemCA))
        assert.True(t, nc.IsCA())
 
        nc.details.isCA = false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/cert_v2.go 
new/nebula-1.10.2/cert/cert_v2.go
--- old/nebula-1.10.0/cert/cert_v2.go   2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/cert/cert_v2.go   2026-01-21 18:42:34.000000000 +0100
@@ -592,7 +592,13 @@
        // Maybe grab the public key
        var rawPublicKey cryptobyte.String
        if len(publicKey) > 0 {
-               rawPublicKey = publicKey
+               // If a public key is passed in, then the handshake certificate 
must
+               // not have a public key present
+               if input.PeekASN1Tag(TagCertPublicKey) {
+                       return nil, ErrCertPubkeyPresent
+               }
+               rawPublicKey = make(cryptobyte.String, len(publicKey))
+               copy(rawPublicKey, publicKey)
        } else if !input.ReadOptionalASN1(&rawPublicKey, nil, TagCertPublicKey) 
{
                return nil, ErrBadFormat
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/cert_v2_test.go 
new/nebula-1.10.2/cert/cert_v2_test.go
--- old/nebula-1.10.0/cert/cert_v2_test.go      2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/cert/cert_v2_test.go      2026-01-21 18:42:34.000000000 
+0100
@@ -76,6 +76,58 @@
        assert.Equal(t, nc.Groups(), nc2.Groups())
 }
 
+func TestCertificateV2_Unmarshal(t *testing.T) {
+       t.Parallel()
+       before := time.Now().Add(time.Second * -60).Round(time.Second)
+       after := time.Now().Add(time.Second * 60).Round(time.Second)
+       pubKey := []byte("1234567890abcedfghij1234567890ab")
+
+       nc := certificateV2{
+               details: detailsV2{
+                       name: "testing",
+                       networks: []netip.Prefix{
+                               mustParsePrefixUnmapped("10.1.1.2/16"),
+                               mustParsePrefixUnmapped("10.1.1.1/24"),
+                       },
+                       unsafeNetworks: []netip.Prefix{
+                               mustParsePrefixUnmapped("9.1.1.3/16"),
+                               mustParsePrefixUnmapped("9.1.1.2/24"),
+                       },
+                       groups:    []string{"test-group1", "test-group2", 
"test-group3"},
+                       notBefore: before,
+                       notAfter:  after,
+                       isCA:      false,
+                       issuer:    "1234567890abcdef1234567890abcdef",
+               },
+               signature: []byte("1234567890abcdef1234567890abcdef"),
+               publicKey: pubKey,
+       }
+
+       db, err := nc.details.Marshal()
+       require.NoError(t, err)
+       nc.rawDetails = db
+
+       certWithPubkey, err := nc.Marshal()
+       require.NoError(t, err)
+       //t.Log("Cert size:", len(b))
+       certWithoutPubkey, err := nc.MarshalForHandshakes()
+       require.NoError(t, err)
+
+       // Cert must not have a pubkey if one is passed in as an argument
+       _, err = unmarshalCertificateV2(certWithPubkey, pubKey, 
Curve_CURVE25519)
+       require.ErrorIs(t, err, ErrCertPubkeyPresent)
+
+       // Certs must have pubkeys
+       _, err = unmarshalCertificateV2(certWithoutPubkey, nil, 
Curve_CURVE25519)
+       require.ErrorIs(t, err, ErrBadFormat)
+
+       // Ensure proper unmarshal if a pubkey is passed in
+       nc2, err := unmarshalCertificateV2(certWithoutPubkey, pubKey, 
Curve_CURVE25519)
+       require.NoError(t, err)
+
+       assert.Equal(t, nc.PublicKey(), nc2.PublicKey())
+}
+
 func TestCertificateV2_PublicKeyPem(t *testing.T) {
        t.Parallel()
        before := time.Now().Add(time.Second * -60).Round(time.Second)
@@ -114,12 +166,19 @@
 AAAAAAAAAAAAAAAAAAAAAAA=
 -----END NEBULA P256 PUBLIC KEY-----
 `)
+
+       pubP256KeyPemCA := []byte(`-----BEGIN NEBULA ECDSA P256 PUBLIC KEY-----
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAA=
+-----END NEBULA ECDSA P256 PUBLIC KEY-----
+`)
+
        pubP256Key, _, _, err := UnmarshalPublicKeyFromPEM(pubP256KeyPem)
        require.NoError(t, err)
        nc.curve = Curve_P256
        nc.publicKey = pubP256Key
        assert.Equal(t, Curve_P256, nc.Curve())
-       assert.Equal(t, string(nc.MarshalPublicKeyPEM()), string(pubP256KeyPem))
+       assert.Equal(t, string(nc.MarshalPublicKeyPEM()), 
string(pubP256KeyPemCA))
        assert.True(t, nc.IsCA())
 
        nc.details.isCA = false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/crypto_test.go 
new/nebula-1.10.2/cert/crypto_test.go
--- old/nebula-1.10.0/cert/crypto_test.go       2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/cert/crypto_test.go       2026-01-21 18:42:34.000000000 
+0100
@@ -79,7 +79,7 @@
        assert.Nil(t, k)
        assert.Equal(t, rest, invalidPem)
 
-       // Fail due to ivalid PEM format, because
+       // Fail due to invalid PEM format, because
        // it's missing the requisite pre-encapsulation boundary.
        curve, k, rest, err = DecryptAndUnmarshalSigningPrivateKey(passphrase, 
rest)
        require.EqualError(t, err, "input did not contain a valid PEM encoded 
block")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/errors.go 
new/nebula-1.10.2/cert/errors.go
--- old/nebula-1.10.0/cert/errors.go    2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/cert/errors.go    2026-01-21 18:42:34.000000000 +0100
@@ -21,6 +21,7 @@
        ErrPrivateKeyEncrypted        = errors.New("private key must be 
decrypted")
        ErrCaNotFound                 = errors.New("could not find ca for the 
certificate")
        ErrUnknownVersion             = errors.New("certificate version 
unrecognized")
+       ErrCertPubkeyPresent          = errors.New("certificate has unexpected 
pubkey present")
 
        ErrInvalidPEMBlock                   = errors.New("input did not 
contain a valid PEM encoded block")
        ErrInvalidPEMCertificateBanner       = errors.New("bytes did not 
contain a proper certificate banner")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/pem.go 
new/nebula-1.10.2/cert/pem.go
--- old/nebula-1.10.0/cert/pem.go       2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/cert/pem.go       2026-01-21 18:42:34.000000000 +0100
@@ -86,7 +86,7 @@
        case Curve_CURVE25519:
                return pem.EncodeToMemory(&pem.Block{Type: 
Ed25519PublicKeyBanner, Bytes: b})
        case Curve_P256:
-               return pem.EncodeToMemory(&pem.Block{Type: P256PublicKeyBanner, 
Bytes: b})
+               return pem.EncodeToMemory(&pem.Block{Type: 
ECDSAP256PublicKeyBanner, Bytes: b})
        default:
                return nil
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cert/pem_test.go 
new/nebula-1.10.2/cert/pem_test.go
--- old/nebula-1.10.0/cert/pem_test.go  2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/cert/pem_test.go  2026-01-21 18:42:34.000000000 +0100
@@ -44,7 +44,7 @@
        assert.Equal(t, rest, invalidPem)
        require.EqualError(t, err, "bytes did not contain a proper certificate 
banner")
 
-       // Fail due to ivalid PEM format, because
+       // Fail due to invalid PEM format, because
        // it's missing the requisite pre-encapsulation boundary.
        cert, rest, err = UnmarshalCertificateFromPEM(rest)
        assert.Nil(t, cert)
@@ -106,7 +106,7 @@
        assert.Equal(t, rest, invalidPem)
        require.EqualError(t, err, "bytes did not contain a proper 
Ed25519/ECDSA private key banner")
 
-       // Fail due to ivalid PEM format, because
+       // Fail due to invalid PEM format, because
        // it's missing the requisite pre-encapsulation boundary.
        k, rest, curve, err = UnmarshalSigningPrivateKeyFromPEM(rest)
        assert.Nil(t, k)
@@ -168,7 +168,7 @@
        assert.Equal(t, rest, invalidPem)
        require.EqualError(t, err, "bytes did not contain a proper private key 
banner")
 
-       // Fail due to ivalid PEM format, because
+       // Fail due to invalid PEM format, because
        // it's missing the requisite pre-encapsulation boundary.
        k, rest, curve, err = UnmarshalPrivateKeyFromPEM(rest)
        assert.Nil(t, k)
@@ -221,7 +221,7 @@
        require.EqualError(t, err, "bytes did not contain a proper public key 
banner")
        assert.Equal(t, rest, invalidPem)
 
-       // Fail due to ivalid PEM format, because
+       // Fail due to invalid PEM format, because
        // it's missing the requisite pre-encapsulation boundary.
        k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
        assert.Nil(t, k)
@@ -299,7 +299,7 @@
        require.EqualError(t, err, "bytes did not contain a proper public key 
banner")
        assert.Equal(t, rest, invalidPem)
 
-       // Fail due to ivalid PEM format, because
+       // Fail due to invalid PEM format, because
        // it's missing the requisite pre-encapsulation boundary.
        k, rest, curve, err = UnmarshalPublicKeyFromPEM(rest)
        assert.Nil(t, k)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/cmd/nebula-cert/ca_test.go 
new/nebula-1.10.2/cmd/nebula-cert/ca_test.go
--- old/nebula-1.10.0/cmd/nebula-cert/ca_test.go        2025-12-04 
20:42:31.000000000 +0100
+++ new/nebula-1.10.2/cmd/nebula-cert/ca_test.go        2026-01-21 
18:42:34.000000000 +0100
@@ -200,7 +200,7 @@
        assert.Empty(t, b)
        assert.Len(t, lKey, 64)
 
-       // test when reading passsword results in an error
+       // test when reading password results in an error
        os.Remove(keyF.Name())
        os.Remove(crtF.Name())
        ob.Reset()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/e2e/helpers_test.go 
new/nebula-1.10.2/e2e/helpers_test.go
--- old/nebula-1.10.0/e2e/helpers_test.go       2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/e2e/helpers_test.go       2026-01-21 18:42:34.000000000 
+0100
@@ -304,7 +304,7 @@
        assertUdpPacket(t, []byte("Hello from A"), aPacket, vpnIpA, vpnIpB, 90, 
80)
 }
 
-func assertHostInfoPair(t *testing.T, addrA, addrB netip.AddrPort, vpnNetsA, 
vpnNetsB []netip.Prefix, controlA, controlB *nebula.Control) {
+func assertHostInfoPair(t testing.TB, addrA, addrB netip.AddrPort, vpnNetsA, 
vpnNetsB []netip.Prefix, controlA, controlB *nebula.Control) {
        // Get both host infos
        //TODO: CERT-V2 we may want to loop over each vpnAddr and assert all 
the things
        hBinA := controlA.GetHostInfoByVpnAddr(vpnNetsB[0].Addr(), false)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/examples/config.yml 
new/nebula-1.10.2/examples/config.yml
--- old/nebula-1.10.0/examples/config.yml       2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/examples/config.yml       2026-01-21 18:42:34.000000000 
+0100
@@ -144,6 +144,10 @@
   # valid values: always, never, private
   # This setting is reloadable.
   #send_recv_error: always
+  # Similar to send_recv_error, this option lets you configure if you want to 
accept "recv_error" packets from remote hosts.
+  # valid values: always, never, private
+  # This setting is reloadable.
+  #accept_recv_error: always
   # The so_sock option is a Linux-specific feature that allows all outgoing 
Nebula packets to be tagged with a specific identifier.
   # This tagging enables IP rule-based filtering. For example, it supports 
0.0.0.0/0 unsafe_routes,
   # allowing for more precise routing decisions based on the packet tags. 
Default is 0 meaning no mark is set.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/go.mod new/nebula-1.10.2/go.mod
--- old/nebula-1.10.0/go.mod    2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/go.mod    2026-01-21 18:42:34.000000000 +0100
@@ -12,27 +12,27 @@
        github.com/gogo/protobuf v1.3.2
        github.com/google/gopacket v1.1.19
        github.com/kardianos/service v1.2.4
-       github.com/miekg/dns v1.1.68
+       github.com/miekg/dns v1.1.70
        github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b
        github.com/nbrownus/go-metrics-prometheus 
v0.0.0-20210712211119-974a6260965f
        github.com/prometheus/client_golang v1.23.2
        github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
-       github.com/sirupsen/logrus v1.9.3
+       github.com/sirupsen/logrus v1.9.4
        github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
        github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6
        github.com/stretchr/testify v1.11.1
        github.com/vishvananda/netlink v1.3.1
        go.yaml.in/yaml/v3 v3.0.4
-       golang.org/x/crypto v0.45.0
+       golang.org/x/crypto v0.47.0
        golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
-       golang.org/x/net v0.47.0
-       golang.org/x/sync v0.18.0
-       golang.org/x/sys v0.38.0
-       golang.org/x/term v0.37.0
+       golang.org/x/net v0.49.0
+       golang.org/x/sync v0.19.0
+       golang.org/x/sys v0.40.0
+       golang.org/x/term v0.39.0
        golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
        golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b
        golang.zx2c4.com/wireguard/windows v0.5.3
-       google.golang.org/protobuf v1.36.10
+       google.golang.org/protobuf v1.36.11
        gopkg.in/yaml.v3 v3.0.1
        gvisor.dev/gvisor v0.0.0-20240423190808-9d7a357edefe
 )
@@ -49,7 +49,7 @@
        github.com/prometheus/procfs v0.16.1 // indirect
        github.com/vishvananda/netns v0.0.5 // indirect
        go.yaml.in/yaml/v2 v2.4.2 // indirect
-       golang.org/x/mod v0.24.0 // indirect
+       golang.org/x/mod v0.31.0 // indirect
        golang.org/x/time v0.5.0 // indirect
-       golang.org/x/tools v0.33.0 // indirect
+       golang.org/x/tools v0.40.0 // indirect
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/go.sum new/nebula-1.10.2/go.sum
--- old/nebula-1.10.0/go.sum    2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/go.sum    2026-01-21 18:42:34.000000000 +0100
@@ -83,8 +83,8 @@
 github.com/kylelemons/godebug v1.1.0 
h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 github.com/kylelemons/godebug v1.1.0/go.mod 
h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod 
h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
-github.com/miekg/dns v1.1.68/go.mod 
h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
+github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA=
+github.com/miekg/dns v1.1.70/go.mod 
h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
 github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b 
h1:J/AzCvg5z0Hn1rqZUJjpbzALUmkKX0Zwbc/i4fw7Sfk=
 github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b/go.mod 
h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod 
h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -131,8 +131,8 @@
 github.com/sirupsen/logrus v1.2.0/go.mod 
h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod 
h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod 
h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.9.3 
h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod 
h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.4 
h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
+github.com/sirupsen/logrus v1.9.4/go.mod 
h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e 
h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod 
h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
 github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 
h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
@@ -162,16 +162,16 @@
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod 
h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
-golang.org/x/crypto v0.45.0/go.mod 
h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
+golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
+golang.org/x/crypto v0.47.0/go.mod 
h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
 golang.org/x/exp v0.0.0-20230725093048-515e97ebf090 
h1:Di6/M8l0O2lCLc6VVRWhgCiApHV8MnQurBnFSHsQtNY=
 golang.org/x/exp v0.0.0-20230725093048-515e97ebf090/go.mod 
h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod 
h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod 
h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
-golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
+golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
+golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod 
h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -182,8 +182,8 @@
 golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod 
h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod 
h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod 
h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
-golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
+golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
+golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod 
h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -191,8 +191,8 @@
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
-golang.org/x/sync v0.18.0/go.mod 
h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod 
h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -206,14 +206,13 @@
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
-golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
+golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
-golang.org/x/term v0.37.0/go.mod 
h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
+golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
+golang.org/x/term v0.39.0/go.mod 
h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -224,8 +223,8 @@
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod 
h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod 
h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
-golang.org/x/tools v0.33.0/go.mod 
h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
+golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
+golang.org/x/tools v0.40.0/go.mod 
h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -244,8 +243,8 @@
 google.golang.org/protobuf v1.21.0/go.mod 
h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
 google.golang.org/protobuf v1.23.0/go.mod 
h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod 
h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.36.10 
h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
-google.golang.org/protobuf v1.36.10/go.mod 
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
+google.golang.org/protobuf v1.36.11 
h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod 
h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod 
h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/handshake_ix.go 
new/nebula-1.10.2/handshake_ix.go
--- old/nebula-1.10.0/handshake_ix.go   2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/handshake_ix.go   2026-01-21 18:42:34.000000000 +0100
@@ -1,6 +1,7 @@
 package nebula
 
 import (
+       "bytes"
        "net/netip"
        "time"
 
@@ -166,6 +167,13 @@
                return
        }
 
+       if !bytes.Equal(remoteCert.Certificate.PublicKey(), ci.H.PeerStatic()) {
+               f.l.WithField("from", via).
+                       WithField("handshake", m{"stage": 1, "style": 
"ix_psk0"}).
+                       WithField("cert", remoteCert).Info("public key mismatch 
between certificate and handshake")
+               return
+       }
+
        if remoteCert.Certificate.Version() != ci.myCert.Version() {
                // We started off using the wrong certificate version, lets see 
if we can match the version that was sent to us
                myCertOtherVersion := 
cs.getCertificate(remoteCert.Certificate.Version())
@@ -535,6 +543,12 @@
                e.Info("Invalid certificate from host")
                return true
        }
+       if !bytes.Equal(remoteCert.Certificate.PublicKey(), ci.H.PeerStatic()) {
+               f.l.WithField("from", via).
+                       WithField("handshake", m{"stage": 2, "style": 
"ix_psk0"}).
+                       WithField("cert", remoteCert).Info("public key mismatch 
between certificate and handshake")
+               return true
+       }
 
        if len(remoteCert.Certificate.Networks()) == 0 {
                f.l.WithError(err).WithField("from", via).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/interface.go 
new/nebula-1.10.2/interface.go
--- old/nebula-1.10.0/interface.go      2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/interface.go      2026-01-21 18:42:34.000000000 +0100
@@ -77,7 +77,8 @@
        reQueryEvery    atomic.Uint32
        reQueryWait     atomic.Int64
 
-       sendRecvErrorConfig sendRecvErrorConfig
+       sendRecvErrorConfig   recvErrorConfig
+       acceptRecvErrorConfig recvErrorConfig
 
        // rebindCount is used to decide if an active tunnel should trigger a 
punch notification through a lighthouse
        rebindCount int8
@@ -110,34 +111,34 @@
        GetCertState() *CertState
 }
 
-type sendRecvErrorConfig uint8
+type recvErrorConfig uint8
 
 const (
-       sendRecvErrorAlways sendRecvErrorConfig = iota
-       sendRecvErrorNever
-       sendRecvErrorPrivate
+       recvErrorAlways recvErrorConfig = iota
+       recvErrorNever
+       recvErrorPrivate
 )
 
-func (s sendRecvErrorConfig) ShouldSendRecvError(endpoint netip.AddrPort) bool 
{
+func (s recvErrorConfig) ShouldRecvError(endpoint netip.AddrPort) bool {
        switch s {
-       case sendRecvErrorPrivate:
+       case recvErrorPrivate:
                return endpoint.Addr().IsPrivate()
-       case sendRecvErrorAlways:
+       case recvErrorAlways:
                return true
-       case sendRecvErrorNever:
+       case recvErrorNever:
                return false
        default:
-               panic(fmt.Errorf("invalid sendRecvErrorConfig value: %d", s))
+               panic(fmt.Errorf("invalid recvErrorConfig value: %d", s))
        }
 }
 
-func (s sendRecvErrorConfig) String() string {
+func (s recvErrorConfig) String() string {
        switch s {
-       case sendRecvErrorAlways:
+       case recvErrorAlways:
                return "always"
-       case sendRecvErrorNever:
+       case recvErrorNever:
                return "never"
-       case sendRecvErrorPrivate:
+       case recvErrorPrivate:
                return "private"
        default:
                return fmt.Sprintf("invalid(%d)", s)
@@ -312,6 +313,7 @@
 func (f *Interface) RegisterConfigChangeCallbacks(c *config.C) {
        c.RegisterReloadCallback(f.reloadFirewall)
        c.RegisterReloadCallback(f.reloadSendRecvError)
+       c.RegisterReloadCallback(f.reloadAcceptRecvError)
        c.RegisterReloadCallback(f.reloadDisconnectInvalid)
        c.RegisterReloadCallback(f.reloadMisc)
 
@@ -375,16 +377,16 @@
 
                switch stringValue {
                case "always":
-                       f.sendRecvErrorConfig = sendRecvErrorAlways
+                       f.sendRecvErrorConfig = recvErrorAlways
                case "never":
-                       f.sendRecvErrorConfig = sendRecvErrorNever
+                       f.sendRecvErrorConfig = recvErrorNever
                case "private":
-                       f.sendRecvErrorConfig = sendRecvErrorPrivate
+                       f.sendRecvErrorConfig = recvErrorPrivate
                default:
                        if c.GetBool("listen.send_recv_error", true) {
-                               f.sendRecvErrorConfig = sendRecvErrorAlways
+                               f.sendRecvErrorConfig = recvErrorAlways
                        } else {
-                               f.sendRecvErrorConfig = sendRecvErrorNever
+                               f.sendRecvErrorConfig = recvErrorNever
                        }
                }
 
@@ -393,6 +395,30 @@
        }
 }
 
+func (f *Interface) reloadAcceptRecvError(c *config.C) {
+       if c.InitialLoad() || c.HasChanged("listen.accept_recv_error") {
+               stringValue := c.GetString("listen.accept_recv_error", "always")
+
+               switch stringValue {
+               case "always":
+                       f.acceptRecvErrorConfig = recvErrorAlways
+               case "never":
+                       f.acceptRecvErrorConfig = recvErrorNever
+               case "private":
+                       f.acceptRecvErrorConfig = recvErrorPrivate
+               default:
+                       if c.GetBool("listen.accept_recv_error", true) {
+                               f.acceptRecvErrorConfig = recvErrorAlways
+                       } else {
+                               f.acceptRecvErrorConfig = recvErrorNever
+                       }
+               }
+
+               f.l.WithField("acceptRecvError", 
f.acceptRecvErrorConfig.String()).
+                       Info("Loaded accept_recv_error config")
+       }
+}
+
 func (f *Interface) reloadMisc(c *config.C) {
        if c.HasChanged("counters.try_promote") {
                n := c.GetUint32("counters.try_promote", defaultPromoteEvery)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/main.go new/nebula-1.10.2/main.go
--- old/nebula-1.10.0/main.go   2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/main.go   2026-01-21 18:42:34.000000000 +0100
@@ -265,6 +265,7 @@
                ifce.RegisterConfigChangeCallbacks(c)
                ifce.reloadDisconnectInvalid(c)
                ifce.reloadSendRecvError(c)
+               ifce.reloadAcceptRecvError(c)
 
                handshakeManager.f = ifce
                go handshakeManager.Run(ctx)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/noiseutil/boring.go 
new/nebula-1.10.2/noiseutil/boring.go
--- old/nebula-1.10.0/noiseutil/boring.go       2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/noiseutil/boring.go       2026-01-21 18:42:34.000000000 
+0100
@@ -22,7 +22,7 @@
 // NewGCMTLS is no longer exposed in go1.19+, so we need to link it in
 // See: https://github.com/golang/go/issues/56326
 //
-// NewGCMTLS is the internal method used with boringcrypto that provices a
+// NewGCMTLS is the internal method used with boringcrypto that provides a
 // validated mode of AES-GCM which enforces the nonce is strictly
 // monotonically increasing.  This is the TLS 1.2 specification for nonce
 // generation (which also matches the method used by the Noise Protocol)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/outside.go new/nebula-1.10.2/outside.go
--- old/nebula-1.10.0/outside.go        2025-12-04 20:42:31.000000000 +0100
+++ new/nebula-1.10.2/outside.go        2026-01-21 18:42:34.000000000 +0100
@@ -516,7 +516,7 @@
 }
 
 func (f *Interface) maybeSendRecvError(endpoint netip.AddrPort, index uint32) {
-       if f.sendRecvErrorConfig.ShouldSendRecvError(endpoint) {
+       if f.sendRecvErrorConfig.ShouldRecvError(endpoint) {
                f.sendRecvError(endpoint, index)
        }
 }
@@ -534,6 +534,13 @@
 }
 
 func (f *Interface) handleRecvError(addr netip.AddrPort, h *header.H) {
+       if !f.acceptRecvErrorConfig.ShouldRecvError(addr) {
+               f.l.WithField("index", h.RemoteIndex).
+                       WithField("udpAddr", addr).
+                       Debug("Recv error received, ignoring")
+               return
+       }
+
        if f.l.Level >= logrus.DebugLevel {
                f.l.WithField("index", h.RemoteIndex).
                        WithField("udpAddr", addr).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/overlay/tun_linux.go 
new/nebula-1.10.2/overlay/tun_linux.go
--- old/nebula-1.10.0/overlay/tun_linux.go      2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/overlay/tun_linux.go      2026-01-21 18:42:34.000000000 
+0100
@@ -10,6 +10,7 @@
        "net/netip"
        "os"
        "strings"
+       "sync"
        "sync/atomic"
        "time"
        "unsafe"
@@ -40,6 +41,11 @@
        useSystemRoutes           bool
        useSystemRoutesBufferSize int
 
+       // These are routes learned from `tun.use_system_route_table`
+       // stored here to make it easier to restore them after a reload
+       routesFromSystem     map[netip.Prefix]routing.Gateways
+       routesFromSystemLock sync.Mutex
+
        l *logrus.Logger
 }
 
@@ -131,6 +137,7 @@
                TXQueueLen:                c.GetInt("tun.tx_queue", 500),
                useSystemRoutes:           
c.GetBool("tun.use_system_route_table", false),
                useSystemRoutesBufferSize: 
c.GetInt("tun.use_system_route_table_buffer_size", 0),
+               routesFromSystem:          map[netip.Prefix]routing.Gateways{},
                l:                         l,
        }
 
@@ -164,6 +171,13 @@
                return err
        }
 
+       // Bring along any routes learned from the system route table on reload
+       t.routesFromSystemLock.Lock()
+       for dst, gw := range t.routesFromSystem {
+               routeTree.Insert(dst, gw)
+       }
+       t.routesFromSystemLock.Unlock()
+
        oldDefaultMTU := t.DefaultMTU
        oldMaxMTU := t.MaxMTU
        newDefaultMTU := c.GetInt("tun.mtu", DefaultMTU)
@@ -673,14 +687,18 @@
 
        newTree := t.routeTree.Load().Clone()
 
+       t.routesFromSystemLock.Lock()
        if r.Type == unix.RTM_NEWROUTE {
                t.l.WithField("destination", dst).WithField("via", 
gateways).Info("Adding route")
+               t.routesFromSystem[dst] = gateways
                newTree.Insert(dst, gateways)
 
        } else {
                t.l.WithField("destination", dst).WithField("via", 
gateways).Info("Removing route")
+               delete(t.routesFromSystem, dst)
                newTree.Delete(dst)
        }
+       t.routesFromSystemLock.Unlock()
        t.routeTree.Store(newTree)
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/routing/gateway.go 
new/nebula-1.10.2/routing/gateway.go
--- old/nebula-1.10.0/routing/gateway.go        2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/routing/gateway.go        2026-01-21 18:42:34.000000000 
+0100
@@ -6,7 +6,7 @@
 )
 
 const (
-       // Sentinal value
+       // Sentinel value
        BucketNotCalculated = -1
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/udp/udp_generic.go 
new/nebula-1.10.2/udp/udp_generic.go
--- old/nebula-1.10.0/udp/udp_generic.go        2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/udp/udp_generic.go        2026-01-21 18:42:34.000000000 
+0100
@@ -10,9 +10,11 @@
 
 import (
        "context"
+       "errors"
        "fmt"
        "net"
        "net/netip"
+       "time"
 
        "github.com/sirupsen/logrus"
        "github.com/slackhq/nebula/config"
@@ -74,12 +76,22 @@
 func (u *GenericConn) ListenOut(r EncReader) {
        buffer := make([]byte, MTU)
 
+       var lastRecvErr time.Time
+
        for {
                // Just read one packet at a time
                n, rua, err := u.ReadFromUDPAddrPort(buffer)
                if err != nil {
-                       u.l.WithError(err).Debug("udp socket is closed, exiting 
read loop")
-                       return
+                       if errors.Is(err, net.ErrClosed) {
+                               u.l.WithError(err).Debug("udp socket is closed, 
exiting read loop")
+                               return
+                       }
+                       // Dampen unexpected message warns to once per minute
+                       if lastRecvErr.IsZero() || time.Since(lastRecvErr) > 
time.Minute {
+                               lastRecvErr = time.Now()
+                               u.l.WithError(err).Warn("unexpected udp socket 
receive error")
+                       }
+                       continue
                }
 
                r(netip.AddrPortFrom(rua.Addr().Unmap(), rua.Port()), 
buffer[:n])
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nebula-1.10.0/udp/udp_rio_windows.go 
new/nebula-1.10.2/udp/udp_rio_windows.go
--- old/nebula-1.10.0/udp/udp_rio_windows.go    2025-12-04 20:42:31.000000000 
+0100
+++ new/nebula-1.10.2/udp/udp_rio_windows.go    2026-01-21 18:42:34.000000000 
+0100
@@ -14,6 +14,7 @@
        "sync"
        "sync/atomic"
        "syscall"
+       "time"
        "unsafe"
 
        "github.com/sirupsen/logrus"
@@ -66,7 +67,7 @@
 
        u := &RIOConn{l: l}
 
-       err := u.bind(&windows.SockaddrInet6{Addr: addr.As16(), Port: port})
+       err := u.bind(l, &windows.SockaddrInet6{Addr: addr.As16(), Port: port})
        if err != nil {
                return nil, fmt.Errorf("bind: %w", err)
        }
@@ -82,11 +83,11 @@
        return u, nil
 }
 
-func (u *RIOConn) bind(sa windows.Sockaddr) error {
+func (u *RIOConn) bind(l *logrus.Logger, sa windows.Sockaddr) error {
        var err error
        u.sock, err = winrio.Socket(windows.AF_INET6, windows.SOCK_DGRAM, 
windows.IPPROTO_UDP)
        if err != nil {
-               return err
+               return fmt.Errorf("winrio.Socket error: %w", err)
        }
 
        // Enable v4 for this socket
@@ -100,35 +101,40 @@
        size := uint32(unsafe.Sizeof(flag))
        err = syscall.WSAIoctl(syscall.Handle(u.sock), 
syscall.SIO_UDP_CONNRESET, (*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, 
nil, 0)
        if err != nil {
-               return err
+               // This is a best-effort to prevent errors from being returned 
by the udp recv operation.
+               // Quietly log a failure and continue.
+               l.WithError(err).Debug("failed to set UDP_CONNRESET ioctl")
        }
+
        ret = 0
        flag = 0
        size = uint32(unsafe.Sizeof(flag))
        SIO_UDP_NETRESET := uint32(syscall.IOC_IN | syscall.IOC_VENDOR | 15)
        err = syscall.WSAIoctl(syscall.Handle(u.sock), SIO_UDP_NETRESET, 
(*byte)(unsafe.Pointer(&flag)), size, nil, 0, &ret, nil, 0)
        if err != nil {
-               return err
+               // This is a best-effort to prevent errors from being returned 
by the udp recv operation.
+               // Quietly log a failure and continue.
+               l.WithError(err).Debug("failed to set UDP_NETRESET ioctl")
        }
 
        err = u.rx.Open()
        if err != nil {
-               return err
+               return fmt.Errorf("error rx.Open(): %w", err)
        }
 
        err = u.tx.Open()
        if err != nil {
-               return err
+               return fmt.Errorf("error tx.Open(): %w", err)
        }
 
        u.rq, err = winrio.CreateRequestQueue(u.sock, packetsPerRing, 1, 
packetsPerRing, 1, u.rx.cq, u.tx.cq, 0)
        if err != nil {
-               return err
+               return fmt.Errorf("error CreateRequestQueue: %w", err)
        }
 
        err = windows.Bind(u.sock, sa)
        if err != nil {
-               return err
+               return fmt.Errorf("error windows.Bind(): %w", err)
        }
 
        return nil
@@ -137,15 +143,22 @@
 func (u *RIOConn) ListenOut(r EncReader) {
        buffer := make([]byte, MTU)
 
+       var lastRecvErr time.Time
+
        for {
                // Just read one packet at a time
                n, rua, err := u.receive(buffer)
+
                if err != nil {
                        if errors.Is(err, net.ErrClosed) {
                                u.l.WithError(err).Debug("udp socket is closed, 
exiting read loop")
                                return
                        }
-                       u.l.WithError(err).Error("unexpected udp socket receive 
error")
+                       // Dampen unexpected message warns to once per minute
+                       if lastRecvErr.IsZero() || time.Since(lastRecvErr) > 
time.Minute {
+                               lastRecvErr = time.Now()
+                               u.l.WithError(err).Warn("unexpected udp socket 
receive error")
+                       }
                        continue
                }
 

++++++ vendor.tar.zst ++++++
++++ 8524 lines of diff (skipped)

Reply via email to