PocketID + Pangolin + Netbird ( work in progress )

I was stuck trying the get Nebird working with Pangolin and traefik.

Writing this post allowed me to get some of the basic stuff to work:

  • Netbird Docker container
  • Pocket Id as the IDP for Netbird (Already working with pangolin)
  • Netbird dashboard with Packet ID authentication
  • Client connection on internal LAN with Packet ID authentication.

https://np.p.example.com now works for the dashboard.

** To Do:**

  • Client relay support through Traefik

  • First Client Peer to Peer config.

  • First Network config.

  • Figure out if the configure.sh script can be modified to support this config.

  • Is it possible to access the netbird container ports without exposing them or redirecting them to other ports.

  • Use the docker hostnames to references the Traefik endpoints internally.

    Example use of Pocket-ID as IDP

    I am running Packet-ID and would love to use it as the IDP. (I think I have it working now)
    I am also using Pangolin as my reverse proxy with authentication is already working with pangolin and Pocket-ID.

    Steps I took

    A couple of attempts of the Self-hosted roll out has been unsuccessful over the last couple of days, with no proper running solution yet.
    I am trying it again as I write this, to get the steps I used and get exact errors.

    1. Used Advanced guide - NetBird Docs
      1.1 Using Docker on Ubuntu 22.04.
      1.2 Pangolin with Traefik for the reverse proxy. I would like run as much as possible through port 443. ( Not all the services are working yet, E.g. Relay not working)
      1.3 I didn’t use the script method, since I don’t have Zitadel.

    2. Setup setup.env with all my settings.
      2.1 Followed the Advanced guide - NetBird Docs for the reverse proxy settings.
      2.2 I edited the management.json file based on a couple of reddit posts to try and get this working. Will post my details below.

    3. Added a OICD client under Pocket-ID.

    • In Pocket-ID Client. Most of the settings are default.

      • Enabled PKCE
      • Set the callback as https://nb.p.example.com/* and http://localhost:53000 (I didn’t understand why there was a requirement for this localhost entry until I setup my first client that needed to authenticate using Pocket ID. The Call back is sent back to the client that is running on local host. PocketID uses the localhost on the client system since this is where the netbird client is listening on port 53000 for the callbacks. )
      • After the config was run, I changed the management.json file one setting at a time and retested until I got it working.
    • OIDC settings in the setup,env file

      • NETBIRD_AUTH_AUDIENCE="Client ID"
      • NETBIRD_AUTH_CLIENT_ID="Client ID"
      • NETBIRD_AUTH_CLIENT_SECRET="Secret"
      • NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://pocket.p.example.com/.well-known/openid-configuration" (tested that the URL returns proper JSON results)
      • NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID="2xxxxxxx-0xxx-4xxx-8xxx-cxxxxxxxxxxx"
      • NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email"
      • NETBIRD_AUTH_REDIRECT_URI=""
      • NETBIRD_AUTH_SILENT_REDIRECT_URI=""
    1. I did not disable the Single account mode.

    I ran the configure.sh and got the following result

    Letsencrypt was disabled, the Https-endpoints cannot be used anymore
     and a reverse-proxy with Https needs to be placed in front of netbird!
    The following forwards have to be setup:
    - https://nb.p.example.com:443 -http-> dashboard:80
    - https://nb.p.example.com:443/api -http-> management:443
    - https://nb.p.example.com:443/management.ManagementService/ -grpc-> management:443
    - https://nb.p.example.com:443/signalexchange.SignalExchange/ -grpc-> signal:80
    - rels://nb.p.example.com:33080/relay/ -http-> relay:33080
    You most likely also have to change NETBIRD_MGMT_API_ENDPOINT in base.setup.env and port-mappings in docker-compose.yml.tmpl and rerun this script.
     The target of the forwards depends on your setup. Beware of the gRPC protocol instead of http for management and signal!
    You are also free to remove any occurrences of the Letsencrypt-volume netbird-letsencrypt
    

    The Resulting docker-compose.yaml file still has ports exposed that are in use by the reverse proxy. The Traefik example config file in the infrastructure_files folder has all the ports removed. That example uses the Traefik lables that I don’t use. I used the Configuration files for the middleware and routers.

    These ports were exposed after the config generation.

    • dashboard has ports 80 and 443 exposed.
    • signal has port 443 exposed.
    • management also has pot 443 exposed.
      Since these ports are in use when I try and start the container, I changed them all to available ports. Then I changed the router config to use the exposed ports.

    Expected behavior

    The authentication should work and return to the dashboard. (This now works)

