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 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.
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.
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 zssh
:
Establish a Ziti Network
Create and enroll two Ziti Endpoints (one for our
ssh
server, one for the client)the
sshd
server will runziti-tunnel
for this demonstration. Conveniently it will run on the same machine I used to setup the Ziti Network.the client will run
zssh
from my local machine, and I'llzssh
to the other endpoint
Create the Ziti Service we'll use and authorize the two endpoints to use this service
Use the
zssh
binary from the client side and theziti-tunnel
binary from the serving side to connectHarden
sshd
further by removing port 22 from any internet-based firewall configuration (for example, from within the security-groups wizard in AWS) or by forcingsshd
to only listen onlocalhost/127.0.0.1
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 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:
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 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.
Have a look at the code over at GitHub