This article describes how to log an event to UpGuard when a user logs into a node via SSH.

UpGuard’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 discovering 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 walk through of how to inject an event into the UpGuard event stream when a user logs into a machine via SSH. For context, a current UpGuard customer wants to log when any of our support Engineers log into the UpGuard appliance itself, for their own internal security policies.

Overview

Here we are going to:

  • Construct an API call that will log a ssh_login event to the Events stream
  • Add this API call to as a SSH login hook
  • Create an Event View in UpGuard to display SSH login events

Creating 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.

require 'httparty'

api_key = "1234"
api_sec = "5678"
hostname = "https://my-hostname.upguard.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
#!/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.upguard.com/api/v2/events.json

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.

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.upguard.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.upguard.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.

Create the binary file by running the following commands:

$ mkdir -p out
$ go run main.go -key 1234 -sec 5678 -url https://you.upguard.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 ...

Installing 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

Viewing Logged Events

Navigate to 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 time a user logs into a machine via SSH for record keeping, but it is also a good idea to be notified when this happens incase there are any unwanted login attempts that successfully gain access to a machine.

See the Events page 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.

Tags: ssh events