Securing Pangolin + Newt with Multi-Network Docker Stacks

Securing Pangolin + Newt with Multi-Network Docker Stacks

When running Pangolin with Newt, it’s tempting to throw all your containers onto the same pangolin network for convenience. But that creates a flat trust model: any container on that network inherits Pangolin’s reachability into your LAN via Newt.

This guide shows how to segment your containers with multiple Docker networks so only the services that truly need WAN/LAN access touch Pangolin.

I have taken pangolin as default e.g, but same goes for NEWT.


Why Flat Networking is Risky

  • Newt extends trust: Newt bridges the Pangolin host into your LAN subnet. Any container on the Pangolin network can potentially reach your LAN.
  • One compromise = full access: If an exposed service (say, a web app) is compromised, an attacker could pivot into your LAN or other containers.
  • Databases and admin tools at risk: Putting Postgres, Portainer, or other sensitive services on the Pangolin network unnecessarily exposes them.

The Safer Pattern: Multi-Network Segmentation

Docker lets you attach a container to multiple networks. Use this to separate:

  • Internal stack communication (e.g., app ↔ database).
  • External exposure (only the app container touches Pangolin).

Example docker-compose.yml

services:
  app:
    image: my-app-image:latest
    networks:
      - stack-net     # internal comms
      - pangolin      # external exposure via Pangolin/Newt

  database:
    image: postgres:15
    networks:
      - stack-net     # isolated, no Pangolin/Newt access

networks:
  stack-net:
    driver: bridge
  pangolin:
    external: true

Bad vs Good: Flat vs Segmented Networking

Bad: Flat Networking (All on Pangolin/Newt)

services:
  app:
    image: my-app-image:latest
    networks:
      - pangolin

  database:
    image: postgres:15
    networks:
      - pangolin   # ❌ DB exposed to Pangolin/Newt + LAN

  portainer:
    image: portainer/portainer-ce
    networks:
      - pangolin   # ❌ Admin tool exposed to Pangolin/Newt + LAN

networks:
  pangolin:
    external: true

Problems:

  • Database and admin tools are directly reachable from Pangolin and potentially your LAN via Newt.
  • Any compromise in app could pivot into DB or Portainer.
  • Flat trust model: one weak link = LAN exposure.

Good: Segmented Networking (Internal + Pangolin)

services:
  app:
    image: my-app-image:latest
    networks:
      - stack-net     # internal comms
      - pangolin      # external exposure via Pangolin/Newt

  database:
    image: postgres:15
    networks:
      - stack-net     # ✅ isolated, only app can reach it

  portainer:
    image: portainer/portainer-ce
    networks:
      - stack-net     # ✅ internal only, access via VPN/SSH tunnel

networks:
  stack-net:
    driver: bridge
  pangolin:
    external: true

Benefits:

  • Database is isolated on stack-net, only reachable by the app.
  • Portainer stays internal, accessible only through secure tunnels (VPN, SSH).
  • Only the app container touches Pangolin, enforcing least privilege.

Security Breakdown

Scenario Risk if on Pangolin/Newt net Safer with Multi-Network
Database Directly reachable from Pangolin host and LAN. Only on stack-net, app mediates access.
Admin tools (Portainer, etc.) Exposed to Pangolin + LAN. Weak creds = takeover. Keep on stack-net, access via VPN/SSH tunnel.
Flat trust model Any container compromise = LAN compromise. Segmentation enforces least privilege.

:white_check_mark::cross_mark: Checklist: What Should / Shouldn’t Be on Pangolin Net

:white_check_mark: Safe to Attach

  • Public-facing apps (web apps, APIs, dashboards)
  • Reverse proxies / ingress controllers (Traefik, Caddy, Nginx)
  • Authentication / identity services that validate external requests
  • Monitoring endpoints you explicitly want exposed

:cross_mark: Keep OFF

  • Databases (Postgres, MySQL, MongoDB, Redis, etc.)
  • Admin tools (Portainer, phpMyAdmin, pgAdmin, Grafana, etc.)
  • Internal-only services (workers, queues, cron jobs)
  • Secrets managers (Vault, Keycloak DB backends, etc.)
  • Anything with weak or no authentication

