├── .gitignore ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Makefile ├── README.md ├── Rakefile └── out └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/gems 2 | vendor/bundle 3 | .bundle 4 | *.xml 5 | *.dot 6 | *.png 7 | *.dat 8 | *.gz 9 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.7.0-buster 2 | # throw errors if Gemfile has been modified since Gemfile.lock 3 | # to update 4 | # docker run --rm -v "$PWD":/usr/src/app -w /usr/src/app ruby:2.7.0-buster bundle install 5 | RUN bundle config --global frozen 1 6 | 7 | WORKDIR /usr/src/app 8 | 9 | RUN apt-get update && apt-get install -y \ 10 | geoip-bin \ 11 | geoip-database-extra \ 12 | graphviz \ 13 | jq \ 14 | nmap 15 | 16 | COPY Gemfile Gemfile.lock ./ 17 | RUN bundle install 18 | 19 | ADD . /usr/src/app/ 20 | ENV LANG C.UTF-8 -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "nokogiri", ">= 1.10.4" 4 | gem "rake" 5 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | mini_portile2 (2.4.0) 5 | nokogiri (1.10.7) 6 | mini_portile2 (~> 2.4.0) 7 | rake (12.3.0) 8 | 9 | PLATFORMS 10 | ruby 11 | 12 | DEPENDENCIES 13 | nokogiri (>= 1.10.4) 14 | rake 15 | 16 | BUNDLED WITH 17 | 2.1.2 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jesse Newland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | build: Gemfile.lock 3 | docker build -t network-tools . 4 | 5 | Gemfile.lock: Gemfile 6 | docker run --rm -v "$$PWD":/usr/src/app -w /usr/src/app ruby:2.7.0-buster bundle install 7 | 8 | graph.png: build 9 | docker run --rm -it -v "$$PWD":/usr/src/app/out network-tools rake graph -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tools for researching networks. 2 | 3 | ### Network path analysis 4 | 5 | Generates a graphviz graph (`graph.png`) representing routes taken by traffic to 6 | a set of target domains. Alexa's top 25 sites in your current country are used 7 | as targets if none are provided. 8 | 9 | #### Usage 10 | 11 | docker run --rm -it -v $(pwd):/usr/src/app/out jnewland/network-tools rake graph 12 | open graph.png 13 | 14 | Use a specific set of targets by setting the `TARGETS` environment variable: 15 | 16 | docker run --rm -it -e TARGETS=github.com,gitlab.com -v $(pwd):/usr/src/app/out jnewland/network-tools rake graph -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "nokogiri" 2 | 3 | if ENV["TARGETS"] 4 | @targets = ENV["TARGETS"].split(",") 5 | else 6 | country = `curl -s ipinfo.io | jq -r '.country'` 7 | alexa = `curl -s https://www.alexa.com/topsites/countries/#{country}` 8 | @targets = alexa.scan(/"\/siteinfo\/(.*)"/).flatten 9 | end 10 | 11 | file "out/nmap.xml" do 12 | `nmap -sn --traceroute #{@targets.join(" ")} -oX out/nmap.xml` 13 | end 14 | 15 | task :nmap => ["out/nmap.xml"] do 16 | # parse the file 17 | f = File.open("out/nmap.xml") 18 | @doc = Nokogiri::XML(f) 19 | f.close 20 | 21 | # do some parsing that we want anytime we use this 22 | @ip_count = Hash.new(0) 23 | @hostnames = {} 24 | @ip_network = {} 25 | 26 | # pull all the hops out of it 27 | hops = @doc.xpath("//hop").each do |h| 28 | ip = h["ipaddr"] 29 | @ip_count[ip] += 1 30 | @hostnames[ip] = host = h["host"] || ip 31 | @ip_network[ip] = `geoiplookup #{h["ipaddr"]} | grep ASNum | cut -f 2 -d : | grep -v "not found"`.strip 32 | end 33 | end 34 | 35 | desc "Generate a diagram" 36 | task :dot => [:nmap] do 37 | edges = [] 38 | hops = [] 39 | File.open("out/graph.dot", 'w') do |dotfile| 40 | dotfile.write "digraph G {\n" 41 | 42 | @doc.xpath("//trace").each do |t| 43 | trace_hops = [] 44 | next if t.xpath("hop").length <= 2 45 | t.xpath("hop").each do |h| 46 | ip = h["ipaddr"] 47 | next if @ip_network[ip] == "" 48 | hop = @hostnames[ip] || ip 49 | hop += " / #{@ip_network[ip]}" 50 | if hop != hops.first 51 | if previous_hop = trace_hops.last 52 | edges << "\"#{previous_hop}\" -> \"#{hop}\";" 53 | end 54 | end 55 | trace_hops << hop 56 | hops << hop 57 | end 58 | end 59 | 60 | edges.uniq.each do |edge| 61 | dotfile.write "#{edge}\n" 62 | end 63 | hops.uniq.each do |hop| 64 | dotfile.write "\"#{hop}\" [fontsize = #{12+(@ip_count[hop]*10)}];\n" 65 | end 66 | 67 | dotfile.write "graph [rankdir=TB];\n" 68 | dotfile.write "}\n" 69 | end 70 | end 71 | 72 | task :graph => [:dot] do 73 | `dot -Tpng out/graph.dot > out/graph.png` 74 | `rm out/graph.dot out/nmap.xml` 75 | end -------------------------------------------------------------------------------- /out/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jnewland/network-tools/95d8177662dde384a15aac520e1ba7e1db761f25/out/.gitkeep --------------------------------------------------------------------------------