From f8ea01ed1b727fac3811db91ca063a7465f45282 Mon Sep 17 00:00:00 2001 From: VetheonGames Date: Thu, 29 Jun 2023 22:36:18 -0600 Subject: [PATCH] Implement Initial System Setup and Packet Capture 1. Initial System Setup: - Implemented a first run initialization process that guides the user through setting up the necessary environment variables. - Created a method to securely ask for the user's sudo password, test it, and store it in an encrypted form in an environment variable for use during the first run setup process. - Added a method to clear the sudo password from memory and the environment variables at the end of the first run setup process. 2. Packet Capture: - Created a PacketCapture class that uses the PCAPRUB library to capture packets from a specified network interface. - Refactored the packet capture process to add each captured packet to a Redis queue for further processing, instead of processing the packets directly. - Removed the manual packet dissection from the packet capture process, as this will be handled by the workers. 3. Networking Setup: - Created a NetworkingGenie class to handle the setup of the necessary networking components. - Added methods to identify the main network interface, create a dummy network interface, and set up traffic mirroring from the main interface to the dummy interface. 4. Logging: - Implemented logging for all major actions and errors throughout the system. 5. General Refactoring and Code Cleanup: - Refactored and cleaned up various parts of the code to improve readability and maintainability. - Fixed various minor bugs and issues. This commit lays the groundwork for the packet processing workers and the orchestrator that will manage them. The next steps will be to implement these components and integrate them with the existing system. --- lib/Gemfile | 8 +++++ lib/Gemfile.lock | 18 ++++++++++ lib/utils/networking_genie.rb | 37 +++++++++++++++++++ lib/utils/packet_capture.rb | 47 ++++++++++++++++++++++++ lib/utils/redis_queue.rb | 8 +++++ lib/utils/system_information_gather.rb | 2 +- lib/utils/utilities.rb | 49 ++++++++++++++++++++++---- 7 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 lib/utils/networking_genie.rb create mode 100644 lib/utils/packet_capture.rb create mode 100644 lib/utils/redis_queue.rb diff --git a/lib/Gemfile b/lib/Gemfile index caf13ec..656bfe1 100644 --- a/lib/Gemfile +++ b/lib/Gemfile @@ -32,3 +32,11 @@ gem 'securerandom', '~> 0.2.2' gem 'dotenv', '~> 2.8' gem "tracer", "~> 0.2.2" + +gem "flay", "~> 2.13" + +gem "pcaprub", "~> 0.13.1" + +gem "packetfu", "~> 2.0" + +gem "sudo", "~> 0.2.0" diff --git a/lib/Gemfile.lock b/lib/Gemfile.lock index 28be0ae..d286094 100644 --- a/lib/Gemfile.lock +++ b/lib/Gemfile.lock @@ -23,9 +23,15 @@ GEM dynamic_curses_input (1.0.0) curses e2mmap (0.1.0) + erubi (1.12.0) fiber-annotation (0.2.0) fiber-local (1.0.0) fiddle (1.1.1) + flay (2.13.0) + erubi (~> 1.10) + path_expander (~> 1.0) + ruby_parser (~> 3.0) + sexp_processor (~> 4.0) gdk3 (4.1.7) cairo-gobject (= 4.1.7) gdk_pixbuf2 (= 4.1.7) @@ -56,6 +62,8 @@ GEM nokogiri (1.15.2-x86_64-linux) racc (~> 1.4) openssl (3.1.0) + packetfu (2.0.0) + pcaprub (~> 0.13.1) pango (4.1.7) cairo-gobject (= 4.1.7) gobject-introspection (= 4.1.7) @@ -63,6 +71,8 @@ GEM parser (3.2.2.3) ast (~> 2.4.1) racc + path_expander (1.1.1) + pcaprub (0.13.1) pkg-config (1.5.1) racc (1.7.0) rainbow (3.1.1) @@ -90,8 +100,11 @@ GEM rubocop-ast (1.29.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) + ruby_parser (3.20.2) + sexp_processor (~> 4.16) securerandom (0.2.2) sequel (5.69.0) + sexp_processor (4.17.0) solargraph (0.49.0) backport (~> 1.2) benchmark @@ -108,6 +121,7 @@ GEM thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) + sudo (0.2.0) thor (1.2.2) tilt (2.2.0) tracer (0.2.2) @@ -124,14 +138,18 @@ DEPENDENCIES curses (~> 1.4) dotenv (~> 2.8) dynamic_curses_input (~> 1.0) + flay (~> 2.13) gtk3 (~> 4.1) mysql2 (~> 0.5.5) openssl (~> 3.1) + packetfu (~> 2.0) + pcaprub (~> 0.13.1) reek (~> 6.1) rubocop (~> 1.52) securerandom (~> 0.2.2) sequel (~> 5.69) solargraph (~> 0.49.0) + sudo (~> 0.2.0) tracer (~> 0.2.2) yaml (~> 0.2.1) diff --git a/lib/utils/networking_genie.rb b/lib/utils/networking_genie.rb new file mode 100644 index 0000000..d5d9b58 --- /dev/null +++ b/lib/utils/networking_genie.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'socket' +require_relative 'logg_man' + +# The class for setting up all the necessary system networking stuff for NETRAVE to work with without +# interferring with the rest of the system +class NetworkingGenie + def initialize(logger) + @loggman = logger + end + + def find_main_interface # rubocop:disable Metrics/MethodLength + @loggman.log_info('Identifying main network interface...') + route_info = `routel`.split("\n") + default_route = route_info.find { |line| line.include?('default') } + if default_route + main_interface = default_route.split.last + @loggman.log_info("Main network interface identified: #{main_interface}") + main_interface + else + @loggman.log_error('Failed to identify main network interface.') + nil + end + rescue StandardError => e + @loggman.log_error("Error occurred while identifying main network interface: #{e.message}") + nil + end + + def create_dummy_interface + # TODO: Implement method to create a dummy network interface + end + + def setup_traffic_mirroring + # TODO: Implement method to set up traffic mirroring from the main interface to the dummy interface + end +end diff --git a/lib/utils/packet_capture.rb b/lib/utils/packet_capture.rb new file mode 100644 index 0000000..413eb82 --- /dev/null +++ b/lib/utils/packet_capture.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'pcaprub' +require 'socket' +require_relative 'databasemanager' +require_relative 'logg_man' +require_relative 'redis_queue' + +# Class used to capture packets and not much else +class PacketCapture + INTERFACE_NAME = 'netrave0' + + def initialize(queue, logger) + @loggman = logger + @loggman.log_info("Initializing packet capture for #{INTERFACE_NAME}...") + @capture = Pcap.open_live(INTERFACE_NAME, 65_535, true, 1) + @capture.setfilter('') + @loggman.log_info('Packet capture initialized successfully!') + @queue = queue + end + + def start_capture_loop # rubocop:disable Metrics/MethodLength + @loggman.log_info("Starting packet capture loop for #{@interface}...") + packet_count = 0 + begin + @loggman.log_info("Packet capture loop started for #{@interface}...") + @capture.each_packet do |packet| + # Add packet to queue + @queue.push(packet) + @loggman.log_info("Packet #{packet_count += 1} added to queue.") + end + rescue StopIteration + @loggman.log_warn("Packet capture loop stopped for #{@interface}.") + rescue StandardError => e + @loggman.log_fatal("Packet capture loop stopped for #{@interface}: #{e.message}\n#{e.backtrace}", false) + sleep 1 + retry + ensure + @capture.close + end + end + + def stop_capture + @loggman.log_warn("Stopping packet capture loop for #{@interface}...") + @stop_flag = true + end +end diff --git a/lib/utils/redis_queue.rb b/lib/utils/redis_queue.rb new file mode 100644 index 0000000..2c54641 --- /dev/null +++ b/lib/utils/redis_queue.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Class for managing the worker queue in Redis +class RedisQueueManager + def initialize(logger) + @loggman = logger + end +end diff --git a/lib/utils/system_information_gather.rb b/lib/utils/system_information_gather.rb index 8aae1aa..9d0efbf 100644 --- a/lib/utils/system_information_gather.rb +++ b/lib/utils/system_information_gather.rb @@ -38,7 +38,7 @@ class SystemInformationGather def ask_for_uplink_speed # rubocop:disable Metrics/MethodLength loop do Curses.clear - Curses.addstr("Please enter your uplink speed (upload speed, e.g., 1000Mbps or 1Gbps).\n" \ + Curses.addstr("Please enter your uplink speed (upload speed, e.g., 150Mbps or 1Gbps).\n" \ "This is typically the maximum upload speed provided by your ISP.\n" \ "You can check your ISP bill, use an online speed test, or contact your ISP if you're unsure.\n\n") Curses.refresh diff --git a/lib/utils/utilities.rb b/lib/utils/utilities.rb index 33fb94a..2ce7838 100644 --- a/lib/utils/utilities.rb +++ b/lib/utils/utilities.rb @@ -4,6 +4,7 @@ require 'securerandom' require 'digest' require 'base64' require 'openssl' +require 'sudo' # Utiltiies Module module Utilities @@ -49,8 +50,7 @@ module Utilities Base64.encode64(encrypted_data).chomp rescue OpenSSL::Cipher::CipherError => e - loggman = LoggMan.new - loggman.log_error("Failed to encrypt data: #{e.message}") + @loggman.log_error("Failed to encrypt data: #{e.message}") nil end @@ -67,13 +67,11 @@ module Utilities if decrypted_data.valid_encoding? decrypted_data else - loggman = LoggMan.new - loggman.log_error("Decrypted data is not valid ASCII: #{decrypted_data.inspect}") + @loggman.log_error("Decrypted data is not valid ASCII: #{decrypted_data.inspect}") nil end rescue OpenSSL::Cipher::CipherError => e - loggman = LoggMan.new - loggman.log_error("Failed to decrypt data: #{e.message}") + @loggman.log_error("Failed to decrypt data: #{e.message}") nil end @@ -104,4 +102,43 @@ module Utilities Curses.attroff(Curses.color_pair(3)) if severity == :warning Curses.attroff(Curses.color_pair(2)) if severity == :error end + + def ask_for_sudo(logger) + @loggman = logger + @loggman.log_info('Asking for sudo password...') + Curses.addstr('Please enter your sudo password: ') + sudo_password = DCI.catch_input(true) + @loggman.log_info('Sudo password received.') + sudo_password + end + + def test_and_deescalate_sudo(sudo_password) + # Run a simple command with sudo privileges + Sudo::Wrapper.run('ls', password: sudo_password) + + # Invalidate the user's cached credentials + Sudo::Wrapper.run('sudo -k', password: sudo_password) + + # Encrypt the sudo password and store it in an environment variable + encrypted_sudo_password = encrypt_string_chacha20(sudo_password, @secret_key) + ENV['SPW'] = encrypted_sudo_password + + true + rescue Sudo::Wrapper::InvalidPassword + false + end + + def clear_sudo_password + # Retrieve the encrypted sudo password from the environment variable + encrypted_sudo_password = ENV['SPW'] + + # Decrypt the sudo password + sudo_password = decrypt_string_chacha20(encrypted_sudo_password, @secret_key) + + # Clear the sudo password from memory + sudo_password.replace(' ' * sudo_password.length) + + # Remove the encrypted sudo password from the environment variables + ENV.delete('SPW') + end end