Class: SSHScan::Worker

Inherits:
Object
  • Object
show all
Defined in:
lib/ssh_scan_worker/worker.rb

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Worker

Returns a new instance of Worker.

Raises:

  • (ArgumentError)


8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/ssh_scan_worker/worker.rb', line 8

def initialize(opts = {})
  raise ArgumentError.new("API server not specified") unless ENV['sshscan.api.host'] || opts["server"]
  @server =  ENV['SSHSCAN_API_HOST'] || opts["server"]
  
  raise ArgumentError.new("API scheme not specified") unless ENV['sshscan.api.host'] || opts["scheme"]
  @scheme = ENV['SSHSCAN_API_SCHEME'] || opts["scheme"]

  raise ArgumentError.new("API verify not specified") unless ENV['sshscan.api.verify'] || opts["verify"]
  @verify = ENV['SSHSCAN_API_VERIFY'] || opts["verify"]

  raise ArgumentError.new("API port not specified") unless ENV['sshscan.api.port'] || opts["port"]
  @port = ENV['SSHSCAN_API_PORT'] || opts["port"]

  raise ArgumentError.new("API auth token not specified") unless ENV['sshscan.api.token'] || opts["token"]
  @auth_token = ENV['SSHSCAN_API_TOKEN'] || opts["token"] 

  @logger = setup_logger(opts["logger"])
  @poll_interval = opts["poll_interval"] || 5 # in seconds
  @poll_restore_interval = opts["poll_restore_interval"] || 5 # in seconds
  @worker_id = SecureRandom.uuid
end

Instance Method Details

#perform_work(work) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ssh_scan_worker/worker.rb', line 98

def perform_work(work)
  @logger.info("Started job: #{work["uuid"]}")
  work["sockets"] = [work["target"] + ":" + work["port"].to_s]
  scan_engine = SSHScan::ScanEngine.new
  work["fingerprint_database"] = File.join(File.dirname(__FILE__),"../../data/fingerprints.yml")
  work["policy"] = File.join(File.dirname(__FILE__),"../../config/policies/mozilla_modern.yml")
  work["timeout"] = 5
  results = scan_engine.scan(work)
  @logger.info("Completed job: #{work["uuid"]}")
  return results
end

#post_results(results, job) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ssh_scan_worker/worker.rb', line 110

def post_results(results, job)
  uri = URI(
    "#{@scheme}://#{@server}:#{@port}/api/v1/\
work/results/#{@worker_id}/#{job["uuid"]}"
  )
  http = Net::HTTP.new(uri.host, uri.port)

  if @scheme == "https"
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @verify == false
    options_mask =
      OpenSSL::SSL::OP_NO_SSLv2 +
      OpenSSL::SSL::OP_NO_SSLv3 +
      OpenSSL::SSL::OP_NO_COMPRESSION
    http.ssl_options = options_mask
  end

  request = Net::HTTP::Post.new(uri.path)
  request.add_field("SSH_SCAN_AUTH_TOKEN", @auth_token) unless @auth_token.nil?
  request.add_field("Content-Type", "application/json")

  request.body = results.to_json
  http.request(request)
  @logger.info("Posted job: #{job["uuid"]}")
end

#retrieve_workObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ssh_scan_worker/worker.rb', line 68

def retrieve_work
  (Net::HTTP::SSL_IVNAMES << :@ssl_options).uniq!
  (Net::HTTP::SSL_ATTRIBUTES << :options).uniq!

  Net::HTTP.class_eval do
    attr_accessor :ssl_options
  end

  uri = URI(
    "#{@scheme}://#{@server}:#{@port}/api/v1/\
work?worker_id=#{@worker_id}"
  )
  http = Net::HTTP.new(uri.host, uri.port)

  if @scheme == "https"
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @verify == false
    options_mask =
      OpenSSL::SSL::OP_NO_SSLv2 +
      OpenSSL::SSL::OP_NO_SSLv3 +
      OpenSSL::SSL::OP_NO_COMPRESSION
    http.ssl_options = options_mask
  end

  request = Net::HTTP::Get.new(uri.path)
  request.add_field("SSH_SCAN_AUTH_TOKEN", @auth_token) unless @auth_token.nil?
  response = http.request(request)
  JSON.parse(response.body)
end

#run!Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/ssh_scan_worker/worker.rb', line 41

def run!
  loop do
    begin
      response = retrieve_work
      
      if response["work"]
        work = response["work"]
        results = perform_work(work)
        post_results(results, work)
      elsif response["error"]
        @logger.info("Error: #{response["error"]}")
        sleep @poll_interval
        next
      else
        @logger.info("No jobs available from #{@server}:#{@port} (waiting #{@poll_interval} seconds)")
        sleep @poll_interval
        next
      end
    rescue Errno::ECONNREFUSED
      @logger.error("Cannot reach API endpoint #{@server}:#{@port}, waiting #{@poll_restore_interval} seconds")
      sleep @poll_restore_interval
    #rescue RuntimeError => e
      @logger.error(e.inspect)
    end
  end
end

#setup_logger(logger) ⇒ Object



30
31
32
33
34
35
36
37
38
39
# File 'lib/ssh_scan_worker/worker.rb', line 30

def setup_logger(logger)
  case logger
  when Logger
    return logger
  when String
    return Logger.new(logger)
  end

  return Logger.new(STDOUT)
end