Log SSH Logins
This article describes how to log an event to Cloudhouse Guardian (Guardian) when a user logs into a node via SSH. Guardian's events functionality provides a rich timeline of appliance events such as nodes being scanned successfully, policy failures or user logins. During debugging or crisis resolution these time-based events can be beneficial to discover the root cause of a problem. Augmenting the inbuilt events with external events can greatly improve the amount of information available when diagnosing an issue. This guide describes a walkthrough of how to inject an event into the Guardian event stream when a user logs into a machine via SSH. For context, a current Guardian user wants to log our support Engineers logging into the Guardian appliance itself, for their own internal security policies.
Overview
Here we are going to:
-
Construct an API call that will log a
ssh_loginevent to the Events stream. -
Add this API call to as a SSH login hook.
-
Create an Event View in Guardian to display SSH login events.
Create the API Call
Here we are going to create a Ruby script that can post an event to the Event stream, but you could also use Python, cURL or some other method to post to the API.
Ruby Example
require 'httparty'
api_key = "1234"
api_sec = "5678"
hostname = "https://my-hostname.cloudhouse.com"
response = HTTParty.post("#{hostname}/api/v2/events.json",
:headers => {
'Authorization' => "Token token=\"#{api_key}#{api_sec}\""
},
:body => {
:variables => {"type" => "ssh_login"}
})
puts response.code
puts response.body
curl Example
#!/bin/bash
API_KEY="1234"
API_SEC="5678"
curl -v -X POST -d 'variables={"type":"ssh_login"}' \
-H "Authorization: Token token=\"$API_KEY$API_SEC\"" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
https://my-hostname.cloudhouse.com/api/v2/events.json
Python SDK Example
# pip install upguard
import upguard
api_key = "1234"
sec_key = "5678"
o = upguard.Account("https://me.cloudhouse.com", api_key, sec_key)
ext = o.new_external_event()
ext.type = "ssh_login"
ext.variables["key"] = "value"
ext.variables["number"] = 123
ext.create()
Here we are calling the /api/v2/events endpoint to log the event. The only required part of the body is a variables parameter with a JSON object with a key called type. See Events - Create for more information about this API endpoint.
You can find the API Key and API Secret by accessing Manage Accounts in the top-right menu, then clicking on the name of our Account to display the Service API Key and Secret Key. Substitute your appliance's hostname into the hostname variable.
A Slightly More Secure API Caller
You could also bake the URL, API key and secret of your appliance account into a binary file that just posts this event type to your instance. The following golang code can be compiled into a "binary builder" executable that can then, in turn, be instructed to build an event posting binary that contains your credentials stored internally.
Copy and paste the following code into a file in your $GOPATH/src folder structure into a file called main.go.
GoLang Example
package main
import (
"bytes"
"flag"
"fmt"
"os"
)
type Buf struct{
buffer bytes.Buffer
}
func (self *Buf) W(format string, args ...interface{}) {
self.buffer.WriteString(fmt.Sprintf(format, args...))
self.buffer.WriteString("\n")
}
func (self *Buf) String() string {
return self.buffer.String()
}
func main() {
url := flag.String("url", "", "The base URL of your appliance, e.g. https://you.cloudhouse.com")
key := flag.String("key", "", "The API Key")
sec := flag.String("sec", "", "The API Secret Key")
flag.Parse()
if *url == "" {
fmt.Println("Missing -url param")
os.Exit(1)
}
if *key == "" {
fmt.Println("Missing -key param")
os.Exit(1)
}
if *sec == "" {
fmt.Println("Missing -sec param")
os.Exit(1)
}
var b Buf
b.W("package main")
b.W("")
b.W("import (")
b.W(" \"bytes\"")
b.W(" \"fmt\"")
b.W(" \"io/ioutil\"")
b.W(" \"net/http\"")
b.W(")")
b.W("")
b.W("func die(err error) {")
b.W(" if err != nil {")
b.W(" panic(err)")
b.W(" }")
b.W("}")
b.W("")
b.W("func main() {")
b.W(" payload := \"variables={\\\"type\\\":\\\"ssh_login\\\"}\"")
b.W(" client := &http.Client{}")
b.W(" req, err := http.NewRequest(\"POST\", \"%s/api/v2/events.json\", bytes.NewBuffer([]byte(payload)))", *url)
b.W(" die(err)")
b.W(" req.Header.Set(\"Authorization\", \"Token token=\\\"%s%s\\\"\")", *key, *sec)
b.W(" req.Header.Set(\"Accept\", \"application/json\")")
b.W(" res, err := client.Do(req)")
b.W(" die(err)")
b.W(" fmt.Printf(\"res.Code: %%v\\n\", res.StatusCode)")
b.W(" content, err := ioutil.ReadAll(res.Body)")
b.W(" die(err)")
b.W(" fmt.Printf(\"res.Body: %%v\\n\", string(content))")
b.W("}")
fmt.Print(b.String())
}
Next, run the code with the help option to see which parameters you can specify.
$ go run main.go -h
Usage:
-key string
The API Key
-sec string
The API Secret Key
-url string
The base URL of your appliance, e.g. https://you.cloudhouse.com
Running this file with the correct key, sec, and url parameters will generate the source of a go file that is able to post an ssh_login event to your appliance with these details baked in.
Note: The baked in credentials can still be extracted from the binary file by using the "strings" command. If you require an even more secure approach, please contact Guardian Support.
Create the binary file by running the following commands:
$ mkdir -p out
$ go run main.go -key 1234 -sec 5678 -url https://you.cloudhouse.com > out/main.go
$ cd out
$ go build -installsuffix 'static' -o log_ssh_logins.out .
$ # test that it worked
$ ./log_ssh_logins.out
... should print HTTP response code and body from API ...
Install the Login Hook
There are many options to execute a command when, for example, all users log into a machine, or when a particular user logs into a machine. For this use case, we're just going to add the execution of this script to the user's ~/.bashrc file. You could also add this to a user's ~/.bash_profile depending on your OS.
I have added this line to the bottom of my ~/.bashrc:
Ruby
ruby /home/username/util/log-when-i-log-in.rb
Golang
/home/user/util/log_ssh_logins.out
View Logged Events
Navigate to the Events tab (Control > Events) and search with the following query:
type=External Event AND variables.type=ssh_login
This should display all events that have been externally logged by this particular external event. If you want to log multiple login attempts from different machines you could either use a more specific variables.type value in the posting script, or add extra variables to query on.
What Next?
It is a good idea to log every login to a machine via SSH for audit purposes, but it is also a good idea to be notified when this happens in case there are any unwanted login attempts that successfully gain access to a machine.
See Events for more information on how to create a permanent view from this query, and how to add an action when an event of this type occurs, such as sending an email to the Ops team, or posting a message to your favorite chat client.