thesp0nge
thesp0nge OSCP guy. I break code and rebuild it with security in mind, taekwon-do, husband, dad. Chaotic good drow ranger. I blog @codiceinsicuro and @the_armoredcode.

Create your own telemetry system

Create your own telemetry system

In order to monitor dawnscanner security scaner usage, I introduced in upcoming version 2.0.0, a telemetry system.

On boot, dawnscanner will check if it has a unique identifier and if not it will ask for one to the telemetry system.

get '/new' do
  i = Id.new
  i.uuid = Id.get_new_id
  i.save

  content_type :json
  {:uuid => i.uuid }.to_json
end

Id is an ActiveRecord class for the identifier model.

require 'securerandom'

class Id < ActiveRecord::Base
  def self.get_new_id
    return SecureRandom.uuid
  end
end

The initialization part happens in the Dawn::Cli class.

$telemetry_url = $config[:telemetry][:endpoint] if $config[:telemetry][:enabled]
debug_me("telemetry url is " + $telemetry_url) unless @telemetry_url.nil?
$telemetry_id = $config[:telemetry][:id] if $config[:telemetry][:enabled]

debug_me("telemetry id is " + $telemetry_id) unless @telemetry_id.nil?
$logger.info("telemetry is disabled in config file") unless $config[:telemetry][:enabled]

The real magic is in the Dawn::Engine class. Here we’ve got a telemetry method that do all the stuff.

The basic idea is to do a post on a URL posting the unique dawnscanner identifier (that is also the URL where the post is made), the IP address and the knowledge base version.

def have_a_telemetry_id?
  debug_me ($telemetry_id != ""  and ! $telemetry_id.nil?)
  return ($telemetry_id != ""  and ! $telemetry_id.nil?)
  
end

def get_a_telemetry_id
  return "" if ($telemetry_url == "" or $telemetry_url.nil?)
  debug_me("T: " + $telemetry_url)

  url = URI.parse($telemetry_url+"/new")
  res = Net::HTTP.get_response(url)

  return "" unless res.code.to_i == 200
  return JSON.parse(res.body)["uuid"]
end


def telemetry
  unless have_a_telemetry_id?
    $telemetry_id = get_a_telemetry_id
    $config[:telemetry][:id] = $telemetry_id
    debug_me($config)
    debug_me("saving config to " + $config_name)
    File.open($config_name, 'w') { |f| f.write $config.to_yaml }
  end

  debug_me("Telemetry ID is: " + $telemetry_id)
  
  uri=URI.parse($telemetry_url+"/"+$telemetry_id)
  header = {'Content-Type': 'text/json'}
  tele = { "kb_version" => Dawn::KnowledgeBase::VERSION , 
           "ip" => Socket.ip_address_list.detect{|intf| intf.ipv4_private?}.ip_address, 
           "message"=> Dawn::KnowledgeBase
        }
  http = Net::HTTP.new(uri.host, uri.port)
  request = Net::HTTP::Post.new(uri.request_uri, header)
  request.body = tele.to_json

  response=http.request(request)
  debug_me(response.inspect)

  return true
  
end

That’s it. No personal data apart from your IP is sent to dawnscanner servers. On backend side, I implemented a simple Sinatra post catcher block of code, saving the data on a SQLite3 database.

post '/:uuid' do
  request.body.rewind
  @request_payload = JSON.parse request.body.read

  i = Id.find_by("uuid", params['uuid'].to_s)
  unless i.nil? 
    l=Log.new
    l.uuid = params['uuid'].to_s
    l.ip=@request_payload['ip']
    l.kb_version=@request_payload['kb_version']
    l.message=@request_payload['message']
    l.save
  end
end

This very basic telemetry system needs tons of improvements and, of course, it’s opensource code hosted on Github.com.

Enjoy it!

comments powered by Disqus