# TCP over HTTP Support

In addition to managing HTTP based applications, Pomerium can be used to protect non-HTTP systems with the same consistent authorization policy. This is achieved by tunneling TCP over HTTP with the help of a client side command built into [`pomerium-cli`](https://www.pomerium.com/docs/deploy/clients.md).

For SSH specifically, Pomerium now offers [**Native SSH Access**](https://www.pomerium.com/docs/capabilities/native-ssh-access.md) with OAuth authentication and ephemeral certificates. This provides a more streamlined SSH experience without tunneling. Use TCP tunneling for other protocols or when you cannot modify SSH server configurations.

Operations and engineering teams frequently require access to lower level administrative and data protocols such as SSH, RDP, Postgres, MySQL, Redis, etc.

Internally, Pomerium uses the [`CONNECT` method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT) to establish the TCP tunnel.

To minimize issues with TCP support, Pomerium should not be placed behind another HTTP proxy. Instead, configure your load balancer in L4 or TCP mode.

Otherwise, the HTTP proxy in front of Pomerium must know how to properly handle the `CONNECT` command and proxy it upstream. This capability will be specific to each proxy implementation.

## Two ports, one transport

A TCP route involves two ports that are easy to confuse:

1. The **transport port** the client uses to reach Pomerium. This should match the port in Pomerium's [`address`](https://www.pomerium.com/docs/reference/address.md) setting, unless you are using another reverse proxy in front of Pomerium that listens on a different port. If this is different from the default HTTPS port 443, pass `--pomerium-url` and include the port number in the URL.
2. The **route-match port** in `from: tcp+https://HOST:PORT`. This is the port carried in the HTTP CONNECT `:authority` header and is what Pomerium uses to match the right route. Use the destination service port here (`:22` for SSH, `:5432` for Postgres, `:3389` for RDP, etc.).

The same hostname can carry multiple TCP routes for different services by varying the route-match port. Do not change the `from` URL to `:443` only to satisfy an outbound firewall rule. Use the service port your client requests instead; for a typical SSH configuration, that is `:22`. If the client intentionally requests `HOST:443`, then the route-match port must also be `:443`.

For a complete SSH example with an L4 edge in front of Pomerium, see [SSH over port 443 through an L4 edge](https://www.pomerium.com/docs/guides/ssh-tcp-l4-passthrough.md).

## Configure TCP routes

TCP configuration is simple. Just specify the correct scheme and ports in your route [`to`](https://www.pomerium.com/docs/reference/routes/to.md) and [`from`](https://www.pomerium.com/docs/reference/routes/from.md) fields.

Example:

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

When creating TCP routes, note the following:

- When configuring a TCP route, any HTTP specific settings such as `regex_rewrite_pattern` or `set_request_headers` have no effect.
- While data is encrypted from a user system to Pomerium's proxy, the underlying application protocol must also support encryption for data to be fully encrypted end-to-end. Otherwise, traffic from the Pomerium Proxy service to the upstream service will be unencrypted.
- The ports in `from` and `to` are independent. Users only need to know the `from` URL to connect. The `to` can be changed without end user participation.
- The port defined in `from` does not dictate what port the tunneled traffic uses. This will always be the port defined by [`address`](https://www.pomerium.com/docs/reference/address.md) in your Pomerium configuration (`443` by default). The port instead differentiates multiple routes to the same hostname for different services.

## Connect to TCP Routes

While HTTP routes can be consumed with just a normal browser, `pomerium-cli` or Pomerium Desktop must serve as a proxy for TCP routes. See [Pomerium Desktop and CLI Clients](https://www.pomerium.com/docs/deploy/clients.md) for more information.

To connect, you normally need just the external hostname and port of your TCP route:

```shell-session
$ pomerium-cli tcp redis.localhost.pomerium.io:6739
2023/10/02 11:19:59 listening on 127.0.0.1:52046
```

By default, `pomerium-cli` will start a listener on loopback on a random port.

On first connection, you will be sent through a standard Pomerium HTTP authentication flow. After completing this, your TCP connection should be established!

```shell-session
$ redis-cli -h localhost -p 52046
localhost:52046> keys *
(empty array)
localhost:52046>
```

## Advanced capabilities

### Listen configuration

You may specify an optional address and port for the `tcp` command to listen on.

`-` specifies that STDIN and STDOUT should be directly attached to the remote TCP connection. This is useful for [SSH](https://www.pomerium.com/docs/capabilities/non-http/examples/ssh.md#tunnel-and-connect-simultaneously) or for sending data through a shell pipe.

### Bastion host

If the Pomerium proxy is not reachable through port `443` or the route is not in external DNS, you can use Pomerium as a bastion host using the extended TCP URL syntax in your route definition:

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

And then using the same URL in the pomerium-cli command invocation:

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

The command above connects to `https://pomerium.corp.example.com:8443` and then requests the TCP route for `redis.internal.example.com:6379`.

### Proxy chaining support

The TCP route example above uses a `to` URL with the scheme `tcp://`. For a TCP route like this, Pomerium will open a raw TCP connection to the upstream service.

Alternatively, you can configure a TCP route to proxy an HTTP CONNECT request to the upstream service. This is useful if you want to place another HTTP-to-TCP proxy behind Pomerium.

To configure Pomerium to chain TCP connection requests:

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

The guides below demonstrate how to proxy TCP tunnels with Pomerium to well-known services:

- [**Git**](https://www.pomerium.com/docs/capabilities/non-http/examples/git.md)
- [**Microsoft SQL**](https://www.pomerium.com/docs/capabilities/non-http/examples/ms-sql.md)
- [**MySQL and MariaDB**](https://www.pomerium.com/docs/capabilities/non-http/examples/mysql.md)
- [**RDP**](https://www.pomerium.com/docs/capabilities/non-http/examples/rdp.md)
- [**Redis**](https://www.pomerium.com/docs/capabilities/non-http/examples/redis.md)
- [**SSH**](https://www.pomerium.com/docs/capabilities/non-http/examples/ssh.md)
- [**SSH over port 443 through an L4 edge**](https://www.pomerium.com/docs/guides/ssh-tcp-l4-passthrough.md) - for environments restricted to outbound port 443