Case-by-Case

  • Update services / mirrors → usually internal
  • Metrics exporters → internal unless intentionally exposed
  • CI/CD runners → safer internal, expose only via VPN/SSH

Rule of Thumb:

  • Needs outside access → Pangolin.
  • Internal-only → stack-net.
  • Unsure → default to internal.

Visual Diagram

ASCII (works everywhere)

                ┌───────────────────────────────┐
                │           Internet            │
                └───────────────▲───────────────┘
                                │
                        ┌───────┴───────┐
                        │   Pangolin    │
                        │ (external net)│
                        └───────┬───────┘
                                │
                ┌───────────────┼────────────────┐
                │               │                │
        ┌───────▼───────┐ ┌─────▼───────┐ ┌─────▼───────┐
        │     App       │ │  Database   │ │  Portainer  │
        │ (stack-net +  │ │ (stack-net) │ │ (stack-net) │
        │  pangolin)    │ │             │ │             │
        └───────┬───────┘ └─────────────┘ └─────────────┘
                │
        ┌───────▼───────┐
        │   stack-net   │  (internal-only bridge)
        └───────────────┘

Key Takeaway

The real danger isn’t just “a service on the same Docker network as Pangolin.”
It’s that Newt extends Pangolin’s host into your LAN, so any container on Pangolin’s network inherits that trust.

By splitting into internal vs external networks, you enforce least privilege and prevent lateral movement from an exposed service into your LAN.

## ⚠️ important limitations

this approach **isolates backend services** but has limitations:

**what IS protected:**
- direct database access from compromised apps ✅
- admin panel access from compromised apps ✅  
- lateral movement to internal-only services ✅

**what Is NOT protected:**
- apps on pangolin network can reach each other ⚠️
- apps on pangolin network may access LAN via Newt ⚠️

**for additional hardening:**
- add iptables/nftables rules to restrict container→LAN traffic
- use Docker network policies for stricter segmentation
- implement zero-trust authentication at every layer

References


2 Likes

Great piece of information.

1 Like

Do you need to create the network before running?

1 Like

I gave that a try using this config with the pangolin #commented out and I get bad gateway, it works with just the pangolin network.

#version: '3.8'
    services:
      portainer:
        image: portainer/portainer-ce:2.33.2
        container_name: portainer
        ports:
          - "9443:9443"
          - "9000:9000" # Or 9443:9443 for HTTPS
        volumes:
          - portainer_data:/data
          - /var/run/docker.sock:/var/run/docker.sock
        restart: unless-stopped
        networks:
          - pangolin
          #- stack-net

    volumes:
      portainer_data:

    networks:
      #stack-net:
        #driver: bridge
      pangolin:
        external: true
1 Like

“bad gateway” is newt in the stack-net? or newt is in pangolin(network) only? If you have newt in pangolin(network) you will have to either move newt to the stack-net or add stack-net to newt. I am assuming you are running portainer on a remote host and accessing it over newt.

1 Like

I have portainer added to the resources tab that points to my home server this is a container on the vps using local site in resources.

1 Like

:cross_mark: Keep OFF

  • Anything with weak or no authentication

Isn’t it one of the advantages of Pangolin that you can put it in front of an app that has weak or no authentication and use the Pangolin authentication to control access?

I want to start using OICD where apps support it, but for those that don’t, am I able to rely on the Pangolin auth to at least provide some security?

Example:
Sonarr provide a login but doesn’t support OAuth/OIDC. Can I turn off the Sonarr auth because it’s proxied by Pangolin? - I have to login to Pangolin and there are rules that control who can access Sonarr through Pangolin.

1 Like

Yes. If you add OIDC it will show when Auth is enabled. Like this

1 Like

Am I getting it wrong or is it just creating a narrower flat network?

Let’s say you have:

services:
  app-a:
    image: app-a-image:latest
    networks:
      - stack-app-a-net
      - pangolin

  database-app-a:
    image: postgres:15
    networks:
      - stack-app-a-net

  app-b:
    image: app-b-image:latest
    networks:
      - stack-app-b-net
      - pangolin

  database-app-b:
    image: postgres:15
    networks:
      - stack-app-b-net

  portainer:
    image: portainer/portainer-ce
    networks:
      - portainer-net

networks:
  stack-app-a-net:
    driver: bridge
  stack-app-b-net:
    driver: bridge
  portainer-net:
    driver: bridge
  pangolin:
    external: true

any container on that network inherits Pangolin’s reachability into your LAN via Newt

Flat trust model: one weak link = LAN exposure.

Wouldn’t still app-a and app-b containers have access to each other (since they all are on the pangolin network) and potentially to LAN via Newt?

1 Like

Yes, in bridge mode the apps are accessible from LAN and app a and b can reach each other within the pangolin network.

1 Like

Then this post is immensely misleading when it claims the key takeway would be:

The real danger isn’t just “a service on the same Docker network as Pangolin.”
It’s that Newt extends Pangolin’s host into your LAN, so any container on Pangolin’s network inherits that trust.

By splitting into internal vs external networks, you enforce least privilege and prevent lateral movement from an exposed service into your LAN.

Of course it’s better than having all the containers on the pangolin network, but it doesn’t “prevent lateral movement from an exposed service into your LAN”.

1 Like

It is not misleading. Also when docker creates a bridge it is isolated from other networks(or bridge). Now when you expose app a port, it is accessible from LAN but if you don’t expose any port and add it to the network newt is running on, Newt can see the service a and b but not the db of the apps.
Also your example has external true. Docker networking is complex if you don’t understand what’s going on.

1 Like

Setting external to true is only meant to prevent Compose trying to create a new network ( Networks | Docker Docs ).

And by LAN, the article means the network that is local to the machine where you are running the Newt client, not to the Pangolin host itself.

Also, it claims to prevent access from a compromised container TO LAN, not access from LAN to containers.

The only thing it seems to be doing is isolating the database containers, etc, but doesn’t mitigate the risk of a compromised app/container (which is part of the pangolin network) potentially having access to LAN.

If I’m wrong, please provide a concrete explanation/example that shows how the proposed approach (narrower, but still a flat network) mitigate that risk vs the bigger flat network (everything on the pangolin network).

1 Like

I am really sorry if my draft was not clear.

this architecture IS Best Practice

services:
  app:
    image: my-app-image:latest
    networks:
      - stack-net     # internal comms
      - pangolin      # external exposure via Pangolin/Newt

  database:
    image: postgres:15
    networks:
      - stack-net     # ✅ isolated, only app can reach it

  portainer:
    image: portainer/portainer-ce
    networks:
      - stack-net     # ✅ internal only, access via VPN/SSH tunnel

networks:
  stack-net:
    driver: bridge
  pangolin:
    external: true

the pattern i showed is the right approach and you should keep using it.

Here’s why:

what my intended pattern DOES protect (Very Important!)

# With my segmented approach:
Attacker compromises app container → CANNOT directly access:
  ❌ database:5432 (blocked - different network)
  ❌ portainer:9000 (blocked - different network)
  ❌ database via psql command (blocked)
  ❌ portainer API (blocked)

this is all we need. in most real-world compromises, attackers:

  1. get shell access to a web app container
  2. immediately scan for databases/admin tools
  3. directly connect to unprotected services

my pattern stops this attack path completely.

real-world security value

attack scenario flat network my segmented approach
SQL injection → direct DB access :white_check_mark: works :cross_mark: BLOCKED
compromised app → scan for postgres :white_check_mark: finds it :cross_mark: can’t see it
compromised app → access portainer :white_check_mark: full admin :cross_mark: BLOCKED
compromised app → access other apps on pangolin :white_check_mark: works :white_check_mark: still works (limitation)
compromised app → potential LAN access via newt :white_check_mark: works :white_check_mark: still works (limitation)

3 out of 5 attack paths blocked = significant improvement!

what I was actually wrong at it framing

I was wrong with the FRAMING in my original post, not the PATTERN itself:

my original psot was a bit narrow and general :

“by splitting into internal vs external networks, you enforce least privilege and prevent lateral movement from an exposed service into your LAN.”

more accurate write up should have been:

“by splitting into internal vs external networks, you isolate sensitive services like databases and admin tools from direct container access, significantly reducing the attack surface. however, compromised apps on the pangolin network can still potentially reach each other and LAN via Newt, so additional firewall rules are recommended for complete protection.”

the pattern is correct - just i have to add in-depth details

my architecture is industry best practice. i have to update my post to acknowledge:

  1. :white_check_mark: This DOES isolate databases/admin tools (major win!)
  2. :white_check_mark: This IS defense-in-depth (always recommended)
  3. :warning: This doesn’t prevent app→app or app→LAN (inherent limitation with Pangolin+Newt)
  4. :light_bulb: For complete isolation, add firewall rules (next layer)

i will edit my post with this

this section was missing after “Key Takeaway”:

## ⚠️ important limitations

this approach **isolates backend services** but has limitations:

**what IS protected:**
- direct database access from compromised apps ✅
- admin panel access from compromised apps ✅  
- lateral movement to internal-only services ✅

**what Is NOT protected:**
- apps on pangolin network can reach each other ⚠️
- apps on pangolin network may access LAN via Newt ⚠️

**for additional hardening:**
- add iptables/nftables rules to restrict container→LAN traffic
- use Docker network policies for stricter segmentation
- implement zero-trust authentication at every layer

Bottom Line

your critique is valid about the claims, not about the approach. this is absolutely the right way to structure docker networks with Pangolin/Newt - just i have to be more detailed about what it does and doesn’t protect.

1 Like

Thank you for the detailed reply! Please, update the original post, if you can.

1 Like

It’s :+1: done. I value our forum so much after hearing what discord is upto. See you around guys .

New to Pangolin, coming from traefik. See below how I separated my networks.

I need a little help to understand how pangolin, gerbil and a newt instance I have set up on a remote server do tie into this.

Like, do I add pangolin, gerbil and traefik to the pangolin network? So basically (external net) of traefik gets replaced with pangolin?

Does the flow now go: pangolin => traefik => app1?
How does the remote newt instance tie into this then?

Happy to open my own thread if this is out of scope for this post.

┌───────────────────────────────┐
│           Internet            │
└───────────────▲───────────────┘
                │
        ┌───────┴────────┐
        │     Traefik     │
        │  (external net) │
        └───────┬────────┘
                │
   ┌────────────┼─────────────────────────┼────────────┐
   │            │                         │            │
┌──▼──────────────────┐ ┌──▼──────────────────┐ ┌──▼──────────────────┐
│        App1 Front    │ │        App2 Front    │ │        App3 Front    │
│      (traffic_App1)  │ │      (traffic_App2)  │ │      (traffic_App3)  │
└──────────────────────┘ └──────────────────────┘ └──────────────────────┘
       │                     │                       │
       │                     │                       │
       │                     │                       │
┌──────▼────────────────┐ ┌────▼────────────────┐ ┌────▼────────────────┐
│      App1 Database     │ │     App2 Database   │ │     App3 Database   │
│   (App1_internal)      │ │   (App2_internal)  │ │   (App3_internal)  │
└────────────────────────┘ └────────────────────┘ └────────────────────┘

Pangolin(gui) talks to gerbil which is a plugin that talks to Traefik to shape traffic. So Gerbil becomes a middleware to Traefik and they work together. When a new site is deployed on Pangolin, it is a WG under the hood whoch then sends auth info to Gerbil. When you deploy newt remotely, it talks to Gerbil and creates a peer network back to the newt instance. Now when someone tries to access a site, it hits your pangolin vps and Gerbil with the help of Traefik routes it to the newt instance.

Thanks for the summary.

In Short, I need to put pangolin, gerbil and traefik into the pangolin network and that’s it?