diff --git a/cmd/neighbor/neighbor.go b/cmd/neighbor/neighbor.go new file mode 100644 index 0000000..e058c78 --- /dev/null +++ b/cmd/neighbor/neighbor.go @@ -0,0 +1,87 @@ +package neighbor + +import ( + "bufio" + "fmt" + "net" + "os" + "strings" + + cmdx "github.com/esonhugh/k8spider/cmd" + "github.com/esonhugh/k8spider/define" + "github.com/esonhugh/k8spider/pkg" + "github.com/esonhugh/k8spider/pkg/mutli" + "github.com/esonhugh/k8spider/pkg/printer" + log "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var Opts = struct { + NamespaceWordlist string + NamespaceList []string + PodCidr string +}{} + +func init() { + cmdx.RootCmd.AddCommand(NeighborCmd) + NeighborCmd.Flags().StringVar(&Opts.NamespaceWordlist, "ns-file", "", "namespace wordlist file") + NeighborCmd.Flags().StringSliceVar(&Opts.NamespaceList, "ns", []string{}, "namespace list") + NeighborCmd.Flags().StringVarP(&Opts.PodCidr, "pod-cidr", "p", defaultPodCidr(), "pod cidr list, watch out for the network interface name, default is eth0") +} + +func defaultPodCidr() string { + interfaces, _ := net.Interfaces() + for _, i := range interfaces { + if i.Name == "eth0" { + addrs, _ := i.Addrs() + if addrs != nil || len(addrs) > 0 { + ip := strings.Split(addrs[0].String(), "/")[0] + return fmt.Sprintf("%v/16", ip) + } + } + } + return "10.0.0.1/16" +} + +var NeighborCmd = &cobra.Command{ + Use: "neighbor", + Short: "neighbor is a tool to discover k8s pod and available ip in subnet (require k8s coredns with pod verified config)", + Aliases: []string{"n", "nei"}, + Run: func(cmd *cobra.Command, args []string) { + if !pkg.TestPodVerified() { + log.Fatalf("k8s coredns with pod verified config could not be set") + } + if Opts.NamespaceWordlist != "" { + f, e := os.OpenFile(Opts.NamespaceWordlist, os.O_RDONLY, 0666) + if e != nil { + log.Fatalf("open file %v failed: %v", Opts.NamespaceWordlist, e) + } + defer f.Close() + fileScanner := bufio.NewScanner(f) + fileScanner.Split(bufio.ScanLines) + for fileScanner.Scan() { + Opts.NamespaceList = append(Opts.NamespaceList, fileScanner.Text()) + } + } + log.Tracef("namespace list: %v", Opts.NamespaceList) + ipNets, err := pkg.ParseStringToIPNet(Opts.PodCidr) + if err != nil { + log.Warnf("ParseStringToIPNet failed: %v", err) + return + } + r := RunMultiThread(Opts.NamespaceList, ipNets, cmdx.Opts.ThreadingNum) + printer.PrintResult(r, cmdx.Opts.OutputFile) + }, +} + +func RunMultiThread(ns []string, net *net.IPNet, num int) (finalRecord define.Records) { + scan := mutli.ScanNeighbor(ns, net, num) + for r := range scan { + finalRecord = append(finalRecord, r...) + } + if len(finalRecord) == 0 { + log.Warn("ScanSubnet Found Nothing") + return + } + return +} diff --git a/cmd/root.go b/cmd/root.go index 2f4fef6..7211e13 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -49,6 +49,7 @@ var RootCmd = &cobra.Command{ }, } } + pkg.Zone = Opts.Zone }, Run: func(cmd *cobra.Command, args []string) { _ = cmd.Help() diff --git a/main.go b/main.go index c05d09d..3f11506 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "github.com/esonhugh/k8spider/cmd" _ "github.com/esonhugh/k8spider/cmd/all" _ "github.com/esonhugh/k8spider/cmd/axfr" + _ "github.com/esonhugh/k8spider/cmd/neighbor" _ "github.com/esonhugh/k8spider/cmd/service" _ "github.com/esonhugh/k8spider/cmd/subnet" _ "github.com/esonhugh/k8spider/cmd/wildcard" diff --git a/pkg/mutli/executor.go b/pkg/mutli/executor.go index d188547..8978608 100644 --- a/pkg/mutli/executor.go +++ b/pkg/mutli/executor.go @@ -12,8 +12,10 @@ func ScanAll(subnet *net.IPNet, num int) (result <-chan []define.Record) { return result } -func ScanNeighbor(subnet *net.IPNet, num int) (result <-chan []define.Record) { - subs := NewSubnetScanner(num) - result = subs.ScanSubnet(subnet) - return result +func ScanNeighbor(namespace []string, subnet *net.IPNet, num int) <-chan []define.Record { + subs := NewNeighborScanner(num) + if len(namespace) == 1 { + return subs.ScanSingleNeighbor(namespace[0], subnet) + } + return subs.ScanMultiNeighbor(namespace, subnet) } diff --git a/pkg/mutli/neigbhor.go b/pkg/mutli/neigbhor.go index 0946749..f2e54ca 100644 --- a/pkg/mutli/neigbhor.go +++ b/pkg/mutli/neigbhor.go @@ -1,10 +1,15 @@ package mutli import ( + "fmt" "net" "sync" + "time" "github.com/esonhugh/k8spider/define" + "github.com/esonhugh/k8spider/pkg" + "github.com/esonhugh/k8spider/pkg/scanner" + log "github.com/sirupsen/logrus" ) type NeighborScanner struct { @@ -25,6 +30,56 @@ func NewNeighborScanner(threading ...int) *NeighborScanner { } } -func (s *NeighborScanner) ScanNeighbor(subnet *net.IPNet) <-chan []define.Record { - return nil // todo: implement this +func (s *NeighborScanner) ScanSingleNeighbor(ns string, subnet *net.IPNet) <-chan []define.Record { + if subnet == nil { + log.Debugf("subnet is nil") + return nil + } + out := make(chan []define.Record, 100) + go func() { + // if subnets, err := pkg.SubnetShift(subnet, 4); err != nil { + if subnets, err := pkg.SubnetInto(subnet, s.count); err != nil { + log.Errorf("Subnet split into %v failed, fallback to single mode, reason: %v", s.count, err) + go s.scan(ns, subnet, out) + } else { + log.Debugf("Subnet split into %v success", len(subnets)) + for _, sn := range subnets { + go s.scan(ns, sn, out) + } + } + time.Sleep(10 * time.Millisecond) // wait for all goroutines to start + s.wg.Wait() + close(out) + }() + return out +} + +func (s *NeighborScanner) ScanMultiNeighbor(nss []string, subnet *net.IPNet) <-chan []define.Record { + out := make(chan []define.Record, 100) + go func() { + for _, ns := range nss { + go s.scan(ns, subnet, out) + } + time.Sleep(10 * time.Millisecond) // wait for all goroutines to start + s.wg.Wait() + close(out) + }() + return out +} + +func (s *NeighborScanner) scan(ns string, subnet *net.IPNet, to chan []define.Record) { + s.wg.Add(1) + // to <- scanner.ScanSubnet(subnet) + for _, ip := range pkg.ParseIPNetToIPs(subnet) { + if scanner.ScanPodExist(ip, ns) { + newRecord := define.Record{ + Ip: ip, + Extra: fmt.Sprintf("%v. 0 IN A %v", pkg.IPtoPodHostName(ip.String(), ns), ip.String()), + } + to <- []define.Record{newRecord} + } else { + continue + } + } + s.wg.Done() } diff --git a/pkg/post/records_test.go b/pkg/post/records_test.go index 0cf1d56..8256f6c 100644 --- a/pkg/post/records_test.go +++ b/pkg/post/records_test.go @@ -9,7 +9,7 @@ import ( ) func TestRecordsDump(t *testing.T) { - rs, err := scanner.DumpAXFR("zonetransfer.me.", "81.4.108.41:53") + rs, err := scanner.DumpAXFR("zonetransfer.me.", "34.225.33.2:53") if err != nil { t.Errorf("DumpAXFR failed: %v", err) } diff --git a/pkg/scanner/neigbhor.go b/pkg/scanner/neigbhor.go index 2ddc535..4a92d49 100644 --- a/pkg/scanner/neigbhor.go +++ b/pkg/scanner/neigbhor.go @@ -1,27 +1,23 @@ package scanner import ( + "net" + "github.com/esonhugh/k8spider/pkg" + log "github.com/sirupsen/logrus" ) -func TestPodVerified(zone string) bool { - iplist := []string{ - "8.8.8.8", - "1.1.1.1", - "114.114.114.114", +func ScanPodExist(ip net.IP, ns string) bool { + targetHostName := pkg.IPtoPodHostName(ip.String(), ns) + ips, err := pkg.ARecord(targetHostName) + if err != nil { + log.Tracef("ScanPodExist %v failed: %v", ip.String(), err) + return false } - for _, ip := range iplist { - targetHostName := pkg.IPtoPodHostName(ip, zone) - ips, err := pkg.ARecord(targetHostName) - if err != nil { - continue - } - // all of this ip should not return correct ip if verified is set. - for _, i := range ips { - if i.String() == ip { - return false - } + for _, i := range ips { + if i.String() == ip.String() { + return true } } - return true + return false } diff --git a/pkg/utils.go b/pkg/utils.go index f09793d..4a2ba60 100644 --- a/pkg/utils.go +++ b/pkg/utils.go @@ -12,6 +12,8 @@ import ( var NetResolver *net.Resolver = net.DefaultResolver +var Zone string // Zone is the domain name of the cluster + func ParseStringToIPNet(s string) (ipnet *net.IPNet, err error) { _, ipnet, err = net.ParseCIDR(s) return @@ -55,6 +57,29 @@ func ARecord(domain string) (ips []net.IP, err error) { return } -func IPtoPodHostName(ip string, zone string) string { - return fmt.Sprintf("%s.default.pod.%s", strings.ReplaceAll(ip, ".", "-"), zone) +func IPtoPodHostName(ip, namespace string) string { + return fmt.Sprintf("%s.%s.pod.%s", strings.ReplaceAll(ip, ".", "-"), namespace, Zone) +} + +func TestPodVerified() bool { + iplist := []string{ + "8.8.8.8", + "1.1.1.1", + "114.114.114.114", + } + for _, ip := range iplist { + targetHostName := IPtoPodHostName(ip, "kube-system") + log.Tracef("test if record %v is ip: %v", targetHostName, ip) + ips, err := ARecord(targetHostName) + if err != nil { + continue + } + // all of this ip should not return correct ip if verified is set. + for _, i := range ips { + if i.String() == ip { + return false + } + } + } + return true }