How do I create a service that connects to a Subnet via a router instead of just a single host?

Hi Ziti,

I have been able to get routers up and running, and services, and even configure services to intercept a /24 subnet on the client, but I haven’t been able to figure out how to configure the “terminator” that makes this service work with a router.

Can someone give me a working example? Router is Linux with tproxy binding.

1 Like

Yah, sorry… This is a bit of doc that hasn’t been updated. If you found there are files that back the schemas for the configs (“host.v1” or “host.v2”) you can look at them and discover there are forwardProtocol, forwardAddress, forwardPort fields…

You would want to set those to true accordingly. If you’re using the ziti cli you could use something like:

ziti edge create config zftp-server-config host.v1 '{"forwardProtocol":true, "allowedProtocols":["tcp","udp"], "forwardAddress":true, "allowedAddresses":[""], "forwardPort":true, "allowedPortRanges":[ {"low":50000,"high":50000}, {"low":21,"high":21}] }'

If that doesn’t get you what you need, let us know.

also, note that if you forward, you need to set the corresponding “allows” too: allowedProtocols, allowedAddresses, allowedPorts

Thanks! I think that clears up the service config part for me. Will try it out soon.

Do you also have an example of how I then bind this config/service to a specific router with the “ziti edge create terminator …” ?

I am a bit confused as to what address and port belong in these fields if I want to connect to an entire subnet that is only reachable from a particular Ziti router.

You don’t need to create a terminator. Instead, I generally create a “bind” service policy. I’m not entirely sure if it’s what you want but I usually do something like this:

ziti edge create service-policy "${service_name}"-binding Bind --service-roles '@'"${service_name}" --identity-roles '#'"${service_name}"'ServerEndpoints

