class Inspec::Resources::LinuxPorts
extract port information from netstat
def info
def info cmd = inspec.command('netstat -tulpen') return nil if cmd.exit_status.to_i != 0 ports = [] # parse all lines cmd.stdout.each_line do |line| port_info = parse_netstat_line(line) # only push protocols we are interested in next unless %w{tcp tcp6 udp udp6}.include?(port_info['protocol']) ports.push(port_info) end ports end
def parse_net_address(net_addr, protocol)
def parse_net_address(net_addr, protocol) if protocol.eql?('tcp6') || protocol.eql?('udp6') # prep for URI parsing, parse ip6 port ip6 = /^(\S+):(\d+)$/.match(net_addr) ip6addr = ip6[1] ip6addr = '::' if ip6addr =~ /^:::$/ # build uri ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}") # replace [] host = ip_addr.host[1..ip_addr.host.size-2] else ip_addr = URI('addr://'+net_addr) host = ip_addr.host end port = ip_addr.port [host, port] rescue URI::InvalidURIError => e warn "Could not parse #{net_addr}, #{e}" nil end
def parse_netstat_line(line)
def parse_netstat_line(line) # parse each line # 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 - Inode, 8 - PID/Program name parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)\s+(\S+)\s+(\S+)/.match(line) return {} if parsed.nil? || line.match(/^proto/i) # parse ip4 and ip6 addresses protocol = parsed[1].downcase # detect protocol if not provided protocol += '6' if parsed[4].count(':') > 1 && %w{tcp udp}.include?(protocol) # extract host and port information host, port = parse_net_address(parsed[4], protocol) # extract PID process = parsed[9].split('/') pid = process[0] pid = pid.to_i if pid =~ /^\d+$/ process = process[1] { 'port' => port, 'address' => host, 'protocol' => protocol, 'process' => process, 'pid' => pid, } end