SSH Banner Not Forwarded from Server to Client (ZET v1.7.11)

Summary

SSH connections through OpenZiti hang immediately after TCP handshake. The client never receives the SSH banner from the server, despite sshd successfully sending it to the local ZET socket.

Environment

  • Server (host): Ubuntu, ziti-edge-tunnel v1.7.11, Ziti SDK v1.8.3

  • Client: macOS, Ziti Desktop Edge (ZDE)

  • Controller: v1.6.7

  • Edge Router: v1.6.7

  • Service Config: host.v1 with address: 127.0.0.1, port: 22

Symptoms

  1. DNS resolution works: vault.ziti resolves correctly

  2. TCP connect succeeds: nc -vz vault.ziti 22 returns success

  3. SSH hangs immediately after connection:

   debug1: Connecting to vault.ziti port 22.
   debug1: Connection established.
   debug1: Local version string SSH-2.0-OpenSSH_10.0
   [HANGS FOREVER - NO SERVER BANNER]

What We Found

ZET Logs Show Connection Accepted

INFO tunnel-cbs:ziti_hosting.c:706 on_hosted_client_connect() hosted_service[vault-ssh] client[ssh-client] client_src_addr[tcp:100.100.0.1:52636] dst_addr[tcp:127.0.0.1:22]: incoming connection

:white_check_mark: ZET accepts the connection and forwards to local sshd.

tcpdump on Loopback Shows Banner Sent

06:30:03.534162 IP 127.0.0.1.22 > 127.0.0.1.60158: Flags [P.], seq 1:44, ack 1, win 512, length 43: 
SSH: SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13.13

:white_check_mark: sshd successfully sends the SSH banner to ZET's local socket.

Active Connections Show Data Not Forwarded

CLOSE-WAIT 0      0    127.0.0.1:60158  127.0.0.1:22  users:(("ziti-edge-tunne",pid=32975,fd=23))

:white_check_mark: ZET has an active connection to sshd with the banner received.

Mac Client Never Receives Banner

:cross_mark: The SSH client on Mac never receives the SSH-2.0-OpenSSH... banner from the server, causing the connection to hang indefinitely.

Root Cause Analysis

ZET successfully:

  1. Accepts incoming Ziti tunnel connection from client

  2. Opens local TCP connection to 127.0.0.1:22

  3. Receives SSH banner from sshd on loopback

ZET FAILS to:

  • Forward the server→client data (SSH banner) back through the Ziti tunnel to the remote client

This appears to be a bidirectional data forwarding issue specifically affecting server-first protocols (SSH, FTP, SMTP) where the server must send initial data before the client speaks.

Steps to Reproduce

  1. Configure ZET v1.7.11 on Ubuntu server with host.v1 service for SSH (port 22)

  2. Configure ZDE on macOS client

  3. Attempt SSH connection: ssh -vvv vault.ziti

  4. Observe: Connection hangs after "Local version string" - no banner received

  5. On server, run: sudo tcpdump -i lo -A 'tcp port 22 and host 127.0.0.1'

  6. Observe: sshd sends banner to ZET's local socket, but client never receives it

Expected Behavior

The SSH banner sent by sshd should be forwarded through the Ziti tunnel to the client, allowing the SSH handshake to proceed normally.

Actual Behavior

The banner is received by ZET but never forwarded to the client, causing SSH to hang indefinitely.

Configuration

Service (host.v1)

json

{
  "protocol": "tcp",
  "address": "127.0.0.1",
  "port": 22
}

ZET Service Status

INFO tunnel-cbs:ziti_tunnel_ctrl.c:976 on_service() hosting server_address[tcp:127.0.0.1:22] service[vault-ssh]

Service is properly bound and accepting connections.

Additional Notes

  • Other services work: vault.engchat.app on port 8200 (HTTP) works correctly through the same ZET instance

  • Local SSH works: Direct SSH to 127.0.0.1:22 on server works perfectly

  • TCP connectivity works: nc -vz vault.ziti 22 succeeds, proving tunnel connectivity

  • No firewall issues: All traffic is on loopback (127.0.0.1) for the server side

  • No errors in ZET logs: Only shows on_hosted_client_connect(), no subsequent data transfer or error messages