Here you can see I am indicating a specific service (because I use the @ for name match) is able to be bound by any identities with the specified attribute (because I used the # for attribute match)

You would maybe want to use exact match by name?

1 Like

Oh - one more thing here. You’ll need to make sure you pass the -t flag to the router when you create it. That will put the router in to “combined tunneler mode” and that will then create an identity in the controller you can use to assign as a ‘binding’ identity.

That’s important… If you have the router and didn’t use -t, you’ll have to recreate the router and reenroll it

1 Like

I think I want this to be an “Intercept.v1” config, so that it intercepts on the ziti client tunneller software, and then outputs on a remote network that is not reachable from the Internet.

If I add this as a host.v1 config, then it does not show up in the client tunneller software as a service. Is it right to assume that I need an “intercept” config, and then I require a corresponding “terminator” to make that work?

I would like to intercept “” on the “Ziti Desktop Edge” software and then route traffic to that remote subnet that shares an interface with a remote Ziti router

1 Like

generally speaking i would suspect that your remote tunneller is using an identity which does not have a “Bind” policy assigned to it. Making a “Bind” policy will allow the tunneller to “host” that service (meaning serve as a termination/exit point)

Here’s a bunch of commands I just whipped up… I think this is what you need. You probably have most of this already but if not - here you go… i’d also suggest not opening ‘every port’ but - you’ll figure that part out :slight_smile:

We’ll get you there, I’m sure of it!!!

ziti edge create identity device near -o /tmp/near.jwt 
ziti edge enroll /tmp/near.jwt

ziti edge create identity device far -o /tmp/far.jwt 
ziti edge enroll /tmp/far.jwt

# create the needed configs to expose "
ziti edge create config "cidrsvc-client-config" intercept.v1 '{"protocols":["tcp"],"addresses":[""], "portRanges":[{"low":1, "high":65535}]}'
ziti edge create config "cidrsvc-server-config" host.v1 '{"forwardProtocol":true, "allowedProtocols":["tcp","udp"], "forwardAddress":true, "allowedAddresses":[""], "forwardPort":true, "allowedPortRanges":[ {"low":1,"high":65535}] }'

ziti edge create service cidrsvc --configs "cidrsvc-client-config,cidrsvc-server-config"

ziti edge create service-policy 172subnet.bind.policy Bind --service-roles '@cidrsvc' --identity-roles "@far"
ziti edge create service-policy 172client.dial.policy Dial --service-roles '@cidrsvc' --identity-roles "@near"
1 Like

Thanks, it worked!

Your example highlights that there is a powerful and considered structure behind all of this, openziti has exceeded my expectations.


YAY! Tell all your friends now? :slight_smile: When you have more questions - we’ll be here to help!

Hi Clint,

I have tried to setup a network according to the commands given by you in this post and it works great.

We are however trying to do something slightly different and I am not sure if it is possible and how we would go about it.

In brief we have many remote IOT devices which are not “Zitifiable” and which all have the same local IP / subnet (eg. We are able to setup a separate endpoint (eg. at each of these sites with an onboard “ziti-edge-tunnel” or Custom-SDK application.

However as we understand it, with the given ZIti setup we would need to define both a service and an endpoint for each of these locations. Our “administration” endpoints would then need to be setup to have access to each and every service, which becomes very cumbersome and is definitely not scalable.

Is there any easy way of setting up hundreds of endpoints each exposing the same “subnet services”, which are then reachable only by “Administration endpoints” when needed?

ie Identity “Admin” is able to connect (only when required) to Identity “IOT_GW_100” which exposes subnet and gives access to IOT_Device_100 (



Don’t get scared

This is going to be a long response because you’ve asked an excellent question. But as with most things - there’s a lot of nuance in this response so hopefully this big wall of text doesn’t scare you off…

At the bottom of the post is a diagram that I made. If it’s wrong please correct it. This is my understanding of what you’re looking to do ‘roughly’.

Custom SDK App?

The first big question from me is about what is connecting to these sites on the ‘left side’ shown. Are you expecting to author code and make a custom ziti app that knows how to do these things? I expect “no” but thought I’d ask… Same is true for the far side. When you’re writing code on both sides - well… you can do ANYTHING you want!!! :slight_smile: and you could absolutely do what you want to do.

I expect that’s writing code is not what you want, since that’s “more effort” (and hey, I don’t blame you :slight_smile: ). With a tunneller on the left side your options do start to narrow since you’ll be reliant on the tech we’ve implemented to date. Assuming you’re using a tunneller on the left, at this time, I don’t see how you can really get away from declaring “a bunch of services”, a block of them for each site. Yah, I agree it’s more cumbersome but with APIs/ziti cli it can all be scripted out. It might not be “that bad” but I do understand the point you’re making - certainly not ideal.

ZSSH might be for you

One option that came to me talking this over with a couple of fellas and reading your post. If you want “Johnny Admin” to be able to SSH to the iot gateway (shown in green on the image), well zssh is literally all you would need. zssh is a ‘zitification’ - an app that allows you to ssh to a machine over ziti. It supports this feature you would make use of called “addressable terminators”. Let’s explore zssh for a moment. With zssh you could make ONE service - called “zssh-to-gateway”. That ONE service would then allow people to ‘zssh’ to the ‘identity name’… for exampe you would run this command:
zssh remoteUserName@site1. and presto - you are now on that top green box inside that subnet. zssh remoteUserName@site2 and now you’re in the middle box… If that’s all you want then you CAN do this today. The difference is that you’ll be using an ssh implementation we made from go on the ‘left side’. You can watch this short (under 4 minutes) video of me doing that. I’m happy to talk more about that if you like that approach…

You’ll also notice I put orange ‘ziti’ bubbles ON the iot devices. It sounded to me like this isn’t an option. If that is an option do let me know, but I assume it’s not.

In Summary

Right now with a tunneler on the left and on the right (green bubbles) what you want is not currently available. If you were to use a custom ziti app on both sides, well you can do anything you want and if you are only looking to “ssh” from the admin box to the “iot gateway” (in green) well zssh might be all you really want and need all ready for you to use! :slight_smile:

Clint’s Diagram


Hi Clint,

Thanks for the excellent response. The diagram you made at the bottom is exactly what we are trying to achieve.

Our IOT devices don’t have the ability to host any Ziti applications unfortunately.

I think that your zssh solution might be perfect as we don’t really want to have to code a custom app and zssh would be an out of the box solution :grinning: . It would be an ideal solution as we could access each site by name and then access our IOT devices directly via their local IP address which is exactly what we are looking for.

I will have a look at your video and give it a try.

Thanks again for an excellent solution

A question and a comment.

  1. Why can’t your devices be zitified? Not enough horsepower, like Arduinos or similar, or commercial devices you don’t have the access necessary to? Just wondering as a use case thing.
  2. Remember, naming conventions are your friends. Since Ziti can utilize its own internal FQDNs, a little planning upfront can allow a human readable name for services that is specific, such as location.application.enumeration, or whatever makes sense in your environment. It makes it so much easier to implement, audit, troubleshoot, etc.

Thanks Mike

  1. Our setup has some industrial equipment which are just not suitable for any high level code and others which are not programmable but have TCP/IP interfaces to reach them.
  2. Thank for the comment we will keep it in mind as this would make life a lot easier in managing everything

Hi Clint,

I have been trying to get this all to work as you suggested but have hit against a problem that I can’t figure out…

When running zssh on my client I get following error:

PS C:\Users\VMware\Desktop\Zssh> .\zssh.exe -s zsshSvc kmr@sshSvcServer -c .\zsshSvcClient.json -d
INFO username set to: kmr
INFO targetIdentity set to: sshSvcServer
INFO connection to edge router using token 7e386151-484a-4b46-8c56-99740af21450
FATAL error when dialing service name zsshSvc. unable to dial service ‘zsshSvc’: dial failed: service Qf8-t8O.8 has no terminators for identity sshSvcServer

I have setup my identities/service with following script:

source ~/.ziti/quickstart/$(hostname)/$(hostname).env
ziti edge login

#establish some variables which are used below

#Remove Identities if present
ziti edge delete identity device “${server_identity}”
ziti edge delete identity device “${client_identity}”

#create two identities. one host - one client. Only necessary if you want/need them. Skippable if you
#already have an identity. provided here to just ‘make it easy’ to test/try
ziti edge create identity device “${server_identity}” -a "${service_name}"ServerEndpoints -o “${server_identity}”.jwt
ziti edge create identity device “${client_identity}” -a "${service_name}"ClientEndpoints -o “${client_identity}”.jwt

#if you want to modify anything, often deleting the configs/services is easier than updating them
#it’s easier to delete all the items too - so until you understand exactly how ziti works,
#make sure you clean them all up before making a change
ziti edge delete config “${service_name}”-host.v1
ziti edge delete config “${service_name}”-client-config
ziti edge delete service “${service_name}”
ziti edge delete service-policy “${service_name}”-binding
ziti edge delete service-policy “${service_name}”-dialing

ziti edge create config “${service_name}”-host.v1 host.v1 ‘{“protocol”:“tcp”, “address”:“”,“port”:’"${the_port}"’, “listenOptions”: {“bindUsingEdgeIdentity”:true}}’
#intercept is not needed for zscp/zssh but make it for testing if you like
ziti edge create config “${service_name}”-client-config intercept.v1 ‘{“protocols”:[“tcp”],“addresses”:["’"${service_name}.ziti"’"], “portRanges”:[{“low”:’"${the_port}"’, “high”:’"${the_port}"’}]}’
ziti edge create service “${service_name}” --configs “${service_name}”-client-config,"${service_name}"-host.v1
ziti edge create service-policy “${service_name}”-binding Bind --service-roles ‘@’"${service_name}" --identity-roles ‘#’"${service_name}"‘ServerEndpoints’
ziti edge create service-policy “${service_name}”-dialing Dial --service-roles ‘@’"${service_name}" --identity-roles ‘#’"${service_name}"‘ClientEndpoints’

Is there something that I am missing here?



Thank you very much for providing the steps! Very helpful! The error you are reporting, in my experience, is pretty much always because the identity assigned to the ziti-edge-tunnel on the remote machine is just not running. Looking at your script I can see you are dialing:


I THINK you really want to be dialing


What’s happening here is you are telling zssh to dial an identity with the NAME of sshSvcServer. But you are making an identity with the name of zsshSvcServer :stuck_out_tongue:

I think that’s the problem… try

.\zssh.exe -s zsshSvc kmr@zsshSvcServer -c .\zsshSvcClient.json

and lemme know?

1 Like

Thanks Clint that was exactly the problem and have got a step further, but am still stuck unfortunately.

I now get the following error which I haven’t been able to figure out:

PS C:\Users\VMware\Desktop\Zssh> .\zssh.exe -s zsshSvc kmr@zsshSvcServer -c .\zsshSvcClient.json -d
INFO username set to: kmr
INFO targetIdentity set to: zsshSvcServer
INFO connection to edge router using token 7e386151-484a-4b46-8c56-99740af21450
FATAL error dialing SSH Conn: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain

I have tried normal SSH’ing from one win10 machine to the other without ziti and that works fine, I am able to login

When using zssh however, using wireshark I get network traffic between my zsshSvcClient and the Ziti Controller/Router machine but nothing seems to passed on through the tunneler to the zsshSvcServer machine.

Not sure if this will be any help in understanding the issue:

any ideas of what could be going wrong here?


Ah… Ok. here is a “decision” we made with zssh that we might need to ‘fix’… You see for zssh we decided to ONLY support certificate based authentication because frankly it was “less work”. That means you cannot enter your password. You’ll need to use an ssh key… (at this time) I will take an action to document that since it’s really not crystal clear

I can whip up a super fast “how to” video for that if it’d be helpful. But if you already know how to use keys for ssh auth - well all you need to do is:

  1. open local terminal
  2. run: cat ~/.ssh/ (or ~/.ssh/ whatever key you want to use)
  3. ssh to the far machine (you’ll have to do this once)
  4. add the key to your authorized_keys:
    1. run: mkdir ~/.ssh
    2. run: cat [copy/paste your PUBLIC key from above here] >> ~/.ssh/authorized_keys
  5. run zssh

You can also choose to specify the key on the zssh command line by running:

.\zssh.exe -s zsshSvc kmr@zsshSvcServer -c .\zsshSvcClient.json -d -i .\path\to\ssh\key

There are numerous blogs/videos out there showing you how to make a key, and how to do all this.

If you want user/password auth to work - I can file an issue on zssh. Hopefully this will get you started though?

Filed two issues just now: