Skip to content

Commit

Permalink
fixup! add metadata providers for azure, alicloud, ikoula and oracle …
Browse files Browse the repository at this point in the history
…cloud infrastructure

Signed-off-by: Steve Fan <29133953+stevefan1999-personal@users.noreply.github.com>
  • Loading branch information
stevefan1999-personal committed Aug 26, 2020
1 parent 7808cbc commit e765469
Show file tree
Hide file tree
Showing 324 changed files with 104,954 additions and 386 deletions.
58 changes: 58 additions & 0 deletions pkg/metadata/dhcp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"github.com/sirupsen/logrus"
)

// DHCPServerLocator locate DHCP servers
type DHCPServerLocator interface {
Probe() (possibleServers []string, err error)
}

// FindPossibleDHCPServers find possible dhcp servers
func FindPossibleDHCPServers() (possibleDhcpServAddr []string, err error) {
var locatorFacade interface{}

state := "file"
loop:
for {
switch state {
case "file":
logrus.WithField("method", "existing DHCP lease information").
Infof("checking DHCP servers")
locatorFacade = &DHCPServerLeaseFileLocator{}
state = "run"
case "direct":
logrus.WithField("method", "directly send DHCP packets").
Infof("checking DHCP servers")
locatorFacade = &DHCPServerDirectLocator{}
state = "run"
case "run":
if locator, ok := locatorFacade.(DHCPServerLocator); ok {
possibleDhcpServAddr, err = locator.Probe()
if err != nil {
if _, ok := locatorFacade.(*DHCPServerLeaseFileLocator); ok {
logrus.WithError(err).
Warn("cannot find DHCP server")
state = "direct"
} else {
state = "fail"
}
} else {
state = "Success"
}
} else {
state = "fail"
}
case "fail":
logrus.
Debug("unable to find DHCP servers with known methods")
break loop
case "Success":
logrus.WithField("servers", possibleDhcpServAddr).
Debugf("found DHCP servers")
break loop
}
}
return
}
63 changes: 63 additions & 0 deletions pkg/metadata/dhcp_direct.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"net"
"regexp"
"sync"
"time"

"github.com/digineo/go-dhclient"
"github.com/sirupsen/logrus"
"github.com/thoas/go-funk"
)

// DHCPServerDirectLocator This is an extreme method of trying to find a DHCP server by sending DHCP packets directly
type DHCPServerDirectLocator struct {

}

// ethernetInterfaceNamesRegex a simple regex to determine ethernet interfaces
// example: eth0, eth1, enps1p4
var ethernetInterfaceNamesRegex = regexp.MustCompile(`eth\d+|enp\d+s\d+`)

// Probe find DHCP servers
func (d *DHCPServerDirectLocator) Probe() (possibleAddresses []string, err error) {
defer func() {
if err == nil {
possibleAddresses = funk.UniqString(possibleAddresses)
}
}()

var ifaces []net.Interface
ifaces, err = net.Interfaces()
if err != nil {
return
}
ifaces = funk.Filter(ifaces, func (p net.Interface) bool {
return ethernetInterfaceNamesRegex.MatchString(p.Name)
}).([]net.Interface)

var wg sync.WaitGroup
for _, iface := range ifaces {
wg.Add(1)
go func(wg *sync.WaitGroup, iface *net.Interface) {
logrus.Debugf("starting DHCP broadcast on interface %s...", iface.Name)

var client dhclient.Client
client = dhclient.Client{Iface: iface, OnBound: func(lease *dhclient.Lease) {
// I think this could potentially panic here because what if the wait group instead timing out?
// then the method exits and GC kicks in causing the possibleAddresses array to be in undefined state/garbage value
// i.e. UAF, but anyway this is just a small utility and panicking is acceptable,
// and using select to handle these situations by gracefully stopping all spawned goroutines would probably be nicer
possibleAddresses = append(possibleAddresses, lease.ServerID.String())
go client.Stop()
wg.Done()
}}
client.Start()
}(&wg, &iface)
}

// either wait group finishes or time out after 30 seconds
waitTimeout(&wg, 30*time.Second)
return
}
69 changes: 69 additions & 0 deletions pkg/metadata/dhcp_lease.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
"path/filepath"
"regexp"