Questions

  1. Is this a known issue in ZET v1.7.11 / SDK v1.8.3?

  2. Has this been fixed in newer versions (v2.0.0-alpha)?

  3. Is there a workaround configuration (e.g., forwardProtocol, different binding mode)?

  4. Should I enable DEBUG logging to capture more detail about the data path?

Diagnostic Files Available

I have comprehensive diagnostic captures including:

  • Full ZET logs showing connection acceptance

  • tcpdump proving sshd sends banner to ZET

  • Active connection states showing CLOSE-WAIT

  • Network topology and routing tables

Happy to provide any additional information needed to diagnose this issue.


This completely blocks SSH access through OpenZiti for server-first protocols, requiring fallback to direct IP connections which defeats the purpose of zero-trust networking.
any help can be provided or any explanation?

Hello, this is certainly unexpected! I use openziti (with Ziti Desktop Edge for macOS and ziti-edge-tunnel) to connect to my hosts with ssh in both directions, so I don’t think this is a bug in the openziti clients. btw the “v2.0.0" alpha builds are actually quite outdated, please don’t use them.

Let’s take a closer look at what might be happening with your ssh connections. Can you please include the following:

  • intercept configuration for your service
  • ssh client output with -vvv for extra verbose logging
  • logs from the hosting ziti-edge-tunnel at TRACE level (use “-v6” on the command line)
  • appex.log from the initiating Ziti Desktop Edge client, also at TRACE level (can be set from the “Z” menu)

Thanks!

Thanks mate for your reply, please note the following:

  • Service name: vault-ssh

  • Client intercept: DNS vault.ziti on TCP 22 (intercepted by ZDE; DNS answers in 100.64.0.0/10).

  • Host (terminator): server_address: tcp:127.0.0.1:22 via identity vault-host.

root@vault:/# journalctl -u ziti-edge-tunnel -n 5 --no-pager

Oct 24 10:40:55 vault ziti-edge-tunnel[43671]: (43671)[ 3251.404] INFO tunnel-cbs:ziti_hosting.c:706 on_hosted_client_connect() hosted_service[vault] client[backend-host] client_src_addr[tcp:100.64.0.1:37282] dst_addr[tcp:127.0.0.1:8200]: incoming connection

Oct 24 10:50:56 vault ziti-edge-tunnel[43671]: (43671)[ 3851.658] INFO tunnel-cbs:ziti_hosting.c:706 on_hosted_client_connect() hosted_service[vault] client[backend-host] client_src_addr[tcp:100.64.0.1:44636] dst_addr[tcp:127.0.0.1:8200]: incoming connection

Oct 24 10:59:55 vault ziti-edge-tunnel[43671]: (43671)[ 4390.733] INFO tunnel-cbs:ziti_hosting.c:706 on_hosted_client_connect() hosted_service[vault-ssh] client[ssh-client] client_src_addr[tcp:100.100.0.1:59055] dst_addr[tcp:127.0.0.1:22]: incoming connection

Oct 24 11:00:02 vault ziti-edge-tunnel[43671]: (43671)[ 4397.629] INFO tunnel-cbs:ziti_hosting.c:706 on_hosted_client_connect() hosted_service[vault-ssh] client[ssh-client] client_src_addr[tcp:100.100.0.1:59084] dst_addr[tcp:127.0.0.1:22]: incoming connection

Oct 24 11:00:56 vault ziti-edge-tunnel[43671]: (43671)[ 4452.417] INFO tunnel-cbs:ziti_hosting.c:706 on_hosted_client_connect() hosted_service[vault] client[backend-host] client_src_addr[tcp:100.64.0.1:34366] dst_addr[tcp:127.0.0.1:8200]: incoming connection

33@MacBook-Air ~ % ssh -vvv -oHostName=vault.ziti -oIdentitiesOnly=yes -oPreferredAuthentications=publickey root@vault

debug1: OpenSSH_10.0p2, LibreSSL 3.3.6

debug3: Running on Darwin 25.0.0 Darwin Kernel Version 25.0.0: Wed Sep 17 21:38:03 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T8112 arm64

