I am trying to configure my Ziti router to forward HTTPS traffic from a port (for example, 825) to an external domain (destination.domain.zone). Essentially, I would like the router to terminate the TCP connection. I would like to expose a public service behind the Ziti network (in the spirit of cloudflared tunnel)
However, the router does not listen on port 825, and I am unsure if it is possible.
Currently, my configuration is as follows:
listeners:
# bindings of edge and tunnel requires an "edge" section below
- binding: edge
address: tls:0.0.0.0:3022
options:
advertise: pub.domain.zone:3022
connectTimeoutMs: 5000
getSessionTimeout: 60
- binding: tunnel
options:
mode: proxy
services:
- http:825
service:
{
"name": "http",
"roleAttributes": [
"rtrhosted"
],
"configs": [
"65pvdXVIM8dbD8TMl7BWtb"
],
"encryptionRequired": true,
"terminatorStrategy": "smartrouting",
"tags": {}
}
config: host.v1
{
"name": "http",
"configTypeId": "NH5p4FpGR",
"data": {
"protocol": "tcp",
"address": "destination.domain.zone",
"port": 825,
"httpChecks": [],
"portChecks": []
},
"tags": {}
}
policy:
{
"name": "http",
"appData": "",
"edgeRouterRoles": [
"#all"
],
"serviceRoles": [
"#rtrhosted"
],
"semantic": "AnyOf",
"tags": {}
}
With OpenZiti, there's nothing special to do when forwarding HTTPS or any TLS connection. It works identically to HTTP. You just forward the connection to the port and HTTPS will handle the rest.
The most likely cause of a problem is a mismatch between what host you intercept and what certificates are presented by the server at the far end. You can either add the intercept address to your certificate or you can change the intercept to match the certificate and then it'll "just work".
Apparently I am doing something wrong.
I have added the following to the standard configuration.
diff -u /var/lib/ziti-router/config.yml.031225 /var/lib/ziti-router/config.yml
- binding: tunnel
options:
mode: host #tproxy|host
+ - binding: tunnel
+ options:
+ mode: proxy
+ services:
+ - http
+
But the router dislikes it
{
"error": "failed to initialize proxy interceptor: invalid argument 'http'",
"file": "github.com/openziti/ziti/router/xgress_edge_tunnel/factory_wrapper.go:157",
"func": "github.com/openziti/ziti/router/xgress_edge_tunnel.NewFactoryWrapper.func2",
"level": "fatal",
"msg": "error starting",
"time": "2025-12-05T14:09:55.048Z"
}
I have added only host.v1. Do I need create intercept.v1 configuration?
ziti edge create service http -c gcloud -a rtrhosted
ziti edge list configs 'limit none' | grep gcloud
│ 65pvdXVIM8dbD8TMl7BWtb │ gcloud │ host.v1 │
Adding intercept.v1 does not help. The router does not start.
{
"error": "failed to initialize proxy interceptor: invalid argument 'http'",
"file": "github.com/openziti/ziti/router/xgress_edge_tunnel/factory_wrapper.go:157",
"func": "github.com/openziti/ziti/router/xgress_edge_tunnel.NewFactoryWrapper.func2",
"level": "fatal",
"msg": "error starting",
"time": "2025-12-05T16:51:24.801Z"
}
ziti edge list services 'limit none' | grep http
│ 4aC7lOakVafl0MQ1detveJ │ http │ true │ smartrouting │ rtrhosted │
ziti edge list configs 'limit none' | grep gcloud
│ 5ZxChbhSkMnkPKBrUPmSFo │ gcloud-intercept │ intercept.v1 │
│ 65pvdXVIM8dbD8TMl7BWtb │ gcloud │ host.v1 │
Does router support the proxy mode?
/opt/openziti/bin/ziti router run config.yml --extend
You need to specify the port that you want the service to be intercepted on. For example:
- binding: tunnel
options:
mode: proxy
services:
- http:80
Also the intercept configuration is irrelevant when using proxy mode. Basically the port is your intercept configuration with proxy mode.
edit:
I forgot to mention that controller v1.7.0 or later supports a “proxy.v1” configuration type. Services with a proxy.v1 configuration do not need to be listed in the configuration file at all.
Thank you. It starts now, but the router does not open this port. It only listens on its edge port 3022.
listeners:
# bindings of edge and tunnel requires an "edge" section below
- binding: edge
address: tls:0.0.0.0:3022
options:
advertise: pub.domain.zone:3022
connectTimeoutMs: 5000
getSessionTimeout: 60
- binding: tunnel
options:
mode: host #tproxy|host
- binding: tunnel
options:
mode: proxy
services:
- http:99
ziti version
v1.6.10
I see 1.7.0 contains the binding. How do I specify the binding in my case?
{
"port": 8080,
"protocols": ["tcp"],
"binding": "0.0.0.0"
}
I start ziti router run (not ziti tunnel proxy)
I’m not sure, but I wonder if the router will only process a single binding of a given type (e.g. “tunnel”)?
At any rate, the binding that uses “host” mode is not needed, can you try removing it? “host” mode really exists for scenarios where the user wants to exclusively host services, and not intercept. Hosting is implied for “proxy” and “proxy” modes.
The “binding” is only available when using the config type. When using the config file the bind address is always 0.0.0.0.
Removing the host mode has no effect; my router continues to operate as usual, listening only on port 3022.
It appears that I may need to run ziti tunnel in order to enable reverse proxy functionality.
listeners:
# bindings of edge and tunnel requires an "edge" section below
- binding: edge
address: tls:0.0.0.0:3022
options:
advertise: pub.domain.zone:3022
connectTimeoutMs: 5000
getSessionTimeout: 60
- binding: tunnel
options:
mode: proxy
services:
- http:99
I would like to ask a related question: is it possible to run ziti tunnel proxy using the same configuration file as the router? For example by manually modifying the ziti-router.service?
ExecStartPre=/opt/openziti/etc/router/entrypoint.bash check config.yml
ExecStart=/opt/openziti/bin/ziti router run config.yml ${ZITI_ARGS}
ziti router implements the tunnel binding, so ziti tunnel is not necessary (in fact it’s slated for deprecation). Is the tunneler enabled on this router? You can check with this:
ziti edge list ers -j | jq -r '.data[] | .name, .isTunnelerEnabled'
You would have passed the -t option when you ran ziti edge create router. If you forgot the -t option when creating it you can update the router with:
ziti edge update er MY-ROUTER -t
I have enabled the tunneler
ziti edge update er ovh78 -t
Unfortunately, my router is still only binding to port 3022.
Edge Router:
"isTunnelerEnabled": true,
"isVerified": true,
"roleAttributes": [
"clients",
"eu",
"hosts"
],
Service Policy: http-dial
"identityRoles": [
"#clients"
],
"identityRolesDisplay": [
{
"name": "#clients",
"role": "#clients"
}
],
Service Policy: http-bind
"identityRoles": [
"#hosts"
],
"identityRolesDisplay": [
{
"name": "#hosts",
"role": "#hosts"
}
],
"name": "http-bind",
I have updated the policy, and the router is now successfully opening the proxy port.
So what was wrong with “hosts” and “clients” router’s attributes
"identityRolesDisplay": [
{
"name": "@ovh78",
"role": "@wl1mwBnP8p"
}
],
"name": "http-bind",
"identityRolesDisplay": [
{
"name": "@ovh78",
"role": "@wl1mwBnP8p"
}
],
"name": "http-dial",
On Linux, the router process needs kernel capability net bind service to listen on a privileged low port like 99/tcp.
Great, I’m glad it’s working! It looked to me like you hadn’t associated your ‘http’ service with the role attributes from your service policies. Generally the ziti edge policy-advisor command is very helpful for diagnosing policy issues.
Yes, thank you very much. I am still a bit confused about the roles I had initially assigned to the router. It turns out that these roles cannot be used in bind and dial service policies, that I had misunderstood. I have now replaced them with the router’s ID, and it works perfectly.
It seems that the router’s attribute is not the same as the identity attribute.