Skip to main content

Securing TCP-based services

Learn how to use Pomerium's TCP Proxying support to connect to TCP services, such as databases and non-HTTP protocols.

Background

When replacing a traditional VPN, there are often non-HTTP based applications you still need to reach. Pomerium can provide the same type of protection to these services with Pomerium CLI, a client-side application to proxy TCP connections.

Authentication and authorization configuration is shared with standard HTTP routes, and the underlying transport is still encrypted between the end-user and Pomerium.

note
  • Pomerium authorizes TCP connections on a per-connection basis (unlike HTTP requests, which it authorizes on a per-request basis)
  • Pomerium only authorizes the TCP connection; it does not interact with application level authorization systems

Overview

Review the steps below to understand how you will secure TCP connections with Pomerium:

  1. Run Pomerium CLI's tcp command in your workstation to listen for TCP connections on loopback
  2. When an inbound connection is made, Pomerium CLI proxies the connection through pomerium, authenticating the user if needed
  3. Pomerium authorizes the connection and forwards it to the upstream service

Before you start

To complete this guide, you need:

Certificates (optional)

If you want to generate your own locally trusted certificates with mkcert, run the following command:

mkcert -install
mkcert '*.localhost.pomerium.io'

This will install a trusted CA and generate a new wildcard certificate:

  • _wildcard.localhost.pomerium.io.pem
  • _wildcard.localhost.pomerium.io-key.pem

Configure

Pomerium

Add the configuration below to your configuration file:

config.yaml
authenticate_service_url: https://authenticate.pomerium.app

# Uncomment to use certificates (optional)
# certificates:
# - cert: /pomerium/cert.pem
# key: /pomerium/key.pem

databroker_storage_type: postgres
databroker_storage_connection_string: postgresql://postgres:postgres@pgsql:5432

routes:
- from: tcp+https://redis.localhost.pomerium.io:6379
to: tcp://redis:6379
policy:
- allow:
or:
- domain:
is: example.com

- from: tcp+https://ssh.localhost.pomerium.io:22
to: tcp://ssh:2222
policy:
- allow:
or:
- domain:
is: example.com

- from: tcp+https://pgsql.localhost.pomerium.io:5432
to: tcp://pgsql:5432
policy:
- allow:
or:
- domain:
is: example.com
note

Make sure you update each example.com instance to match the domain of your email address.

Docker Compose

Create a docker-compose.yaml file to run Pomerium and the services you will connect to over TCP.

The following services are included in the Docker Compose file:

  • Redis
  • SSH
  • Postgres
docker-compose.md
version: "3"
services:
pomerium:
image: pomerium/pomerium:latest
volumes:
# Uncomment to mount certificates (optional)
# - ./_wildcard.localhost.pomerium.io.pem:/pomerium/cert.pem:ro
# - ./_wildcard.localhost.pomerium.io-key.pem:/pomerium/key.pem:ro
- ./config.yaml:/pomerium/config.yaml:ro
ports:
- 443:443

redis:
image: redis:latest
expose:
- 6379

ssh:
image: linuxserver/openssh-server:latest
expose:
- 2222
environment:
PASSWORD_ACCESS: "true"
USER_PASSWORD: supersecret
USER_NAME: user

pgsql:
image: postgres
restart: always
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=postgres
- POSTGRES_DB=postgres
expose:
- 5432

Connect

To connect to your service, ensure pomerium-cli is in your $PATH and run the tcp command, specifying the service you wish to reach.

pomerium-cli tcp [hostname]:[port]

pomerium-cli will select a random port on localhost by default, but you can specify a port manually if desired. Keep reading for some specific application examples using the sample docker-compose.yaml.

caution

If you haven't configured a trusted certificate, you'll need to add the --disable-tls-verification flag to the pomerium-cli commands below.

Without this flag, TCP connections will fail unless you include a trusted CA and certificate in your configuration.

In a production environment, it's critical that you configure Pomerium to use correct server certificates.

Redis

Start a proxy to Redis in the background:

$ pomerium-cli tcp redis.localhost.pomerium.io:6379 --listen localhost:6379
2023/10/02 12:20:05 listening on 127.0.0.1:6379

Start the Redis client:

$ redis-cli info
# Server
redis_version:7.0.5
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:d9291579292e26e3
redis_mode:standalone
os:Linux 6.3.13-linuxkit aarch64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:10.2.1
process_id:1
process_supervised:no
run_id:e4c843df14511765f0e6284690be2f10c6b39e28
tcp_port:6379
server_time_usec:1696349965506132
uptime_in_seconds:75
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:1851149
executable:/data/redis-server
config_file:
io_threads_active:0

Postgres

In your Docker Compose file, postgres is the configured password for the postgres user and database.

Start a proxy to Postgres in the background:

$ pomerium-cli tcp pgsql.localhost.pomerium.io:5432 --listen localhost:5432
2023/10/03 12:22:08 listening on 127.0.0.1:5432

After password authentication, connect and list the schemas:

$  psql -h localhost -W -U postgres -c '\dn'
Password:
List of schemas
Name | Owner
----------+-------------------
pomerium | postgres
public | pg_database_owner
(2 rows)

SSH

Configure

To configure your SSH client to use Pomerium's TCP support for SSH routes, create the following entry in your ssh_config or ~/.ssh/config:

Host *.localhost.pomerium.io
ProxyCommand pomerium-cli tcp --listen - %h:%p
  • Be sure to substitute your domain for localhost.pomerium.io
  • Be sure pomerium-cli is in your $PATH
info

SSH clients can use external programs to establish a connection to a host. Most frequently, this is for using an SSH jump host to reach a target system. However, you can use any transport application. You can use Pomerium CLI's tcp command in conjunction with this configuration.

See the following links for more information:

Connect

Start a proxy to the SSH client in the background:

$ pomerium-cli tcp ssh.localhost.pomerium.io:22 --listen :2222
2023/10/03 12:29:17 listening on 127.0.0.1:50741

Initiate the SSH connection:

$ ssh myuser@ssh.localhost.pomerium.io -p 2222
myuser@ssh.localhost.pomerium.io's password:
Welcome to OpenSSH Server

4e737e02c43f:~$

In your example Docker Compose file, you have an SSH server configured with supersecret as the password for myuser.

That's it! A Pomerium proxy will be started automatically whenever you SSH to a host under localhost.pomerium.io.