MS Remote Desktop Services Farm

Hello everyone,
I am trying to set up access to "Remote Desktop Services Farm" ( multiple RD session host).
Ziti network works and I can access a single RD host with a Ziti desktop client without any problems.
However, not to the farm.
The RD farm is addressed via the RD broker and this then connects to RD host1 or RD host2 depending on the workload.
Is that possible?
How could it work?

I have installed a Ziti router in the remote network in which the RDS farm is located.
What do I need for a mode?
host or tproxy?
Is that the problem?
Thanks for your help :slight_smile:

Hi @td007, welcome to the community and to OpenZiti (and zrok/browzer)!

The only way I can think of right now to do this is to use a wildcard DNS intercept address but the solution will come with some limitations...

OpenZiti allows you to create an intercept for something like *.rdp.host which as you might expect, will match ins1.rdp.host, inst2.rdp.host etc etc. That allows you to match an indeterminate number of hosts, based on the first part of the hostname. That's great....

Then with the far, host/bind side you can implement another ziti tunneling feature: forwardAddress that allows you to dial whatever host name the intercepting tunneler dialed.

So if on the client I tried to concert to inst1.rdp.host, on the far side, the router will try to connect to "inst1.rdp.host".

So the challenges are, you'll have to have all the targets you're trying to connect to similarly named, and you'll have to intercept a similar name on the client side.

I'm pretty sure that would work. If that sounds acceptable, check out this great forum response from
Shawn on how to do this: Single identity for many services - #6 by Nopalin

If you get stuck, post back what you've done I'll show you how to make any changes

You only need the router in host mode (the default mode) but you do need to ensure you created it with tunneler mode enabled

Hi @TheLumberjack , thanks for the warm welcome and for the quick help and response.
A great community :star_struck:
I hope I have understood everything.
I will try this at my leisure and then let you know about my progress.

Unfortunately, it does not work.
As already written, an RDP connection to an RD host works without any problems.
A connection to the farm with example.ziti.local does not work.
(Server of the farm rd1.ziti.local, rd2.ziti.local etc.)
What have I done:

Creating the router:
#!/usr/bin/env bash

Working directory for the router

export ZITI_HOME=/var/lib/ziti-router

Address and port of the end point of the control level

export ZITI_CTRL_ADVERTISED_ADDRESS=externdns.de
ZITI_CTRL_ADVERTISED_PORT=9440

Address and port of this router

export ZITI_ROUTER_ADVERTISED_ADDRESS=extern-dns.de
ZITI_ROUTER_PORT=9442
ZITI_ROUTER_LISTENER_BIND_PORT=9442

ziti create config router edge --routerName router-colo1 --tunnelerMode host --private > /var/lib/ziti-router/config.yml
ziti edge create edge-router “router-colo1” --jwt-output-file router-colo1.jwt --tunneler-enabled
ziti router enroll /var/lib/private/ziti-router/config.yml --jwt /var/lib/private/ziti-router/router-colo1.jwt

