# Zitifying SSH

As we learned in the [opening post](https://blog.openziti.io/zitification), "zitifying" an application means to embed a Ziti SDK into an application and leverage the power of a [Ziti Network](https://openziti.io/docs) to provide secure, truly zero-trust access to your application no matter where in the world that application goes. In this post, we will see how we have zitified `ssh` and why. Future posts will expand on this even further by showing how NetFoundry uses `zssh` to support our customers.

%[https://youtu.be/WyZ8GRvtgGs] 

---

## Why SSH?

As I sit here typing these words, I can tell you're skeptical. I can tell you're wondering why in the world we would even attempt to mess with `ssh` at all. After all, `ssh` has been a foundation of the administration of not only home networks but also corporate networks and the internet itself. Surely if millions (billions?) of computers can interact every day safely and securely using `ssh` there is "no need" for us to be spending time zitifying `ssh` right? (Spoiler alert: wrong)

I'm sure you've guessed that this is not the case whatsoever. After all, attackers don't leave `ssh` alone just because it's not worth it to try! Put a machine on the open internet, expose `ssh` on port 22 and watch for yourself all the attempts to access `ssh` using known default/weak/bad passwords flood in. Attacks don't only come from the internet either! Attacks from a single compromised machine on your network very well could behave in the same way as an outside attacker. This is particularly true for ransomware-style attacks as the compromised machine attempts to expand/multiply. The problems don't just stop here either. DoS attacks, other zero-day type bugs and more are all waiting for any service sitting on the open internet.

A zitified `ssh` client is superior since the port used by `ssh` can be eliminated from the internet-based firewall preventing any connections whatsoever from any network client. In this configuration the `ssh` process is effectively "  
dark". The only way to `ssh` to a machine configured in this way is to have an identity authorized for that [Ziti Network](https://openziti.github.io/ziti/overview.html#overview-of-a-ziti-network).

It doesn't stop there though. A Ziti Network mandates the use of a strong identity. You cannot access any services defined in a [Ziti Network](https://openziti.io/docs) without having gone through the enrollment process to create a strong identity used for bidirectional authentication and authorization. With Ziti, you can't even connect to SSH without first being authorized to connect to the remote SSH server.

Contrast that to SSH. With SSH you need access the sshd port before starting the authentication process. This requires the port to be exposed to the network, exposing it to attack. With SSH you are also usually allowed to authenticate without providing a strong identity using a username and password. Even if you are choosing to use the more secure pub/private key authentication for SSH, the remote machine still needed the public key added to the authorized\_keys file before allowing connections to it via SSH. This is all-too-often a step which a human will do, making the process of authorizing a user or revoking access relatively cumbersome. Ziti provides a secure, centralized location to manage authorization of users to services. Ziti makes it trivial to grant or revoke access to a given set of services to users immediately.

Lastly, Ziti provides support for continual authorization through the use of policy checks. These policy checks run continuously. If a user suddenly fails to meet a particular policy, access to the services provided via the [Ziti Network](https://openziti.io/docs) are revoked immediately.

Cool right? Let's see how we did it and how you can do the same thing using a [Ziti Network](https://openziti.io/docs).

#### Overview of SSH - notice how port 22 is open to inbound connections:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1715775317970/ef9d66ab-193b-4768-81e1-f6bd1915cbcc.png align="left")

---

## How It's Done

There are a few steps necessary before being able to use `zssh`:

* Establish a [Ziti Network](https://openziti.io/docs/learn/quickstarts/network/hosted)
    
* Create and enroll two Ziti Endpoints (one for our `ssh` server, one for the client)
    
    * the `sshd` server will run `ziti-tunnel` for this demonstration. Conveniently it will run on the same machine I used to setup the [Ziti Network](https://openziti.io/docs).
        
    * the client will run `zssh` from my local machine, and I'll `zssh` to the other endpoint
        
* Create the [Ziti Service](https://openziti.io/docs/learn/core-concepts/services/overview) we'll use and authorize the two endpoints to use this service
    
* Use the `zssh` binary from the client side and the `ziti-tunnel` binary from the serving side to connect
    
* Harden `sshd` further by removing port 22 from any internet-based firewall configuration (for example, from within the security-groups wizard in AWS) or by forcing `sshd` to only listen on `localhost/127.0.0.1`
    

#### Overview of ZSSH - notice port 22 is no longer open to inbound connections:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1715775306181/b102647e-44cb-4dc1-841f-50ec7ca14d2b.png align="left")

After performing these steps you'll have an `sshd` server that is dark to the internet. Accessing the server via `ssh`  
must now occur using the Ziti Network. Since the service is no longer accessible directly through a network, it is no longer susceptible to the attacks mentioned previously!

---

## Zssh in Action

Once the prerequisites are satisfied, we can see `zssh` in action. Download the binary for your platform:

* [Linux](https://github.com/openziti-incubator/zssh/releases/latest/download/zssh-linux-amd64)
    
* [Windows](https://github.com/openziti-incubator/zssh/releases/latest/download/zssh-windows-amd64.exe)
    
* [MacOs](https://github.com/openziti-incubator/zssh/releases/latest/download/zssh-macos-amd64)
    

Once you have the executable download, make sure it is named `zssh` and for simplicity's sake we'll assume it's on the path. A goal for `zssh` is to make the usage of the command very similar to the usage of `ssh`. Anyone familiar with `ssh` should be able to pick up `zssh` easily. As with most tooling, executing the binary with no arguments will display the expected usage. The general format when using `zssh` will be similar to that of `ssh`: `zssh <remoteUsername>@<targetIdentity>`

Below you can see me `zssh` from my local machine to the AWS machine secured by `ziti-tunnel`:

./zssh ubuntu@ziti-tunnel-aws INFO\[0000\] connection to edge router using token 95c45123-9415-49d6-930a-275ada9ae06f connected. ubuntu@ip-172-31-27-154:~$

It really was that simple! Now let's break down the current flags for `zssh` and exactly how this worked.

---

## Zssh Flags

We know that `zssh` requires access to a [Ziti Network](https://openziti.io/docs) but it is not clear from the example above is where `zssh`  
found the credentials required to access the network. `zssh` supports three basic flags:

\-i, --SshKeyPath string Path to ssh key. default: $HOME/.ssh/id\_rsa -c, --ZConfig string Path to ziti config file. default: $HOME/.ziti/zssh.json -d, --debug pass to enable additional debug information -h, --help help for this command -s, --service string service name. default: zssh (default "zssh")

What you see above is exactly the output `zssh` provides should you pass the `-h/--help` flag or execute `zssh` without any parameters. The `-i/--SshKeyPath` flag is congruent to the `-i` flag for `ssh`. You would use it to supply your key to the `ssh` client. Under the hood of `zssh` is a full-fledged `ssh` client that works similarly to how `ssh` does. If your `~/.ssh/id_rsa` file is in the `authorized_keys` of the remote machine, then you won't need to specify the `-i/`  
flag (as I didn't in my example). Using `zssh` requires the use of a public/private key in order for the `zssh` client to connect to the remote machine.

The `-c/--ZConfig` flag controls access to the network. A configuration file must be supplied to use `zssh` but does not need to be supplied as part of the command. By default, `zssh` will look at your home directory in a folder named `.ziti` for a file named `zssh.json`. In bash this is would be the equivalent of `$HOME`. In Windows this is the equivalent the environment variable named `USERPROFILE`. You do not need to supply this flag if a file exists at the default location. You can specify this flag to use `zssh` with other networks.

The `-s/--service` flag is for passing in a different service name other than "zssh". By defualt, the service name will be "zssh", but if you would like to access a different service use the `-s` flag followed by the service name.

The `-d/--debug` flag outputs additional information to assist you with debugging. For example:

$ ./zssh ubuntu@ziti-tunnel-aws -d INFO\[0000\] sshKeyPath set to: /home/myUser/.ssh/id\_rsa INFO\[0000\] ZConfig set to: /home/myUser/.ziti/zssh.json INFO\[0000\] username set to: ubuntu INFO\[0000\] targetIdentity set to: ziti-tunnel-aws INFO\[0000\] connection to edge router using token 95c45123-a234-412e-8997-96139fbd1938 connected. ubuntu@ip-172-31-27-154:~$

Shown above is also one additional piece of information, the remote username. Shown in the example above I have `zssh`ed to an ubuntu image in AWS. When it was provisioned AWS used the username `ubuntu`. In order to `zssh` to this machine I need to tell the remote `sshd` server that I wish to attach as the `ubuntu` user. If your username is the same for your local environment as the remote machine you do not need to specify the username. For example, my local username is `cd` (my initials). When I `zssh` to my dev machine I can simply use `zssh ClintLinux`:

$ ./zssh ClintLinux INFO\[0000\] connection to edge router using token 909dfb4f-fa83-4f73-af8e-ed251bcd30be connected. cd@clint-linux-vm ~

Hopefully this post has been helpful and insightful. Zitifying an application is *POWERFUL*!!!!

The next post in this series will cover how we extended the same code we used for `zssh` and [zitified scp](https://blog.openziti.io/zitifying-scp).

Have a look at the code over at [GitHub](https://github.com/openziti-test-kitchen/zssh/blob/main/zssh/zssh/main.go)
