Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package netbird for openSUSE:Factory checked in at 2026-10-27 18:23:29 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/netbird (Old) and /work/SRC/openSUSE:Factory/.netbird.new.2252680 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "netbird" Tue Oct 27 18:23:29 2026 rev:52 rq:1393129 version:0.81.5 Changes: -------- --- /work/SRC/openSUSE:Factory/netbird/netbird.changes 2026-10-26 09:58:55.267990578 +0100 +++ /work/SRC/openSUSE:Factory/.netbird.new.2252680/netbird.changes 2026-10-27 18:24:02.055338042 +0100 @@ -1,0 +2,9 @@ +Mon Oct 26 20:34:39 UTC 2026 - Marcus Rueckert <[email protected]> + +- Update to 0.81.5 + - [client] Validate and bound SSH session requests and channels + (#8344) by @lixmal in #8368 + - [client] Bound the SSH session request queue and keep resizes + from blocking the session loop (#8363) by @lixmal in #8369 + +------------------------------------------------------------------- Old: ---- netbird-0.81.4.obscpio New: ---- netbird-0.81.5.obscpio update.sh ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ netbird.spec ++++++ --- /var/tmp/diff_new_pack.7Wk9td/_old 2026-10-27 18:24:07.623570785 +0100 +++ /var/tmp/diff_new_pack.7Wk9td/_new 2026-10-27 18:24:07.625570868 +0100 @@ -32,7 +32,7 @@ %bcond_with stub_config Name: netbird -Version: 0.81.4 +Version: 0.81.5 Release: 0 Summary: Mesh VPN based on WireGuard License: AGPL-3.0-only AND BSD-3-Clause @@ -43,6 +43,7 @@ Source3: %{name}-management.service Source4: %{name}-signal.service Source5: series +Source99: update.sh Patch0: service-install-cli-change.patch Patch1: skip-ui.patch BuildRequires: fdupes @@ -351,6 +352,7 @@ TestWaitJWTToken_DropsTokenFromASessionThatEndedBeforeTheWait TestLogin_ProfileSwitchRejectsJWTObtainedUnderPreviousConfig TestExchangeWithFallback_TCPTruncatesToClientSize + TestSession_ResizeWhileRunningIsApplied ) # Assemble skip string by replacing spaces with a pipe. disable=$(echo ${failing_tests[*]} | sed 's/ /|/g') ++++++ _service ++++++ --- /var/tmp/diff_new_pack.7Wk9td/_old 2026-10-27 18:24:07.673572874 +0100 +++ /var/tmp/diff_new_pack.7Wk9td/_new 2026-10-27 18:24:07.681573209 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/netbirdio/netbird.git</param> <param name="scm">git</param> <param name="package-meta">yes</param> - <param name="revision">refs/tags/v0.81.4</param> + <param name="revision">refs/tags/v0.81.5</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">disable</param> ++++++ netbird-0.81.4.obscpio -> netbird-0.81.5.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/FETCH_HEAD new/netbird-0.81.5/.git/FETCH_HEAD --- old/netbird-0.81.4/.git/FETCH_HEAD 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/FETCH_HEAD 2026-10-26 17:02:31.000000000 +0100 @@ -33,6 +33,7 @@ 691a90516df4dc029a00a61f0d60dbefda708c40 not-for-merge branch 'bug/ios-hanging-reconection' of https://github.com/netbirdio/netbird 1474f977319a8cc310422a0de38d824b4170004d not-for-merge branch 'bug/rw-active-profile' of https://github.com/netbirdio/netbird ab987f17f072b2e0a4840fe4c87307e7313f637e not-for-merge branch 'bug/update-ios-client-code-build-tags' of https://github.com/netbirdio/netbird +0965f8a8888c5817b09a336d410a365d4078fc6c not-for-merge branch 'bump/wails-v3.0.0' of https://github.com/netbirdio/netbird 5b975f4681c919247dbf119f2e536f09cc04cc92 not-for-merge branch 'bump_wireguard_go' of https://github.com/netbirdio/netbird 5ebd39ad3349caaa29bfbdf5c60b93267701eb90 not-for-merge branch 'cached-serial-check-on-sync' of https://github.com/netbirdio/netbird e6d8591a6a3c9e0983ffc402ecab810bb3728f6e not-for-merge branch 'chore/benchmark-with-large-runner' of https://github.com/netbirdio/netbird @@ -62,6 +63,7 @@ 8c5648bb7b522ca79c60d4d1481aa947249a6a92 not-for-merge branch 'coderabbitai/docstrings/b7e98ac' of https://github.com/netbirdio/netbird c11622721cf6d278562ae26e32065eb8d111e1e7 not-for-merge branch 'coderabbitai/utg/1e5b0a5' of https://github.com/netbirdio/netbird cb1eaf9e0d30daf1300dfa005abe9b10fe1a1adc not-for-merge branch 'coderabbitai/utg/8ae8f20' of https://github.com/netbirdio/netbird +af1a9f667e4c6d750493c6df5990c172f1e6fbd0 not-for-merge branch 'combined-disable-stun-relay' of https://github.com/netbirdio/netbird 572bea6a718be862bfcd6729e510d95e0ad37a6f not-for-merge branch 'conntrack-stats' of https://github.com/netbirdio/netbird 20a79c5c555b6ab78c7837b4ebf7f80b5967db98 not-for-merge branch 'crowdsec-selfhosted' of https://github.com/netbirdio/netbird f44040feb083c8c569ec7eb16138eaf5129163b3 not-for-merge branch 'daemon-ipc-acl' of https://github.com/netbirdio/netbird @@ -169,6 +171,7 @@ ec4d09215c956909fc691110585ebd75ffa28264 not-for-merge branch 'feat/better-daemon-connection-lost-message' of https://github.com/netbirdio/netbird c262c1b2520fb6489d7594635e0c119ed1f861d4 not-for-merge branch 'feat/buffer-account-peers-update' of https://github.com/netbirdio/netbird d23f993fa3b1ac9deb92930ddfe9229d0cb92fcf not-for-merge branch 'feat/client-hebrew-rtl' of https://github.com/netbirdio/netbird +95dceb4b51e8fac08c8b9a3e245215799e83da39 not-for-merge branch 'feat/crowdsec-appsec-installer' of https://github.com/netbirdio/netbird 4d69874770aed598b5a0e575bc3a3b7ae0625b93 not-for-merge branch 'feat/events-metrics' of https://github.com/netbirdio/netbird f74d66c9ad3e235101dd56ccd33afa20e90a75a0 not-for-merge branch 'feat/getting-started-unified-wizard' of https://github.com/netbirdio/netbird 9291e3134bedcaaad6931adb5a439d2bd0efe3e6 not-for-merge branch 'feat/integrate-zitadel' of https://github.com/netbirdio/netbird @@ -389,7 +392,7 @@ 97ad3307ddb9e07f40a71eaa9489ca74947a6791 not-for-merge branch 'log/conn-disconn' of https://github.com/netbirdio/netbird a69dc29e7e7e24cf6cf7746632d7b8046c0a70e7 not-for-merge branch 'log/getaccount-by-peer' of https://github.com/netbirdio/netbird 0886b67ce951e5827d87571ac079b6f516027915 not-for-merge branch 'logs/peerlogs-addpeer' of https://github.com/netbirdio/netbird -9dd51ca42e975780bc6c9691111a712b7f08f430 not-for-merge branch 'main' of https://github.com/netbirdio/netbird +2c68a84a976e8e3fb7f90aa59347fe2271e8de1d not-for-merge branch 'main' of https://github.com/netbirdio/netbird b03343bc4d249af295a4833161289d8339a0052a not-for-merge branch 'manual-peer-logout' of https://github.com/netbirdio/netbird 6efc1a61fe8568c3ae0704329aa2589c537e801b not-for-merge branch 'merged-fixes' of https://github.com/netbirdio/netbird 82cec5fb1d4c2127be0b7590481aceec508c7c4f not-for-merge branch 'mlsmaycon-patch-1' of https://github.com/netbirdio/netbird @@ -421,7 +424,6 @@ 8f7949ca945fb4692938b06a931b113fec9e47ff not-for-merge branch 'poc/goose-migration' of https://github.com/netbirdio/netbird 0252da7b243f91c66225e0b7c7cff27432f89495 not-for-merge branch 'poc/goose-migration-sqlc' of https://github.com/netbirdio/netbird 0c4c2a8cb24f48c44d74fc213f7cf5150c51bd1f not-for-merge branch 'poc/netstack' of https://github.com/netbirdio/netbird -bd4a844f4a96f8356016a44ceaf769b3f299bd76 not-for-merge branch 'poc/nmdata-on-read-replica' of https://github.com/netbirdio/netbird 51eee4c5acecec3a9cf93e6fc67a4c66b25f8ea5 not-for-merge branch 'poc/preprocessed-map' of https://github.com/netbirdio/netbird 0ef8371627a8fae0816fe560f81878cc7ff4ee8a not-for-merge branch 'poc/udpmux-txid-routing' of https://github.com/netbirdio/netbird ea01ef79f7aa6753b2dad8ab81360272f0376e07 not-for-merge branch 'posture-checks-poc' of https://github.com/netbirdio/netbird @@ -429,7 +431,7 @@ 59cc28702ba85660ba0ab4c7e8431328cea8775e not-for-merge branch 'profile-bindings-ios' of https://github.com/netbirdio/netbird 0a61d7b20e99aa4852d68e996806265d42e14f38 not-for-merge branch 'profile-id' of https://github.com/netbirdio/netbird ac6ee89cd5196fa0da052f019a0e5f5026d86fc8 not-for-merge branch 'profile-id-name-test' of https://github.com/netbirdio/netbird -df6c55f1a4b15d2fc604487eab112c1cd017db24 not-for-merge branch 'profile-ownership' of https://github.com/netbirdio/netbird +4ab791ba23bf4ed23f7d9f2552b5ade191fb166a not-for-merge branch 'profile-ownership' of https://github.com/netbirdio/netbird 3d6591b053555fa1065a3641a619541a1ea407e7 not-for-merge branch 'profile-ownership-cleanup' of https://github.com/netbirdio/netbird 4605d0feba9a68f28fa5df8b424a152e5eb66963 not-for-merge branch 'profile-ownership-migration' of https://github.com/netbirdio/netbird 04b8bd27efb70dc40ad4b3eaec58cec1e3d3fbe1 not-for-merge branch 'profile-test-merge' of https://github.com/netbirdio/netbird @@ -469,6 +471,9 @@ 42867c7a59fde64ad6771feabc71daa48c20cbe0 not-for-merge branch 'refactor/simplify-affected-peers' of https://github.com/netbirdio/netbird 093b8cb41d1a11023690e71958b633df1656057b not-for-merge branch 'refactor/wails-update-105' of https://github.com/netbirdio/netbird 5568be0bb1266e3c25ce873fae1d64c4fd671859 not-for-merge branch 'relay-hmac-cleanup' of https://github.com/netbirdio/netbird +e7f2a00cce4b856f2b6acaced8a73af5210a51cc not-for-merge branch 'relay-only-session-restart' of https://github.com/netbirdio/netbird +173d5eaf41935665cb187057aa2bbba75b3a639e not-for-merge branch 'relay-peer-reregistration' of https://github.com/netbirdio/netbird +69d4676d2835f71860989504083c6bed0e902437 not-for-merge branch 'relay-picker-abort-losing-dials' of https://github.com/netbirdio/netbird 95ad62ea85f9c79888ad66856ab22d76c7ea657f not-for-merge branch 'relay/2800_quic' of https://github.com/netbirdio/netbird 32fbb7da5125b80e9c471b49d07367ef91e46f53 not-for-merge branch 'relay/debug' of https://github.com/netbirdio/netbird 56badd75351be4fc1d1662db90886285a01a4912 not-for-merge branch 'relay/eliminate-gob' of https://github.com/netbirdio/netbird @@ -476,15 +481,19 @@ 300b6695012158bdede367738a0389a25d83cf04 not-for-merge branch 'release-0.78' of https://github.com/netbirdio/netbird 396795ebb6822402ad4eff88c90682d99f77d987 not-for-merge branch 'release-0.79' of https://github.com/netbirdio/netbird fca64287cf51a85552a41b022013abbfdb335452 not-for-merge branch 'release-0.80' of https://github.com/netbirdio/netbird -7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 not-for-merge branch 'release-0.81' of https://github.com/netbirdio/netbird +396976b8be081fbf0d3d959698352585e80618e3 not-for-merge branch 'release-0.81' of https://github.com/netbirdio/netbird 099b3bdf442fba90930a2085b9f03c0d337bc234 not-for-merge branch 'release-0.81-bump-quic-go' of https://github.com/netbirdio/netbird be52f270d5edf74b2866f1566fe12e27de678714 not-for-merge branch 'release-0.81-certificate-posture-followups' of https://github.com/netbirdio/netbird 2d1905ae31585cf91e3ffdeaf9eaaad65840c9a4 not-for-merge branch 'release-0.81-emudhra-distrust' of https://github.com/netbirdio/netbird e308df9c57cafde06b8799a8a92c204546bd3efd not-for-merge branch 'release-0.81-fix-affected-snapshot' of https://github.com/netbirdio/netbird 0cd8fa6c4fc5aa5f75f6c9f6990cc78586fdc418 not-for-merge branch 'release-0.81-is-child' of https://github.com/netbirdio/netbird 6e7e21fa30116635dee20b263ba552a81e18c2e4 not-for-merge branch 'release-0.81-signal-registration-wait' of https://github.com/netbirdio/netbird +082918ea123c1358d2ab5418e1ae9e3dc97e3745 not-for-merge branch 'release-0.81-ssh-session-limits' of https://github.com/netbirdio/netbird +fa49fb67aaf9c8acbead4ebf8eaf47a5ce75316c not-for-merge branch 'release-0.81-ssh-session-limits-followup' of https://github.com/netbirdio/netbird 3d9333a2b82b6aa3e9b5650b572f6b6bd1cd9533 not-for-merge branch 'release-0.81-udpmux-discovery-outside-bind-open' of https://github.com/netbirdio/netbird +2c68a84a976e8e3fb7f90aa59347fe2271e8de1d not-for-merge branch 'release-0.82' of https://github.com/netbirdio/netbird 3dd34c920ec30f3797aad0ec87fb3d92a328dda1 not-for-merge branch 'release/0.68.3' of https://github.com/netbirdio/netbird +91bfdd9fb4d434e54c097d817289e78d95eee1eb not-for-merge branch 'remove-ios-bump-trigger' of https://github.com/netbirdio/netbird 1bb306fae42e6990b573ffba1de3695574e4a1fc not-for-merge branch 'remove-new-routing' of https://github.com/netbirdio/netbird 1e273c901716b779f621334cb859ee8bb9f58bb4 not-for-merge branch 'remove-ubuntu2004-runners' of https://github.com/netbirdio/netbird fae27656c238c0c9e60719919aa7eb60172ee9fd not-for-merge branch 'reorder-dns-shutdown' of https://github.com/netbirdio/netbird @@ -503,6 +512,7 @@ c187dcac0a0ad250261ed2a20c50a14b2849d5f2 not-for-merge branch 'revert-ice-5820' of https://github.com/netbirdio/netbird 2bb55b6c8856067b582fd911af340b5670db8d2e not-for-merge branch 'revert/component-types-hookup' of https://github.com/netbirdio/netbird 460778abb996670802611016419ef00d2ce938f3 not-for-merge branch 'revert/component-types-test-suite' of https://github.com/netbirdio/netbird +b06ebaa6295a836785d00183aa2c80b4207f75b6 not-for-merge branch 'rosenpass_deprecation_notice' of https://github.com/netbirdio/netbird ef55b9eccc22bf01743b5476738d85bef70e1688 not-for-merge branch 'route-get-account-refactoring' of https://github.com/netbirdio/netbird 13537e6640ea7c3fc79c72f707f859201ce5c859 not-for-merge branch 'router-dns' of https://github.com/netbirdio/netbird 945f8809ee49a6bd4a39ee0eed0ca15fd22147e3 not-for-merge branch 'rp_key_persistency' of https://github.com/netbirdio/netbird @@ -525,7 +535,6 @@ 6e76a14a8fef933e245de8108f2349812f52572e not-for-merge branch 'sqlite-async-peer-status' of https://github.com/netbirdio/netbird c45f1083d667d042b08d46d2142b3c94070a07c8 not-for-merge branch 'ssh' of https://github.com/netbirdio/netbird d28b229baf4cbe538b2dc78d565c8482e916c906 not-for-merge branch 'ssh-permitted-targets' of https://github.com/netbirdio/netbird -70a8c1522560a6cf009fb9a4f3e02e5d754e5d29 not-for-merge branch 'status-peers-table' of https://github.com/netbirdio/netbird 5f5d597c59b1553489691bc114b1962670132589 not-for-merge branch 'stop-using-locking-share' of https://github.com/netbirdio/netbird 447b06c62bf7c1162d68599a76dbe4760c805230 not-for-merge branch 'swap-dns-prio' of https://github.com/netbirdio/netbird 5236c71d2f930a47dc127a15b9274c0de036ef29 not-for-merge branch 'sync-serial-bump' of https://github.com/netbirdio/netbird @@ -600,12 +609,14 @@ d48fe5360935f249af33101ef48cae47b5c376bf not-for-merge branch 'windows-dns-firewall' of https://github.com/netbirdio/netbird e3d1b9ca880ff4f1e6624e306b6957aa6c30e5c1 not-for-merge branch 'windows-search-domains' of https://github.com/netbirdio/netbird 25fe7d13d91a4227f67bddf1b6aeabf842bd611c not-for-merge branch 'windows-sleep-detector' of https://github.com/netbirdio/netbird +e1bd29ad07ac486a5d8f1b0f264867d5201b80be not-for-merge branch 'windows-toast-injection' of https://github.com/netbirdio/netbird 9b05593a4e2f6d9e38e6e59d688009e20ad11fd1 not-for-merge branch 'work/admiring-planck-1l2xh2' of https://github.com/netbirdio/netbird 5d9b1fc444fd2d188e3204120988789ab3438f52 not-for-merge branch 'work/amazing-wright-6xehqh' of https://github.com/netbirdio/netbird df45a84ba7ed531e6fbcfc4e9645b002e0af9d05 not-for-merge branch 'work/bold-faraday-dcn47i' of https://github.com/netbirdio/netbird 9bd8c57153ee853cc35cbc12bdfd5ca47f799c91 not-for-merge branch 'work/cool-fermat-awn9fu' of https://github.com/netbirdio/netbird -ba8a99df320c0ebcc75db30c26566e5057a0d14e not-for-merge branch 'work/cool-pasteur-erog5e' of https://github.com/netbirdio/netbird -2b95e0f563424b52b1bb2fed0bcffcca4cf6295d not-for-merge branch 'work/cool-pasteur-erog5e-mcp' of https://github.com/netbirdio/netbird +28589e215fcfc7452ae2e3d78d73b8bffc32d6a5 not-for-merge branch 'work/cool-pasteur-erog5e' of https://github.com/netbirdio/netbird +2df979ab7faec0acc370115fa642a3ae1a9e7596 not-for-merge branch 'work/cool-pasteur-erog5e-guides' of https://github.com/netbirdio/netbird +6bfe0f5a16a4cd32c08a21bcc411dc5d311fb624 not-for-merge branch 'work/cool-pasteur-erog5e-mcp' of https://github.com/netbirdio/netbird 487075af74a16d396eef52cc2d85ca8caba4aa43 not-for-merge branch 'work/exciting-lamport-q85fl9' of https://github.com/netbirdio/netbird c60b71adb6ac982bfb4d5a8e24f1de18eb5030ee not-for-merge branch 'work/funny-goodall-vw54vx' of https://github.com/netbirdio/netbird 8f0e0c67d82329460b4b5e116c44cd51600a047a not-for-merge branch 'work/optimistic-ptolemy-krc00k' of https://github.com/netbirdio/netbird diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/HEAD new/netbird-0.81.5/.git/HEAD --- old/netbird-0.81.4/.git/HEAD 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/HEAD 2026-10-26 17:02:31.000000000 +0100 @@ -1 +1 @@ -7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 +396976b8be081fbf0d3d959698352585e80618e3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/ORIG_HEAD new/netbird-0.81.5/.git/ORIG_HEAD --- old/netbird-0.81.4/.git/ORIG_HEAD 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/ORIG_HEAD 2026-10-26 17:02:31.000000000 +0100 @@ -1 +1 @@ -7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 +396976b8be081fbf0d3d959698352585e80618e3 Binary files old/netbird-0.81.4/.git/index and new/netbird-0.81.5/.git/index differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/HEAD new/netbird-0.81.5/.git/logs/HEAD --- old/netbird-0.81.4/.git/logs/HEAD 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/HEAD 2026-10-26 17:02:31.000000000 +0100 @@ -60,3 +60,4 @@ 838378b4c2913bafb43f9f933f6b88e39f3ba43d 28657607c14d4b5135c48cb79fcb9f4fd0600b42 Marcus 'darix' Rückert <[email protected]> 1792115424 +0200 reset: moving to refs/tags/v0.81.1 28657607c14d4b5135c48cb79fcb9f4fd0600b42 f47ace7d52a182d2ee75e6d821c26260e601c89e Marcus Rückert <[email protected]> 1792444443 +0200 checkout: moving from 28657607c14d4b5135c48cb79fcb9f4fd0600b42 to v0.81.3 f47ace7d52a182d2ee75e6d821c26260e601c89e 7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 Marcus Rückert <[email protected]> 1792850852 +0200 checkout: moving from f47ace7d52a182d2ee75e6d821c26260e601c89e to v0.81.4 +7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 396976b8be081fbf0d3d959698352585e80618e3 Marcus Rückert <[email protected]> 1793046869 +0100 checkout: moving from 7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 to v0.81.5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/bump/wails-v3.0.0 new/netbird-0.81.5/.git/logs/refs/remotes/origin/bump/wails-v3.0.0 --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/bump/wails-v3.0.0 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/bump/wails-v3.0.0 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 0965f8a8888c5817b09a336d410a365d4078fc6c Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/combined-disable-stun-relay new/netbird-0.81.5/.git/logs/refs/remotes/origin/combined-disable-stun-relay --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/combined-disable-stun-relay 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/combined-disable-stun-relay 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 af1a9f667e4c6d750493c6df5990c172f1e6fbd0 Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer new/netbird-0.81.5/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/feat/crowdsec-appsec-installer 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 95dceb4b51e8fac08c8b9a3e245215799e83da39 Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/main new/netbird-0.81.5/.git/logs/refs/remotes/origin/main --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/main 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/main 2026-10-26 17:02:31.000000000 +0100 @@ -54,3 +54,5 @@ afd5c6df6a26eec3c0a81df2374dfe1ce9983ea8 28657607c14d4b5135c48cb79fcb9f4fd0600b42 Marcus Rückert <[email protected]> 1792115337 +0200 pull: fast-forward 28657607c14d4b5135c48cb79fcb9f4fd0600b42 8fef487e4256e592c1ff916fd9cd155f1849b881 Marcus Rückert <[email protected]> 1792444414 +0200 pull: fast-forward 8fef487e4256e592c1ff916fd9cd155f1849b881 9dd51ca42e975780bc6c9691111a712b7f08f430 Marcus Rückert <[email protected]> 1792850847 +0200 pull: fast-forward +9dd51ca42e975780bc6c9691111a712b7f08f430 dd38fabc82d7314c11500d7fb8f1ee8161258250 Marcus Rückert <[email protected]> 1792880562 +0200 fetch --tags: fast-forward +dd38fabc82d7314c11500d7fb8f1ee8161258250 2c68a84a976e8e3fb7f90aa59347fe2271e8de1d Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: fast-forward diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica new/netbird-0.81.5/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/poc/nmdata-on-read-replica 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 bd4a844f4a96f8356016a44ceaf769b3f299bd76 Marcus Rückert <[email protected]> 1792850847 +0200 pull: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/profile-ownership new/netbird-0.81.5/.git/logs/refs/remotes/origin/profile-ownership --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/profile-ownership 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/profile-ownership 2026-10-26 17:02:31.000000000 +0100 @@ -5,3 +5,4 @@ b7fada83b9d43adcd708a24b33c7cc5f38b42d91 6167431e0a505ea8c9451e3ab2251c2f1cc49879 Marcus Rückert <[email protected]> 1792075781 +0200 pull: fast-forward 6167431e0a505ea8c9451e3ab2251c2f1cc49879 69ecec2161afb91cdb336cc66ed8c5d552de00be Marcus Rückert <[email protected]> 1792444414 +0200 pull: fast-forward 69ecec2161afb91cdb336cc66ed8c5d552de00be df6c55f1a4b15d2fc604487eab112c1cd017db24 Marcus Rückert <[email protected]> 1792850847 +0200 pull: fast-forward +df6c55f1a4b15d2fc604487eab112c1cd017db24 4ab791ba23bf4ed23f7d9f2552b5ade191fb166a Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: fast-forward diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-only-session-restart new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-only-session-restart --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-only-session-restart 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-only-session-restart 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 e7f2a00cce4b856f2b6acaced8a73af5210a51cc Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-peer-reregistration new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-peer-reregistration --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-peer-reregistration 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-peer-reregistration 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 173d5eaf41935665cb187057aa2bbba75b3a639e Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/relay-picker-abort-losing-dials 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 69d4676d2835f71860989504083c6bed0e902437 Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81 new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81 --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81 2026-10-26 17:02:31.000000000 +0100 @@ -2,3 +2,4 @@ c0f090448c14fe106144a248669e3f854d383f65 bb90c9787a8a3591bcb3cccd106a024d1a30b0ef Marcus Rückert <[email protected]> 1792115337 +0200 pull: fast-forward bb90c9787a8a3591bcb3cccd106a024d1a30b0ef f47ace7d52a182d2ee75e6d821c26260e601c89e Marcus Rückert <[email protected]> 1792444414 +0200 pull: fast-forward f47ace7d52a182d2ee75e6d821c26260e601c89e 7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 Marcus Rückert <[email protected]> 1792850847 +0200 pull: fast-forward +7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 396976b8be081fbf0d3d959698352585e80618e3 Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: fast-forward diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 082918ea123c1358d2ab5418e1ae9e3dc97e3745 Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.81-ssh-session-limits-followup 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 fa49fb67aaf9c8acbead4ebf8eaf47a5ce75316c Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.82 new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.82 --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/release-0.82 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/release-0.82 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 2c68a84a976e8e3fb7f90aa59347fe2271e8de1d Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/remove-ios-bump-trigger new/netbird-0.81.5/.git/logs/refs/remotes/origin/remove-ios-bump-trigger --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/remove-ios-bump-trigger 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/remove-ios-bump-trigger 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 91bfdd9fb4d434e54c097d817289e78d95eee1eb Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice new/netbird-0.81.5/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/rosenpass_deprecation_notice 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 b06ebaa6295a836785d00183aa2c80b4207f75b6 Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/status-peers-table new/netbird-0.81.5/.git/logs/refs/remotes/origin/status-peers-table --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/status-peers-table 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/status-peers-table 1970-01-01 01:00:00.000000000 +0100 @@ -1 +0,0 @@ -0000000000000000000000000000000000000000 70a8c1522560a6cf009fb9a4f3e02e5d754e5d29 Marcus Rückert <[email protected]> 1792850847 +0200 pull: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/windows-toast-injection new/netbird-0.81.5/.git/logs/refs/remotes/origin/windows-toast-injection --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/windows-toast-injection 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/windows-toast-injection 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 e1bd29ad07ac486a5d8f1b0f264867d5201b80be Marcus Rückert <[email protected]> 1793046868 +0100 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e 2026-10-26 17:02:31.000000000 +0100 @@ -1 +1,2 @@ 0000000000000000000000000000000000000000 ba8a99df320c0ebcc75db30c26566e5057a0d14e Marcus Rückert <[email protected]> 1792850847 +0200 pull: storing head +ba8a99df320c0ebcc75db30c26566e5057a0d14e 28589e215fcfc7452ae2e3d78d73b8bffc32d6a5 Marcus Rückert <[email protected]> 1792880562 +0200 fetch --tags: fast-forward diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-guides 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 2df979ab7faec0acc370115fa642a3ae1a9e7596 Marcus Rückert <[email protected]> 1792880562 +0200 fetch --tags: storing head diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp --- old/netbird-0.81.4/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/logs/refs/remotes/origin/work/cool-pasteur-erog5e-mcp 2026-10-26 17:02:31.000000000 +0100 @@ -1 +1,2 @@ 0000000000000000000000000000000000000000 2b95e0f563424b52b1bb2fed0bcffcca4cf6295d Marcus Rückert <[email protected]> 1792850847 +0200 pull: storing head +2b95e0f563424b52b1bb2fed0bcffcca4cf6295d 6bfe0f5a16a4cd32c08a21bcc411dc5d311fb624 Marcus Rückert <[email protected]> 1792880562 +0200 fetch --tags: fast-forward Binary files old/netbird-0.81.4/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.idx and new/netbird-0.81.5/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.idx differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.pack and new/netbird-0.81.5/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.pack differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.rev and new/netbird-0.81.5/.git/objects/pack/pack-3ac4f806d90d51ee545b47197ab92dd4376c6bf7.rev differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.idx and new/netbird-0.81.5/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.idx differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.pack and new/netbird-0.81.5/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.pack differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.rev and new/netbird-0.81.5/.git/objects/pack/pack-86453fec019843479730691baac169c57c3401f7.rev differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.idx and new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.idx differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.pack and new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.pack differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor --- old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.promisor 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +4efc597ebb09637baa373b2aa7b13b57688cd793 4efc597ebb09637baa373b2aa7b13b57688cd793 Binary files old/netbird-0.81.4/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.rev and new/netbird-0.81.5/.git/objects/pack/pack-bd7c2308cc3c46250e7401186a4a7df7c1f9af27.rev differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.idx and new/netbird-0.81.5/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.idx differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.pack and new/netbird-0.81.5/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.pack differ Binary files old/netbird-0.81.4/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.rev and new/netbird-0.81.5/.git/objects/pack/pack-d0d78b136593b4c930c4456f71dcffcf907278e6.rev differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/packed-refs new/netbird-0.81.5/.git/packed-refs --- old/netbird-0.81.4/.git/packed-refs 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/.git/packed-refs 2026-10-26 17:02:31.000000000 +0100 @@ -423,7 +423,6 @@ 8f7949ca945fb4692938b06a931b113fec9e47ff refs/remotes/origin/poc/goose-migration 0252da7b243f91c66225e0b7c7cff27432f89495 refs/remotes/origin/poc/goose-migration-sqlc 0c4c2a8cb24f48c44d74fc213f7cf5150c51bd1f refs/remotes/origin/poc/netstack -bd4a844f4a96f8356016a44ceaf769b3f299bd76 refs/remotes/origin/poc/nmdata-on-read-replica 51eee4c5acecec3a9cf93e6fc67a4c66b25f8ea5 refs/remotes/origin/poc/preprocessed-map 0ef8371627a8fae0816fe560f81878cc7ff4ee8a refs/remotes/origin/poc/udpmux-txid-routing ea01ef79f7aa6753b2dad8ab81360272f0376e07 refs/remotes/origin/posture-checks-poc @@ -527,7 +526,6 @@ 6e76a14a8fef933e245de8108f2349812f52572e refs/remotes/origin/sqlite-async-peer-status c45f1083d667d042b08d46d2142b3c94070a07c8 refs/remotes/origin/ssh d28b229baf4cbe538b2dc78d565c8482e916c906 refs/remotes/origin/ssh-permitted-targets -70a8c1522560a6cf009fb9a4f3e02e5d754e5d29 refs/remotes/origin/status-peers-table 5f5d597c59b1553489691bc114b1962670132589 refs/remotes/origin/stop-using-locking-share 447b06c62bf7c1162d68599a76dbe4760c805230 refs/remotes/origin/swap-dns-prio 5236c71d2f930a47dc127a15b9274c0de036ef29 refs/remotes/origin/sync-serial-bump diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/bump/wails-v3.0.0 new/netbird-0.81.5/.git/refs/remotes/origin/bump/wails-v3.0.0 --- old/netbird-0.81.4/.git/refs/remotes/origin/bump/wails-v3.0.0 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/bump/wails-v3.0.0 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +0965f8a8888c5817b09a336d410a365d4078fc6c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/combined-disable-stun-relay new/netbird-0.81.5/.git/refs/remotes/origin/combined-disable-stun-relay --- old/netbird-0.81.4/.git/refs/remotes/origin/combined-disable-stun-relay 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/combined-disable-stun-relay 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +af1a9f667e4c6d750493c6df5990c172f1e6fbd0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/feat/crowdsec-appsec-installer new/netbird-0.81.5/.git/refs/remotes/origin/feat/crowdsec-appsec-installer --- old/netbird-0.81.4/.git/refs/remotes/origin/feat/crowdsec-appsec-installer 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/feat/crowdsec-appsec-installer 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +95dceb4b51e8fac08c8b9a3e245215799e83da39 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/main new/netbird-0.81.5/.git/refs/remotes/origin/main --- old/netbird-0.81.4/.git/refs/remotes/origin/main 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/main 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +2c68a84a976e8e3fb7f90aa59347fe2271e8de1d diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/profile-ownership new/netbird-0.81.5/.git/refs/remotes/origin/profile-ownership --- old/netbird-0.81.4/.git/refs/remotes/origin/profile-ownership 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/profile-ownership 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +4ab791ba23bf4ed23f7d9f2552b5ade191fb166a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/relay-only-session-restart new/netbird-0.81.5/.git/refs/remotes/origin/relay-only-session-restart --- old/netbird-0.81.4/.git/refs/remotes/origin/relay-only-session-restart 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/relay-only-session-restart 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +e7f2a00cce4b856f2b6acaced8a73af5210a51cc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/relay-peer-reregistration new/netbird-0.81.5/.git/refs/remotes/origin/relay-peer-reregistration --- old/netbird-0.81.4/.git/refs/remotes/origin/relay-peer-reregistration 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/relay-peer-reregistration 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +173d5eaf41935665cb187057aa2bbba75b3a639e diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/relay-picker-abort-losing-dials new/netbird-0.81.5/.git/refs/remotes/origin/relay-picker-abort-losing-dials --- old/netbird-0.81.4/.git/refs/remotes/origin/relay-picker-abort-losing-dials 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/relay-picker-abort-losing-dials 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +69d4676d2835f71860989504083c6bed0e902437 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81 new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81 --- old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +396976b8be081fbf0d3d959698352585e80618e3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits --- old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +082918ea123c1358d2ab5418e1ae9e3dc97e3745 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup --- old/netbird-0.81.4/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/release-0.81-ssh-session-limits-followup 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +fa49fb67aaf9c8acbead4ebf8eaf47a5ce75316c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/release-0.82 new/netbird-0.81.5/.git/refs/remotes/origin/release-0.82 --- old/netbird-0.81.4/.git/refs/remotes/origin/release-0.82 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/release-0.82 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +2c68a84a976e8e3fb7f90aa59347fe2271e8de1d diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/remove-ios-bump-trigger new/netbird-0.81.5/.git/refs/remotes/origin/remove-ios-bump-trigger --- old/netbird-0.81.4/.git/refs/remotes/origin/remove-ios-bump-trigger 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/remove-ios-bump-trigger 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +91bfdd9fb4d434e54c097d817289e78d95eee1eb diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/rosenpass_deprecation_notice new/netbird-0.81.5/.git/refs/remotes/origin/rosenpass_deprecation_notice --- old/netbird-0.81.4/.git/refs/remotes/origin/rosenpass_deprecation_notice 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/rosenpass_deprecation_notice 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +b06ebaa6295a836785d00183aa2c80b4207f75b6 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/windows-toast-injection new/netbird-0.81.5/.git/refs/remotes/origin/windows-toast-injection --- old/netbird-0.81.4/.git/refs/remotes/origin/windows-toast-injection 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/windows-toast-injection 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +e1bd29ad07ac486a5d8f1b0f264867d5201b80be diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e --- old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +28589e215fcfc7452ae2e3d78d73b8bffc32d6a5 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides --- old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-guides 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +2df979ab7faec0acc370115fa642a3ae1a9e7596 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp --- old/netbird-0.81.4/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/remotes/origin/work/cool-pasteur-erog5e-mcp 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +6bfe0f5a16a4cd32c08a21bcc411dc5d311fb624 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/tags/v0.81.5 new/netbird-0.81.5/.git/refs/tags/v0.81.5 --- old/netbird-0.81.4/.git/refs/tags/v0.81.5 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/tags/v0.81.5 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +396976b8be081fbf0d3d959698352585e80618e3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/.git/refs/tags/v0.82.0-rc.1 new/netbird-0.81.5/.git/refs/tags/v0.82.0-rc.1 --- old/netbird-0.81.4/.git/refs/tags/v0.82.0-rc.1 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/.git/refs/tags/v0.82.0-rc.1 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1 @@ +ecd07be76f20f4b05be57d4d94c31fb0f78756a2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/command_execution_windows.go new/netbird-0.81.5/client/ssh/server/command_execution_windows.go --- old/netbird-0.81.4/client/ssh/server/command_execution_windows.go 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/client/ssh/server/command_execution_windows.go 2026-10-26 17:02:31.000000000 +0100 @@ -267,7 +267,7 @@ return env } -func (s *Server) handlePtyLogin(logger *log.Entry, session ssh.Session, privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, _ <-chan ssh.Window) bool { +func (s *Server) handlePtyLogin(logger *log.Entry, session ssh.Session, privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, winCh <-chan ssh.Window) bool { if privilegeResult.User == nil { logger.Errorf("no user in privilege result") return false @@ -276,7 +276,7 @@ shell := getUserShell(privilegeResult.User.Uid) logger.Infof("starting interactive shell: %s", shell) - s.executeCommandWithPty(logger, session, nil, privilegeResult, ptyReq, nil) + s.executeCommandWithPty(logger, session, nil, privilegeResult, ptyReq, winCh) return true } @@ -376,7 +376,9 @@ } // executeCommandWithPty executes a command with PTY allocation on Windows using ConPty -func (s *Server) executeCommandWithPty(logger *log.Entry, session ssh.Session, _ *exec.Cmd, privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, _ <-chan ssh.Window) bool { +func (s *Server) executeCommandWithPty(logger *log.Entry, session ssh.Session, _ *exec.Cmd, privilegeResult PrivilegeCheckResult, ptyReq ssh.Pty, winCh <-chan ssh.Window) bool { + discardWindowChanges(winCh) + localUser := privilegeResult.User if localUser == nil { logger.Errorf("no user in privilege result") @@ -406,3 +408,18 @@ logger.Debug("ConPty execution completed") return true } + +// discardWindowChanges drains the session's window-change channel. The ConPty is sized +// once at start, and an undrained channel blocks the session's request loop on the +// next resize. +func discardWindowChanges(winCh <-chan ssh.Window) { + if winCh == nil { + return + } + go func() { + for range winCh { + // The ConPty keeps the size it was started with. + continue + } + }() +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/server.go new/netbird-0.81.5/client/ssh/server/server.go --- old/netbird-0.81.4/client/ssh/server/server.go 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/client/ssh/server/server.go 2026-10-26 17:02:31.000000000 +0100 @@ -58,6 +58,10 @@ // the forwarded destination before rejecting the SSH channel. const directTCPIPDialTimeout = 30 * time.Second +// defaultHandshakeTimeout bounds how long a connection may take to complete the SSH +// handshake and authentication before it is dropped. +const defaultHandshakeTimeout = 60 * time.Second + var ( ErrPrivilegedUserDisabled = errors.New(msgPrivilegedUserDisabled) ErrUserNotFound = errors.New("user not found") @@ -165,6 +169,7 @@ allowRemotePortForwarding bool allowRootLogin bool allowSFTP bool + handshakeTimeout time.Duration jwtEnabled bool netstackNet *netstack.Net @@ -227,6 +232,7 @@ jwtEnabled: config.JWT != nil, jwtConfig: config.JWT, authorizer: sshauth.NewAuthorizer(), + handshakeTimeout: defaultHandshakeTimeout, } if config.Auth != nil { @@ -884,15 +890,16 @@ }, HostSigners: []ssh.Signer{}, ChannelHandlers: map[string]ssh.ChannelHandler{ - "session": ssh.DefaultSessionHandler, + "session": s.sessionChannelHandler, "direct-tcpip": s.directTCPIPHandler, }, RequestHandlers: map[string]ssh.RequestHandler{ "tcpip-forward": s.tcpipForwardHandler, "cancel-tcpip-forward": s.cancelTcpipForwardHandler, }, - ConnCallback: s.connectionValidator, - Version: serverVersion, + ConnCallback: s.connectionValidator, + HandshakeTimeout: s.handshakeTimeout, + Version: serverVersion, } if s.jwtEnabled { @@ -934,6 +941,14 @@ func (s *Server) directTCPIPHandler(srv *ssh.Server, conn *cryptossh.ServerConn, newChan cryptossh.NewChannel, ctx ssh.Context) { logger := s.getRequestLogger(ctx) + release, ok := acquireConnSlot(ctx, directTCPIPCountKey{}, maxDirectTCPIPPerConn) + if !ok { + logger.Warnf("local port forwarding denied: too many forwarded connections") + _ = newChan.Reject(cryptossh.ResourceShortage, "too many forwarded connections") + return + } + defer release() + var payload struct { Host string Port uint32 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/session_handlers.go new/netbird-0.81.5/client/ssh/server/session_handlers.go --- old/netbird-0.81.4/client/ssh/server/session_handlers.go 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/client/ssh/server/session_handlers.go 2026-10-26 17:02:31.000000000 +0100 @@ -53,13 +53,20 @@ logger.Infof("SSH session closed after %v", duration) }() + ptyReq, winCh, isPty := session.Pty() + if isPty { + // The request loop blocks sending a resize nobody reads, and only sees its request + // stream close once that send completes. Covers the paths below that return + // without consuming window changes. + defer drainWindowChanges(winCh) + } + privilegeResult, err := s.userPrivilegeCheck(session.User()) if err != nil { s.handlePrivError(logger, session, err) return } - ptyReq, winCh, isPty := session.Pty() hasCommand := session.RawCommand() != "" if isPty && !hasCommand { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/session_hardening_test.go new/netbird-0.81.5/client/ssh/server/session_hardening_test.go --- old/netbird-0.81.4/client/ssh/server/session_hardening_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/client/ssh/server/session_hardening_test.go 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1,402 @@ +//go:build !js + +package server + +import ( + "bytes" + "encoding/binary" + "errors" + "io" + "net" + "os/user" + "runtime" + "strings" + "testing" + "time" + + cryptossh "golang.org/x/crypto/ssh" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + nbssh "github.com/netbirdio/netbird/client/ssh" +) + +func dialNoAuthServer(t *testing.T, addr string, hostKey []byte) *cryptossh.Client { + t.Helper() + + clientKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519) + require.NoError(t, err) + signer, err := cryptossh.ParsePrivateKey(clientKey) + require.NoError(t, err) + hostSigner, err := cryptossh.ParsePrivateKey(hostKey) + require.NoError(t, err) + current, err := user.Current() + require.NoError(t, err) + + client, err := cryptossh.Dial("tcp", addr, &cryptossh.ClientConfig{ + User: current.Username, + Auth: []cryptossh.AuthMethod{cryptossh.PublicKeys(signer)}, + HostKeyCallback: cryptossh.FixedHostKey(hostSigner.PublicKey()), + Timeout: 3 * time.Second, + }) + require.NoError(t, err) + t.Cleanup(func() { _ = client.Close() }) + return client +} + +func startNoAuthServer(t *testing.T) (string, []byte) { + t.Helper() + + hostKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519) + require.NoError(t, err) + srv := New(&Config{HostKeyPEM: hostKey}) + srv.SetAllowRootLogin(true) + addr := StartTestServer(t, srv) + t.Cleanup(func() { require.NoError(t, srv.Stop()) }) + return addr, hostKey +} + +// ptyReqPayload builds a pty-req payload whose term string claims the given length. +func ptyReqPayload(termLen uint32, term string) []byte { + buf := make([]byte, 4, 64) + binary.BigEndian.PutUint32(buf, termLen) + buf = append(buf, term...) + buf = append(buf, make([]byte, 16)...) + return buf +} + +// A pty-req with a term length close to the uint32 maximum must be refused and must +// not take the daemon down. +func TestSession_MalformedPtyReqDoesNotCrashServer(t *testing.T) { + addr, hostKey := startNoAuthServer(t) + client := dialNoAuthServer(t, addr, hostKey) + + session, err := client.NewSession() + require.NoError(t, err) + + ok, err := session.SendRequest("pty-req", true, ptyReqPayload(0xFFFFFFFF, "xterm")) + require.NoError(t, err) + assert.False(t, ok, "malformed pty-req should be rejected") + _ = session.Close() + + // The server must still serve a fresh connection. + again := dialNoAuthServer(t, addr, hostKey) + s2, err := again.NewSession() + require.NoError(t, err) + require.NoError(t, s2.Close()) +} + +// Env requests past the per-session cap are refused in order, and the ones before it +// are still accepted. +func TestSession_EnvRequestsAreCapped(t *testing.T) { + addr, hostKey := startNoAuthServer(t) + client := dialNoAuthServer(t, addr, hostKey) + + session, err := client.NewSession() + require.NoError(t, err) + t.Cleanup(func() { _ = session.Close() }) + + payload := cryptossh.Marshal(struct{ Key, Value string }{"FOO", "bar"}) + accepted := 0 + for i := 0; i < maxEnvRequests+50; i++ { + ok, err := session.SendRequest("env", true, payload) + require.NoError(t, err) + if ok { + accepted++ + } + } + assert.Equal(t, maxEnvRequests, accepted, "only the first maxEnvRequests env requests should be accepted") + + oversized := cryptossh.Marshal(struct{ Key, Value string }{"BIG", string(make([]byte, maxEnvPayload))}) + fresh, err := client.NewSession() + require.NoError(t, err) + t.Cleanup(func() { _ = fresh.Close() }) + ok, err := fresh.SendRequest("env", true, oversized) + require.NoError(t, err) + assert.False(t, ok, "an env request over the payload limit should be refused") +} + +// A flood of window-change requests before the command starts must not stall the +// session's request loop: the next request still gets its reply. +func TestSession_WindowChangeFloodDoesNotStallRequests(t *testing.T) { + addr, hostKey := startNoAuthServer(t) + client := dialNoAuthServer(t, addr, hostKey) + + session, err := client.NewSession() + require.NoError(t, err) + t.Cleanup(func() { _ = session.Close() }) + + require.NoError(t, session.RequestPty("xterm", 24, 80, cryptossh.TerminalModes{})) + + winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0}) + for i := 0; i < 500; i++ { + _, err := session.SendRequest("window-change", false, winch) + require.NoError(t, err) + } + + done := make(chan bool, 1) + go func() { + ok, err := session.SendRequest("env", true, cryptossh.Marshal(struct{ Key, Value string }{"A", "b"})) + done <- err == nil && ok + }() + select { + case ok := <-done: + assert.True(t, ok, "env request after the flood should be accepted") + case <-time.After(5 * time.Second): + t.Fatal("session request loop stalled behind window-change requests") + } +} + +func TestSession_PerConnectionSessionLimit(t *testing.T) { + addr, hostKey := startNoAuthServer(t) + client := dialNoAuthServer(t, addr, hostKey) + + for i := 0; i < maxSessionsPerConn; i++ { + s, err := client.NewSession() + require.NoError(t, err, "session %d should open", i) + t.Cleanup(func() { _ = s.Close() }) + } + + _, err := client.NewSession() + assert.Error(t, err, "session beyond the limit should be refused") + + // The limit is per connection: another connection is unaffected. + other := dialNoAuthServer(t, addr, hostKey) + s, err := other.NewSession() + require.NoError(t, err, "a second connection should still open sessions") + require.NoError(t, s.Close()) +} + +func TestValidPtyReqPayload(t *testing.T) { + assert.True(t, validPtyReqPayload(ptyReqPayload(5, "xterm"))) + assert.False(t, validPtyReqPayload(nil), "empty payload") + assert.False(t, validPtyReqPayload([]byte{0, 0, 0}), "short length prefix") + assert.False(t, validPtyReqPayload(ptyReqPayload(0xFFFFFFFF, "xterm")), "length that wraps uint32") + assert.False(t, validPtyReqPayload(ptyReqPayload(1000, "xterm")), "length beyond the payload") +} + +// A resize sent while the command runs reaches the pty. +func TestSession_ResizeWhileRunningIsApplied(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("stty is not available") + } + addr, hostKey := startNoAuthServer(t) + client := dialNoAuthServer(t, addr, hostKey) + + session, err := client.NewSession() + require.NoError(t, err) + t.Cleanup(func() { _ = session.Close() }) + + var out bytes.Buffer + session.Stdout = &out + require.NoError(t, session.RequestPty("xterm", 24, 80, cryptossh.TerminalModes{})) + require.NoError(t, session.Start("sleep 1; stty size")) + + // Let the handler read the initial window before resizing. + time.Sleep(300 * time.Millisecond) + require.NoError(t, session.WindowChange(50, 132)) + require.NoError(t, session.Wait()) + // Only the first line is the resize report. Coverage-instrumented binaries can append + // runtime warnings to the same output. + firstLine := strings.TrimSpace(strings.SplitN(out.String(), "\n", 2)[0]) + assert.Equal(t, "50 132", firstLine, "pty should have the resized dimensions") +} + +// Forwarded connections beyond the per-connection cap are refused, and a closed one +// frees its slot. +func TestDirectTCPIP_PerConnectionLimit(t *testing.T) { + hostKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519) + require.NoError(t, err) + srv := New(&Config{HostKeyPEM: hostKey}) + srv.SetAllowRootLogin(true) + srv.SetAllowLocalPortForwarding(true) + addr := StartTestServer(t, srv) + t.Cleanup(func() { require.NoError(t, srv.Stop()) }) + + // Target that holds each connection open until the peer half-closes, then closes it + // like a regular server. + target, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + t.Cleanup(func() { _ = target.Close() }) + go func() { + for { + c, err := target.Accept() + if err != nil { + return + } + go func() { + _, _ = io.Copy(io.Discard, c) + _ = c.Close() + }() + } + }() + + client := dialNoAuthServer(t, addr, hostKey) + + conns := make([]net.Conn, 0, maxDirectTCPIPPerConn) + for i := 0; i < maxDirectTCPIPPerConn; i++ { + c, err := client.Dial("tcp", target.Addr().String()) + require.NoError(t, err, "forward %d should open", i) + conns = append(conns, c) + } + + _, err = client.Dial("tcp", target.Addr().String()) + assert.Error(t, err, "forward beyond the limit should be refused") + + // The limit is per connection: another connection is unaffected. + other := dialNoAuthServer(t, addr, hostKey) + oc, err := other.Dial("tcp", target.Addr().String()) + require.NoError(t, err, "a second connection should still open forwards") + require.NoError(t, oc.Close()) + + require.NoError(t, conns[0].Close()) + assert.Eventually(t, func() bool { + c, err := client.Dial("tcp", target.Addr().String()) + if err != nil { + return false + } + _ = c.Close() + return true + }, 3*time.Second, 50*time.Millisecond, "a closed forward should free its slot") + + for _, c := range conns[1:] { + _ = c.Close() + } +} + +// A connection that never completes the SSH handshake is dropped, and one that does +// complete it is not subject to the handshake deadline afterwards. +func TestHandshakeTimeout(t *testing.T) { + hostKey, err := nbssh.GeneratePrivateKey(nbssh.ED25519) + require.NoError(t, err) + srv := New(&Config{HostKeyPEM: hostKey}) + srv.handshakeTimeout = 500 * time.Millisecond + addr := StartTestServer(t, srv) + t.Cleanup(func() { require.NoError(t, srv.Stop()) }) + + stalled, err := net.Dial("tcp", addr) + require.NoError(t, err) + t.Cleanup(func() { _ = stalled.Close() }) + + // The server sends its version line first; after that it waits for ours. + require.NoError(t, stalled.SetReadDeadline(time.Now().Add(5*time.Second))) + _, err = io.Copy(io.Discard, stalled) + var nerr net.Error + assert.False(t, errors.As(err, &nerr) && nerr.Timeout(), "server should close the stalled connection, not leave it open") + + client := dialNoAuthServer(t, addr, hostKey) + time.Sleep(time.Second) + session, err := client.NewSession() + require.NoError(t, err, "an established connection must outlive the handshake deadline") + require.NoError(t, session.Close()) +} + +// Refused requests are forwarded to the session loop under a type it answers with a +// failure, so every reply is sent by the loop in request order. +func TestFilterSessionRequests_RefusedRequestsStayInOrder(t *testing.T) { + in := make(chan *cryptossh.Request) + out := filterSessionRequests(nil, in) + + validWinch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0}) + reqs := []*cryptossh.Request{ + {Type: "pty-req", Payload: ptyReqPayload(0xFFFFFFFF, "xterm")}, + {Type: "pty-req", Payload: ptyReqPayload(5, "xterm")}, + {Type: "window-change", Payload: validWinch}, + {Type: "shell"}, + {Type: "window-change", Payload: validWinch}, + {Type: "window-change", Payload: validWinch}, + } + want := []string{ + refusedRequestType, // malformed pty-req + "pty-req", + refusedRequestType, // resize before the command starts + "shell", + refusedRequestType, // older of two queued resizes + "window-change", + } + + // Hold all requests in the filter's queue before the loop consumes any, so the + // coalescing of the two trailing resizes is exercised. + for _, r := range reqs { + in <- r + } + for i, w := range want { + select { + case got := <-out: + assert.Equal(t, w, got.Type, "request %d", i) + case <-time.After(2 * time.Second): + t.Fatalf("request %d not delivered", i) + } + } + + close(in) + select { + case _, ok := <-out: + assert.False(t, ok, "filter should close its output when the input closes") + case <-time.After(2 * time.Second): + t.Fatal("filter did not close its output") + } +} + +// A rejected subsystem starts no command, so nothing drains the window channel. A resize +// after it must be refused instead of blocking the session's request loop. +func TestSession_ResizeAfterRejectedSubsystemDoesNotStallLoop(t *testing.T) { + addr, hostKey := startNoAuthServer(t) + client := dialNoAuthServer(t, addr, hostKey) + + session, err := client.NewSession() + require.NoError(t, err) + t.Cleanup(func() { _ = session.Close() }) + + require.NoError(t, session.RequestPty("xterm", 24, 80, cryptossh.TerminalModes{})) + ok, err := session.SendRequest("subsystem", true, cryptossh.Marshal(struct{ Name string }{"nope"})) + require.NoError(t, err) + require.False(t, ok, "unknown subsystem must be rejected") + + winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0}) + _, err = session.SendRequest("window-change", false, winch) + require.NoError(t, err) + + done := make(chan bool, 1) + go func() { + ok, err := session.SendRequest("env", true, cryptossh.Marshal(struct{ Key, Value string }{"A", "b"})) + done <- err == nil && ok + }() + select { + case ok := <-done: + assert.True(t, ok, "env request after the resize should be accepted") + case <-time.After(5 * time.Second): + t.Fatal("session request loop stalled behind a resize with no command running") + } +} + +// A resize that reaches the loop while the command handler never reads window changes +// (sftp) must not leave the loop goroutine behind once the handler returns. +func TestSession_ResizeDuringSFTPDoesNotLeakRequestLoop(t *testing.T) { + addr, hostKey := startNoAuthServer(t) + loops := func() int { + buf := make([]byte, 1<<22) + return strings.Count(string(buf[:runtime.Stack(buf, true)]), "ssh.(*session).handleRequests") + } + before := loops() + + winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0}) + for i := 0; i < 10; i++ { + client := dialNoAuthServer(t, addr, hostKey) + session, err := client.NewSession() + require.NoError(t, err) + require.NoError(t, session.RequestPty("xterm", 24, 80, cryptossh.TerminalModes{})) + ok, err := session.SendRequest("subsystem", true, cryptossh.Marshal(struct{ Name string }{"sftp"})) + require.NoError(t, err) + require.True(t, ok, "the sftp subsystem must be accepted for the resize to matter") + // The sftp handler may already have ended the session, so a send can fail. + _, _ = session.SendRequest("window-change", false, winch) + _, _ = session.SendRequest("window-change", false, winch) + _ = session.Close() + _ = client.Close() + } + + assert.Eventually(t, func() bool { return loops() <= before }, 10*time.Second, 100*time.Millisecond, + "session request loops must exit after the clients disconnect") +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/session_limits.go new/netbird-0.81.5/client/ssh/server/session_limits.go --- old/netbird-0.81.4/client/ssh/server/session_limits.go 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/client/ssh/server/session_limits.go 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1,320 @@ +package server + +import ( + "encoding/binary" + "math" + "sync/atomic" + + "github.com/gliderlabs/ssh" + log "github.com/sirupsen/logrus" + cryptossh "golang.org/x/crypto/ssh" +) + +const ( + // maxSessionsPerConn bounds concurrent session channels on one SSH connection. + maxSessionsPerConn = 64 + + // maxDirectTCPIPPerConn bounds concurrent direct-tcpip channels, dialing or + // relaying, on one SSH connection. + maxDirectTCPIPPerConn = 512 + + // maxEnvRequests and maxEnvPayload bound the environment a single session can + // accumulate before its command starts. + maxEnvRequests = 128 + maxEnvPayload = 8 * 1024 + + // maxQueuedRequests and maxQueuedBytes bound the requests held for a session whose + // request loop is not consuming them. A client that exceeds either has its channel + // closed. + maxQueuedRequests = 128 + maxQueuedBytes = 256 * 1024 +) + +type ( + sessionCountKey struct{} + directTCPIPCountKey struct{} +) + +// sessionChannelHandler serves "session" channels like ssh.DefaultSessionHandler, with +// the per-connection session count capped, the request stream bounded, and any panic +// from the session request loop contained to its channel. +func (s *Server) sessionChannelHandler(srv *ssh.Server, conn *cryptossh.ServerConn, newChan cryptossh.NewChannel, ctx ssh.Context) { + release, ok := acquireConnSlot(ctx, sessionCountKey{}, maxSessionsPerConn) + if !ok { + if err := newChan.Reject(cryptossh.ResourceShortage, "too many sessions"); err != nil { + log.Debugf("reject session channel: %v", err) + } + return + } + defer release() + + guarded := &guardedSessionChannel{NewChannel: newChan} + defer func() { + r := recover() + if r == nil { + return + } + log.Errorf("SSH session request loop panicked from %s: %v", ctx.RemoteAddr(), r) + guarded.closeChannel() + }() + + ssh.DefaultSessionHandler(srv, conn, guarded, ctx) +} + +// drainWindowChanges discards window changes until winCh is closed. It must run after +// the session's command handler has returned. +func drainWindowChanges(winCh <-chan ssh.Window) { + go func() { + for { + if _, ok := <-winCh; !ok { + return + } + } + }() +} + +// acquireConnSlot takes one of limit slots counted under key for the connection that +// ctx belongs to. The returned func releases the slot. +func acquireConnSlot(ctx ssh.Context, key any, limit int32) (func(), bool) { + counter := connCounter(ctx, key) + if counter.Add(1) > limit { + counter.Add(-1) + return nil, false + } + return func() { counter.Add(-1) }, true +} + +func connCounter(ctx ssh.Context, key any) *atomic.Int32 { + ctx.Lock() + defer ctx.Unlock() + + if c, ok := ctx.Value(key).(*atomic.Int32); ok { + return c + } + c := new(atomic.Int32) + ctx.SetValue(key, c) + return c +} + +// guardedSessionChannel hands the session loop a filtered request stream. +type guardedSessionChannel struct { + cryptossh.NewChannel + ch atomic.Pointer[cryptossh.Channel] +} + +// Accept accepts the channel and returns its requests after filtering. +func (g *guardedSessionChannel) Accept() (cryptossh.Channel, <-chan *cryptossh.Request, error) { + ch, reqs, err := g.NewChannel.Accept() + if err != nil { + return nil, nil, err + } + g.ch.Store(&ch) + return ch, filterSessionRequests(ch, reqs), nil +} + +func (g *guardedSessionChannel) closeChannel() { + if ch := g.ch.Load(); ch != nil { + _ = (*ch).Close() + } +} + +// refusedRequestType is the type given to a request the filter refuses. The session +// loop answers unknown request types with a failure reply, so a refused request is +// answered by the loop itself and in its position among the replies, which the +// protocol requires since replies carry no request identifier. +const refusedRequestType = "[email protected]" + +// requestFilter passes session channel requests to the session loop while keeping +// what the loop can accumulate or block on bounded: +// - a malformed pty-req is refused instead of reaching the loop's parser +// - env requests beyond maxEnvRequests, or with an oversized payload, are refused +// - window-change requests are refused unless the session's command is a shell or +// exec, and coalesced after it starts, as the loop blocks sending on a one-slot +// channel that only a shell or exec handler drains. Subsystems never read it. +// - request types the loop does not act on are refused, which also drops their payload +// - the queue behind a stalled loop is capped by count and by payload bytes, after +// which the channel is closed +type requestFilter struct { + ch cryptossh.Channel + queue []*queuedRequest + queuedBytes int + lastWinch *queuedRequest + envCount int + + // commandSeen is set by the first shell, exec or subsystem request, which decides + // the session's command as far as the loop is concerned. + commandSeen bool + // started is set when that first command is a shell or exec. + started bool +} + +// queuedRequest remembers the payload size counted against the queue, as a refusal +// drops the payload and releases that size. +type queuedRequest struct { + req *cryptossh.Request + size int +} + +// filterSessionRequests returns the requests of in as the session loop should see them. +func filterSessionRequests(ch cryptossh.Channel, in <-chan *cryptossh.Request) <-chan *cryptossh.Request { + out := make(chan *cryptossh.Request) + f := &requestFilter{ch: ch} + go func() { + defer close(out) + f.run(in, out) + }() + return out +} + +func (f *requestFilter) run(in <-chan *cryptossh.Request, out chan<- *cryptossh.Request) { + for { + var ( + sendCh chan<- *cryptossh.Request + next *cryptossh.Request + ) + if len(f.queue) > 0 { + sendCh = out + next = f.queue[0].req + } + + select { + case req, ok := <-in: + if !ok { + return + } + if !f.enqueue(req) { + return + } + case sendCh <- next: + f.dropHead() + } + } +} + +func (f *requestFilter) dropHead() { + head := f.queue[0] + if head == f.lastWinch { + f.lastWinch = nil + } + f.queuedBytes -= head.size + f.queue[0] = nil + f.queue = f.queue[1:] +} + +// enqueue classifies req and queues it. It returns false after closing the channel +// because the queue is full. Requests are classified first so a refused request's +// dropped payload does not count against the limit. +func (f *requestFilter) enqueue(req *cryptossh.Request) bool { + q := &queuedRequest{req: req} + f.classify(q) + q.size = len(req.Payload) + + if len(f.queue) >= maxQueuedRequests || f.queuedBytes+q.size > maxQueuedBytes { + log.Warnf("SSH session request queue full, closing channel") + if f.ch != nil { + _ = f.ch.Close() + } + return false + } + + f.queue = append(f.queue, q) + f.queuedBytes += q.size + return true +} + +func (f *requestFilter) classify(q *queuedRequest) { + req := q.req + switch req.Type { + case "pty-req": + if !validPtyReqPayload(req.Payload) { + f.refuse(q) + } + case "env": + f.envCount++ + if f.envCount > maxEnvRequests || len(req.Payload) > maxEnvPayload { + f.refuse(q) + } + case "shell", "exec", "subsystem": + f.noteCommand(req.Type) + case "window-change": + f.coalesceWindowChange(q) + default: + if !loopActsOn(req.Type) { + f.refuse(q) + } + } +} + +// refuse drops the payload of q and releases the size it was charged against the queue. +func (f *requestFilter) refuse(q *queuedRequest) { + f.queuedBytes -= q.size + q.size = 0 + refuseRequest(q.req) +} + +func (f *requestFilter) noteCommand(reqType string) { + if f.commandSeen { + return + } + f.commandSeen = true + f.started = reqType != "subsystem" +} + +func (f *requestFilter) coalesceWindowChange(q *queuedRequest) { + if !f.started || !validWinchPayload(q.req.Payload) { + f.refuse(q) + return + } + if f.lastWinch != nil { + f.refuse(f.lastWinch) + } + f.lastWinch = q +} + +// validWinchPayload reports whether p parses as a window-change payload whose columns and +// rows are non-zero and fit the uint16 the PTY takes. A malformed or unusable replacement +// must not displace a valid queued resize. +func validWinchPayload(p []byte) bool { + var win struct{ Columns, Rows, Width, Height uint32 } + if err := cryptossh.Unmarshal(p, &win); err != nil { + return false + } + return usablePtyDimension(win.Columns) && usablePtyDimension(win.Rows) +} + +func usablePtyDimension(v uint32) bool { + return v > 0 && v <= math.MaxUint16 +} + +// loopActsOn reports whether the session loop does anything with a request type other +// than the ones the filter classifies itself. The loop answers every other type with a +// failure, so its payload is not worth queueing. +func loopActsOn(reqType string) bool { + switch reqType { + case "signal", "break", "[email protected]": + return true + } + return false +} + +func refuseRequest(req *cryptossh.Request) { + req.Type = refusedRequestType + req.Payload = nil +} + +// validPtyReqPayload reports whether p holds a term string followed by the two +// uint32 window dimensions, without overflowing on a hostile string length. The +// dimensions must fit the Windows int16 ConPty coordinate, which is narrower than the +// Unix uint16 winsize; zero is allowed and defaulted by the Unix path. +func validPtyReqPayload(p []byte) bool { + if len(p) < 4 { + return false + } + termLen := uint64(binary.BigEndian.Uint32(p)) + if uint64(len(p)) < 4+termLen+8 { + return false + } + dims := p[4+termLen:] + return binary.BigEndian.Uint32(dims) <= math.MaxInt16 && + binary.BigEndian.Uint32(dims[4:]) <= math.MaxInt16 +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/client/ssh/server/session_limits_test.go new/netbird-0.81.5/client/ssh/server/session_limits_test.go --- old/netbird-0.81.4/client/ssh/server/session_limits_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/netbird-0.81.5/client/ssh/server/session_limits_test.go 2026-10-26 17:02:31.000000000 +0100 @@ -0,0 +1,186 @@ +package server + +import ( + "testing" + "time" + + "github.com/gliderlabs/ssh" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + cryptossh "golang.org/x/crypto/ssh" +) + +// A malformed window-change arriving after a valid one must not cost the valid resize: +// the valid request stays queued for the PTY, and only the malformed one is refused. +func TestRequestFilter_MalformedWinchKeepsQueuedValidResize(t *testing.T) { + f := &requestFilter{} + require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"})) + + valid := &cryptossh.Request{Type: "window-change", Payload: cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0})} + require.True(t, f.enqueue(valid)) + + malformed := &cryptossh.Request{Type: "window-change", Payload: []byte{1, 2, 3}} + require.True(t, f.enqueue(malformed)) + + assert.Equal(t, "window-change", valid.Type, "valid resize must still reach the PTY handler") + assert.Equal(t, refusedRequestType, malformed.Type, "malformed resize must be refused") +} + +// A zero-row or zero-column resize parses but would size the PTY to nothing, so it must +// not displace a valid queued resize either. +func TestRequestFilter_ZeroSizeWinchKeepsQueuedValidResize(t *testing.T) { + f := &requestFilter{} + require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"})) + + valid := &cryptossh.Request{Type: "window-change", Payload: cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0})} + require.True(t, f.enqueue(valid)) + + zeroRows := &cryptossh.Request{Type: "window-change", Payload: cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 0, 0, 0})} + require.True(t, f.enqueue(zeroRows)) + + assert.Equal(t, "window-change", valid.Type, "valid resize must still reach the PTY handler") + assert.Equal(t, refusedRequestType, zeroRows.Type, "zero-row resize must be refused") +} + +// The initial pty-req size must fit both the Unix uint16 winsize and the Windows int16 +// ConPty coordinate, so larger values are refused instead of wrapping. Zero stays +// accepted because the Unix path defaults it to 80x24. +func TestValidPtyReqPayload_BoundsInitialSize(t *testing.T) { + ptyReq := func(columns, rows uint32) []byte { + return cryptossh.Marshal(struct { + Term string + Columns, Rows, Width, Height uint32 + Modes string + }{"xterm", columns, rows, 0, 0, ""}) + } + + assert.True(t, validPtyReqPayload(ptyReq(80, 24)), "ordinary size") + assert.True(t, validPtyReqPayload(ptyReq(0, 0)), "zero size is defaulted later") + assert.True(t, validPtyReqPayload(ptyReq(32767, 32767)), "largest size both platforms take") + assert.False(t, validPtyReqPayload(ptyReq(32768, 24)), "columns past the Windows int16 range") + assert.False(t, validPtyReqPayload(ptyReq(80, 32768)), "rows past the Windows int16 range") + assert.False(t, validPtyReqPayload(ptyReq(65536, 24)), "columns that wrap to zero as uint16") +} + +// Sizes that are zero or do not fit the PTY's uint16 dimensions are refused, so they +// cannot replace a valid queued resize. +func TestValidWinchPayload_RejectsUnusableSizes(t *testing.T) { + tests := []struct { + name string + columns, rows uint32 + }{ + {name: "zero columns", columns: 0, rows: 40}, + {name: "zero rows", columns: 100, rows: 0}, + {name: "columns wrap to zero as uint16", columns: 65536, rows: 40}, + {name: "rows wrap to zero as uint16", columns: 100, rows: 65536}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + payload := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{tc.columns, tc.rows, 0, 0}) + assert.False(t, validWinchPayload(payload)) + }) + } + assert.True(t, validWinchPayload(cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{65535, 65535, 0, 0})), "largest uint16 size is usable") +} + +// A client that stops the loop from reading must not make the server buffer more than +// maxQueuedBytes of payload, however few requests that takes. +func TestRequestFilter_QueueIsBoundedByBytes(t *testing.T) { + f := &requestFilter{} + payload := make([]byte, 32*1024) + + accepted := 0 + for f.enqueue(&cryptossh.Request{Type: "exec", Payload: payload}) { + accepted++ + require.LessOrEqual(t, accepted, maxQueuedRequests, "queue must stop growing") + } + assert.LessOrEqual(t, f.queuedBytes, maxQueuedBytes, "queued payload bytes must stay under the limit") + assert.Equal(t, maxQueuedBytes/len(payload), accepted, "queue must hold payloads up to the byte limit") +} + +// Request types the session loop only answers with a failure carry no useful payload, so +// they are refused and cost nothing against the queue. +func TestRequestFilter_UnhandledTypesDropPayload(t *testing.T) { + f := &requestFilter{} + req := &cryptossh.Request{Type: "x11-req", Payload: make([]byte, 32*1024)} + require.True(t, f.enqueue(req)) + + assert.Equal(t, refusedRequestType, req.Type, "unhandled type must be refused") + assert.Zero(t, f.queuedBytes, "refused request must not count its payload") +} + +// Nothing drains window changes for a subsystem session, so a resize after one must be +// refused for the rest of the session, even if a shell request follows. +func TestRequestFilter_SubsystemSessionRefusesResize(t *testing.T) { + f := &requestFilter{} + winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0}) + + require.True(t, f.enqueue(&cryptossh.Request{Type: "subsystem", Payload: cryptossh.Marshal(struct{ Name string }{"sftp"})})) + require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"})) + resize := &cryptossh.Request{Type: "window-change", Payload: winch} + require.True(t, f.enqueue(resize)) + assert.Equal(t, refusedRequestType, resize.Type, "resize in a subsystem session must be refused") +} + +// A loop blocked sending a resize that no handler reads is released by the drain, and the +// drain ends when the loop closes the channel. +func TestDrainWindowChanges_ReleasesBlockedSender(t *testing.T) { + winCh := make(chan ssh.Window, 1) + winCh <- ssh.Window{Width: 80, Height: 24} + + sent := make(chan struct{}) + go func() { + winCh <- ssh.Window{Width: 100, Height: 40} + close(sent) + }() + select { + case <-sent: + t.Fatal("sender should block while nothing reads the channel") + case <-time.After(50 * time.Millisecond): + } + + drainWindowChanges(winCh) + select { + case <-sent: + case <-time.After(2 * time.Second): + t.Fatal("drain did not release the blocked sender") + } + + // A drain that read one window change and exited would release the first sender but + // leave the loop blocked on the next resize. + for i := 0; i < 5; i++ { + select { + case winCh <- ssh.Window{Width: 120, Height: 48}: + case <-time.After(2 * time.Second): + t.Fatal("drain stopped consuming after the first window change") + } + } + close(winCh) +} + +// A superseded resize keeps its place in the queue so its refusal is answered in order, +// but must stop counting against the byte limit. +func TestRequestFilter_CoalescedResizeReleasesQueuedBytes(t *testing.T) { + f := &requestFilter{} + require.True(t, f.enqueue(&cryptossh.Request{Type: "shell"})) + winch := cryptossh.Marshal(struct{ Columns, Rows, Width, Height uint32 }{100, 40, 0, 0}) + + for i := 0; i < 50; i++ { + require.True(t, f.enqueue(&cryptossh.Request{Type: "window-change", Payload: winch})) + } + + assert.Equal(t, len(winch), f.queuedBytes, "only the newest resize should be charged") +} + +// A large request the loop ignores is refused and its payload dropped, so it cannot close +// the channel by pushing the queue over the byte limit. +func TestRequestFilter_IgnoredLargeRequestDoesNotCloseChannel(t *testing.T) { + f := &requestFilter{} + for i := 0; i < maxQueuedBytes/len(make([]byte, 32*1024)); i++ { + require.True(t, f.enqueue(&cryptossh.Request{Type: "exec", Payload: make([]byte, 32*1024)})) + } + + ignored := &cryptossh.Request{Type: "x11-req", Payload: make([]byte, 200*1024)} + assert.True(t, f.enqueue(ignored), "an ignored request must be refused rather than close the channel") + assert.Equal(t, refusedRequestType, ignored.Type) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/go.mod new/netbird-0.81.5/go.mod --- old/netbird-0.81.4/go.mod 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/go.mod 2026-10-26 17:02:31.000000000 +0100 @@ -57,7 +57,7 @@ github.com/eko/gocache/store/go_cache/v4 v4.2.2 github.com/eko/gocache/store/redis/v4 v4.2.2 github.com/fsnotify/fsnotify v1.9.0 - github.com/gliderlabs/ssh v0.3.8 + github.com/gliderlabs/ssh v0.3.9-0.20250127223818-909fa952d4e4 github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-ole/go-ole v1.3.0 github.com/gobwas/ws v1.4.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/netbird-0.81.4/go.sum new/netbird-0.81.5/go.sum --- old/netbird-0.81.4/go.sum 2026-10-24 10:56:12.000000000 +0200 +++ new/netbird-0.81.5/go.sum 2026-10-26 17:02:31.000000000 +0100 @@ -200,8 +200,8 @@ github.com/fxamacker/cbor/v2 v2.9.1/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= -github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= -github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/gliderlabs/ssh v0.3.9-0.20250127223818-909fa952d4e4 h1:iLDHIkpEUuk277c53BF7v9yCAzgQHOcaY8tOu5ZX9Ws= +github.com/gliderlabs/ssh v0.3.9-0.20250127223818-909fa952d4e4/go.mod h1:1nh+SYql7iCMwTrbe9K8l/lah6xcgJTH7W4ZlVoq22c= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= ++++++ netbird.obsinfo ++++++ --- /var/tmp/diff_new_pack.7Wk9td/_old 2026-10-27 18:24:11.084715454 +0100 +++ /var/tmp/diff_new_pack.7Wk9td/_new 2026-10-27 18:24:11.093715831 +0100 @@ -1,5 +1,5 @@ name: netbird -version: 0.81.4 -mtime: 1792832172 -commit: 7e791d3e2972ddb763bb0e5c3bff4f9e14f70033 +version: 0.81.5 +mtime: 1793030551 +commit: 396976b8be081fbf0d3d959698352585e80618e3 ++++++ update.sh ++++++ #!/bin/bash # QUILT_PATCHES=".quilt-patches" set -x set -e VERSION="${1:?please give me a version}" if ! test -d netbird ; then git clone https://github.com/netbirdio/netbird.git fi pushd netbird git fetch --tags git checkout ${VERSION} ln -s .. ${QUILT_PATCHES} quilt push -a rm -rv client/ui go mod vendor tar --zstd -cvf ../vendor.tar.zst vendor/ go.sum rm -rv vendor/ .pc/ ${QUILT_PATCHES} git reset --hard popd rm *cpio perl -p -i -e "s|refs/tags/[^<]+|refs/tags/${VERSION}|g" _service osc service mr osc ar ++++++ vendor.tar.zst ++++++ /work/SRC/openSUSE:Factory/netbird/vendor.tar.zst /work/SRC/openSUSE:Factory/.netbird.new.2252680/vendor.tar.zst differ: char 7, line 1
