Skip to content

Instantly share code, notes, and snippets.

@StevenACoffman
Last active January 6, 2018 20:29
Show Gist options
  • Save StevenACoffman/8068cb31b008be51a9fce496b754dc9a to your computer and use it in GitHub Desktop.
Save StevenACoffman/8068cb31b008be51a9fce496b754dc9a to your computer and use it in GitHub Desktop.
Bless Me

Table of Contents

  1. Requirements
  2. Usage
  3. Troubleshooting

Requirements

Bless client requires pip, virtualenv and python2.7 installed on your machine.

  • We have different flavors of shell wrappers depending on if people are using virtualenvwrapper, pyenv, or vanilla Mac OS X python.
  • Read blessing-your-ssh-at-lyft

Steps to Install

  • Clone the bless client repo
  • Create a $BLESSPATH environment variable that points to the bless client directory
  • Add $BLESSPATH to your shell's $PATH

For example:

mkdir -p $HOME/github/
cd $HOME/github/
git clone $BLESS_CLIENT_REPO_URL
cd continuous-deployment
LINE="export BLESSPATH=\"$HOME/github/continuous-deployment/bless_client\""
FILE="$HOME/.bashrc"
grep -qF "$LINE" "$FILE" | echo "$LINE" >> "$FILE"
LINE="PATH=\"$PATH:$BLESSPATH\""
grep -qF "$LINE" "$FILE" | echo "$LINE" >> "$FILE"

Reminder, the authentication certificate is currently valid for up to four (4) hours and will expire due to time limit, IP address change, network change, or forced removal.

Troubleshooting

Authentication failed

  • If you have a cached version of the cert and it fails to log you in run ssh-add -D which will clear all of your agent cached

boto errors with the function

  • botocore.exceptions.ConfigParseError: Unable to parse config file: /Users/$USER/.aws/credentials
    • You may have some whitespace in ~/.aws/credentials file that is confusing boto
    • Solution: move the credential file aside and run aws configure again

virtualenv is incompatible with Python installation

  • ERROR: virtualenv is not compatible with this system or executable
    • Try reinstalling virtualenv (may require sudo): pip install --ignore-installed virtualenv