FIXED: Authentication of a client installation works, but I get an error on the return to the Dashboard.
Invalid callback URL, it might be necessary for an admin to fix this.
This is displayed on the Pocket authentication page.
In your Pcket-ID Client config, make sure that the callback is http://localhost:53000. NOT HTTPS.

Are you using NetBird Cloud?
Self-host NetBird’s control plane.
Hosted in a local Docker instance separate from Traefik/Pangolin.

NetBird version

0.51.1

Is any other VPN software installed?

No

Debug output

To help us resolve the problem, please attach the following anonymized status output

netbird status -dA

!! I am still trying to figure out how to do this with the docker compose installation since there is no shell to connect to and I don’t know in which folder the netbird is running from.

Create and upload a debug bundle, and share the returned file key:

netbird debug for 1m -AS -U

!! I am still trying to figure out how to do this with the docker compose installation since there is no shell to connect to and I don’t know in which folder the netbird is running from.

Additional context

I have attempted to follow the instructions:

management.json

RELAY-Secret-Password=32char random value

OICD-Client-ID=PocketID OICD Client ID
OICD-Secret-ID=PocketID OICD Client Password
TURN-Secret-Password=A selfdefines password that also needs to be set int he turnserver.conf. The config will be line this: user=self:TURN-Secret-Password
FIXED1 Relay now working
The below address didn’t match the config in Traefik. It should be:
rels://nb.p.example.com:443/relay. Internally 33080 was used, but since Traefik is now handling the traffic, the external details should be exposed.


    "Relay": {
        "Addresses": [
            "rels://nb.p.example.com:33080"
{
    "Stuns": [
        {
            "Proto": "udp",
            "URI": "stun:nb.p.example.com:3478",
            "Username": "",
            "Password": ""
        }
    ],
    "TURNConfig": {
        "TimeBasedCredentials": false,
        "CredentialsTTL": "12h0m0s",
        "Secret": "secret",
        "Turns": [
            {
                "Proto": "udp",
                "URI": "turn:nb.p.example.com:3478",
                "Username": "self",
                "Password": "TURN-Secret-Password"
            }
        ]
    },
    "Relay": {
        "Addresses": [
            "rels://nb.p.example.com:443/relay"
        ],
        "CredentialsTTL": "24h0m0s",
        "Secret": "RELAY-Secret-Password"
    },
    "Signal": {
        "Proto": "https",
        "URI": "nb.p.example.com:443",
        "Username": "",
        "Password": null
    },
    "Datadir": "/var/lib/netbird/",
    "DataStoreEncryptionKey": "Oxx-ENC-KEY-xk=",
    "HttpConfig": {
        "LetsEncryptDomain": "",
        "CertFile": "",
        "CertKey": "",
        "AuthAudience": "OICD-Client-ID",
        "AuthIssuer": "https://pocket.p.example.com",
        "AuthUserIDClaim": "",
        "AuthKeysLocation": "https://pocket.p.example.com/.well-known/jwks.json",
        "OIDCConfigEndpoint": "https://pocket.p.example.com/.well-known/openid-configuration",
        "IdpSignKeyRefreshEnabled": false,
        "ExtraAuthAudience": ""
    },
    "IdpManagerConfig": {
        "ManagerType": "none",
        "ClientConfig": {
            "Issuer": "https://pocket.p.example.com",
            "TokenEndpoint": "https://pocket.p.example.com/api/oidc/token",
            "ClientID": "OICD-Client-ID",
            "ClientSecret": "",
            "GrantType": "client_credentials"
        },
        "ExtraConfig": {},
        "Auth0ClientCredentials": null,
        "AzureClientCredentials": null,
        "KeycloakClientCredentials": null,
        "ZitadelClientCredentials": null
    },
    "DeviceAuthorizationFlow": {
        "Provider": "hosted",
        "ProviderConfig": {
            "ClientID": "OICD-Client-ID",
            "ClientSecret": "",
            "Domain": "pocket.p.example.com",
            "Audience": "",
            "TokenEndpoint": "https://pocket.p.example.com/api/oidc/token",
            "DeviceAuthEndpoint": "https://pocket.p.example.com/api/oidc/device/authorize",
            "AuthorizationEndpoint": "",
            "Scope": "openid",
            "UseIDToken": false,
            "RedirectURLs": null,
            "DisablePromptLogin": false,
            "LoginFlag": 0
        }
    },
    "PKCEAuthorizationFlow": {
        "ProviderConfig": {
            "ClientID": "OICD-Client-ID",
            "ClientSecret": "OICD-Secret-ID",
            "Domain": "",
            "Audience": "",
            "TokenEndpoint": "https://pocket.p.example.com/api/oidc/token",
            "DeviceAuthEndpoint": "",
            "AuthorizationEndpoint": "https://pocket.p.example.com/authorize",
            "Scope": "openid profile email",
            "UseIDToken": true,
            "RedirectURLs": [
                "http://localhost:53000"
            ],
            "DisablePromptLogin": false,
            "LoginFlag": 0
        }
    },
    "StoreConfig": {
        "Engine": "sqlite"
    },
    "ReverseProxy": {
        "TrustedHTTPProxies": [],
        "TrustedHTTPProxiesCount": 0,
        "TrustedPeers": [
            "0.0.0.0/0"
        ]
    },
    "DisableDefaultPolicy": false
}

compose.yaml

  • I changed all the ports that are 443 and 80 to available ports. Traefik is already running on those ports and I needed to redirect from Traefik to these ports. There probably is a way for Traefik to access the containers without them being exposed. Will look into that.
  • I changed all the volumes to folders that are store in the docker compose folder to allow easier backup and checking files.
  • Lets Encrypt has been disabled since I am using Pangolin that handles the cert management. I talk to all the services internally on HTTP
  • FIXED1: NB_EXPOSED_ADDRESS=rels://nb.p.example.com:33080/relay should match the Traefik and management.json config:
    NB_EXPOSED_ADDRESS=rels://nb.p.example.com:443/relay
services:
  # UI dashboard
  dashboard:
    image: netbirdio/dashboard:latest
    restart: unless-stopped
    ports:
      - 8093:80
      - 8094:443
    environment:
      # Endpoints
      - NETBIRD_MGMT_API_ENDPOINT=https://nb.p.example.com:443
      - NETBIRD_MGMT_GRPC_API_ENDPOINT=https://nb.p.example.com:443
      # OIDC
      - AUTH_AUDIENCE=OICD-Client-ID
      - AUTH_CLIENT_ID=OICD-Client-ID
      - AUTH_CLIENT_SECRET=OICD-Secret-ID
      - AUTH_AUTHORITY=https://pocket.p.example.com
      - USE_AUTH0=false
      - AUTH_SUPPORTED_SCOPES=openid profile email
      - AUTH_REDIRECT_URI=
      - AUTH_SILENT_REDIRECT_URI=
      - NETBIRD_TOKEN_SOURCE=accessToken
      # SSL
      #      - NGINX_SSL_PORT=443
      # Letsencrypt
      - LETSENCRYPT_DOMAIN=
      - LETSENCRYPT_EMAIL=
    volumes:
      #      - ./netbird-letsencrypt:/etc/letsencrypt/
      - ./config/letsencrypt:/etc/letsencrypt/
    logging:
      driver: json-file
      options:
        max-size: 500m
        max-file: "2"
  # Signal
  signal:
    image: netbirdio/signal:latest
    restart: unless-stopped
    volumes:
      #- netbird-signal:/var/lib/netbird
      - ./config/signal:/var/lib/netbird
    ports:
      #      - 443:80
      - 8095:80
    #      # port and command for Let's Encrypt validation
    #      - 443:443
    #    command: ["--letsencrypt-domain", "", "--log-file", "console"]
    logging:
      driver: json-file
      options:
        max-size: 500m
        max-file: "2"
  # Relay
  relay:
    image: netbirdio/relay:latest
    restart: unless-stopped
    environment:
      - NB_LOG_LEVEL=debug
      - NB_LISTEN_ADDRESS=:33080
      - NB_EXPOSED_ADDRESS=rels://nb.p.example.com:443/relay
      - NB_AUTH_SECRET=RELAY-Secret-Password
    ports:
      - 33080:33080
    logging:
      driver: json-file
      options:
        max-size: 500m
        max-file: "2"
  # Management
  management:
    image: netbirdio/management:latest
    restart: unless-stopped
    depends_on:
      - dashboard
    volumes:
      # - netbird-mgmt:/var/lib/netbird
      # - netbird-letsencrypt:/etc/letsencrypt:ro
      # - ./management.json:/etc/netbird/management.json

      - ./config/mgmt:/var/lib/netbird
      - ./config/letsencrypt:/etc/letsencrypt:ro
      - ./config/management.json:/etc/netbird/management.json
    ports:
      - 8096:80 #API port
    #    # command for Let's Encrypt validation without dashboard container
    #    command: ["--letsencrypt-domain", "", "--log-file", "console"]
    command:
      - --port
      - "80"
      - --log-file
      - console
      - --log-level
      - info
      - --disable-anonymous-metrics=true
      - --single-account-mode-domain=nb.p.example.com
      - --dns-domain=netbird.selfhosted
    logging:
      driver: json-file
      options:
        max-size: 500m
        max-file: "2"
    environment:
      - NETBIRD_STORE_ENGINE_POSTGRES_DSN=
      - NETBIRD_STORE_ENGINE_MYSQL_DSN=
  # Coturn
  coturn:
    image: coturn/coturn:latest
    restart: unless-stopped
    #domainname: nb.p.example.com # only needed when TLS is enabled
    volumes:
      - ./config/turnserver.conf:/etc/turnserver.conf:ro
    #      - ./privkey.pem:/etc/coturn/private/privkey.pem:ro
    #      - ./cert.pem:/etc/coturn/certs/cert.pem:ro
    network_mode: host
    command:
      - -c /etc/turnserver.conf
    logging:
      driver: json-file
      options:
        max-size: 500m
        max-file: "2"

# volumes:
#   netbird-mgmt:
#   netbird-signal:
#   netbird-letsencrypt:

Traefik config

  • I added the following router code to handle the direct incoming connections that are not supported by the Pangolin front end.
  • I need to get the internal DNS lookups for the hosts to work so that I don’t have to use the internal IPs of the host.
http:
  routers:
###################################################
# Netbird #########################################
###################################################

    netbird-dashboard:
      rule: "Host(`nb.p.example.com`)"
      service: netbird-dashboard
      entryPoints: 
        - websecure      
      tls:
        certResolver: letsencrypt
    
    # Netbird Relay router 
    netbird-relay-rtr:
      rule: "Host(`nb.p.example.com`) && PathPrefix(`/relay`)"
      service: netbird-relay-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

    netbird-signal:
      rule: "Host(`nb.p.example.com`) && PathPrefix(`/signalexchange.SignalExchange/`)"
      service: netbird-signal-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

    netbird-api:
      rule: "Host(`nb.p.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/oidc/callback`))"
      service: netbird-api-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

    netbird-management:
      rule: "Host(`nb.p.example.com`) && PathPrefix(`/management.ManagementService/`)"
      service: netbird-management-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

  services:
    
 # Netbird services
    netbird-dashboard:
      loadBalancer:
        passHostHeader: true
        servers:
#          - url: "http://netbird-dashboard:80"  # Default Netbird service
          - url: "http://192.168.1.9:8093"  # Default Netbird service          

    netbird-relay-service:
      loadBalancer:
        servers:
          #- url: "http://netbird-relay:33080"  # Netbird Relay service 
          - url: "https://192.168.1.9:33080"  # Netbird Relay service 

    netbird-signal-service:
      loadBalancer:
        servers:
          #- url: "h2c://netbird-signal:10000"
          - url: "h2c://192.168.1.9:8095"

    netbird-api-service:
      loadBalancer:
        servers:
          #- url: "http://netbird-management:80"
          - url: "http://192.168.1.9:8096"

    netbird-management-service:
      loadBalancer:
        servers:
          #- url: "h2c://netbird-management:80"
          - url: "h2c://192.168.1.9:8096"

Edit:

FIXED1 : RELAYs fixed. Marked the two locations fixed with FIXED1.

2 Likes

Good luck!
If you ever get the whole feature set working it would be amazing

2 Likes

@xXAzazelXx @LeonvanHeerden got it working. will publish share it with @LeonvanHeerden and he will publish it soon

3 Likes

Yes, I am running it with Pocket ID at home.

Still want to do some cleanup of the old configs on my side, but it works great.

I can Authenticate on my Windows PC with Pocket-ID, when connecting to Netbird from the Windows Client desktop client.

2 Likes

could you please share a guide on how you got the both running on the same VPS?

3 Likes

Hi, I tried that too, but unfortunately failed miserably. I was hoping I could simply put a haproxy or nginx in front of it and use SNI inspection or http to forward the traffic to the appropriate backends, while letting the rest just pass by, but… well… I’m afraid this is all beyond my capabilities. Do you plan on completing the guide? Otherwise, I’d just try it based on the current description. Thank you very much.

1 Like

Sorry, have not tried this on a VPS.

1 Like

this is basically what the Traefik config is doing here.

Based on the FQDN where it connected too, the traffic is sent to the appropriate back-end IP and port.

The Routers in the Traefik config, is the inspection part, that then directs the FQDN and path to a service entry. The Service entry directs the traffic the configured URLs.

Thanks for your feedback and explanation. I actually got it working now, but I’m not sure if my setup is “good.” The functions seem to be there, but is there something else I might run into problems with later? Here’s a brief overview of what I did and my haproxy.cfg file. I installed both applications one after the other in separate folders and got them running independently behind the haproxy. Then I combined both Compose files into one and extended my haproxy accordingly. What I overlooked during the first attempt was that there were connections (e.g., “Management” container “.well-known/openid-configuration”) that connected to port 443, but apparently without SNI? So I forwarded these connections, and then it worked.

global
    maxconn 2048
    nbthread 1
    hard-stop-after 15m
    daemon
    tune.ssl.default-dh-param 2048

defaults
    log global
    mode tcp
    timeout connect 30s
    timeout client 30s
    timeout server 30s

# FRONTEND HTTPS

frontend Front-https
    bind *:6443
    mode tcp

    # Allow time for TLS ClientHello inspection
    tcp-request inspect-delay 5s
    tcp-request content accept if { req_ssl_hello_type 1 }

    # Detect SNI
    acl NB_domain req_ssl_sni -i netbird.mydomain.com

    # Detect missing SNI
    acl missing_sni req_ssl_sni -m str ""

    # Routing
    use_backend NB_nosni if missing_sni
    use_backend NB_domain-https if NB_domain

    # Fallback backend (SAFER: PG)
    default_backend PG_domain-https


# FRONTEND HTTP

frontend Front-http
    bind *:6080
    mode http

    acl NB_domain hdr(host) -i netbird.mydomain.com

    use_backend NB_domain-http if NB_domain

    default_backend PG_domain-http

# BACKEND – NetBird HTTPS

backend NB_domain-https
    mode tcp

    # TLS stickiness
    stick-table type binary len 32 size 30k expire 30m

    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello
    tcp-response content accept if serverhello

    stick on payload_lv(43,1) if clienthello
    stick store-response payload_lv(43,1) if serverhello

    server caddy caddy:443 check inter 1000


# BACKEND – NetBird HTTPS NO SNI

backend NB_nosni
    mode tcp
    server caddy caddy:443 check inter 1000


# BACKEND – Pangolin HTTPS

backend PG_domain-https
    mode tcp

    stick-table type binary len 32 size 30k expire 30m

    acl clienthello req_ssl_hello_type 1
    acl serverhello rep_ssl_hello_type 2

    tcp-request inspect-delay 5s
    tcp-request content accept if clienthello
    tcp-response content accept if serverhello

    stick on payload_lv(43,1) if clienthello
    stick store-response payload_lv(43,1) if serverhello

    server gerbil gerbil:443 check inter 1000


# BACKENDS – HTTP

backend NB_domain-http
    mode http
    server caddy caddy:80 check inter 1000

backend PG_domain-http
    mode http
    server gerbil gerbil:80 check inter 1000
services:
  # -------------------------------------------
  # PANGOLIN
  # -------------------------------------------
  pangolin:
    image: docker.io/fosrl/pangolin:1.11.1
    container_name: pangolin
    restart: unless-stopped
    networks:
      - pangolin
    volumes:
      - ./pangolin/config:/app/config
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"]
      interval: "10s"
      timeout: "10s"
      retries: 15

  gerbil:
    image: docker.io/fosrl/gerbil:1.2.2
    container_name: gerbil
    restart: unless-stopped
    networks:
      - pangolin
    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --reachableAt=http://gerbil:3004
      - --generateAndSaveKeyTo=/var/config/key
      - --remoteConfig=http://pangolin:3001/api/v1/
    volumes:
      - ./pangolin/config/:/var/config
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    ports:
      - 61820:61820/udp
      - 21820:21820/udp
      - 7443:443
      - 7080:80
      #- 5349:5349
      #- 5349:5249/udp

  traefik:
    image: docker.io/traefik:v3.5
    container_name: traefik
    restart: unless-stopped

    network_mode: service:gerbil # Ports appear on the gerbil service

    depends_on:
      pangolin:
        condition: service_healthy
    command:
      - --configFile=/etc/traefik/traefik_config.yml
    volumes:
      - ./pangolin/config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration
      - ./pangolin/config/letsencrypt:/letsencrypt # Volume to store the Let's Encrypt certificates
      - ./pangolin/config/traefik/logs:/var/log/traefik # Volume to store Traefik logs

  # -------------------------------------------
  # NETBIRD
  # -------------------------------------------

  # Caddy reverse proxy
  caddy:
    image: caddy
    container_name: caddy
    restart: unless-stopped
    networks:
      - netbird
    ports:
      - '9443:443'
      - '443:443/udp'
      - '9080:80'
    volumes:
      - netbird_caddy_data:/data
      - ./netbird/Caddyfile:/etc/caddy/Caddyfile
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # UI dashboard
  dashboard:
    image: netbirdio/dashboard:latest
    container_name: dashboard
    restart: unless-stopped
    networks:
      - netbird
    env_file:
      - ./netbird/dashboard.env
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Signal
  signal:
    image: netbirdio/signal:latest
    container_name: signal
    restart: unless-stopped
    networks:
      - netbird
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Relay
  relay:
    image: netbirdio/relay:latest
    container_name: relay
    restart: unless-stopped
    networks:
      - netbird
    env_file:
      - ./netbird/relay.env
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Management
  management:
    image: netbirdio/management:latest
    container_name: management
    restart: unless-stopped
    networks:
      - netbird
    volumes:
      - netbird_management:/var/lib/netbird
      - ./netbird/management.json:/etc/netbird/management.json
    command: [
      "--port", "80",
      "--log-file", "console",
      "--log-level", "info",
      "--disable-anonymous-metrics=false",
      "--single-account-mode-domain=netbird.selfhosted",
      "--dns-domain=netbird.selfhosted",
      "--idp-sign-key-refresh-enabled",
    ]
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Coturn, AKA relay server
  coturn:
    image: coturn/coturn
    container_name: coturn
    restart: unless-stopped
    #domainname: netbird.relay.selfhosted
    volumes:
      - ./netbird/turnserver.conf:/etc/turnserver.conf:ro
    network_mode: host
    command:
      - -c /etc/turnserver.conf
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Zitadel - identity provider
  zitadel:
    restart: 'always'
    container_name: zitadel
    networks:
      - netbird
    image: 'ghcr.io/zitadel/zitadel:v2.64.1'
    command: 'start-from-init --masterkeyFromEnv --tlsMode external'
    env_file:
      - ./netbird/zitadel.env
    depends_on:
      zdb:
        condition: 'service_healthy'
    volumes:
      - ./netbird/machinekey:/machinekey
      - netbird_zitadel_certs:/zdb-certs:ro
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # Postgres for Zitadel
  zdb:
    restart: 'always'
    container_name: zdb
    networks:
      - netbird
    image: 'postgres:16-alpine'
    env_file:
      - ./netbird/zdb.env
    volumes:
      - netbird_zdb_data:/var/lib/postgresql/data:rw
    healthcheck:
      test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
      interval: 5s
      timeout: 60s
      retries: 10
      start_period: 5s
    logging:
      driver: "json-file"
      options:
        max-size: "500m"
        max-file: "2"

  # -------------------------------------------
  # ROUTER
  # -------------------------------------------
  haproxy:
    image: haproxy:3.0
    container_name: haproxy
    restart: unless-stopped
    ports:
      - "80:6080"
      - "443:6443"
    volumes:
      - ./router/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
    networks:
      - pangolin
      - netbird

volumes:
  netbird_zdb_data:
    external: true
    name: netbird_netbird_zdb_data
  netbird_management:
    external: true
    name: netbird_netbird_management
  netbird_caddy_data:
    external: true
    name: netbird_netbird_caddy_data
  netbird_zitadel_certs:
    external: true
    name: netbird_netbird_zitadel_certs

networks:
  netbird:
    driver: bridge
    name: netbird
  pangolin:
    driver: bridge
    name: pangolin
1 Like

There’s a new version of netbird which doesn’t require any IDP. Things have changed - Release v0.62.0 · netbirdio/netbird · GitHub
Haven’t tested yet tho

Update:
Successfully installed Netbird under Pangolin through newt in Netbird’s docker compose file. Super Clean. Sign up and login works.
Problem - But unable to connect my devices. Maybe I’ve configured the urls incorrectly


Request Logs shows everything fine. No idea what I missed in copying Caddyfile. Maybe it has something to do with this - Configuration for your reverse-proxy

Btw, here are steps I took to install this -

  1. Install Netbird in ~/netbirdX/ folder with x.example.com pointing to netbird vps

  2. Run docker compose down on the installed netbird stack in netbirdX folder

  3. Copy ~/netbirdX/ folder’s content into ~/netbirdY/ folder with command sudo cp -R ~/netbirdX/ ~/netbirdY/

  4. Applied few tweaks to compose file in netbirdY folder which you can find here
    Changes to note -

    • Added hostnames to each services. Don’t know if that will help
    • Make sure to put your pangolin site credentials in newt service part
    • Make sure to change folder netbirdY in management service part to your folder location.
  5. Change domain names in dashboard.env, management.json and relay.env from x.example.com to y.example.com and run docker compose up -d in netbirdY folder

  6. Create new resource at y.example.com in Pangolin for Netbird with the rules similar to the ones in Caddyfile from netbirdY folder. Check my attachment above, to see how it looks after creation.

  7. Disable Authentication for now (with rules would be good way tho)
    Now when you go to y.example.com, it should work

Let me know if connecting devices works for anyone. That is as far I can get. Or If anyone has some solution, please share. Thank you :slight_smile:

1 Like

Is your netbird running on the same vps as pangolin?

I setup Netbird 0.62 on the same instance as pangolin but I am not running it in pangolin like you. I am tempted to try now though.

I set it up similar with the installer, I let the the installer create the compose and env files I removed the caddy info from the compose file and added the services to the pangolin / traefik network ( not the best setup I guess?).

Everything seems to be working

This is my my netbird.yaml for traefik.

http:
  routers:
    netbird-dashboard:
      rule: "Host(`netbird.domain.com`)"
      service: netbird-dashboard
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt

    netbird-oauth:
      rule: "Host(`netbird.domain.com`) && PathPrefix(`/oauth2`)"
      service: netbird-api-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
    netbird-relay-rtr:
      rule: "Host(`netbird.domain.com`) && PathPrefix(`/relay`)"
      service: netbird-relay-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
    netbird-signal:
      rule: "Host(`netbird.domain.com`) && PathPrefix(`/signalexchange.SignalExchange/`)"
      service: netbird-signal-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
    netbird-api:
      rule: "Host(`netbird.domain.com`) && (PathPrefix(`/api`) || PathPrefix(`/oidc/callback`))"
      service: netbird-api-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
    netbird-management:
      rule: "Host(`netbird.domain.com`) && PathPrefix(`/management.ManagementService/`)"
      service: netbird-management-service
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
  services:
    netbird-dashboard:
      loadBalancer:
        passHostHeader: true
        servers:
          - url: "http://netbird-dashboard-1:80"

    netbird-relay-service:
      loadBalancer:
        servers:
          - url: "http://netbird-relay-1:33080"

    netbird-signal-service:
      loadBalancer:
        servers:
          - url: "h2c://netbird-signal-1:10000"

    netbird-api-service:
      loadBalancer:
        servers:
          - url: "http://netbird-management-1:33073"

    netbird-management-service:
      loadBalancer:
        servers:
          - url: "h2c://netbird-management-1:33073"

I did noticed your service ports are different to mine.

1 Like

Oh great! But no, I’m running pangolin and netbird on different instances. I can’t run both on same instance. Because I have each instance with 1 GB RAM only.

I think that could be because they recently changed how we could expose ports I guess. I am not expert in this. Just copied their caddy configuration from their latest Quick start setup guide installation.

Btw, the procedure I shared above is not working for connecting devices, which makes it useless. Only dashboard viewing is working though. That is how far I could get.

So, I’m back to caddy setup using official way for now. But if someone gets it working, it would be great to run Netbird under Pangolin with 2 different machines.

1 Like

i have it working. will post it soon

3 Likes

I am just using the default, I know there is an issues with the signal port mapping being changed atm. Signal service connection issue - incorrect port mapping

I had this problem when I first setup Netbird on another server. This time around I left everyting as default.

Awesome

1 Like

Hey @Selmaks, got it working, check it out here - NetBird + Pangolin (Pangolin as reverse proxy for NetBird) Setup Guide

1 Like

@codewhiz I have pinned your post.

New discussion on NetBird + Pangolin (Pangolin as reverse proxy for NetBird) Setup Guide

Locking this thread

1 Like