Configuring Service to access a Web Interface

Hi all. Apologies if this question has been answered somewhere else in the forum.

I have just started diving in to OpenZiti and am struggling with knowing what I am doing with this service creation and the docs don't seem to cover this. So far, the controller (with console) is deployed and two edge routers are deployed as well and are all working.

To figure out the services, I have a VM that's hosting an app with a web interface and a desktop that I'm treating like a client device. Both have the Ziti Edge client installed. (The VM is rocky linux and the ziti-edge-tunnel is installed correctly according to the Ziti docs. And, the desktop is Windows and installed successfuly.) Both are registered in the Ziti Admin Console and have reported back in with their client and OS info. So, as a baseline, everything seems pretty normal and correct so far.

To create the service, I used the simple service creation wizard in the Ziti admin console. I selected the identity for desktop for what device that can access the service and provided an address (portal.domain.com for example) and port 443. Then, for the hosting configuration, I selected the identity of the VM that hosts the web app I wanted to access, left the protocol on TCP, entered 127.0.0.1 for the address, and used 443 for the port. Saving the service worked and all the necessary configs and policies were created successfully.

Now, I'll admit that I don't fully grasp the hosting configuration. I understand the accessing configuration: what devices are going to be trying to reach Thing A at what address and over what protocol. But the hosting configuration leaves me with more questions and I'm relying on some online guides from OpenZiti to try getting it running. I understand that the identity selected is what device will be hosting the application we're trying to get to (as far as OpenZiti cares: what's at the other end of the tunnel) but I don't quite understand what I've read about the address to be entered here. I initially tried using the actual DNS name of the VM (name.domain.com for example) but, when that didn't work, I edited the service to use the loopback address instead (127.0.0.1). The port was left at 443 because that's where this particular application's web portal is presenting on the hosting VM. I would assume that this port would change to reflect the specific port used by an application's web portal if it were different (1280 for example if I wanted to try putting the Ziti Admin console at the service end of a tunnel).

With this configuration, I would assume that I should be able to go into a web browser and navigate to https://portal.domain.com (as used in the example in the config above) and reach that web portal so long as I am accessing that from the desktop that has the Ziti edge client installed.

Thus far, please feel free to correct me if this is failing because my understanding of either these configs or that expectation of how the connection should work is faulty and the root of this issue.

For further context, both of these devices are on the same network. And, I was already able to access https://portal.domain.com normally in my browser. But, so far, in the Ziti Admin Console, I cannot tell whether this service has been used to know whether this configuration is correct. I expected that the intercept config applied on the desktop would capture that traffic and send it over the tunnel Ziti established with that service. Basically, that Ziti would take precedence as the 'primary' access method. And my hope there was that I would be able to configure services first, confirm access through the service, then block the traffic on the underlay network to get to real ZTNA.

So, this is the 3rd area where I may also be confused or may have not found the correct information in the docs.

Any help would be greatly appreciated. The app seems amazing and I'd love to really get a grasp of it. And it feels like I'm nearly at a solid initial footing.

[Unrelated, a GUI for the Linux edge client (tunneler) like there is for Windows would be amazing for desktop Linux installs with a desktop environment since Microsoft has murdered Windows and Linux on the desktop has stepped into the gap.]

Hi @bvh welcome to the community and to OpenZiti!

Thanks for all these details... You have a lot going on in this one post and I'll do my best to be brief, yet complete... The example you describe sounds to me like you are describing what is documented at Your First Service | NetFoundry Documentation I'll leave that link here in case you didn't find it yet.

understanding host configuration

When using the simple service wizard, the "hosting configuration" will simply indicate the location of the service once it exits (or egresses) the OpenZiti overlay. You understand how it enters (or ingresses) the overlay, but for the example you are describing you need to exit the overlay and go back to plain old IP underlay. MOST people will start either with "zero trust network access" or "zero trust host access".

The difference between ZTNA (network) and ZTHA (host) really just comes down to how much "trusted zone" is between the tunneler and the target service. If you are traversing more than just the host network and traffic is sent outside the host across the remote network (all by classic IP underlay), well that would be over the network or "ZTNA".

If you are targeting an application that is on the same machine as the tunneler, that traffic stays on that host. That is considered to be "ZTHA".

When you set up the 'hosting configuration', you specify the "identities that can host the service". This is one or more identities. You can choose the identities to use by using the @ symbol to specify one or more identities. OpenZiti has other mechanisms to specify identities as well but for now, let's leave those off the table. Since this is your FIRST service, it's easier to just use direct @-based identities. So yes, you pick which identity will "host" the service. This is where traffic exits the overlay back to the IP-based underlay.

The key to this section of the config is the 'address' fields. This value is relative to wherever that "hosting tunneler" is. That make sense?

both machines on the same network

Having both devices on the same network sure sounds like a plan at first but it can lead to misleading results. It can also lead to the confusion you're indicating:

I cannot tell whether this service has been used to know whether this configuration is correct

If you want to do that, it's fine, but I would make sure the hosting device has the firewall block all inbound connections. This, in effect, emulates being in some other network. Once you do that, you will know when OpenZiti is up and running for real because it will be the only way to access your target app. That also makes sense?

linux UI

It's been in the works for several years. We have recently started to hear more and more rumblings in the forums about having such a UI. It's planned, but we don't have it staffed at this time.

I have a few YouTube videos I've made through the years that cover the topic...

Hopefully all this helps?

Thanks @TheLumberjack !

I had been following that 'Your First Service' guide. And, it was honestly really helpful for understanding the underlying configurations that are created by the Simple Service creation wizard in the Ziti Admin Console. What I wasn't entirely sure of when following that guide is what, necessarily, would translate to entries required in that setup wizard from the admin console. I can absolutely follow 1:1 that script to everything shown as created in that completion window. I just seem to be missing the simple stupid step of understanding what values are intended for that exit / hosting configuration. :sweat_smile:

If I understand correctly (and the topology diagram supports this understanding), what I am attempting to create is ZTHA. I'll have client devices initiating a connection which the Ziti Edge Client intercepts with that config, sends over the Ziti overlay, and then exits onto the host where that application resides (obviously having the ziti-edge-tunnel installed on it as well for that to work).

I selected the identity @VMname that's for the VM hosting the application. That much made sense. (And I think you're referring to the attribute values that can be used there in place of identities. Amazing feature that I hope to take advantage of soon once I can sort this baseline out.)