#!/usr/bin/env python
"""bless_client
Usage:
bless_client.py bastion_command <id_rsa.pub to sign> <output id_rsa-cert.pub>
bastion_command: Text information about the SSH request of the bastion_user.
source_ip: The source IP where the SSH connection will be initiated from
id_rsa.pub to sign: The id_rsa.pub that will be used in the SSH request. This is
enforced in the issued certificate.
output id_rsa-cert.pub: The file where the certificate should be saved. Per man SSH(1):
"ssh will also try to load certificate information from the filename
obtained by appending -cert.pub to identity filenames" e.g. the <id_rsa.pub to sign>.
"""
import json
import stat
import sys
import boto3
import getpass
import os
import socket
import kmsauth
import json
def main(argv):
if len(argv) != 3:
print (
'Usage: bless_client.py '
'source_ip '
'<id_rsa.pub to sign> '
'<output id_rsa-cert.pub>')
return -1
source_ip, public_key_filename, certificate_filename = argv
with open(public_key_filename, 'r') as f:
public_key = f.read().strip()
user = boto3.client('iam').get_user()['User']
username = user['UserName'].lower()
token = kmsauth.KMSTokenGenerator(
'arn:aws:kms:us-east-1:ACCOUNT_REDACTED:key/KEY_ID_REDACTED',
{
# We're authenticating to this service
'to':'bless-production',
# It's from this user
'from':username,
# This token is for a user
'user_type': 'user'
},
'us-east-1'
).get_token()
payload = {'bastion_user': username, 'bastion_user_ip': source_ip,
'remote_usernames': 'ubuntu,hadoop', 'bastion_ips': source_ip,
'command': 'login', 'public_key_to_sign': public_key, 'kmsauth_token': token}
payload_json = json.dumps(payload)
lambda_client = boto3.client('lambda', region_name='us-east-1')
response = lambda_client.invoke(FunctionName='arn:aws:lambda:us-east-1:ACCOUNT_REDACTED:function:bless_kms_lambda',
InvocationType='RequestResponse', LogType='None',
Payload=payload_json)
if response['StatusCode'] != 200:
print ('Error creating cert.')
return -1
payload = json.loads(response['Payload'].read())
if 'certificate' not in payload:
print payload
return -1
cert = payload['certificate']
with os.fdopen(os.open(certificate_filename, os.O_WRONLY | os.O_CREAT, 0o600),
'w') as cert_file:
cert_file.write(cert)
# If cert_file already existed with the incorrect permissions, fix them.
file_status = os.stat(certificate_filename)
if 0o600 != (file_status.st_mode & 0o777):
os.chmod(certificate_filename, stat.S_IRUSR | stat.S_IWUSR)
print('Wrote Certificate to: ' + certificate_filename)
if __name__ == '__main__':
main(sys.argv[1:])
#!/bin/bash
# This bash script is intended to be sourced for easier access
# We use pyenv, but with some convenience scripts to make things work more like virtualenvwrapper
function bless_me() {
# To set up:
# mkdir -p $HOME/Documents/git
# cd $HOME/Documents/git
# git clone $BLESS_CLIENT_REPO_URL
# mkvirtualenv 2.7 venv-bless
# workon venv-bless
# pip install --ignore-installed six boto3 kmsauth
BLESS_DIR="$HOME/Documents/git/continuous-deployment/bless_client"
pyenv deactivate &>/dev/null
export PYENV_VIRTUALENV_DISABLE_PROMPT=1
gopy
workon venv-bless
if [ $# -eq 1 ]; then
echo "Usage: $0 [host_to_login test|prod [command]]"
return 1
fi
RSA_KEY_FILE="$HOME/.ssh/bless_rsa"
PUBLIC_KEY="${RSA_KEY_FILE}.pub"
BLESS_KEY="${RSA_KEY_FILE}-cert.pub"
SOURCE_IP=$(ifconfig "$(route get 172.28.0.0 | awk '/interface: / {print $2}')" | awk '/inet / {print $2}')
if [ -f "${BLESS_KEY}" ]; then
cert_time=$(ssh-keygen -L -f "${BLESS_KEY}" | grep "Valid:" | awk '{print $NF}')
certtime=$(date -j -f "%Y-%m-%dT%H:%M:%S" "${cert_time}" "+%s")
currenttime=$(date +%s)
ip_from_cert=$(ssh-keygen -L -f "${BLESS_KEY}" | grep "source-address" | awk '{print $2}')
if [[ ( "${currenttime}" -ge "${certtime}" ) || ( "${ip_from_cert}" != "${SOURCE_IP}" ) ]]; then
ssh-add -d $RSA_KEY_FILE
$BLESS_DIR/bless_client.py ${SOURCE_IP} ${PUBLIC_KEY} ${BLESS_KEY}
if [ -f "${BLESS_KEY}" ]; then
ssh-add -t 14440 $RSA_KEY_FILE
fi
fi
elif [ ! -f "${BLESS_KEY}" ]; then
if [ ! -f ${RSA_KEY_FILE} ]; then
ssh-keygen -f ${RSA_KEY_FILE} -b 4096 -t rsa -N ''
fi
ssh-add -d $RSA_KEY_FILE
$BLESS_DIR/bless_client.py ${SOURCE_IP} ${PUBLIC_KEY} ${BLESS_KEY}
if [ -f "${BLESS_KEY}" ]; then
ssh-add -t 14440 $RSA_KEY_FILE
fi
fi
pyenv deactivate &>/dev/null
}
function bless_ssh() {
bless_me
RSA_KEY_FILE="$HOME/.ssh/bless_rsa"
ssh-keygen -R "$1"
ssh -o "IdentitiesOnly=yes" \
-o "UserKnownHostsFile=/dev/null" \
-o "StrictHostKeyChecking=no" \
-i ${RSA_KEY_FILE} "ubuntu@$1"
}
function bless_scp() {
bless_me
RSA_KEY_FILE="$HOME/.ssh/bless_rsa"
ssh-keygen -R "$1"
scp -o "IdentitiesOnly=yes" \
-o "UserKnownHostsFile=/dev/null" \
-o "StrictHostKeyChecking=no" \
-i ${RSA_KEY_FILE} "$@"
}
function bless_sftp() {
bless_me
RSA_KEY_FILE="$HOME/.ssh/bless_rsa"
ssh-keygen -R "$1"
sftp -o "IdentitiesOnly=yes" \
-o "UserKnownHostsFile=/dev/null" \
-o "StrictHostKeyChecking=no" \
-i ${RSA_KEY_FILE} "$@"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment