As we learned in the opening post, "zitifying" an application means to embed a Ziti SDK into an application and leverage the power of a Ziti Network to provide secure, truly zero-trust access to your application no matter where in the world that application goes. In this post we are going to 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.
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.
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.
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 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 are revoked immediately.
Cool right? Let's see how we did it and how you can do the same thing using a Ziti Network.
Overview of SSH - notice how port 22 is open to inbound connections:
How It's Done
There are a few steps necessary before being able to use
- Establish a Ziti Network
- Create and enroll two Ziti Endpoints (one for our
sshserver, one for the client)
sshdserver will run
ziti-tunnelfor this demonstration. Conveniently it will run on the same machine I used to setup the Ziti Network.
- the client will run
zsshfrom my local machine, and I'll
zsshto the other endpoint
- Create the Ziti Service we'll use and authorize the two endpoints to use this service
- Use the
zsshbinary from the client side and the
ziti-tunnelbinary from the serving side to connect
sshdfurther by removing port 22 from any internet-based firewall configuration (for example, from within the security-groups wizard in AWS) or by forcing
sshdto only listen on
Overview of ZSSH - notice port 22 is no longer open to inbound connections:
After performing these steps you'll have an
sshd server that is dark to the internet. Accessing the server via
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 types of attacks mentioned previously!
Zssh in Action
Once the prerequisites are satisfied, we can see
zssh in action. Simply download the binary for your platform:
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
Below you can see me
zssh from my local machine to the AWS machine secured by
INFO connection to edge router using token 95c45123-9415-49d6-930a-275ada9ae06f
It really was that simple! Now let's break down the current flags for
zssh and exactly how this worked.
We know that
zssh requires access to a Ziti Network but it is not clear from the example above is where
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
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.
-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.
-s/--service flag is for passing in a different service name other than "zssh". By default, 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.
-d/--debug flag outputs additional information to assist you with debugging. For example:
$ ./zssh ubuntu@ziti-tunnel-aws -d
INFO sshKeyPath set to: /home/myUser/.ssh/id_rsa
INFO ZConfig set to: /home/myUser/.ziti/zssh.json
INFO username set to: ubuntu
INFO targetIdentity set to: ziti-tunnel-aws
INFO connection to edge router using token 95c45123-a234-412e-8997-96139fbd1938
Shown above is also one additional piece of information, the remote username. Shown in the example above I have
zsshed 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
INFO connection to edge router using token 909dfb4f-fa83-4f73-af8e-ed251bcd30be
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.
Have a look at the code over at GitHub