Your explanation there of "The key to this section of the config is the 'address' fields. This value is relative to wherever that "hosting tunneler" is." makes a lot of sense and I think is finally giving me the right frame of reference for knowing what should be going in as the address. Because the traffic is set to exit the Ziti overlay at the tunneler on the identity @VMname, the address specified then needs to be the respective address to that exit point (on @VMname) where the application can be located (sort of like defining a next hop). In this case, since the VM with the ziti-edge-tunnel installed is also the VM hosting the application (because this is ZTHA I am trying to accomplish), the address specified should be the loopback address: so that it points straight back to itself to direct that traffic. Is that the correct understanding and application? And then the port selection for the hosting configuration portion of the service is just the port which that application is configured to be exposed on. (However, by comparison, if this were a network entry point for applications elsewhere on the subnet as shown in the other topology where the host running the ziti-edge-tunnel is acting more like a traditional VPN endpoint, the address information relative to that Identity would be the actual IPv4 address of that other VM on the network with the desired application.)

If that all is the right way to look at it and configure it, then I think this service may actually be almost entirely correct at the moment.

An interesting question this leaves me with then is: Is Ziti allowing us to do some fun things with port mismatches for intercepting traffic on any port we want and then 'depositing' it to the correct port on the exit side? Totally beside the core question. But I like the fun this opens up if that understanding is correct.

As for testing on the same network, makes perfect sense. I was thinking of going one step further toward the end state and only allowing 3022 to that vm from the ziti edge routers IP addresses on the underlay, really force myself to make the service work one way or another. :joy:

