Skip to content

Commit

Permalink
Example code to support ForwardedIPs the right way
Browse files Browse the repository at this point in the history
Warning this code has a few problems:
- It is not plugged in to Talos. as I could not find the needed hooks
- `ip` is not available, we should use the talos api's here, which need modification as scope is always global
- consecutive calls to func ForwardedIPs, ForwardedIPs should use the waiting parameter in the url, to make the thread block until there is a new update of the metadata
- the helper functions are mostly copied from google-agent, not sure we may use them in Talos.
  • Loading branch information
nberlee committed Aug 22, 2021
1 parent 576ba19 commit d5f6cad
Showing 1 changed file with 167 additions and 1 deletion.
168 changes: 167 additions & 1 deletion internal/app/machined/pkg/runtime/v1alpha1/platform/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package gcp

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand All @@ -13,6 +14,9 @@ import (
"net"
"net/http"

"os/exec"
"strings"

"github.com/talos-systems/go-procfs/procfs"

"github.com/talos-systems/talos/internal/app/machined/pkg/runtime"
Expand All @@ -25,7 +29,7 @@ import (
const (
// GCUserDataEndpoint is the local metadata endpoint inside of DO.
GCUserDataEndpoint = "http://metadata.google.internal/computeMetadata/v1/instance/attributes/user-data"
// GCExternalIPEndpoint displays all external addresses associated with the instance.
// GCExternalIPEndpoint displays all addresses associated with the instance.
GCExternalIPEndpoint = "http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces/?recursive=true"
)

Expand All @@ -39,6 +43,7 @@ func (g *GCP) Name() string {

// Configuration implements the platform.Platform interface.
func (g *GCP) Configuration(ctx context.Context) ([]byte, error) {

log.Printf("fetching machine config from: %q", GCUserDataEndpoint)

return download.Download(ctx, GCUserDataEndpoint,
Expand Down Expand Up @@ -107,6 +112,167 @@ func (g *GCP) ExternalIPs(ctx context.Context) (addrs []net.IP, err error) {
return addrs, err
}

func forwardedIPs(ctx context.Context) (err error) {
var (
body []byte
req *http.Request
resp *http.Response
)
// maybe run a second time with &wait_for_change=true
if req, err = http.NewRequestWithContext(ctx, "GET", GCExternalIPEndpoint, nil); err != nil {
return
}

req.Header.Add("Metadata-Flavor", "Google")

client := &http.Client{}
if resp, err = client.Do(req); err != nil {
return
}

//nolint:errcheck
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to retrieve forwarded addresses for instance")
}

type metadata []struct {
ForwardedIP []string `json:"forwardedIps"`
Mac string `json:"mac"`
}

if body, err = ioutil.ReadAll(resp.Body); err != nil {
return
}

m := metadata{}
if err = json.Unmarshal(body, &m); err != nil {
return
}

for _, networkInterface := range m {
iface, err := getInterfaceByMAC(networkInterface.Mac)
if err != nil {
continue
}

var configuredRoutes []net.IP = nil
var desiredRoutes []net.IP = nil
for _, ip := range networkInterface.ForwardedIP {
desiredRoutes = append(desiredRoutes, net.ParseIP(ip))
}
configuredRoutes, err = getLocalRoutes(iface.Name)
if err != nil {
continue
}
for _, desiredRoute := range desiredRoutes {
if !containsIP(desiredRoute, configuredRoutes) {
args := fmt.Sprintf("route add to local %s/32 scope host dev %s proto 66", desiredRoute, iface.Name)
runCmd(exec.Command("ip", strings.Split(args, " ")...))
}
}

for _, configuredRoute := range configuredRoutes {
if !containsIP(configuredRoute, desiredRoutes) {
args := fmt.Sprintf("route delete to local %s/32 scope host dev %s proto 66", configuredRoute, iface.Name)
runCmd(exec.Command("ip", strings.Split(args, " ")...))
}
}
}

return err
}
func containsIP(s net.IP, ss []net.IP) bool {
for _, a := range ss {
if a.Equal(s) {
return true
}
}
return false
}

// === functions below are copied from https://github.com/GoogleCloudPlatform/guest-agent/blob/main/google_guest_agent/addresses.go
func getLocalRoutes(ifname string) ([]net.IP, error) {
args := fmt.Sprintf("route list table local type local scope host dev %s proto 66", ifname)
out := runCmdOutput(exec.Command("ip", strings.Split(args, " ")...))
if out.ExitCode() != 0 {
return nil, error(out)
}
var res []net.IP
for _, line := range strings.Split(out.Stdout(), "\n") {
line = strings.TrimPrefix(line, "local ")
line = strings.TrimSpace(line)
if line != "" {
res = append(res, net.ParseIP(line))
}
}
return res, nil
}

func getInterfaceByMAC(mac string) (net.Interface, error) {
hwaddr, err := net.ParseMAC(mac)
if err != nil {
return net.Interface{}, err
}
interfaces, err := net.Interfaces()
if err != nil {
return net.Interface{}, err
}

for _, iface := range interfaces {
if iface.HardwareAddr.String() == hwaddr.String() {
return iface, nil
}
}
return net.Interface{}, fmt.Errorf("no interface found with MAC %s", mac)
}

func runCmd(cmd *exec.Cmd) error {
res := runCmdOutput(cmd)
if res.ExitCode() != 0 {
return res
}
return nil
}

type execResult struct {
// Return code. Set to -1 if we failed to run the command.
code int
// Stderr or err.Error if we failed to run the command.
err string
// Stdout or "" if we failed to run the command.
out string
}

func (e execResult) Error() string {
return strings.TrimSuffix(e.err, "\n")
}

func (e execResult) ExitCode() int {
return e.code
}

func (e execResult) Stdout() string {
return e.out
}

func runCmdOutput(cmd *exec.Cmd) *execResult {
var stdout, stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr

err := cmd.Run()
if err != nil {
if ee, ok := err.(*exec.ExitError); ok {
return &execResult{code: ee.ExitCode(), out: stdout.String(), err: stderr.String()}
}
return &execResult{code: -1, err: err.Error()}
}
return &execResult{code: 0, out: stdout.String()}
}

// === end
// KernelArgs implements the runtime.Platform interface.
func (g *GCP) KernelArgs() procfs.Parameters {
return []*procfs.Parameter{
Expand Down

0 comments on commit d5f6cad

Please sign in to comment.