zuplo 7.0.2 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/articles/connect-to-aws-alb-with-mtls.mdx +1 -1
- package/docs/articles/gke-with-upstream-auth-policy.mdx +1 -1
- package/docs/articles/secure-tunnel.mdx +156 -80
- package/docs/articles/securing-backend-mtls.mdx +2 -1
- package/docs/articles/securing-your-backend.mdx +53 -48
- package/docs/articles/tunnel-advanced.mdx +202 -0
- package/docs/articles/tunnel-services.mdx +165 -0
- package/docs/articles/tunnel-setup.mdx +151 -198
- package/docs/articles/tunnel-troubleshooting.mdx +136 -33
- package/docs/articles/upstream-iam-auth.mdx +100 -0
- package/docs/cli/tunnel-create.mdx +5 -3
- package/docs/cli/tunnel-create.partial.mdx +7 -3
- package/docs/cli/tunnel-delete.mdx +5 -3
- package/docs/cli/tunnel-delete.partial.mdx +7 -3
- package/docs/cli/tunnel-describe.mdx +8 -6
- package/docs/cli/tunnel-describe.partial.mdx +13 -5
- package/docs/cli/tunnel-list.mdx +5 -3
- package/docs/cli/tunnel-list.partial.mdx +7 -3
- package/docs/cli/tunnel-rotate-token.mdx +8 -0
- package/docs/cli/tunnel-rotate-token.partial.mdx +13 -0
- package/docs/cli/tunnel-services-describe.mdx +5 -3
- package/docs/cli/tunnel-services-describe.partial.mdx +7 -3
- package/docs/cli/tunnel-services-update.mdx +8 -0
- package/docs/cli/tunnel-services-update.partial.mdx +13 -0
- package/package.json +5 -5
- package/docs/cli/tunnel-rate-token.partial.mdx +0 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Connect to an AWS ALB with mTLS
|
|
3
|
-
sidebar_label:
|
|
3
|
+
sidebar_label: AWS ALB
|
|
4
4
|
description:
|
|
5
5
|
Configure Zuplo to authenticate to an AWS Application Load Balancer using a
|
|
6
6
|
mutual TLS client certificate, so the ALB only accepts traffic that comes
|
|
@@ -1,84 +1,160 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Secure Tunnel
|
|
3
|
+
sidebar_label: Overview
|
|
3
4
|
---
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
Zuplo
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
service
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
6
|
+
<EnterpriseFeature name="Secure tunneling" />
|
|
7
|
+
|
|
8
|
+
A secure tunnel connects your Zuplo gateway to a backend API that isn't
|
|
9
|
+
reachable from the public internet. You run a small tunnel service inside your
|
|
10
|
+
network, it opens an outbound connection to Zuplo, and your gateway routes
|
|
11
|
+
requests back through that connection to your private API.
|
|
12
|
+
|
|
13
|
+
Tunnels suit backends that run where IAM roles and mTLS aren't available — bare
|
|
14
|
+
metal, on-premises data centers, or a non-cloud hosting provider. For the other
|
|
15
|
+
ways to secure gateway-to-backend traffic, see
|
|
16
|
+
[Securing your backend](./securing-your-backend.mdx).
|
|
17
|
+
|
|
18
|
+
A tunnel gives you three things:
|
|
19
|
+
|
|
20
|
+
- Your API doesn't need to be exposed on the internet at all, because the tunnel
|
|
21
|
+
dials out rather than accepting inbound connections.
|
|
22
|
+
- All traffic between Zuplo and your API is encrypted.
|
|
23
|
+
- You don't need to configure ingress, firewall rules, or load balancers to let
|
|
24
|
+
gateway traffic in. Install the tunnel and Zuplo routes to it.
|
|
25
|
+
|
|
26
|
+
## How a tunnel works
|
|
27
|
+
|
|
28
|
+
The tunnel is a lightweight service that runs on Linux, most often as a Docker
|
|
29
|
+
container. On startup it authenticates with a token you generate in Zuplo and
|
|
30
|
+
establishes an outbound connection to Zuplo's network.
|
|
31
|
+
|
|
32
|
+
Once connected, the tunnel can reach internal services on your network by their
|
|
33
|
+
private DNS names. You tell Zuplo which of those services to expose by naming
|
|
34
|
+
them in a tunnel configuration file. Your gateway then calls each one through
|
|
35
|
+
the `service://` URL scheme instead of a hostname. A service named
|
|
36
|
+
`payments-api` becomes `service://payments-api`, and the tunnel forwards those
|
|
37
|
+
requests to whatever internal endpoint you mapped it to, such as
|
|
38
|
+
`http://payments.internal:8080`.
|
|
39
|
+
|
|
40
|
+
The following diagram shows the path a request takes. Note that the gateway
|
|
41
|
+
never opens a connection into your network — it reuses the connection the tunnel
|
|
42
|
+
already established.
|
|
43
|
+
|
|
44
|
+
<Diagram height="h-64" direction="horizontal">
|
|
45
|
+
<DiagramNode id="client">API client</DiagramNode>
|
|
46
|
+
<DiagramNode id="gateway" variant="zuplo">
|
|
47
|
+
Zuplo Gateway
|
|
48
|
+
</DiagramNode>
|
|
49
|
+
<DiagramGroup id="private" label="Your private network">
|
|
50
|
+
<DiagramNode id="tunnel" variant="blue">
|
|
51
|
+
Tunnel service
|
|
52
|
+
</DiagramNode>
|
|
53
|
+
<DiagramNode id="api" variant="green">
|
|
54
|
+
Private API
|
|
55
|
+
</DiagramNode>
|
|
56
|
+
</DiagramGroup>
|
|
57
|
+
<DiagramEdge from="client" to="gateway" label="HTTPS" />
|
|
58
|
+
<DiagramEdge from="gateway" to="tunnel" label="service://" animated />
|
|
59
|
+
<DiagramEdge from="tunnel" to="api" fromSide="bottom" toSide="top" />
|
|
60
|
+
</Diagram>
|
|
61
|
+
|
|
62
|
+
Because the tunnel controls which internal endpoints it forwards to, and Zuplo
|
|
63
|
+
controls which projects and environments can use each service, no traffic
|
|
64
|
+
reaches your API until you configure both halves.
|
|
65
|
+
|
|
66
|
+
## Architecture example
|
|
67
|
+
|
|
68
|
+
The following diagram shows a tunnel running in an AWS ECS cluster. The VPC is
|
|
69
|
+
fully private: there's no public IP address and no inbound traffic. The tunnel
|
|
70
|
+
makes an outbound connection to Zuplo, then uses internal DNS to reach the
|
|
71
|
+
private API.
|
|
72
|
+
|
|
73
|
+
<Framed>
|
|
74
|
+
|
|
75
|
+

