Logging SSH logins to Slack

I’m using Slack to alert and log a few things in my environment, and one of the things I use it for is to alert me if someone logs on via SSH to my public facing Jumphost.

For a good walkthrough on how to set up such a host, check out Tunnel all your remote connections through ssh with a linux jumpbox by Luca Dell’Oca.

My Ubuntu 16.04 Jumphost is set up to only accept Key-Based Authentication, to secure it as much as possible, but I would still like to get instant notification if someone logs into it interactively.

How to set up SSH login notification to Slack.

  1. screenshot-2016-10-06-13-04-51First of all, we need  an Incoming WebHook in Slack in order to receive the notifications.
    You configure those from the Apps & Integration menu item. This in turn opens up the Slack App Directory, find Build on the top right and then choose Make a Custom Integration.
  2. screenshot-2016-10-06-13-08-09One your are in the Build a Custom Integration section, find (or search) Incoming WebHooks and select that.
  3. Next up, define which Slack channel should be the integration point, and click on Add Incoming WebHooks integration.
  4. Copy the Webhook URL presented on the next screen
    Note: keep this one a secret, anyone with access to this URL will be able to post to your Slack channel.
  5. On my Ubuntu 16.04 Linux jumphost I’ve created a small bash script called /etc/ssh/notify.sh. This script utilizes curl  and the WebHook URL to post information directly to Slack. The script looks like this:notify.sh
    #!/bin/sh
    url="https://hooks.slack.com/services/*********"
    channel="#messages"
    host="`hostname`"
    content=""attachments": [ { "mrkdwn_in": ["text", "fallback"], "fallback": "SSH login: $USER connected to \`$host\`", "text": "SSH login to \`$host\`", "fields": [ { "title": "User", "value": "$USER", "short": true }, { "title": "IP Address", "value": "$SSH_CLIENT", "short": true } ], "color": "#F35A00" } ]"
    curl -s -X POST --data-urlencode "payload={"channel": "$channel", "mrkdwn": true, "username": "ssh-bot", $content, "icon_emoji": ":computer:"}" $url
    /bin/bash

    Replace the  the WebHook URL with your own from step 4 and which channel to post to and you should be ready to go.  This script logs the username and the IP address the connection comes from, and then posts it to the Slack WebHook with the help of curl.Note: I’ve chosen to include the WebHook name etc in the script itself, instead of via the WebHook definition on Slack, mostly since I don’t want to create a WebHook for all hosts I want logging from. With this setup, I can just change the username part of the curl command. It already logs the hostname, so this is pretty much superficial, but hey, that’s how I made it.

  6. chmod +x /etc/ssh/notify.sh to make it executable, and test it. If everything works as expected, you should see an immediate log entry in your chosen Slack channel.
  7. On order to make this script runs every time someone logs into the Jumphost, I added a ForceCommand to the end of my /etc/ssh/sshd_config file, like this:
    ForceCommand /etc/ssh/notify.sh

And that’s it. A login via SSH on the Jumphost now looks like this in my Slack channel:

screenshot-2016-10-06-13-26-10

How awesome is that? Of course, this just scratches the surface of what is possible with Slack’s Incoming WebHooks, I’m using a similar approach for logging new devices discovered in phpmyipam but that’s for another post.

Homelab: CloudFlare Dynamic DNS Update Script (cf-ddns.sh)

As a part of my Homelab project, I’ve created a proper bash script to provide dynamic DNS updates for external resources, via CloudFlare. More details on the reasoning behind it can be found in Using CloudFlare for Dynamic DNS, but since that was posted I’ve fleshed the script out quite a bit more.

The new and updated script can be found on GitHub: cf-ddns.sh. It now writes events to syslog when it runs, so you can use VMware Log Insight (or another log solution) to capture the events, and use it to track public IP changes if you want. I’ve also added a -f parameter to it, so you can force an IP update even if the values have not changed since the last run.

It’s pretty self explanatory, just replace your own values from CloudFlare for the variables, and if you want to update more than one record, just copy the update block and edit it for the extra entries.

Hopefully someone will find this useful.

Homelab: Using CloudFlare for Dynamic DNS

In my previous post, I tried to lay out the foundation and reasoning behind requiring a Dynamic DNS Service, and here is how I solved it using CloudFlare.

First of all, I moved my chosen domain name to CloudFlare, and made sure everything resolved ok with static records. Once that was working, I started playing around with the CloudFlare API, using Cocoa Rest Client. I’m no developer (as is probably very apparent by the script below), nor API wizard of any kind, but it was fairly easy figuring out how to craft a request that lists my DNS zone.

By using the List DNS Records query, I found the unique ID for the hostnames I wanted to update, and created a new Update DNS record query to update it with a new IP address. Since the Cocoa Rest Client is pretty clever, it has an option to “Copy Curl Command”, that basically gives you a preformatted  curl command to run the query you just crafted in it. Pasting that into a Terminal window on my Mac, verified that it worked as intended. From there on, I simply wrapped these commands in a little bash script, to avoid hitting the API unless there was an actual public IP change.

In the end, my script ended up looking like this.

UPDATE:
I’ve published a more fleshed out script on GitHub, details here.
NOTE: You will need to fill out your own values for {TOKEN}, {EMAIL}, {DOMAIN}, {ID} and {HOSTNAME} in line 18 and 21 for this to work for you. 

cloudflare-ddns.sh

#!/bin/sh

#get public ip

MYIP=$(curl ifconfig.me/ip)
OLDIP=<code>cat oldip.txt</code>

echo "Current public IP is:" $MYIP

if [ "$MYIP" = "$OLDIP" ]
then
echo "No change detected. Exiting"
else

echo "IP change detected, updating CloudFlare"

#WEB01
curl -k -L -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'a=rec_edit&tkn={TOKEN}&email={EMAIL}&z={DOMAIN}&id={ID}&type=A&name={HOSTNAME}&ttl=1&content='$MYIP 'https://www.cloudflare.com/api_json.html'

#WEB02
curl -k -L -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d 'a=rec_edit&tkn={TOKEN}&email={EMAIL}&z={DOMAIN}&id={ID}&type=A&name={HOSTNAME}&ttl=1&content='$MYIP 'https://www.cloudflare.com/api_json.html'

echo $MYIP > oldip.txt
fi
Explanation:

First off, the script checks what the current public IP is (line 5), then goes on to compare that with the stored IP address in the oldip.txt file (line 10).

If it matches, it ends execution as there is no need to update the public records. If there is a mismatch between the two, it goes on to execute the request to CloudFlare, replacing the currently configured IP with the new IP address stored in the $MYIP variable (lines 17-21).

It then writes the new IP address to the oldip.txt file (line 23) and exits.

Configure this as a cronjob the runs every 5 – 10 minutes or so and you’re set! Simple, not pretty, but oh-so awesome!