Create the configurations:
ziti edge create config rdp.ziti.local.cfg.intercept intercept.v1 '{
“addresses“: [”*.ziti.local"],
“protocols“: [”tcp"],
“portRanges“: [ {”low":3389, ‘high’:3389} ],
“dialOptions": { ‘identity’: ‘$dst_hostname’ }
}'

ziti edge create config rdp.ziti.local.cfg.host host.v1 '{
"address": "127.0.0.1",
"protocol": "tcp",
"forwardPort": true,
"allowedPortRanges": [ {"low":3389,"high":3389} ],
"listenOptions": { "bindUsingEdgeIdentity": true }
}'

Create service. service_rdp.ziti.local
bind policy = @router-colo1
dial-policy = #group-identites

I get the following errors on the router:
...failed to intercept service: can not intercept services in host mode...
...ERROR ziti/tunnel/dns.NewDnsServer: system resolver test failed: failed to resolve ziti-tunnel.resolver.test: lookup ziti-tunnel.resolver.test on 127.0.0.53:53: no such host.....

On the controller:
8T15:13:01.913Z","token":"d772c1c2-f6a2-408e-aafd-aeb298528681","type":"EdgeConnectType"}
2024-10-28T15:13:02.444630+00:00 openziti ziti[116219]: {"_context":"ch{ad9eoEJBm}-\u003eu{classic}-\u003ei{7k8p}","error":"service bFs4IjCEa4x6J14Aot9Zc has no terminators for instanceId example.ziti.local.local","file":"github.com/openziti/ziti/controller/handler_edge_ctrl/common.go:79","func":"github.com/openziti/ziti/controller/handler_edge_ctrl.(*baseRequestHandler).returnError","level":"error","msg":"responded with error","operation":"create.circuit","routerId":"ad9eoEJBm","time":"2024-10-28T15:13:02.442Z","token":"d772c1c2-f6a2-408e-aafd-aeb298528681"}
2024-10-28T15:13:02.445968+00:00 openziti ziti[49366]: {"_context":"ch{edge}-\u003eu{classic}-\u003ei{mDb8}","chSeq":163762,"connId":91,"edgeSeq":0,"error":"service bFs4IjCEa4x6J14Aot9Zc has no terminators for instanceId example.ziti.local.local","file":"github.com/openziti/ziti/router/xgress_edge/listener.go:199","func":"github.com/openziti/ziti/router/xgress_edge.(*edgeClientConn).processConnect","level":"warning","msg":"failed to dial fabric","time":"2024-10-28T15:13:02.443Z","token":"d772c1c2-f6a2-408e-aafd-aeb298528681","type":"EdgeConnectType"}
2024-10-28T15:13:02.968779+00:00 openziti ziti[116219]: {"_context":"ch{ad9eoEJBm}-\u003eu{classic}-\u003ei{7k8p}","error":"service bFs4IjCEa4x6J14Aot9Zc has no terminators for instanceId example.ziti.local.local","file":"github.com/openziti/ziti/controller/handler_edge_ctrl/common.go:79","func":"github.com/openziti/ziti/controller/handler_edge_ctrl.(*baseRequestHandler).returnError","level":"error","msg":"responded with error","operation":"create.circuit","routerId":"ad9eoEJBm","time":"2024-10-28T15:13:02.968Z","token":"d772c1c2-f6a2-408e-aafd-aeb298528681"}
2024-10-28T15:13:02.968983+00:00 openziti ziti[49366]: {"_context":"ch{edge}-\u003eu{classic}-\u003ei{mDb8}","chSeq":163765,"connId":92,"edgeSeq":0,"error":"service bFs4IjCEa4x6J14Aot9Zc has no terminators for instanceId ......

Thanks for your help :slight_smile:

Yeah you misunderstood a bit is all.

You will want the intercepting side to use the same host/domain information as within the cluster. Or put a different way, you'll need the cluster to have DNS entries which map to whatever you intercept.

If you're getting this - it means you have assigned the intercepting configuration to the router side via a dial policy. That is what you'd want. You want the router side to be the 'host.v1' side and you'd assign the router's identity a BIND policy (not dial).

Your clients would all get the DIAL policy. It's possible you gave both to each or both to the router by mistake.

For this, I use the ziti CLI and issue:

ziti edge policy-advisor identities -q

It will tell you if an identity can bind, dial or both services.

Remember, the key to this working is the address you intercept, will be used to connect to the RDP instance on the far side.

Can you share the hostname pattern within the RDP farm? If it were me, I'd use that pattern in the intercept as changing the ziti side of things is easier than renaming all your RDP instances.

That help?

Sorry I still don't understand it.
I have a service and I assign the host and intercept to it.
Where do I assign the host to the router.
I tried it via ZAC.

Do you need more information, or can you give me an example, then I hope I understand better

Thank you

Can you give me an example of what your RDP farm hostnames look like? It might be easier if you tell me your side of the equation -- then I can make a better recommendation.

It might be easier to visualize if i can use an example more familiar to you?

Wow, have you been waiting for me? I am very happy to hear from you :slight_smile:
I've been sick in bed since yesterday but now I'm feeling better again.

My RD farm(name changed slightly):
Collection(Broker) desktop-365.domain.local IP 192.168.100.70
RD Session Host: rds-host1.domain.local IP 192.168.100.71
RD Session Host: rds-host2,domain.local IP 192.168.100.72

The connection is established via the DNS desktop-365.domain.local.
The broker then assigns the correct RDS.

Here is the output of:
ziti edge policy-advisor identities -q
OK : router-colo1 (2) -> service_rdp.ziti.local (3) Common Routers: (2/2) Dial: Y Bind: Y

Perfect, those hostnames are all consistent so it looks like it would work just fine. I'm not sure what the 'broker' is or how it plays into the equation.

Are you expecting users to install the Ziti Desktop Edge for Windows and then use RDP to access the appropriate RDP server? I assume that's the case? And in the RD farm, you'll have one or more routers, correct? If that sounds like the setup you're going for, i'm not sure I understand the role of the broker or if it's relevant? If you have a diagram that might help me, but I assume that's the setup you're going for?


yes that's my setup, I'll send the explanation of the broker right away


The RD connection broker accepts the connection and then forwards it to the available RDS host, e.g. rds-host1 or rds-host2

Cool. Here's what you do... You make a service that has an intercept.v1 config that look similar to this:
n

See how the address field is "*.something"? That represents the intercepting side of things. In my case the remote addresses will all start with "parkplace-via-dhcp"...

the host.v1 config would look like this:

notice the "forward" toggles are all toggled on and the "allowed addresses" matches the intercept wildcard address from before

You'll just replace "parkplace-via-dhcp" with yoru domain and things should work just fine.

hope that helps!

I despair, it doesn't work.

In the service log of the desktop client, I get this error:
[2024-10-30T14:03:13.429Z] ERROR tunnel-cbs:ziti_tunnel_cbs.c:103 on_ziti_connect() ziti dial failed: connection is closed
[2024-10-30T14:03:13.965Z] ERROR ziti-sdk:connect.c:963 connect_reply_cb() conn[0.108/4HZ-y0Lo/Connecting] failed to connect, reason=service bFs4IjCEa4x6J14Aot9Zc has no terminators for instanceId desktop-365.domain.local

If I enter the IP e.g. 192.168.100.70 instead of *.domain.local in host-v1, and the address desktop-365.domain.local in intercept, I get a session, but with this one host.

Thank you for your help.

I deleted everything again and redid it, now I have these messages:

router:
[5259.096] INFO ziti/router/handler_ctrl. (*validateTerminatorsV2Handler).validateTerminators.func1 [ch{ctrl}->u{reconnecting}->i{vRBv}]: {terminatorId=[6YaTxE1zBdhEKg04PuR2TZ]} validating terminator
[5277.216] WARNING ziti/router/xgress. (*Xgress).rx [{c/f-T6YDlkA|@/pXD7}]: read failed (read tcp 192.168.100.15:51518->192.168.100.70:3389: use of closed network connection)

client:
[2024-10-30T14:53:43.574Z] INFO tunnel-cbs:ziti_dns.c:519 format_resp() found record[100.64.0.8] for query[1:desktop-365.domain.local]
[2024-10-30T14:54:12.517Z] ERROR tunnel-sdk:tunnel_tcp.c:190 on_tcp_client_err() client=tcp:100.64.0.1:50807 err=-14, terminating connection
[2024-10-30T14:54:36.881Z] ERROR ziti-sdk:channel.c:709 ch_connect_timeout() ch[0] connect timeout
[2024-10-30T14:54:36.881Z] INFO ziti-sdk:channel.c:775 reconnect_channel() ch[0] reconnecting in 45154ms (attempt = 22)
[2024-10-30T1

This almost always means you didn't assign the bind privilege to any tunneling-enabled identity. in your case the remote edge router...

After you redid things - it surely looks correct to me. You can see the local tunneler (ziti desktop edge for windows?) is finding the intercept:

found record[100.64.0.8] for query[1:desktop-365.domain.local]

that's good, then you see:

connect timeout

That connect timeout almost certainly means the traffic made it to the router, and the router tried to dial the connection but it failed.

Please look in the router logs now to see if there's anything helpful in there. Looking at the intercept log message, if you actually intercepted: desktop-365.domain.local, from the router you should be able to ssh to that router and verify the router is able to send traffic to desktop-365.domain.local:3389

not sure you need it but this might help
https://www.cyberciti.biz/faq/ping-test-a-specific-port-of-machine-ip-address-using-linux-unix/

That's the next step here

What did I do wrong?
Could it be that something doesn't fit on the router?
Here is the exam:
root@ziti-router-colo1:/var/lib/ziti-router# telnet desktop-365.domain.local 3389
Trying 192.168.100.70...
Connected to desktop-365.domain.local.
Escape character is '^]'.
^CConnection closed by foreign host.
root@ziti-router-colo1:/var/lib/ziti-router# ping desktop-365.domain.local
PING desktop-365.domain.local (192.168.100.70) 56(84) bytes of data.
64 bytes from desktop-365.domain.local (192.168.100.70): icmp_seq=1 ttl=128 time=0.290 ms
64 bytes from desktop-365.domain.local (192.168.100.70): icmp_seq=2 ttl=128 time=0.311 ms
64 bytes from desktop-365.domain.local (192.168.100.70): icmp_seq=3 ttl=128 time=0.350 ms
^C
--- desktop-365.domain.local ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2060ms

root@ziti-router-colo1:/var/lib/ziti-router# resolvectl
Global
Protocols: -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
resolv.conf mode: stub
Current DNS Server: 192.168.100.253
DNS Servers: 192.168.100.253 192.168.100.15

Link 2 (ens160)
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 192.168.100.253
DNS Servers: 192.168.100.253 1.1.1.1

v: 3

identity:
cert: "/var/lib/ziti-router/router-colo1.cert"
server_cert: "/var/lib/ziti-router/router-colo1.server.chain.cert"
key: "/var/lib/ziti-router/router-colo1.key"
ca: "/var/lib/ziti-router/router-colo1.cas"

ctrl:
endpoint: tls:xxxxxxxxxxxx:9440

link:
dialers:
- binding: transport

listeners:

  • binding: edge
    address: tls:0.0.0.0:9442
    options:
    advertise: ziti-router-colo1.xxxxxxxxxx:9442
    connectTimeoutMs: 5000
    getSessionTimeout: 60
  • binding: tunnel
    options:
    mode: host #tproxy|host

edge:
csr:
country: US
province: NC
locality: Charlotte
organization: NetFoundry
organizationalUnit: Ziti
sans:
dns:
- localhost
- ziti-router-colo1.xxxxxxxxx.de
- ziti-router-colo1
ip:
- "127.0.0.1"
- "::1"

forwarder:
latencyProbeInterval: 0
xgressDialQueueLength: 1000
xgressDialWorkerCount: 128
linkDialQueueLength: 1000
linkDialWorkerCount: 32

Would you please share the router logs when you're doing this? I don't need the router configuration file at this time, i don't think it's related.

Thanks for verifying desktop-365.domain.local works, i assume it's port 3389 ? we never really verified that -- i just assumed

embarrassing question, where can I find the logs?
There is nothing to be found in /var/log/.
It's an Ubuntu and the installation was via apt