|
|
76
|
+
|
|
77
|
+
</Framed>
|
|
78
|
+
|
|
79
|
+
The same pattern applies to Azure Container Instances, GCP Cloud Run,
|
|
80
|
+
Kubernetes, and plain Linux hosts. Only the deployment mechanics change.
|
|
81
|
+
|
|
82
|
+
## Security model
|
|
83
|
+
|
|
84
|
+
Zuplo's tunnel is built on [cloudflared](./tunnel-advanced.mdx), Cloudflare's
|
|
85
|
+
open source tunnel connector, and inherits Cloudflare's network for transport.
|
|
86
|
+
Several layers control what can reach your API:
|
|
87
|
+
|
|
88
|
+
- **Token authentication.** Each tunnel has its own secret token. Without it, a
|
|
89
|
+
tunnel instance can't connect.
|
|
90
|
+
- **Per-tunnel access policies.** Each tunnel accepts connections only from the
|
|
91
|
+
Zuplo gateways you authorize.
|
|
92
|
+
- **Per-service scoping.** Each service in your tunnel configuration names the
|
|
93
|
+
projects and environments allowed to call it, so a preview environment can't
|
|
94
|
+
reach a production backend unless you say so.
|
|
95
|
+
- **Edge protections.** Incoming requests terminate on Cloudflare's network,
|
|
96
|
+
which adds DDoS, bot, and threat protection ahead of your gateway.
|
|
97
|
+
- **Gateway policies.** Requests then pass through your Zuplo gateway, where
|
|
98
|
+
your own policies and code apply. Until you configure routes and URL rewrites,
|
|
99
|
+
the gateway sends nothing to your API.
|
|
100
|
+
|
|
101
|
+
Traffic is terminated with TLS at the edge and encrypted for the rest of the
|
|
102
|
+
path to your API. Requests your gateway makes to Zuplo's internal services, such
|
|
103
|
+
as API key management and rate limiting, travel over encrypted connections as
|
|
104
|
+
well.
|
|
105
|
+
|
|
106
|
+
Every request is logged, and you can
|
|
107
|
+
[forward gateway logs](./monitoring-your-gateway.mdx) to the log service of your
|
|
108
|
+
choice.
|
|
109
|
+
|
|
110
|
+
## Availability and scale
|
|
111
|
+
|
|
112
|
+
Each tunnel instance opens several connections to more than one Cloudflare data
|
|
113
|
+
center, so a single instance survives the loss of an individual connection. Run
|
|
114
|
+
at least two instances so the tunnel keeps serving traffic if a host or pod
|
|
115
|
+
fails. Instances that share a token act as replicas of the same tunnel, and
|
|
116
|
+
Zuplo distributes traffic across whichever ones are available.
|
|
117
|
+
|
|
118
|
+
A single instance handles a high request volume, and adding instances or
|
|
119
|
+
enabling autoscaling raises the ceiling further. If you have specific throughput
|
|
120
|
+
requirements, [contact support](mailto:support@zuplo.com).
|
|
121
|
+
|
|
122
|
+
## Network access control
|
|
123
|
+
|
|
124
|
+
The tunnel can reach anything its host can reach, so treat its network position
|
|
125
|
+
as the security boundary. Restrict it to the services you want your gateway to
|
|
126
|
+
call, using whichever mechanism fits your platform:
|
|
127
|
+
|
|
128
|
+
- IAM roles or service accounts scoped to specific resources
|
|
129
|
+
- Network segmentation, security groups, or egress rules
|
|
130
|
+
- Service mesh authorization policies
|
|
131
|
+
|
|
132
|
+
Naming a service in your tunnel configuration is what makes it callable from
|
|
133
|
+
Zuplo. Restricting the host is what limits the damage if the tunnel is
|
|
134
|
+
misconfigured. Do both.
|
|
135
|
+
|
|
136
|
+
## Requirements
|
|
137
|
+
|
|
138
|
+
Running a tunnel requires:
|
|
139
|
+
|
|
140
|
+
- A Linux host or container runtime. The
|
|
141
|
+
[`zuplo/tunnel` image](https://hub.docker.com/r/zuplo/tunnel) is a Linux
|
|
142
|
+
container, and non-containerized deployments must target Linux.
|
|
143
|
+
- Outbound internet access from the tunnel host, so it can reach Zuplo's
|
|
144
|
+
network.
|
|
145
|
+
- Internal DNS resolution from the tunnel host to your backend services.
|
|
146
|
+
|
|
147
|
+
Managed container platforms such as AWS ECS, Azure Container Instances, GCP
|
|
148
|
+
Cloud Run, and Kubernetes run Linux containers by default and need no extra
|
|
149
|
+
configuration.
|
|
150
|
+
|
|
151
|
+
## Next steps
|
|
152
|
+
|
|
153
|
+
- [Set up a tunnel](./tunnel-setup.mdx) — create a tunnel, run the container,
|
|
154
|
+
and expose your first service.
|
|
155
|
+
- [Connect to tunnel services](./tunnel-services.mdx) — call `service://` URLs
|
|
156
|
+
from your gateway code and configuration.
|
|
157
|
+
- [Advanced tunnel configuration](./tunnel-advanced.mdx) — the cloudflared
|
|
158
|
+
foundation, environment variable reference, and self-managed images.
|
|
159
|
+
- [Troubleshoot a tunnel](./tunnel-troubleshooting.mdx) — diagnose a tunnel
|
|
160
|
+
that's down or not passing traffic.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Gateway to Origin mTLS Authentication
|
|
3
|
+
sidebar_label: Gateway to Origin
|
|
3
4
|
---
|
|
4
5
|
|
|
5
6
|
<EnterpriseFeature name="mTLS Client Certificates" />
|
|
@@ -496,7 +497,7 @@ For more information on securing your backend, see:
|
|
|
496
497
|
|
|
497
498
|
- [Securing your Backend](./securing-your-backend.mdx) - Overview of all backend
|
|
498
499
|
security options
|
|
499
|
-
- [Shared Secret / API Key](./securing-your-backend.mdx#
|
|
500
|
+
- [Shared Secret / API Key](./securing-your-backend.mdx#shared-secret-or-api-key) -
|
|
500
501
|
Alternative approach using shared secrets
|
|
501
502
|
- [Secure Tunnels](./secure-tunnel.mdx) - Connect to private backends without
|
|
502
503
|
exposing them to the internet
|
|
@@ -8,10 +8,22 @@ correctly applied to all traffic.
|
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
That means securing the communication between Zuplo and your backend API
|
|
12
|
+
(origin). Several options do this, and the right one depends on where your
|
|
13
|
+
backend runs and what it can already verify.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
| Approach | Use it when | Plan |
|
|
16
|
+
| -------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------- |
|
|
17
|
+
| [Shared secret](#shared-secret-or-api-key) | Your backend can check a header. The most common choice, and the simplest. | All |
|
|
18
|
+
| [IAM authentication](./upstream-iam-auth.mdx) | Your backend runs behind AWS, Azure, or Google Cloud IAM. | Enterprise |
|
|
19
|
+
| [mTLS](./securing-backend-mtls.mdx) | You want both sides to authenticate each other with certificates. | Enterprise |
|
|
20
|
+
| [Secure tunnel](./secure-tunnel.mdx) | Your backend has no public endpoint, or runs on bare metal or on premises. | Enterprise |
|
|
21
|
+
| [Custom networking](#custom-networking-managed-dedicated-only) | You're on managed dedicated and want VPC peering or PrivateLink. | Dedicated |
|
|
22
|
+
|
|
23
|
+
The rest of this page covers the shared secret approach in full. Each other
|
|
24
|
+
approach has its own section in the sidebar.
|
|
25
|
+
|
|
26
|
+
## Shared secret or API key
|
|
15
27
|
|
|
16
28
|
This is the most popular option and is used by companies like Supabase,
|
|
17
29
|
Firebase, and Stripe to secure their own APIs. In this solution the backend
|
|
@@ -95,62 +107,55 @@ app.use((req, res, next) => {
|
|
|
95
107
|
});
|
|
96
108
|
```
|
|
97
109
|
|
|
98
|
-
##
|
|
99
|
-
|
|
100
|
-
This is a new option where you can configure your cloud service (for example,
|
|
101
|
-
GCP or AWS) to trust a JWT token created by the Zuplo runtime. If you're
|
|
102
|
-
interested in using this option please contact us at `support@zuplo.com`.
|
|
110
|
+
## IAM authentication
|
|
103
111
|
|
|
104
|
-
|
|
112
|
+
Let your cloud provider decide whether a request from Zuplo is allowed through.
|
|
113
|
+
Your gateway proves its identity to AWS, Azure, or Google Cloud, and your
|
|
114
|
+
provider's authorization rules apply — so there's no shared secret to
|
|
115
|
+
distribute.
|
|
105
116
|
|
|
106
|
-
|
|
107
|
-
|
|
117
|
+
Where the provider supports it, Zuplo can present a short-lived OIDC token and
|
|
118
|
+
exchange it for temporary credentials, which means no long-lived key exists at
|
|
119
|
+
all. Otherwise the policies use a key or client secret you store as an
|
|
120
|
+
environment variable.
|
|
108
121
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
policy. This uses Microsoft Entra ID (formerly Azure AD) App registrations to
|
|
112
|
-
create a token that Zuplo sends with requests to Azure.
|
|
122
|
+
For the policy for each provider and the trade-off between the two approaches,
|
|
123
|
+
see [IAM authentication for your backend](./upstream-iam-auth.mdx).
|
|
113
124
|
|
|
114
|
-
|
|
115
|
-
[Upstream GCP Service AUth](../policies/upstream-gcp-service-auth-inbound.mdx)
|
|
116
|
-
or [Upstream GCP JWT](../policies/upstream-gcp-jwt-inbound.mdx) policies.
|
|
117
|
-
These use a `service.json` credential to create or issue JWT tokens that Zuplo
|
|
118
|
-
will send to requests to GCP.
|
|
125
|
+
## mTLS authentication
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
Mutual TLS establishes a trust relationship between your gateway and your
|
|
128
|
+
backend using client certificates. Both sides authenticate each other, which
|
|
129
|
+
gives you a zero trust posture between the two.
|
|
121
130
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
To learn how to set up mTLS with client certificates, see the
|
|
129
|
-
[Securing your Backend with mTLS](./securing-backend-mtls.mdx) article. This is
|
|
131
|
+
Zuplo manages the client certificates, presents them on upstream requests, and
|
|
132
|
+
supports rotation and per-environment certificates. See
|
|
133
|
+
[Gateway to origin mTLS authentication](./securing-backend-mtls.mdx). For a
|
|
134
|
+
worked example against an AWS load balancer, see
|
|
135
|
+
[Connect to an AWS ALB with mTLS](./connect-to-aws-alb-with-mtls.mdx). This is
|
|
130
136
|
an [enterprise feature](https://zuplo.com/pricing).
|
|
131
137
|
|
|
132
|
-
##
|
|
138
|
+
## Secure tunneling
|
|
139
|
+
|
|
140
|
+
A [secure tunnel](./secure-tunnel.mdx) runs a small connector inside your VPC or
|
|
141
|
+
private data center that makes an outbound connection to your Zuplo gateway. It
|
|
142
|
+
suits workloads without IAM or mTLS capabilities — bare metal, on premises, or a
|
|
143
|
+
non-cloud provider. Your backend needs no public endpoint at all.
|
|
133
144
|
|
|
134
|
-
|
|
135
|
-
allow you to create a WireGuard based tunnel from your VPC or private
|
|
136
|
-
data-center that connects directly to your Zuplo gateway. This option is useful
|
|
137
|
-
when running workloads in a non-cloud provider (for example, bare metal, on
|
|
138
|
-
premises, etc.) that don't have IAM or mTLS capabilities. In this solution, your
|
|
139
|
-
backend API doesn't need to be exposed to the internet at all. This is a more
|
|
140
|
-
complex setup and is only available on our
|
|
145
|
+
This is a more involved setup than the other options and is available on the
|
|
141
146
|
[enterprise plan](https://zuplo.com/pricing).
|
|
142
147
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
148
|
+
## Custom networking (managed dedicated only)
|
|
149
|
+
|
|
150
|
+
On the managed dedicated plan, Zuplo can provide custom networking to reach your
|
|
151
|
+
backend, using your cloud provider's VPC connectivity — AWS Transit Gateway,
|
|
152
|
+
PrivateLink, or VPC peering.
|
|
146
153
|
|
|
147
|
-
|
|
154
|
+
For more information, see the
|
|
155
|
+
[networking documentation](../dedicated/networking.mdx).
|
|
148
156
|
|
|
149
|
-
|
|
150
|
-
connect your backend to Zuplo. This can include using VPC connectivity
|
|
151
|
-
capabilities from your cloud provider (for example AWS, Azure, GCP, etc.) such
|
|
152
|
-
as AWS Transit Gateway, PrivateLink, or VPC Peering to connect to your backend
|
|
153
|
-
services.
|
|
157
|
+
## Get help choosing
|
|
154
158
|
|
|
155
|
-
|
|
156
|
-
[
|
|
159
|
+
To discuss security and connectivity options, the
|
|
160
|
+
[Zuplo Discord](https://discord.zuplo.com) has active participation from the
|
|
161
|
+
Zuplo team, or contact [support](mailto:support@zuplo.com).
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Advanced Tunnel Configuration
|
|
3
|
+
sidebar_label: Advanced
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<EnterpriseFeature name="Secure tunneling" />
|
|
7
|
+
|
|
8
|
+
The [`zuplo/tunnel`](https://hub.docker.com/r/zuplo/tunnel) image covers most
|
|
9
|
+
deployments without configuration beyond `TUNNEL_TOKEN`. This page is for teams
|
|
10
|
+
that need more control: pinning versions, tuning the transport, rotating tokens,
|
|
11
|
+
or building and running the connector image themselves.
|
|
12
|
+
|
|
13
|
+
## The cloudflared foundation
|
|
14
|
+
|
|
15
|
+
Zuplo's tunnel is a thin packaging of
|
|
16
|
+
[cloudflared](https://github.com/cloudflare/cloudflared), Cloudflare's open
|
|
17
|
+
source tunnel connector. It isn't a Zuplo-specific protocol or a proprietary
|
|
18
|
+
agent. The entire `zuplo/tunnel` image is the upstream
|
|
19
|
+
[`cloudflare/cloudflared`](https://hub.docker.com/r/cloudflare/cloudflared)
|
|
20
|
+
image with a fixed entrypoint:
|
|
21
|
+
|
|
22
|
+
```dockerfile title="Dockerfile"
|
|
23
|
+
FROM cloudflare/cloudflared:2026.7.3-amd64
|
|
24
|
+
ENTRYPOINT ["cloudflared", "tunnel", "--no-autoupdate", "--protocol", "http2", "run"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Two consequences follow from this. First, everything the
|
|
28
|
+
[cloudflared documentation](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/)
|
|
29
|
+
says about deploying, monitoring, and tuning a connector applies to your Zuplo
|
|
30
|
+
tunnel. Second, you can run the upstream image yourself instead of Zuplo's — see
|
|
31
|
+
[Run your own connector image](#run-your-own-connector-image).
|
|
32
|
+
|
|
33
|
+
### Entrypoint flags
|
|
34
|
+
|
|
35
|
+
| Flag | Why the image sets it |
|
|
36
|
+
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
37
|
+
| `--no-autoupdate` | Stops cloudflared from replacing its own binary while running. The image tag is then the only thing that determines the version, which keeps containers immutable and deployments reproducible. |
|
|
38
|
+
| `--protocol http2` | Forces the HTTP/2 transport. The cloudflared default, `auto`, prefers QUIC over UDP port 7844 and falls back to HTTP/2 when it can't open UDP connections. Many corporate networks block outbound UDP, so pinning HTTP/2 skips the fallback. |
|
|
39
|
+
| `run` | Runs a remotely-managed tunnel. The tunnel's routing configuration lives in Zuplo, not in a local config file, and cloudflared reads its credentials from `TUNNEL_TOKEN`. |
|
|
40
|
+
|
|
41
|
+
### Container characteristics
|
|
42
|
+
|
|
43
|
+
Because the image inherits the upstream cloudflared base, it has properties
|
|
44
|
+
worth knowing when you write a deployment manifest or a security policy:
|
|
45
|
+
|
|
46
|
+
- **Distroless base, no shell.** You can't exec into a running tunnel container
|
|
47
|
+
to debug it. Run a separate debug container in the same network namespace
|
|
48
|
+
instead — see
|
|
49
|
+
[Check connectivity to your backend](./tunnel-troubleshooting.mdx#check-connectivity-to-your-backend).
|
|
50
|
+
- **Runs as a non-root user** (UID 65532). It needs no elevated privileges, no
|
|
51
|
+
additional Linux capabilities, and no writable root filesystem.
|
|
52
|
+
- **Needs no persistent volume.** The tunnel holds no state between restarts and
|
|
53
|
+
fetches its configuration from Zuplo on startup.
|
|
54
|
+
- **Listens on no inbound ports** unless you set `TUNNEL_METRICS`.
|
|
55
|
+
|
|
56
|
+
## Environment variables
|
|
57
|
+
|
|
58
|
+
The tunnel reads cloudflared's environment variables. The ones that matter for a
|
|
59
|
+
Zuplo tunnel:
|
|
60
|
+
|
|
61
|
+
| Variable | Default | Description |
|
|
62
|
+
| --------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
|
|
63
|
+
| `TUNNEL_TOKEN` | none | The tunnel's connection token, from `zuplo tunnel describe`. Required. |
|
|
64
|
+
| `TUNNEL_LOGLEVEL` | `info` | Log verbosity. One of `debug`, `info`, `warn`, `error`, or `fatal`. Use `debug` when troubleshooting. |
|
|
65
|
+
| `TUNNEL_METRICS` | none | Address to serve Prometheus metrics on, such as `0.0.0.0:2000`. Useful for scraping connection health. |
|
|
66
|
+
| `TUNNEL_TRANSPORT_PROTOCOL` | `auto` | Transport to Cloudflare's edge. One of `auto`, `http2`, or `quic`. |
|
|
67
|
+
| `NO_AUTOUPDATE` | `false` | Disables in-place binary updates. |
|
|
68
|
+
| `TUNNEL_EDGE_IP_VERSION` | `4` | IP version for reaching the edge. One of `4`, `6`, or `auto`. |
|
|
69
|
+
|
|
70
|
+
:::caution{title="Flags override environment variables"}
|
|
71
|
+
|
|
72
|
+
The `zuplo/tunnel` image sets `--no-autoupdate` and `--protocol http2` as
|
|
73
|
+
command-line flags in its entrypoint, and cloudflared gives command-line flags
|
|
74
|
+
precedence over environment variables. Setting `NO_AUTOUPDATE` or
|
|
75
|
+
`TUNNEL_TRANSPORT_PROTOCOL` on a `zuplo/tunnel` container has no effect. To
|
|
76
|
+
change either one, build your own image with a different entrypoint.
|
|
77
|
+
|
|
78
|
+
:::
|
|
79
|
+
|
|
80
|
+
## Pin and upgrade the image version
|
|
81
|
+
|
|
82
|
+
The `zuplo/tunnel` tags mirror the cloudflared release they wrap, so
|
|
83
|
+
`zuplo/tunnel:2025.10.1` contains cloudflared 2025.10.1. The `latest` tag always
|
|
84
|
+
points at the most recent release.
|
|
85
|
+
|
|
86
|
+
For production, pin an explicit tag rather than using `latest`, so a redeploy
|
|
87
|
+
can't silently change connector versions:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
docker run -d --name zuplo-tunnel \
|
|
91
|
+
-e TUNNEL_TOKEN=<YOUR_TUNNEL_TOKEN> \
|
|
92
|
+
zuplo/tunnel:2025.10.1
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Then upgrade deliberately. Cloudflare supports cloudflared versions released
|
|
96
|
+
within one year of the most recent release, and may introduce breaking changes
|
|
97
|
+
that affect older versions. Check for a newer tag on a regular cadence and treat
|
|
98
|
+
the upgrade as routine maintenance. Test each new version in a staging
|
|
99
|
+
environment before rolling it out to production.
|
|
100
|
+
|
|
101
|
+
Because tunnel instances that share a token act as replicas, you can upgrade
|
|
102
|
+
without downtime: start instances on the new version, wait for them to connect,
|
|
103
|
+
then stop the old ones.
|
|
104
|
+
|
|
105
|
+
## Rotate the tunnel token
|
|
106
|
+
|
|
107
|
+
Rotate a token on a schedule, and immediately if you suspect it's been exposed.
|
|
108
|
+
With at least two instances running, you can rotate without dropping traffic.
|
|
109
|
+
|
|
110
|
+
<Stepper>
|
|
111
|
+
|
|
112
|
+
1. Issue a new token for the tunnel:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
zuplo tunnel rotate-token --tunnel-id tnl_TRMZwunq2PLNQDwhu6A04Bmx
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
After rotation, the old token can't establish _new_ connections, but
|
|
119
|
+
instances already connected with it keep serving traffic.
|
|
120
|
+
|
|
121
|
+
1. Update the token in your secret manager.
|
|
122
|
+
|
|
123
|
+
1. Restart half your instances so they pick up the new token, and wait for them
|
|
124
|
+
to report as connected.
|
|
125
|
+
|
|
126
|
+
1. Restart the remaining instances.
|
|
127
|
+
|
|
128
|
+
</Stepper>
|
|
129
|
+
|
|
130
|
+
Rotating with a single instance running causes a brief interruption while the
|
|
131
|
+
instance restarts. Schedule it during a maintenance window.
|
|
132
|
+
|
|
133
|
+
## Run your own connector image
|
|
134
|
+
|
|
135
|
+
Because the tunnel is stock cloudflared, you can build and run the connector
|
|
136
|
+
yourself. Consider this when you need to:
|
|
137
|
+
|
|
138
|
+
- Change the entrypoint flags, such as running QUIC instead of HTTP/2, or
|
|
139
|
+
exposing the metrics endpoint.
|
|
140
|
+
- Mirror the image into your own registry to satisfy a policy that forbids
|
|
141
|
+
pulling from public registries.
|
|
142
|
+
- Meet a scanning, signing, or base-image requirement your platform enforces.
|
|
143
|
+
- Run on `arm64`, using a matching upstream tag.
|
|
144
|
+
|
|
145
|
+
To match the behavior of `zuplo/tunnel`, build from the upstream image and
|
|
146
|
+
replicate the entrypoint:
|
|
147
|
+
|
|
148
|
+
```dockerfile title="Dockerfile"
|
|
149
|
+
FROM cloudflare/cloudflared:2026.7.3-amd64
|
|
150
|
+
ENTRYPOINT ["cloudflared", "tunnel", "--no-autoupdate", "--protocol", "http2", "run"]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Replace `2026.7.3` with a current tag from
|
|
154
|
+
[Docker Hub](https://hub.docker.com/r/cloudflare/cloudflared/tags) — a version
|
|
155
|
+
pinned here goes stale, and running a connector more than a year behind the
|
|
156
|
+
latest release falls outside Cloudflare's support window. Use the `-amd64` or
|
|
157
|
+
`-arm64` suffix that matches your host architecture.
|
|
158
|
+
|
|
159
|
+
Build and run it with the same `TUNNEL_TOKEN` you'd give the Zuplo image:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
docker build -t my-registry/tunnel:2026.7.3 .
|
|
163
|
+
docker run -d --name tunnel \
|
|
164
|
+
-e TUNNEL_TOKEN=<YOUR_TUNNEL_TOKEN> \
|
|
165
|
+
my-registry/tunnel:2026.7.3
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
You can also run the upstream image directly, without a Dockerfile, by supplying
|
|
169
|
+
the command yourself:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
docker run -d --name tunnel \
|
|
173
|
+
-e TUNNEL_TOKEN=<YOUR_TUNNEL_TOKEN> \
|
|
174
|
+
cloudflare/cloudflared:2026.7.3-amd64 \
|
|
175
|
+
tunnel --no-autoupdate --protocol http2 run
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
:::caution
|
|
179
|
+
|
|
180
|
+
Keep the `run` subcommand and don't add a local configuration file, credentials
|
|
181
|
+
file, or `--url` argument. Your tunnel is remotely managed: Zuplo owns the
|
|
182
|
+
routing configuration, and cloudflared fetches it using the token. Local
|
|
183
|
+
configuration conflicts with that and can stop traffic reaching your services.
|
|
184
|
+
|
|
185
|
+
:::
|
|
186
|
+
|
|
187
|
+
:::note
|
|
188
|
+
|
|
189
|
+
Zuplo tests and supports the `zuplo/tunnel` image. A connector image you build
|
|
190
|
+
and operate yourself is yours to maintain, and support can help with the tunnel
|
|
191
|
+
and its configuration in Zuplo but not with your image or build pipeline. If
|
|
192
|
+
you're unsure whether a change is safe, ask
|
|
193
|
+
[Zuplo support](mailto:support@zuplo.com) before rolling it out.
|
|
194
|
+
|
|
195
|
+
:::
|
|
196
|
+
|
|
197
|
+
## Next steps
|
|
198
|
+
|
|
199
|
+
- [Troubleshoot a tunnel](./tunnel-troubleshooting.mdx) — diagnose a tunnel
|
|
200
|
+
that's down or not passing traffic.
|
|
201
|
+
- [Connect to tunnel services](./tunnel-services.mdx) — the service
|
|
202
|
+
configuration reference and `service://` usage.
|