Skip to main content

Pomerium Clients for Tunneling Non-HTTP Protocols

Pomerium can secure non-HTTP protocols such as SSH, RDP, MySQL, Redis, or DNS with the same identity-aware policy enforcement used for HTTP. This document merges several references to provide a comprehensive guide covering:

  • TCP Over HTTP and UDP Over HTTP capabilities
  • How to configure routes for TCP and UDP
  • Pomerium CLI and Desktop usage and installation details
  • Advanced configurations, best practices, and enterprise considerations

Pomerium's non-HTTP proxying allows you to secure SSH, MySQL, DNS, or any other TCP/UDP-based service behind Pomerium's identity-aware access control. When a user connects through Pomerium CLI or Pomerium Desktop, they:

  1. Are redirected to the IdP to authenticate (if they aren't already).
  2. Receive an authorized tunnel for the requested port/protocol.
  3. Enjoy zero trust-style security without needing a separate VPN or dedicated tunnels.

Why Use Pomerium for TCP and UDP?

  • Single Sign-On (SSO): Unified authentication with your existing IdP.
  • Granular Authorization: Leverage Pomerium's policies (e.g. allow by email, group membership).
  • Audit & Visibility: Centralize logs and control for non-HTTP traffic.
  • Consistent Security: Enforce the same policies for both HTTP and non-HTTP connections.

Install Pomerium CLI and Desktop

Pomerium offers a command-line interface (CLI) and a graphical Desktop client for connecting to non-HTTP routes.

CLI Installation

ARCH=[your-arch]
OS=[your-os]
VERSION=[desired-version]
curl -L https://github.com/pomerium/cli/releases/download/${VERSION}/pomerium-cli-${OS}-${ARCH}.tar.gz \
| tar -z -x

See GitHub Releases for a full list.

Desktop Installation

Download the .exe from Desktop Releases.
Run the installer. Right-click the tray icon to manage connections.

Create TCP Routes

Use tcp+https:// in the route From field and tcp:// in To. Example:

routes:
- from: tcp+https://redis.corp.example.com:6379
to: tcp://redis.internal.example.com:6379
policy:
- allow:
or:
- email:
is: contractor@notexample.com
- claim/groups: 'datascience@example.com'

Points to remember:

  • HTTP-specific settings (like regex_rewrite_pattern) do not apply to TCP routes.
  • Pomerium encrypts traffic from user to proxy. If you want full end-to-end encryption, ensure the upstream also runs TLS or mTLS.
  • The port in From (e.g., :6379) differentiates multiple routes on the same domain, even though requests typically arrive on port 443.

Create a New Route and select TCP. Fill in From (for example, tcp+https://ssh.example.com:22) and To (tcp://127.0.0.1:22), then define policy. Click Publish to apply changes.

Example TCP route for SSH

info

Long-lived

connections

When you create a TCP or Websocket connection, Pomerium validates the access policy at the time the connection is made.

Currently, there is no mechanism in place to terminate long-running connections if a policy becomes invalid.

Advanced TCP Usage

Listen configuration

When using the CLI, specify --listen to set a custom local address/port. If you use -, the CLI pipes STDIN/STDOUT directly (useful for ssh -o ProxyCommand).

Bastion host style

If the route is not publicly resolvable or Pomerium is on a non-standard port:

from: tcp+https://proxy.corp.example.com:8443/redis.internal.example.com:6379

Then:

pomerium-cli tcp tcp+https://proxy.corp.example.com:8443/redis.internal.example.com:6379

Proxy chaining

If you want Pomerium to send an HTTP CONNECT to another proxy, use an HTTP scheme in To:

routes:
- from: tcp+https://example.corp.com:10002
to: http://another-proxy.corp.com:10003

Create UDP Routes

Starting with v0.29, Pomerium supports UDP. Use udp+https:// in From and udp:// in To:

routes:
- from: udp+https://time.corp.example.com:13
to: udp://time.internal.example.com:13
policy:
- allow:
or:
- email:
is: contractor@notexample.com
- claim/groups: 'datascience@example.com'
  • CONNECT-UDP is used internally, so no HTTP-specific settings apply.
  • The port in From (e.g. :13) is for route selection; inbound traffic is usually on 443.
  • Latency can matter for DNS or real-time apps. Keep Pomerium near users to minimize round-trip times.

Connecting via Pomerium CLI

TCP Example

pomerium-cli tcp ssh.example.com:22
# listening on 127.0.0.1:52672
ssh 127.0.0.1 -p 52672

Or set your own local port:

pomerium-cli tcp ssh.example.com:22 --listen :2222
ssh 127.0.0.1 -p 2222

If you are not logged in yet, your browser opens for IdP authentication.

SSH ProxyCommand

ssh -o ProxyCommand='pomerium-cli tcp --listen - %h:%p' ssh.example.com

UDP Example

pomerium-cli udp dns.example.com:53
# listening on 127.0.0.1:52544
dig @127.0.0.1 -p 52544 google.com
pomerium-cli tcp [destination] [flags]

Flags

FlagsDescriptionType
#--alternate-ca-pathPath to CA certificate to use for HTTP requests.string
#--browser-cmdCustom browser command to run when opening a URL.string
#--ca-certPath to CA certificate to use for HTTP requests.string
#--client-cert(optional) PEM-encoded client certificate.string
# --client-key(optional) PEM-encoded client certificate key.string
# --client-cert-from-store(optional) If provided, pomerium-cli will attempt to use a client certificate from the system trust store (macOS and Windows only), searching for a certificate based on the trusted CA names advertised by Pomerium in the TLS handshake.none
# --client-cert-issuer(optional) When used in combination with --client-cert-from-store, restricts the client certificate search based on a particular attribute of the certificate's Issuer name.string
# --client-cert-subject(optional) When used in combination with --client-cert-from-store, restricts the client certificate search based on a particular attribute of the certificate's Subject name.string
#--disable-tls-verificationDisables TLS verification.none
#-h, --helpHelp for tcp.none
#--listenLocal address to start a listener on (default "127.0.0.1:0").string
#--pomerium-urlThe URL of the Pomerium server to connect to.string
#-v, --versionVersion for pomerium-cli.none

Certificate name filters

The certificate name filter syntax is attribute=value. A name filter can accept only one name attribute. The value must be an exact match (not a substring match). Make sure to quote name filters as appropriate for your shell.

For example, --client-cert-issuer "CN=My Trusted CA" would filter for a certificate directly issued by a CA with the Common Name "My Trusted CA".

Or, --client-cert-subject "OU=My Department" would filter for a certificate whose Subject name contains the Organizational Unit Name "My Department".

The supported name attributes are:

  • commonName (CN)
  • countryName (C)
  • localityName (L)
  • organizationName (O)
  • organizationalUnitName (OU)
  • postalCode
  • serialNumber
  • stateOrProvinceName (ST)
  • streetAddress (STREET)

Either the long or abbreviated attribute name may be used (for example, localityName=New York or L=New York).

Values are case sensitive: L=new york will not match the Locality Name "New York".

Desktop client steps

If you haven't, install Pomerium Desktop.

Then, add a connection by filling in the fields defined below:

  • Name: A local name for the route
  • Destination: Matches the From value of the route, without the protocol. Always include the port specified in the route, and do not include the https:// protocol.
  • Local Address: The local address and port number from which to access the service locally. If left blank, the client will choose a random port to listen to on the loopback address.
  • Tags: Customizable tags to sort and organize TCP routes

Adding a new connection in the Pomerium Desktop client

Advanced Settings

  • Pomerium URL: The Pomerium Proxy service address. This is required if the Destination URL can't be resolved from DNS or a local hosts entry, or if the Proxy service uses a non-standard port.
  • Disable TLS Verification: Allows untrusted certificates from the Pomerium gateway
  • Client Certificates: For routes that enforce mTLS, you can set a client certificate manually or automatically search the OS certificate store for a trusted certificate (note: macOS and Windows only).

Reviewing the Advanced Settings in the Pomerium Desktop client

Pomerium CLI steps

If you haven't, install Pomerium CLI.

Then, connect to a TCP route:

  1. Invoke pomerium-cli with the tcp option, and provide the route to your service (as defined in from in your Route specification).

    $ pomerium-cli tcp ssh.localhost.pomerium.io:22
    2023/10/02 11:29:22 listening on 127.0.0.1:53656

    You can optionally supply an address and/or port to the listen flag:

    $ pomerium-cli tcp ssh.localhost.pomerium.io:22 --listen :2222
    2023/10/02 11:30:03 listening on [::]:2222
  2. Connect to your service using the local address and port specified in the output of pomerium-cli:

    ssh 127.0.0.1 -p 2222
  3. When the connection starts, the CLI will open your browser and direct you to your Identity Provider to authenticate your session. Once authenticated, the connection will continue and you can close the browser window.

  4. In this example, since we are using SSH we can consolidate the TCP and SSH connections into a single command:

    ssh -o ProxyCommand='pomerium-cli tcp --listen - %h:%p' ssh.localhost.pomerium.io

Best Practices

  • mTLS
    If the upstream requires mutual TLS, both CLI and Desktop can supply client certs.
  • Multiple services on one domain
    Use different ports in From (e.g. :22, :6379, :3306, :53).
  • Performance
    For latency-sensitive protocols (like DNS over UDP), minimize round trips by placing Pomerium near users.
  • Logging and Audit
    Pomerium logs each connection attempt, including identity. This provides an audit trail.
  • Long-lived sessions
    Non-HTTP connections remain open as long as your Pomerium session is valid.

Further Reading

Pomerium unifies access for HTTP, TCP, and UDP behind a single identity-based control plane. This gives you SSO, granular authorization, and consistent security logs for all your applications and services, without needing a separate VPN or manual tunnels.