[PR #4550] [MERGED] [client, management] Add ssh authenatication with jwt #20646

Closed
opened 2026-08-05 05:07:31 -04:00 by saavagebueno · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/netbirdio/netbird/pull/4550
Author: @lixmal
Created: 9/26/2025
Status: Merged
Merged: 10/7/2025
Merged by: @lixmal

Base: ssh-rewriteHead: ssh-rewrite-auth


📝 Commits (10+)

📊 Changes

56 files changed (+4471 additions, -2417 deletions)

View changed files

📝 client/android/client.go (+1 -1)
📝 client/cmd/ssh.go (+125 -41)
📝 client/cmd/up.go (+11 -0)
📝 client/embed/embed.go (+49 -19)
📝 client/grpc/dialer.go (+3 -4)
📝 client/internal/acl/manager.go (+1 -2)
📝 client/internal/connect.go (+3 -1)
📝 client/internal/engine.go (+9 -21)
📝 client/internal/engine_ssh.go (+82 -96)
📝 client/internal/engine_test.go (+9 -1)
📝 client/internal/login.go (+2 -0)
📝 client/internal/peer/status.go (+1 -1)
📝 client/internal/profilemanager/config.go (+12 -0)
📝 client/internal/routemanager/dynamic/route.go (+1 -1)
📝 client/ios/NetBirdSDK/client.go (+1 -1)
📝 client/proto/daemon.pb.go (+375 -60)
📝 client/proto/daemon.proto (+50 -0)
📝 client/proto/daemon_grpc.pb.go (+76 -0)
client/server/jwt_cache.go (+73 -0)
📝 client/server/network.go (+1 -1)

...and 36 more files

📄 Description

Describe your changes

Changes

  • Management now passes its jwks config to peers with enabled ssh server
  • SSH server peers verify incoming clients' jwt with max token age
  • SSH clients request jwts from the IdP and send these for authentication with remote ssh peers
  • Add netbird ssh detect command to detect if the remote peer is running the NetBird server
  • Add netbird ssh proxy command for native ssh and sftp clients. The proxy requests jwts just like netbird ssh, passes them for authentication and bridges the connection between the native client and the remote server.
  • Add jwt capability to the wasm ssh client

Flows

  1. NetBird SSH Flow (netbird ssh <peer>)
flowchart TD
    A[User: netbird ssh &lt;peer&gt;] --> B[Connect to peer:22]
    B --> C[Detect Server Type]
    C --> D[Send 'netbird-detect' request]

    D --> E{Server Response}

    E -->|No NetBird identifier| F[Regular SSH Server]
    E -->|NetBird + JWT required| G[NetBird with JWT Auth]
    E -->|NetBird + No JWT| H[NetBird without JWT]

    F --> I[Standard SSH Connection]
    H --> I

    G --> J[Request JWT from NetBird daemon]

    J --> J1{Check JWT cache}
    J1 -->|Cache valid| J2[Use cached JWT token]
    J1 -->|No cache/expired| J3[OIDC flow:<br/>User authorizes via IDP callback]
    J3 --> J4[Receive & cache JWT token]
    J4 --> K[Connect to peer SSH server]
    J2 --> K

    K --> M[Send JWT authentication request]
    M --> N{JWT Valid?}

    N -->|No| O[Connection Rejected]
    N -->|Yes| P[SSH Session Established]
    I --> P

    P --> Q[Interactive Shell / Execute Command / Port Forwarding]

    style G fill:#f57c00,color:#fff
    style J fill:#1976d2,color:#fff
    style J1 fill:#1976d2,color:#fff
    style J3 fill:#e65100,color:#fff
    style P fill:#388e3c,color:#fff
  1. Native SSH Flow (e.g. openssh client)
flowchart TD
    A[User: ssh &lt;peer&gt;] --> B[OpenSSH loads config:<br/>/etc/ssh/ssh_config.d/99-netbird.conf]
    B --> D{Host matches NetBird pattern?}

    D -->|No| E[Standard SSH connection]

    D -->|Yes| F[Run detection check:<br/>netbird ssh detect &lt;peer&gt; 22]
    F --> G{Is NetBird SSH server?}

    G -->|No| E

    G -->|Yes - JWT required| H[Activate ProxyCommand:<br/>netbird ssh proxy &lt;peer&gt; 22]

    H --> I[Local SSH Proxy Started]
    I --> J[OpenSSH connects to proxy via stdio]

    J --> K[Proxy requests JWT from daemon]

    K --> K1{Check JWT cache}
    K1 -->|Cache valid| K2[Use cached JWT token]
    K1 -->|No cache/expired| K3[OIDC flow:<br/>User authorizes via IDP callback]
    K3 --> K4[Receive & cache JWT token]
    K4 --> L[Proxy connects to peer SSH server]
    K2 --> L

    L --> M[Proxy sends JWT authentication]

    M --> N{JWT Valid?}
    N -->|No| O[Connection Rejected]
    N -->|Yes| P[Proxy establishes session]

    P --> Q[Bidirectional forwarding:<br/>OpenSSH ↔ Proxy ↔ Peer SSH Server]

    Q --> R[User interacts with remote shell]

    style H fill:#f57c00,color:#fff
    style I fill:#1976d2,color:#fff
    style K fill:#1976d2,color:#fff
    style K1 fill:#1976d2,color:#fff
    style K3 fill:#e65100,color:#fff
    style Q fill:#388e3c,color:#fff

    classDef proxyBox fill:#1565c0,stroke:#0d47a1,stroke-width:2px,color:#fff
    class I,K,L,M,P,Q proxyBox

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)
    .

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/netbirdio/netbird/pull/4550 **Author:** [@lixmal](https://github.com/lixmal) **Created:** 9/26/2025 **Status:** ✅ Merged **Merged:** 10/7/2025 **Merged by:** [@lixmal](https://github.com/lixmal) **Base:** `ssh-rewrite` ← **Head:** `ssh-rewrite-auth` --- ### 📝 Commits (10+) - [`641c8f7`](https://github.com/netbirdio/netbird/commit/641c8f71a0b180b5223697f583a2a89cebdd1ce0) Add JWT authentication for NetBird SSH - [`9acc514`](https://github.com/netbirdio/netbird/commit/9acc5148ccde97fd6e27fde4e5ee4a5a64e532e9) Reduce complexity - [`27415aa`](https://github.com/netbirdio/netbird/commit/27415aa94b2b862c803ff4655dd648fd711d0f29) Address linter issues - [`fb2a3a9`](https://github.com/netbirdio/netbird/commit/fb2a3a9657a4f6f50420a8e553a4d28e3744a462) Use cmd ctx - [`8ef7ce5`](https://github.com/netbirdio/netbird/commit/8ef7ce5c6441f9c4620a6b134e0c47947116cc23) Log session id - [`1d1b3db`](https://github.com/netbirdio/netbird/commit/1d1b3db4df58fd57073e7ca7257c556d6b6ee3b9) Remove ensurejwt in validation logic - [`bce258d`](https://github.com/netbirdio/netbird/commit/bce258d28d984ab1be2ed7be2cfcfba2dbbcb202) Delete auth session when ssh session ends - [`34d841b`](https://github.com/netbirdio/netbird/commit/34d841be58d38d4b1123e434690dc3d57f8d9c5c) Fix test - [`2071cef`](https://github.com/netbirdio/netbird/commit/2071cef9090400bafd70f7b0663e031db3cd2e89) Move authentication to auth layer - [`abc63f4`](https://github.com/netbirdio/netbird/commit/abc63f492ff33fcad58edc91f7bf09aa68a3b47e) Fix windows test ### 📊 Changes **56 files changed** (+4471 additions, -2417 deletions) <details> <summary>View changed files</summary> 📝 `client/android/client.go` (+1 -1) 📝 `client/cmd/ssh.go` (+125 -41) 📝 `client/cmd/up.go` (+11 -0) 📝 `client/embed/embed.go` (+49 -19) 📝 `client/grpc/dialer.go` (+3 -4) 📝 `client/internal/acl/manager.go` (+1 -2) 📝 `client/internal/connect.go` (+3 -1) 📝 `client/internal/engine.go` (+9 -21) 📝 `client/internal/engine_ssh.go` (+82 -96) 📝 `client/internal/engine_test.go` (+9 -1) 📝 `client/internal/login.go` (+2 -0) 📝 `client/internal/peer/status.go` (+1 -1) 📝 `client/internal/profilemanager/config.go` (+12 -0) 📝 `client/internal/routemanager/dynamic/route.go` (+1 -1) 📝 `client/ios/NetBirdSDK/client.go` (+1 -1) 📝 `client/proto/daemon.pb.go` (+375 -60) 📝 `client/proto/daemon.proto` (+50 -0) 📝 `client/proto/daemon_grpc.pb.go` (+76 -0) ➕ `client/server/jwt_cache.go` (+73 -0) 📝 `client/server/network.go` (+1 -1) _...and 36 more files_ </details> ### 📄 Description ## Describe your changes ### Changes - Management now passes its `jwks` config to peers with enabled ssh server - SSH server peers verify incoming clients' `jwt` with max token age - SSH clients request `jwt`s from the IdP and send these for authentication with remote ssh peers - Add `netbird ssh detect` command to detect if the remote peer is running the NetBird server - Add `netbird ssh proxy` command for native ssh and sftp clients. The proxy requests `jwt`s just like `netbird ssh`, passes them for authentication and bridges the connection between the native client and the remote server. - Add jwt capability to the wasm ssh client ### Flows 1. NetBird SSH Flow (`netbird ssh <peer>`) ```mermaid flowchart TD A[User: netbird ssh &lt;peer&gt;] --> B[Connect to peer:22] B --> C[Detect Server Type] C --> D[Send 'netbird-detect' request] D --> E{Server Response} E -->|No NetBird identifier| F[Regular SSH Server] E -->|NetBird + JWT required| G[NetBird with JWT Auth] E -->|NetBird + No JWT| H[NetBird without JWT] F --> I[Standard SSH Connection] H --> I G --> J[Request JWT from NetBird daemon] J --> J1{Check JWT cache} J1 -->|Cache valid| J2[Use cached JWT token] J1 -->|No cache/expired| J3[OIDC flow:<br/>User authorizes via IDP callback] J3 --> J4[Receive & cache JWT token] J4 --> K[Connect to peer SSH server] J2 --> K K --> M[Send JWT authentication request] M --> N{JWT Valid?} N -->|No| O[Connection Rejected] N -->|Yes| P[SSH Session Established] I --> P P --> Q[Interactive Shell / Execute Command / Port Forwarding] style G fill:#f57c00,color:#fff style J fill:#1976d2,color:#fff style J1 fill:#1976d2,color:#fff style J3 fill:#e65100,color:#fff style P fill:#388e3c,color:#fff ``` 2. Native SSH Flow (e.g. openssh client) ```mermaid flowchart TD A[User: ssh &lt;peer&gt;] --> B[OpenSSH loads config:<br/>/etc/ssh/ssh_config.d/99-netbird.conf] B --> D{Host matches NetBird pattern?} D -->|No| E[Standard SSH connection] D -->|Yes| F[Run detection check:<br/>netbird ssh detect &lt;peer&gt; 22] F --> G{Is NetBird SSH server?} G -->|No| E G -->|Yes - JWT required| H[Activate ProxyCommand:<br/>netbird ssh proxy &lt;peer&gt; 22] H --> I[Local SSH Proxy Started] I --> J[OpenSSH connects to proxy via stdio] J --> K[Proxy requests JWT from daemon] K --> K1{Check JWT cache} K1 -->|Cache valid| K2[Use cached JWT token] K1 -->|No cache/expired| K3[OIDC flow:<br/>User authorizes via IDP callback] K3 --> K4[Receive & cache JWT token] K4 --> L[Proxy connects to peer SSH server] K2 --> L L --> M[Proxy sends JWT authentication] M --> N{JWT Valid?} N -->|No| O[Connection Rejected] N -->|Yes| P[Proxy establishes session] P --> Q[Bidirectional forwarding:<br/>OpenSSH ↔ Proxy ↔ Peer SSH Server] Q --> R[User interacts with remote shell] style H fill:#f57c00,color:#fff style I fill:#1976d2,color:#fff style K fill:#1976d2,color:#fff style K1 fill:#1976d2,color:#fff style K3 fill:#e65100,color:#fff style Q fill:#388e3c,color:#fff classDef proxyBox fill:#1565c0,stroke:#0d47a1,stroke-width:2px,color:#fff class I,K,L,M,P,Q proxyBox ``` ## Stack <!-- branch-stack --> ### Checklist - [ ] Is it a bug fix - [ ] Is a typo/documentation fix - [x] Is a feature enhancement - [ ] It is a refactor - [ ] Created tests that fail without the change (if possible) > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) . ### Docs PR URL (required if "docs added" is checked) Paste the PR link from https://github.com/netbirdio/docs here: https://github.com/netbirdio/docs/pull/__ --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
saavagebueno added the pull-request label 2026-08-05 05:07:31 -04:00
Sign in to join this conversation.
No Label pull-request
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#20646