"github.com/thoas/go-funk"
)

// DHCPServerLeaseFileLocator Find dhcp server by looking up the lease file generated.
// This implies the existence of dhclient
type DHCPServerLeaseFileLocator struct {

}

const (
// DHCPDirsPattern This is to match dhcp and dhcp3 and various many others
DHCPDirsPattern = "/var/lib/dhcp*/*.leases"
)

// DHCPServerRegex I do not care about whether the IP address is correct, rather I assumed the DHCP client
// will always produce and validate the DHCP server to be in octet range
// IP address example: 127.10.0.1
var DHCPServerRegex = regexp.MustCompile(`option[ \t]+dhcp-server-identifier[ \t]+((?:[0-9]{1,3}\.){3}[0-9]{1,3});?`)

// Probe Guesses possible DHCP server addresses
// Used here to figure out the metadata service server
func (d *DHCPServerLeaseFileLocator) Probe() (possibleAddresses []string, err error) {
// i actually wanted set data structure in golang...and golang team said barely anybody use it so nope
// f*ck it, i roll my own. ken thompson why would you make such a stupid mistake?
defer func() {
if err == nil {
possibleAddresses = funk.UniqString(possibleAddresses)
}
}()

var matches []string
// error or no match
if matches, err = filepath.Glob(DHCPDirsPattern); err != nil || len(matches) < 1 {
return
}

for _, value := range matches {
// fail fast if there's an error already
if err != nil {
return
}
func() {
var file MemoryMappedFile
if file, err = OpenMemoryMappedFile(value); err != nil {
return
}
defer func() {
if err = file.Close(); err != nil {
return
}
}()

// submatch 0 is the matched expression source
// so starting from submatch 1 it will be the capture groups...at least 2 elements
if submatches := DHCPServerRegex.FindSubmatch(*file.data); len(submatches) > 1 {
possibleAddresses = funk.Map(funk.Tail(submatches), func(submatch []byte) string {
return string(submatch)
}).([]string)
}
}()
}
return
}
4 changes: 2 additions & 2 deletions pkg/metadata/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func writeConfigFiles(target string, current Entry) {
if isFile(current) {
filemode, err := parseFileMode(current.Perm, 0644)
if err != nil {
log.Printf("Failed to parse permission %+v: %s", current, err)
log.Printf("Failed to parse Perm %+v: %s", current, err)
return
}
if err := ioutil.WriteFile(target, []byte(*current.Content), filemode); err != nil {
Expand All @@ -237,7 +237,7 @@ func writeConfigFiles(target string, current Entry) {
} else if isDirectory(current) {
filemode, err := parseFileMode(current.Perm, 0755)
if err != nil {
log.Printf("Failed to parse permission %+v: %s", current, err)
log.Printf("Failed to parse Perm %+v: %s", current, err)
return
}
if err := os.MkdirAll(target, filemode); err != nil {
Expand Down
50 changes: 50 additions & 0 deletions pkg/metadata/mmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"os"
"syscall"
)

// MemoryMappedFile A file and its mapped memory. Linux exclusive
type MemoryMappedFile struct {
file *os.File
data *[]byte
}

// OpenMemoryMappedFile opens a file and mmap it
func OpenMemoryMappedFile(path string) (mmf MemoryMappedFile, err error) {
var (
stat os.FileInfo
data []byte
)
// can't really use := because name shadowing
if mmf.file, err = os.Open(path); err != nil {
return
}
if stat, err = mmf.file.Stat(); err != nil {
return
}
// since we work on linux exclusive platform this is fine
if data, err = syscall.Mmap(int(mmf.file.Fd()), 0, int(stat.Size()), syscall.PROT_READ, syscall.MAP_SHARED); err != nil {
return
}
mmf.data = &data
return
}

// Close munmap the mmap'd buffer if the file has opened before and then close the file
func (m *MemoryMappedFile) Close() (err error) {
if m.file != nil {
if m.data != nil {
err = syscall.Munmap(*m.data)
m.data = nil
}
if err == nil {
err = m.file.Close()
m.file = nil
}
}
return
}


0 comments on commit e765469

Please sign in to comment.