diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..2dbfd6f --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--colour +--format documentation \ No newline at end of file diff --git a/bin/mass_scan b/bin/mass_scan index 8421c56..f388d8c 100644 --- a/bin/mass_scan +++ b/bin/mass_scan @@ -3,10 +3,13 @@ filename = ARGV[0] network_file = File.open(filename) +# Generated via SecureRandom.uuid for example purposes... +scan_perspective_id = "fc88d1b5-9487-45e9-99ce-ddf2a41b96aa" + system("docker build -t nmap-agent .") network_file.each_line do |network| system( - "docker run -t -d -i -e SCAN_NETWORK='#{network.chomp}' -e SCAN_TCP_PORTS='20,21,22,23,25,80,110,443,512,522,8080,1080,22222' -e AWS_REGION='INSERT_AWS_REGION' -e AWS_S3_BUCKET='INSERT_AWS_S3_BUCKET' -e AWS_ACCESS_KEY_ID='INSERT_AWS_ACCESS_KEY_ID' -e AWS_SECRET_ACCESS_KEY='INSERT_AWS_SECRET_ACCESS_KEY' nmap-agent:latest /app/bin/scan" + "docker run -t -d -i -e SCAN_PERSPECTIVE_ID='#{scan_perspective_id}' -e SCAN_NETWORK='#{network.chomp}' -e SCAN_TCP_PORTS='20,21,22,23,25,80,110,443,512,522,8080,1080,22222' -e AWS_REGION='INSERT_AWS_REGION' -e AWS_S3_BUCKET='INSERT_AWS_S3_BUCKET' -e AWS_ACCESS_KEY_ID='INSERT_AWS_ACCESS_KEY_ID' -e AWS_SECRET_ACCESS_KEY='INSERT_AWS_SECRET_ACCESS_KEY' nmap-agent:latest /app/bin/scan" ) end \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index d95b0dc..5ca8177 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,7 @@ services: - default working_dir: /app environment: + - SCAN_PERSPECTIVE_ID=eaa1141f-89ac-469b-9439-8fc00d8cec4d - SCAN_NETWORK=192.168.10.1/32 - SCAN_TCP_PORTS=20,21,22,23,25,80,110,443,512,522,8080,1080,22222 - SCAN_COOLOFF=60 diff --git a/lib/nmap_agent/agent.rb b/lib/nmap_agent/agent.rb index 9dbb434..109b551 100644 --- a/lib/nmap_agent/agent.rb +++ b/lib/nmap_agent/agent.rb @@ -1,49 +1,68 @@ require 'json' -require 'nmap_agent' require 'nmap/program' require 'nmap/xml' -class Agent - def port_scan(output_file='latest.xml') - puts "[+] Started scanning #{ENV['SCAN_NETWORK']}" - - suppress_output do - Nmap::Program.scan do |nmap| - nmap.service_scan = false - nmap.os_fingerprint = false - nmap.xml = output_file - - # Example: 20,21,22,23,25,80,110,443,512,522,8080,1080 - nmap.ports = ENV['SCAN_TCP_PORTS'].split(",") - - # Example: 192.168.0.* - nmap.targets = ENV['SCAN_NETWORK'] - - # -Pn - nmap.skip_discovery +module NmapAgent + class Agent + # Verify at an array of ENV vars are present and set, if not raise an exception with what we're missing + def has_required_envs?(env_names=[]) + env_names.each do |env_name| + raise "Environment variable '#{env_name}' is not set, aborting" if ENV[env_name].nil? || ENV[env_name].empty? end + + return true + end + + def port_scan(output_file='latest.xml') + has_required_envs?( + [ + 'SCAN_TCP_PORTS', + 'SCAN_NETWORK' + ] + ) + + # Suppressing output is helpful to stop noise, however, it has potential to miss real issues... + # suppress_output do + + # See Nmap::Task for options setting help... + # + # https://github.com/sophsec/ruby-nmap/blob/master/lib/nmap/task.rb + # + Nmap::Program.scan do |nmap| + nmap.service_scan = false + nmap.os_fingerprint = false + nmap.xml = output_file + nmap.ports = ENV['SCAN_TCP_PORTS'].split(",") + nmap.targets = ENV['SCAN_NETWORK'] + nmap.skip_discovery + end + + # end + end + + def send2s3(upload_file='latest.xml') + has_required_envs?( + [ + 'SCAN_NETWORK', + 'SCAN_PERSPECTIVE_ID', + 'AWS_ACCESS_KEY_ID', + 'AWS_SECRET_ACCESS_KEY', + 'AWS_S3_BUCKET' + ] + ) + + key = "xml/" + ENV['SCAN_PERSPECTIVE_ID'] + "/" + ENV['SCAN_NETWORK'].gsub(/\//,"_") + ".xml" + client = Aws::S3::Client.new( + access_key_id: ENV['AWS_ACCESS_KEY_ID'], + secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] + ) + + resp = client.put_object({ + bucket: ENV['AWS_S3_BUCKET'], + key: key, + body: File.read(upload_file), + }) end - puts "[+] Finished scanning #{ENV['SCAN_NETWORK']}" end - - def send2s3(upload_file='latest.xml') - key = "xml/" + ENV['SCAN_NETWORK'].gsub(/\//,"_") + ".xml" - - puts "[+] Started uploading #{key} to S3" - - client = Aws::S3::Client.new( - access_key_id: ENV['AWS_ACCESS_KEY_ID'], - secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'] - ) - - resp = client.put_object({ - bucket: ENV['AWS_S3_BUCKET'], - key: key, - body: File.read(upload_file), - }) - - puts "[+] Finished uploading #{key} to S3" - end - end \ No newline at end of file diff --git a/spec/nmap-agent/agent_spec.rb b/spec/nmap-agent/agent_spec.rb new file mode 100644 index 0000000..cd0fb83 --- /dev/null +++ b/spec/nmap-agent/agent_spec.rb @@ -0,0 +1,18 @@ +require 'rspec' +require 'nmap_agent/agent' +require 'securerandom' + +describe NmapAgent::Agent do + it "NmapAgent::Agent should not raise when there are no expectations" do + agent = NmapAgent::Agent.new() + + expect(agent.has_required_envs?([])).to be(true) + end + + it "NmapAgent::Agent should raise when expectations are not met" do + agent = NmapAgent::Agent.new() + random_env_name = SecureRandom.uuid + + expect { agent.has_required_envs?([random_env_name]) }.to raise_error("Environment variable '#{random_env_name}' is not set, aborting") + end +end \ No newline at end of file diff --git a/spec/nmap-agent/version_spec.rb b/spec/nmap-agent/version_spec.rb new file mode 100644 index 0000000..cf074a2 --- /dev/null +++ b/spec/nmap-agent/version_spec.rb @@ -0,0 +1,19 @@ +require 'rspec' +require 'nmap_agent/version' + +describe 'NmapAgent::VERSION' do + it "NmapAgent::VERSION should be a string" do + expect(NmapAgent::VERSION).to be_kind_of(::String) + end + + it "NmapAgent::VERSION should have 3 levels" do + expect(NmapAgent::VERSION.split('.').size).to eql(3) + end + + it "NmapAgent::VERSION should have a number between 1-20 for each octet" do + NmapAgent::VERSION.split('.').each do |octet| + expect(octet.to_i).to be >= 0 + expect(octet.to_i).to be <= 40 + end + end +end \ No newline at end of file