Internet streaming platforms are digital services that allow users to stream audio and video content from the internet. These services usually offer a wide range of content, from movies to television…
Daily, I end up having to SSH between VMs in the cloud, my desk workstation in the office, my home server, and things I cannot remember right now. Most of the time just doing a ssh user@<desired host>
doesn't cut it. If it did, this blog would end right here.
To give some context, I work on a few different projects. A project may have some presence in the cloud and some presence in the data centers that the company operates. Most of these servers are running in-progress development environments with new features being tested out. And in the cloud, these environments might be spread across cloud-regions for various reasons.
Apart from my desk workstation and my home server, for security reasons, the hosts I need to connect to are walled off behind a bastion host(s), with generally a different set of bastions per environment and or project. Hence to actually connect to the hosts of interest through the bastion, I need to use a feature of SSH called ProxyCommand
or more recently ProxyForward
which allows SSH to use the bastion as a proxy host and connect to the server of interest, while looking like a regular SSH connection to the user.
Lets start with an example with the following setting:
All this on the command line would look like
Now imagine you have to type that command in multiple times everyday….
SSH has a config file to deal with this situation. You can define all of the above in the config file and just use a short hand name and let SSH apply the configs from the file and get the same result.
The default location of your SSH config file is ~/.ssh/config
and it probably won't exist if you have never used it. You can create one by running
And open it in your favorite editor.
The config file consists of a bunch of what is called a Host
sections which apply a bunch of options to a given set of hosts.
The format is roughly
While ~/.ssh/config
is the default config file that ssh will load, you can specify an arbitrary config file using the -F
option to ssh:
With the default being equivalent to
And hence you can keep your config files anywhere you would like and use them using the above option and save you a bunch of time.
Here is an example of what can be in ~/.ssh/config
:
Within a give ssh config file, multiple host patterns might match a given host, in such a case, ssh will make a union of all options with the firstof overlapping options taking affect. For example you could have the following in your config file:
When you now run ssh bastion.us-west-2.example.com
both Option1
and Option2
will be used. We will see how to use this feature to our advantage later in this blog.
Putting it all together for my nginx example above, we can have the following in ~/.ssh/config
And now all I need to type on my terminal is ssh nginx.us-west-2.example.com
and I will be going through the bastion host.
As I alluded to earlier, the main point of this blog is easily managing different SSH configurations per environment and/or project you are working on. The config file I described above becomes an important piece of the solution, which might seem very obvious by now: a config file per environment.
Now you could very well put all your config files named by environment and/or project in a single directory and get away with it. However this can easily be hard to manage, a problem I personally encountered.
Here’s the structure I came up with. Let’s start by looking at the ~/.ssh
directory:
I just used ~/.ssh
as the directory of choice to use for managing the configurations since everything in there is related to SSH, however you could very well choose any other directory for this purpose.
Next we have one sub-folder per-project and a nested sub-folder per environment of that project. I have used the AWS regions us-east-1
, us-west-1
, and us-west-2
as example environments above. Each of these folders contain a file called config
that contains all the SSH configuration needed for it, a private and public key file (this can be omitted if you have one public-private key across everywhere you need to access). The config
will be configured to use the correct private key file and will need an absolute path (you can still use ~
) to it due to lack of features in the ssh config file.
All of this would work pretty nicely for anyone who don't need any fancy stuff like ProxyCommand
.
But I do, and here's why it doesn't just work as expected.
Let's say the config file I had at the end of the last section is the one in ~/.ssh/project1/us-west-2/config
. Here's what it looks like:
Now I should in theory be able to do the following:
And since I claimed it worked when the config file was in ~/.ssh/config
, this should in the most logical case work.
But it doesn't, and here's the reason why. If you look at the ProxyCommand
line, there is another ssh command being executed. So what is actually is happening is when I run ssh -F ~/.ssh/project1/us-west-2/config nginx.us-west-2.example.com
is the following:
All of this looks good so far, but the devil is in the details. The child process created above executes blindly the command provided, which in this case is ssh user@bastion.us-west-2.example.com nc %h %p
. This looks like a regular SSH command and does not contain the -F ~/.ssh/project1/us-west-2/config
option and hence will resort to using ~/.ssh/config,
if one exists, to look for options to be applied for bastion.us-west-2.example.com
. If you are lucky and something does match, the connection might go through, but I found out the hard way. It turns out that the -F
option is not passed in to any sub process that are spawned off as a result of the ProxyCommand, which from the SSH developers perspective is a reasonable assumption, but doesn't seem to be well documented, or I was just expecting too much!
So the easy fix is to update the config file to look as follows:
I personally feel typing ssh -F ~/.ssh/project1/us-west-2/config nginx.us-west-2.example.com
is too much to type on the shell when I want to quickly get into the host, especially if there is an ongoing issue with it.
First, let's shorten the host part. Since SSH is doing a regex match on the Host
entries with what is provided on the command line, we can do the following:
Now the following would work just as before ssh -F ~/.ssh/project1/us-west-2/config nginx
.
Now to get rid off the path to config file, I just used BASH aliases to fix it. For example, for the above you could have the following in your bash configuration.
And now you can sshp1uw2 nginx
and get the same effect.
The alias idea can be applied to other SSH based commands such as scp
.
I mentioned before that SSH will match all matching Host patterns for a given host in the config file. This allows us to use wildcards and move common configurations to one section.
Above you can see that some of the common configuration such as IdentityFile
are defined in the single section Host *
which matches all hosts. We then have a Match
section which adds options when specific conditions are met. In my example I have added a condition to add the ProxyCommand
configuration for all non-bastion hosts. Without the Match
section, we would encounter an infinite loop trying to ssh into the bastion because you would spawn off a new ssh process as a result of the ProxyCommand
that loads the same file and applies the ProxyCommand
again and repeats infinitely.
So now, if you have your Bash alias and the above config, the following would work
Notice the following two options in the catch-all host section:
Everything you just read sounds, at a high level, very similar to the virtualenv
in the Python world for example. Currently, I created all the directory hierarchies and the config files within them manually. But at some point this will definitely get out of hand and having something similar to virtualenv
where I can activate and deactivate SSH environments would be really great.
I hope the blog helps you streamline your SSH configuration files and prevents a lot of typing on the shell when you are trying to SSH into different machines.
Regardless of the growth of the SYC token, 48% of ICO revenue where be payed directly to the holder wallet in Ethereum. This means when you hold 1000 SYC Tokens in your wallet, you become 0.04 ETH…
Tarot breathes language into our hopes, dreams, struggles, and fears that we keep under wraps consciously, or unconsciously. The biggest illusion seems to be that they don’t matter or exist if we…