2018-07-05 22:39:14 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2018-07-05 22:58:35 +02:00
|
|
|
"log"
|
2018-07-05 22:39:14 +02:00
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
cidr = "0.0.0.0/0"
|
|
|
|
beginPort = 80
|
|
|
|
endPort = 65535
|
|
|
|
|
|
|
|
arinAPIEndpoint = "http://whois.arin.net/rest/ip/%s"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
// On ^C, or SIGTERM handle exit.
|
|
|
|
c := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(c, os.Interrupt)
|
|
|
|
signal.Notify(c, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
|
|
for sig := range c {
|
|
|
|
logrus.Infof("Received %s, exiting.", sig.String())
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2018-07-05 22:58:35 +02:00
|
|
|
// Set the logger to nil so we ignore messages from the Dial that don't matter.
|
|
|
|
// See: https://github.com/golang/go/issues/19895#issuecomment-292793756
|
|
|
|
log.SetFlags(0)
|
|
|
|
log.SetOutput(ioutil.Discard)
|
|
|
|
|
2018-07-05 22:39:14 +02:00
|
|
|
logrus.Infof("Scanning for Kubernetes Dashboards and API Servers on %s over port range %d-%d", cidr, beginPort, endPort)
|
|
|
|
logrus.Infof("This may take a bit...")
|
|
|
|
|
2018-07-05 22:58:35 +02:00
|
|
|
startTime := time.Now()
|
|
|
|
|
2018-07-05 22:39:14 +02:00
|
|
|
ip, ipnet, err := net.ParseCIDR(cidr)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); inc(ip) {
|
|
|
|
wg.Add(1)
|
|
|
|
go func(ip string) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2018-07-06 15:48:28 +02:00
|
|
|
scanIP(ip)
|
2018-07-05 22:39:14 +02:00
|
|
|
}(ip.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Wait()
|
2018-07-05 22:58:35 +02:00
|
|
|
|
|
|
|
since := time.Since(startTime)
|
|
|
|
logrus.Infof("Scan took: %s", since.String())
|
2018-07-05 22:39:14 +02:00
|
|
|
}
|
|
|
|
|
2018-07-06 15:48:28 +02:00
|
|
|
func scanIP(ip string) {
|
|
|
|
for port := beginPort; port <= endPort; port++ {
|
|
|
|
// Check if the port is open.
|
|
|
|
ok := portOpen(ip, port)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if it's a kubernetes dashboard.
|
|
|
|
ok = isKubernetesDashboard(ip, port)
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
fmt.Printf("%s:%d\n", ip, port)
|
|
|
|
// Get the info for the ip address.
|
|
|
|
info, err := getIPInfo(ip)
|
|
|
|
if err != nil {
|
|
|
|
logrus.Warnf("ip info err: %v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
fmt.Printf("%s:%d\t%s\t%s\t%s\n",
|
|
|
|
ip, port,
|
|
|
|
info.Net.Organization.Handle, info.Net.Organization.Name, info.Net.Organization.Reference)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-05 22:39:14 +02:00
|
|
|
func portOpen(ip string, port int) bool {
|
|
|
|
c, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), 2*time.Second)
|
|
|
|
if err != nil {
|
|
|
|
// logrus.Warnf("listen at %s:%s failed: %v", ip, port, err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
defer c.Close()
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func isKubernetesDashboard(ip string, port int) bool {
|
|
|
|
client := &http.Client{
|
|
|
|
Timeout: time.Second * 3,
|
|
|
|
Transport: &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{
|
|
|
|
InsecureSkipVerify: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
tryAddrs := []string{
|
|
|
|
fmt.Sprintf("http://%s:%d", ip, port),
|
|
|
|
fmt.Sprintf("https://%s:%d", ip, port),
|
|
|
|
fmt.Sprintf("http://%s:%d/api/", ip, port),
|
|
|
|
fmt.Sprintf("https://%s:%d/api/", ip, port),
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
resp *http.Response
|
|
|
|
err = errors.New("not yet run")
|
|
|
|
uri string
|
|
|
|
)
|
|
|
|
|
|
|
|
for i := 0; i < len(tryAddrs) && err != nil; i++ {
|
|
|
|
uri = tryAddrs[i]
|
|
|
|
resp, err = client.Get(uri)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
//logrus.Warnf("getting %s:%s failed: %v", ip, port, err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
body := strings.ToLower(string(b))
|
2018-07-06 16:10:20 +02:00
|
|
|
if (strings.Contains(body, "kubernetes") && strings.Contains(body, "dashboard")) ||
|
|
|
|
(strings.Contains(body, `"versions"`) && strings.Contains(body, `"serverAddress`)) ||
|
|
|
|
(strings.Contains(body, `"paths"`) && strings.Contains(body, `"/api"`)) {
|
2018-07-05 22:39:14 +02:00
|
|
|
logrus.Infof("uri: %s", uri)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
type ARINResponse struct {
|
2018-07-05 23:33:35 +02:00
|
|
|
Net NetJSON `json:"net,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type NetJSON struct {
|
2018-07-06 00:18:31 +02:00
|
|
|
Organization OrganizationJSON `json:"orgRef,omitempty"`
|
2018-07-05 22:39:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type OrganizationJSON struct {
|
|
|
|
Handle string `json:"@handle,omitempty"`
|
|
|
|
Name string `json:"@name,omitempty"`
|
|
|
|
Reference string `json:"$,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func getIPInfo(ip string) (b ARINResponse, err error) {
|
|
|
|
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(arinAPIEndpoint, ip), nil)
|
|
|
|
if err != nil {
|
|
|
|
return b, err
|
|
|
|
}
|
|
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return b, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
if err := json.NewDecoder(resp.Body).Decode(&b); err != nil {
|
|
|
|
return b, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func inc(ip net.IP) {
|
|
|
|
for j := len(ip) - 1; j >= 0; j-- {
|
|
|
|
ip[j]++
|
|
|
|
if ip[j] > 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|