debug3: Started with: ssh -vvv -oHostName=vault.ziti -oIdentitiesOnly=yes -oPreferredAuthentications=publickey root@vault

debug1: Reading configuration data /Users/e33/.ssh/config

debug1: /Users/e33/.ssh/config line 10: Applying options for vault

debug1: Reading configuration data /etc/ssh/ssh_config

debug1: /etc/ssh/ssh_config line 21: include /etc/ssh/ssh_config.d/* matched no files

debug1: /etc/ssh/ssh_config line 54: Applying options for *

debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/Users/e33/.ssh/known_hosts'

debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '/Users/e33/.ssh/known_hosts2'

debug1: Authenticator provider $SSH_SK_PROVIDER did not resolve; disabling

debug3: channel_clear_timeouts: clearing

debug1: Connecting to vault.ziti port 22.

debug1: Connection established.

debug1: identity file /Users/e33/.ssh/id_ed25519 type 3

debug1: identity file /Users/e33/.ssh/id_ed25519-cert type -1

debug1: Local version string SSH-2.0-OpenSSH_10.0
*** Hang here ***

(48269)[ 10.177] TRACE ziti-sdk:ziti.c:1765 ztx_prep_deadlines() ztx[1] processing deadlines in 9885

(48269)[ 10.177] VERBOSE ziti-sdk:ziti_ctrl.c:200 ctrl_resp_cb() ctrl received headers GET[/current-identity]

(48269)[ 10.177] VERBOSE ziti-sdk:ziti_ctrl.c:429 ctrl_body_cb() ctrl HTTP RESPONSE: {"data":{"_links":{"mfa":{"href":"./current-identity/mfa"},"self":{"href":"./current-identity"}},"createdAt":"2025-10-16T03:46:58.636Z","id":"rWR.tZez0","tags":{},"updatedAt":"2025-10-23T18:57:50.548Z","appData":{},"authPolicy":{"_links":{"self":{"href":"./auth-policies/default"}},"entity":"auth-policies","id":"default","name":"Default"},"authPolicyId":"default","authenticators":{"cert":{"fingerprint":"dfb68e3173a548e46460f64d79c744526c57d867","id":"EJNvPys80"}},"defaultHostingCost":0,"defaultHostingPrecedence":"default","disabled":false,"edgeRouterConnectionStatus":"online","enrollment":{},"envInfo":{"arch":"x86_64","domain":"(none)","hostname":"vault","os":"Linux","osRelease":"6.8.0-86-generic","osVersion":"#87-Ubuntu SMP PREEMPT_DYNAMIC Mon Sep 22 18:03:36 UTC 2025"},"externalId":null,"hasApiSession":true,"hasEdgeRouterConnection":true,"interfaces":null,"isAdmin":false,"isDefaultAdmin":false,"isMfaEnabled":false,"name":"vault-host","roleAttributes":["vaul

(48269)[ 10.177] DEBUG ziti-sdk:ziti_ctrl.c:502 ctrl_body_cb() ctrl completed GET[/current-identity] in 0.039 s

(48269)[ 10.178] TRACE ziti-sdk:ziti.c:1765 ztx_prep_deadlines() ztx[1] processing deadlines in 9884

(48269)[ 10.178] TRACE ziti-sdk:ziti.c:1765 ztx_prep_deadlines() ztx[1] processing deadlines in 9884

(48269)[ 10.181] VERBOSE ziti-sdk:ziti_ctrl.c:200 ctrl_resp_cb() ctrl received headers GET[/current-identity/edge-routers?limit=25&offset=0]

(48269)[ 10.181] VERBOSE ziti-sdk:ziti_ctrl.c:429 ctrl_body_cb() ctrl] HTTP RESPONSE: {"data":[{"_links":{"edge-router-policies":{"href":"./edge-routers/Qy3G33RNmP/edge-router-policies"},"self":{"href":"./edge-routers/Qy3G33RNmP"}},"createdAt":"2025-08-27T14:54:16.308Z","id":"Qy3G33RNmP","tags":{},"updatedAt":"2025-10-20T03:32:57.904Z","cost":null,"disabled":null,"hostname":"","isOnline":true,"name":"Ziti-edge-router","noTraversal":null,"supportedProtocols":{"tls":"tls://ziti.engchat.app:8442"},"syncStatus":"SYNC_DONE"}],"meta":{"filterableFields":["isTunnelerEnabled","createdAt","tags","roleAttributes","noTraversal","cost","online","fingerprint","updatedAt","disabled","name","connected","id","isSystem","isVerified"],"pagination":{"limit":25,"offset":0,"totalCount":1}}}

I am still trying to solve this issue since the last 48 hours and any help will be appreciated.

I did not paste the full output, is any specific commands required to post it here???

I’d really like to see the full logs from both tunnelers. I’m guessing you’ll need to compress them and attach to a DM here or if our discourse rules get in the way you can email the logs to me at scarey@netfoundry.io.

It might also be interesting to see the sshd logs. The best way that I know of to get them is:

  1. stop sshd: systemctl stop sshd
  2. start sshd from shell: sudo /usr/sbin/sshd -D -ddd

Note the service names, paths, etc may differ depending on your distro.

Also, does your sshd configuration enable the UseDNS option?

$ sudo grep -r UseDNS /etc/ssh/sshd_config*

I would like to thank you for sending your email address and your follow up.

The real problem which I start facing it since 48 hours is that SSH hangs immediately after TCP handshake. All identities/services are green.

it occurred with me few weeks ago and honestly I dont remember how I solved it.


root@vault:~# systemctl stop sshd
root@vault:~# sudo /usr/sbin/sshd -D -ddd
debug2: load_server_config: filename /etc/ssh/sshd_config
debug2: load_server_config: done config len = 3893
debug2: parse_server_config_depth: config /etc/ssh/sshd_config len 3893
debug2: ssh/sshd_config line 12: new include ssh/sshd_config.d/*.conf
debug2: ssh/sshd_config line 12: including /ssh/sshd_config.d/99-ziti-ssh.conf
debug2: load_server_config: filename /ssh/sshd_config.d/99-ziti-ssh.conf
debug2: load_server_config: done config len = 223
debug2: parse_server_config_depth: config /etc/ssh/sshd_config.d/99-ziti-ssh.conf len 223
debug3: /ssh/sshd_config.d/99-ziti-ssh.conf:1 setting PasswordAuthentication no
debug3: /ssh/sshd_config.d/99-ziti-ssh.conf:2 setting KbdInteractiveAuthentication no
debug3:/ssh/sshd_config.d/99-ziti-ssh.conf:3 setting ChallengeResponseAuthentication no
debug3: /ssh/sshd_config.d/99-ziti-ssh.conf:4 setting PubkeyAuthentication yes
debug3: /ssh/sshd_config.d/99-ziti-ssh.conf:5 setting PermitEmptyPasswords no
debug /ssh/sshd_config.d/99-ziti-ssh.conf:6 setting PermitRootLogin yes
debug3: /ssh/sshd_config.d/99-ziti-ssh.conf:7 setting MaxAuthTries 3
debug3: /ssh/sshd_config.d/99-ziti-ssh.conf:8 setting LoginGraceTime 20s
debug3: /ssh/sshd_config:39 setting PermitRootLogin no
debug3: /ssh/sshd_config:44 setting PubkeyAuthentication yes
debug3: /ssh/sshd_config:63 setting PasswordAuthentication no
debug3: /ssh/sshd_config:68 setting KbdInteractiveAuthentication no
debug3: /ssh/sshd_config:77 setting GSSAPIAuthentication no
debug3: /etc/ssh/sshd_config:91 setting UsePAM yes
debug3: /etc/ssh/sshd_config:96 setting X11Forwarding yes
debug3: /etc/ssh/sshd_config:106 setting UseDNS no
debug3: /etc/ssh/sshd_config:108 setting MaxStartups 100:100:200
debug3: /etc/ssh/sshd_config:117 setting AcceptEnv LANG LC_*
debug3: /etc/ssh/sshd_config:120 setting Subsystem sftp	/usr/lib/openssh/sftp-server
debug3: /etc/ssh/sshd_config:128 setting PrintMotd no
debug3: /etc/ssh/sshd_config:129 setting PermitRootLogin no
debug3: /etc/ssh/sshd_config:130 setting AddressFamily inet
debug3: /etc/ssh/sshd_config:131 setting AddressFamily inet
debug3: /etc/ssh/sshd_config:132 setting AddressFamily inet
debug3: /etc/ssh/sshd_config:133 setting GSSAPIAuthentication no
debug3: /etc/ssh/sshd_config:134 setting UseDNS no
debug3: /etc/ssh/sshd_config:135 setting IPQoS 0x00 0x00
debug3: /etc/ssh/sshd_config:137 setting UseDNS no
debug3: /etc/ssh/sshd_config:139 setting AddressFamily inet
debug3: /etc/ssh/sshd_config:140 setting Protocol 2
debug2: /etc/ssh/sshd_config line 140: Deprecated option Protocol
debug3: /etc/ssh/sshd_config:141 setting UseDNS no
debug3: /etc/ssh/sshd_config:143 setting PasswordAuthentication no
debug3: /etc/ssh/sshd_config:144 setting PubkeyAuthentication yes
debug3: /etc/ssh/sshd_config:145 setting KexAlgorithms curve25519-sha256
debug3: kex names ok: [curve25519-sha256]
debug3: /etc/ssh/sshd_config:146 setting Ciphers chacha20-poly1305@openssh.com
debug3: /etc/ssh/sshd_config:147 setting MACs hmac-sha2-256
debug3: /etc/ssh/sshd_config:148 setting LogLevel VERBOSE
debug1: sshd version OpenSSH_9.6, OpenSSL 3.0.13 30 Jan 2024
debug1: private host key #0: ssh-rsa SHA256:zk6WBWg0GP6KvLR2kxYa/KeJQFXJzQgXYdQWsqSguHU
debug1: private host key #1: ************* SHA256:****************
debug1: private host key #2: ssh-ed25519 SHA256:*********************
Missing privilege separation directory: /run/sshd
e33@MacBook-Air .ssh % cat  ~/Desktop/ssh-verbose.log
debug1: OpenSSH_10.0p2, LibreSSL 3.3.6
debug3: Running on Darwin 25.0.0 Darwin Kernel Version 25.0.0: Wed Sep 17 21:38:03 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T8112 arm64
debug3: Started with: ssh -vvv ziti
debug1: Reading configuration data /Users/e33/.ssh/config
debug1: /.ssh/config line 20: Applying options for ziti
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 21: include /etc/ssh/ssh_config.d/* matched no files
debug1: /etc/ssh/ssh_config line 54: Applying options for *
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts' -> '/.ssh/known_hosts'
debug3: expanded UserKnownHostsFile '~/.ssh/known_hosts2' -> '.ssh/known_hosts2'
debug1: Authenticator provider $SSH_SK_PROVIDER did not resolve; disabling
debug3: channel_clear_timeouts: clearing
debug1: Connecting to ssh.ziti port 22.
debug1: Connection established.
debug1: identity file /.ssh/e type 3
debug1: identity file /.ssh/i type -1
debug1: Local version string SSH-2.0-OpenSSH_10.0 and then it hang here

Now, I am connecting to my servers using VPS IP, but I want to make ziti network back to normal.

How? no any clue.

I may be able to help if you use my email address to send me the tunneler logs :smiley: Until I see those logs, I don’t have much to go on.

Also I was hoping to see an attempted ssh session (using the tunnelers) with the sshd debug logging. The log that you sent here just shows the sshd server starting, but no incoming connections. They would look something like this:

Connection from 127.0.0.1 port 55102 on 127.0.0.1 port 22 rdomain ""
debug1: Local version string SSH-2.0-OpenSSH_9.9
debug1: Remote protocol version 2.0, remote software version OpenSSH_10.0
debug1: compat_banner: match: OpenSSH_10.0 pat OpenSSH* compat 0x04000000

In fact I’m not even sure sshd started successfully. I’d expect to at least see something like this once it is accepting connections:

...
debug1: Bind to port 22 on 0.0.0.0.
Server listening on 0.0.0.0 port 22.
...

Maybe this is your problem?

Missing privilege separation directory: /run/sshd

Email sent, hope to hear form you with a solution cause for me this is the end.

The best way is to delete everything and start from scratch again