TLS Termination
Overview
This module enables you to configure TLS termination at either your upstream service, the ngrok edge or the ngrok agent. It also allows you to configure the parameters of that termination.
By default, TLS endpoints terminate TLS connections at your upstream service. This means that by default, ngrok does not terminate TLS connections, your upstream service must handle TLS termination.
Terminating TLS at the ngrok edge is a good choice in the following circumstances:
- You want ngrok to automatically manage TLS certificate provisioning for you.
- You want ngrok to enforce Mutual TLS. TLS Termination is required to enforce mTLS.
- You want to accelerate TLS termination by executing it at ngrok's edge, closer to your clients
- Your upstream service can't terminate TLS. This is common for legacy software.
Example Usage
Terminate at upstream service
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok tls 443 \
--terminate-at upstream \
--url app.example.com
tunnels:
example:
proto: tls
domain: app.example.com
addr: 443
terminate_at: upstream
ssh -R app.example.com:443:localhost:443 v2@connect.ngrok-agent.com tls
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.TLSEndpoint(
config.WithDomain("app.example.com"),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
proto: "tls",
domain: "app.example.com",
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
proto="tls",
domain="app.example.com")
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.tls_endpoint()
.domain("app.example.com")
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
TLS endpoints are not supported by the Kubernetes Ingress Controller
Terminate at ngrok edge
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok tls 80 --terminate-at edge
tunnels:
example:
proto: tls
addr: 80
terminate_at: edge
SSH does not support termination at the edge
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
return ngrok.Listen(ctx,
config.TLSEndpoint(
config.WithTLSTermination(
config.WithTLSTerminationAt(config.TLSAtEdge),
),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
proto: "tls",
crt: "",
key: "",
});
console.log(`Ingress established at: ${listener.url()}`);
})();
An empty certificate and key will default to the ngrok edge's automatically provisioned keypair.
Javascript SDK Docs:
import ngrok
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
proto="tls"
crt=bytearray(),
key=bytearray())
print(f"Ingress established at: {listener.url()}");
An empty certificate and key will default to the ngrok edge's automatically provisioned keypair.
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.tls_endpoint()
.termination(Bytes::new(), Bytes::new())
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
An empty certificate and key will default to the ngrok edge's automatically provisioned keypair.
Rust Crate Docs:
TLS endpoints are not supported by the Kubernetes Ingress Controller
Terminate at ngrok agent
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok tls 80 \
--terminate-at agent \
--url app.example.com \
--crt /path/to/app-example-com-crt.pem \
--key /path/to/app-example-com-key.pem
tunnels:
example:
proto: tls
terminate_at: agent
domain: app.example.com
addr: 80
crt: /path/to/app-example-com-crt.pem
key: /path/to/app-example-com-key.pem
SSH does not support termination at the agent
The Go SDK does not support TLS termination at the SDK
The Javascript SDK does not support TLS termination at the SDK.
The Python SDK does not support TLS termination at the SDK.
The Rust SDK does not support TLS termination at the SDK.
TLS endpoints are not supported by the Kubernetes Ingress Controller
Custom cert at ngrok edge
- Agent CLI
- Agent Config
- SSH
- Go
- Javascript
- Python
- Rust
- Kubernetes Controller
ngrok tls 80
--url your-name.ngrok.app \
--terminate-at edge \
--crt /path/to/app-example-com-crt.pem \
--key /path/to/app-example-com-key.pem
tunnels:
example:
proto: tls
terminate_at: edge
domain: app.example.com
addr: 80
crt: /path/to/app-example-com-crt.pem
key: /path/to/app-example-com-key.pem
SSH does not support termination at the edge
import (
"context"
"net"
"golang.ngrok.com/ngrok"
"golang.ngrok.com/ngrok/config"
)
func ngrokListener(ctx context.Context) (net.Listener, error) {
crtBytes := os.ReadFile("/path/to/app-example-com-crt.pem")
keyBytes := os.ReadFile("/path/to/app-example-com-key.pem")
return ngrok.Listen(ctx,
config.TLSEndpoint(
config.WithDomain("example.ngrok.app"),
config.WithTLSTermination(
config.WithTLSTerminationAt(config.TLSAtEdge),
config.WithTLSTerminationKeyPair(certBytes, keyBytes),
),
),
ngrok.WithAuthtokenFromEnv(),
)
}
Go Package Docs:
const ngrok = require("@ngrok/ngrok");
const fs = require("fs");
(async function () {
const listener = await ngrok.forward({
addr: 8080,
authtoken_from_env: true,
proto: "tls",
crt: fs.readFileSync("/path/to/app-example-com-crt.pem", "utf8"),
key: fs.readFileSync("/path/to/app-example-com-key.pem", "utf8"),
});
console.log(`Ingress established at: ${listener.url()}`);
})();
Javascript SDK Docs:
import ngrok
def load_file(name):
with open(name, "r") as crt:
return bytearray(crt.read().encode())
listener = ngrok.forward("localhost:8080", authtoken_from_env=True,
proto="tls",
crt=load_file("/path/to/app-example-com-crt.pem"),
key=load_file("/path/to/app-example-com-key.pem"))
print(f"Ingress established at: {listener.url()}");
Python SDK Docs:
use ngrok::prelude::*;
async fn listen_ngrok() -> anyhow::Result<impl Tunnel> {
let cert: &[u8] = load_bytes!("/path/to/app-example-com-crt.pem");
let key: &[u8] = load_bytes!("/path/to/app-example-com-key.pem");
let sess = ngrok::Session::builder()
.authtoken_from_env()
.connect()
.await?;
let tun = sess
.tls_endpoint()
.domain("app.example.com")
.termination(cert.into(), key.into())
.termination(
.listen()
.await?;
println!("Listening on URL: {:?}", tun.url());
Ok(tun)
}
Rust Crate Docs:
TLS endpoints are not supported by the Kubernetes Ingress Controller
Behavior
Certificates
The following section will help you resolve any certificate errors you run into while using TLS termination. Because TLS endpoints are flexible enough to terminate TLS at either the ngrok edge, the ngrok agent or your upstream service, you may encounter certificate errors. Understanding how the ngrok platform handles certificates can help resolve them quickly.
When you terminate TLS at the ngrok edge, you may choose between bringing your own certificates and allowing ngrok to automatically provision certificates for you from an ACME provider like Let's Encrypt. If you use automatic certificates, ngrok will choose a matching, valid certificate for you and it will provision and renew that, if necessary. This works even if you're listening on a randomly-chosen ngrok domain.
When using Zero-Knowledge TLS by terminating at the ngrok agent or at your upstream service, you should always specify a domain. If you don't specify a domain to listen on, you won't have already provisioned a signed certificate for that domain. Make sure that the certificate you're using exactly matches the name of the domain you've asked ngrok to listen on.
Zero-Knowledge TLS
When TLS is not terminated at the ngrok edge, then your TLS connections are end-to-end encrypted (E2EE) which we call Zero-Knowledge TLS. When your TLS endpoints operate in this mode, the ngrok edge can not see the payloads that transfer through your endpoints.
The ngrok edge enables Zero-Knowledge TLS by using the SNI data of incoming TLS connections to route them to the proper endpoint without terminating TLS. The unterminated connections are forwarded all the way through to your upstream application which is then responsible for TLS termination.
Zero-Knowledge TLS at the Agent
If the service you are exposing does not support TLS termination, you can still take advantage of Zero-Knowledge TLS. The ngrok agent can terminate TLS for you allowing you to encrypt your traffic end-to-end without reconfiguring your upstream service. This is often a good choice if your upstream service is a piece of legacy software that doesn't support TLS or if it's operated out of your control.
Specify --terminate-at agent
with both the --crt
and --key
command-line
options when starting the agent to specify the filesystem paths to your TLS
certificate and key and the ngrok agent will take care of terminating TLS
connections for you.
TLS Version Choice
If you configure ngrok to do TLS termination, either at the edge or in the agent, it will prefer to use TLS 1.3 if the client supports it.
When terminating at the ngrok edge, a minimum supported version of TLS 1.2 is chosen by default. Clients that do not support TLS 1.2 will not be able to connect. This value is configurable when using Edges.
When terminating at the ngrok agent, a minimum supported version of TLS 1.2 is chosen by default. Clients that do not support TLS 1.2 will not be able to connect. This value is not configurable.
Reference
Configuration
Parameter | Default | Description |
---|---|---|
Terminate At | upstream | Where to terminate TLS. One of: edge , upstream , agent |
Minimum TLS Version | 1.2 | The minimum version of TLS to negotiate. Clients which do not support at least this version of TLS will be unable to connect. |
Errors
This module does not return any errors.
Events
No event data is captured for this module on TLS endpoints.
Edges
TLS Termination is a supported TLS Edge module.
Pricing
This module is available on the Enterprise plan.
Try it out
This documentation is incomplete. Please check back later, we appreciate your patience.