Tuesday, April 26, 2016

SSH Via GateOne

If you're like me, you occasionally work from locations that have restrictive firewall rules. Those rules may even be so restrictive that you're unable to SSH to remote hosts that you need to manage or do development work on.

The solution to this scenario is to use an SSH-over-HTML proxy service. In my case, I use GateOne. It's a light-weight service that's quick and easy to configure. The software maintainers even make Docker-ready bundles for it.

In my case, I create a GateOne-enabled SSH-proxy in AWS. Doing so is as simple as picking an EL6-compatible AMI (CentOS, RHEL, Scientific Linux, Amazon Linux, etc.) and giving the AMI launch-tool the following user-data:
#!/bin/sh

yum update -y
yum install -y git
pip install --upgrade pip
pip install --upgrade tornado
git clone https://github.com/liftoff/GateOne.git /tmp/GateOne
(cd /tmp/GateOne ; python setup.py install)
chkconfig gateone on
service gateone start
printf "Sleeping for 15s..."
sleep 15
echo "Done!"
pkill gateone
sed -i -e '/"https_redirect"/s/: .*$/: true,/' \
    -e '/"origins":/s/:.*$/: ["*"],/' \
    $(readlink -f /etc/gateone/conf.d/10server.conf)
sed -i '/"auth"/s/: .*$/: "pam",/' \
    $(readlink -f /etc/gateone/conf.d/20authentication.conf)
service gateone restart
When installed from its Git repository, the default configuration of GateOne only allows connection from an instances local interfaces. The `sed` operation against the "/etc/gateone/conf.d/10server.conf" configuration-file opens up this restriction. Setting the "origins" definition to '"*"' will allow browser connections from anywhere (if this is unacceptable, you can use AWS security-groups to lock things back down a bit).

Because I like to think of myself as lazy, I also enable a port 80 → 443 automatic redirect. Saves me from having to type "https://" into my browser (every keystroke saved counts!). This redirect is created by setting the "/etc/gateone/conf.d/10server.conf" configuration-file's "https_redirect" configuration-option to "true".

Because I prefer to do key-based logins and I don't want to make my SSH keys easily snarfable, I disable anonymous logins. This also has the side-effect of making it so it's not as easy for randos to discover your SSH proxy and use it for their own purposes. Disabling anonymous logins is done by setting the "/etc/gateone/conf.d/20authentication.conf" file's "auth" parameter. By setting it to "pam", as above, you instruct GateOne to allow local user-credentials to be used for logins.

When GateOne's authentication is set to "pam", it will be necessary to set up a valid user/password at the OS layer to be able to login via GateOne. You can do this via userdata or by logging into the instance and manually creating the GateOne user-credentials. I prefer the userdata method, as it means I can fully-automate the whole GateOne deployment process (e.g., via a CLI-script, CloudFormation template, etc.).

Note on the sleep statement: GateOne's configuration files are not present until the first time the service starts. Without the sleep, the `sed` operations will fail with a "missing file" error.

Tuesday, April 19, 2016

But I Don't Like That Username

One of the clients I do work for is in the process of adopting commercial cloud solutions. Early in the process, they had a private network that they were doing executing virtualization and private-cloud efforts. The maintainers of those two environments had created standard builds for use within those environments. For better or worse, my client has a number of developer teams they've contracted out to whose primary efforts are conducted either in-house (to the contractor) or within AWS, Azure or Google Compute Engine.

The group I work for has been tasked with standardizing and automating the deployment of enterprise components across all of the various environments and providing stewardship of the other contracted-out development efforts. When we were first given this task, the customer's build engineers would not provide any methods to replicate the production build - not even sufficient documentation that would allow us to accurately mimic it well enough to enable the other developers with a seamless dev -> test -> prod workflow.

Early in our involvement, I ended up creating my own build. Eventually, in the previously-described vacuum, others decided to adopt my build. Now, there's enough different groups using that build that it's pressuring the maintainers of the internal build to abandon theirs and adopt mine.

Fun part of DevOps is that when tends to be consensus-driven. If there's a slow or unresponsive link in the chain, the old "top down" approach frequently becomes a casualty when critical mass is achieved bottom up..

At any rate, we were recently in discussions with the enterprise build maintainers to show them how to adopt the consensus build. One of their pushbacks was "but that build doesn't include the 'break-glass' account that the legacy builds do." They wanted to know if we could modify the build to be compliant with that user-account.

This struck me odd, since, our group's (and other dev-groups') approach to such issues is "modify it in code". This isn't an approach familiar to the enterprise team. They're very "golden image" oriented. So, I provideded them a quick set of instructions on how to take the incoming standard build and make it compatible with their tools' expectations
#cloud-config
system_info:
  default_user:
    name: ent-adm
For enterprise components that will be migrated from private virtualization and cloud solutions to commercial cloud offerings, the above allows them to take not just my build, but any build that's enabled for automated provisioning and inject their account. Instead of launching a system with whatever the default-user is that's baked in, the above allows them to reset any system's initial username to be whatever they want (the above's 'ent-adm' is just an example - I dunno what their preferred account name is - I've simply used the above whenever I'm deploying instance-templates and don't want to remember "instance X uses userid A; instance Y uses userid B; and instance Z uses userid C").

Even more fun if they don't like any of the attributes associated with the default user's account, they can override it with any user-parameter available in cloud-init.