you got it. if you're doing ZTHA the address is always some local address to the tunneler, be it 127.0.0.1, ::1, localhost or some other local IP. take special note of localhost. On some machines in the past we have seen the OS favor ipv6 for localhost (::1) when the app was bound to IPv4 only (127.0.0.1). This was very difficul to discover so I tend to favor using 127.0.0.1 to eliminate any odd confusions like that.

It surely can be used to translate a port that way, yes. You can intercept first.service.intercept:443 and send that to 127.0.0.1:4443, intercept second.service.intercept:443 and send it to 127.0.0.1:5443 and so on... There are other interesting (and advanced) and fun ways to use OpenZiti as well such as 'addressable terminators' for services like ssh or rdp when you want to send traffic to specific identity while only having one service declared/defined...

Sounds like you're on the right path now and it's clicking for you. Have fun :slight_smile:

Thanks for taking the time to help and explain. I had stumbled into a working config but now understand why and how. That tip that the address in the hosting config is where the host with the tunnel's exit needs to send that traffic next was the key to really understanding it.

Out of curiosity, is there a session log in the Ziti Admin Console I could look at to see evidence of sessions initiating and all?

yes/no. It's not captured in the ZAC but you can use the ziti CLI to stream events:

ziti fabric stream events --circuits
{"namespace":"circuit","event_src_id":"NetFoundry Inc. Server s0GbhVWDq","timestamp":"2026-04-22T16:30:32.977941675Z","version":2,"event_type":"created","circuit_id":"4WnSHPAaNQz5LA55JsY5qK","client_id":"cmoa9r6qn2wpy93qkey078vow","service_id":"2IKzw4GBM5G9t1oJqWldtt","terminator_id":"5rr4YsGgsxXybe0s2WeX0b","instance_id":"","creation_timespan":2079737,"path":{"nodes":["eM0NBWcsdI"],"links":null,"ingress_id":"4XZVSgW90CX1OiSICpw0ua","egress_id":"5aqwNxjbht7HmOv3ZdsMYL"},"link_count":0,"path_cost":1073741823,"tags":{"clientId":"NQUpmBRI5","hostId":"yYvpmBYI5","serviceId":"2IKzw4GBM5G9t1oJqWldtt"}}
{"namespace":"circuit","event_src_id":"NetFoundry Inc. Server s0GbhVWDq","timestamp":"2026-04-22T16:30:34.001059476Z","version":2,"event_type":"deleted","circuit_id":"4WnSHPAaNQz5LA55JsY5qK","client_id":"cmoa9r6qn2wpy93qkey078vow","service_id":"2IKzw4GBM5G9t1oJqWldtt","terminator_id":"5rr4YsGgsxXybe0s2WeX0b","instance_id":"","path":{"nodes":["eM0NBWcsdI"],"links":null,"ingress_id":"4XZVSgW90CX1OiSICpw0ua","egress_id":"5aqwNxjbht7HmOv3ZdsMYL"},"link_count":0,"duration":1023120521,"tags":{"clientId":"NQUpmBRI5","hostId":"yYvpmBYI5","serviceId":"2IKzw4GBM5G9t1oJqWldtt"}}

You can reproduce this example by using a separate ziti cli command ziti ops verify traffic which is how i made these events:

ziti ops verify traffic
WARNING no prefix and mode [] is not 'both'. default prefix of 2026-04-22-1634 will be used
INFO    generating P-384 EC key
INFO    generating P-384 EC key
INFO    waiting 10s for terminator for service: 2026-04-22-1634.traffic
INFO    successfully bound service: 2026-04-22-1634.traffic.

INFO    Server is listening for a connection and will exit when one is received.
INFO    found terminator for service: 2026-04-22-1634.traffic
INFO    found service named: 2026-04-22-1634.traffic
INFO    Server has accepted a connection and will exit soon.
INFO    successfully dialed service: 2026-04-22-1634.traffic.
INFO    traffic test successfully detected
INFO    client complete
INFO    Server complete. exiting