package mirko import ( "fmt" "net" "net/http" "strconv" "strings" ) // parsePort parses a string as a port number from 1 to 65535. func parsePort(s string) (uint16, error) { port, err := strconv.ParseUint(s, 10, 16) if err != nil { return 0, fmt.Errorf("could not parse port %q: %v", s, err) } if port < 1 || port > 65535 { return 0, fmt.Errorf("port %d out of range", port) } return uint16(port), nil } // GetHTTPRemoteClient returns the IP address and source port of the client // initiating the given HTTP request. This will either interpret the remote // side of the HTTP connection if not running within a cluster, or the source // IP/port as reported by the cluster reverse proxy (nginx-ingress-controller). // An error will be returned if the request is unparseable for this data. In // this case, the caller should assume that the environment is misconfigured, // and that the client source cannot be deduced. func GetHTTPRemoteClient(r *http.Request) (net.IP, uint16, error) { if KubernetesClient() == nil { // We're not running inside a cluster (we're probably running on a dev // machine), so just return whatever net/http says. host, portStr, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return nil, 0, fmt.Errorf("could not split hostport: %v", err) } ip := net.ParseIP(host) if ip == nil { return nil, 0, fmt.Errorf("could not parse host %q to IP address", host) } port, err := parsePort(portStr) if err != nil { return nil, 0, err } return ip, uint16(port), nil } // We are running in a cluster, so we can expect Hscloud-* headers. // These are configured in the nginx-ingress-controller, //cluster/kube/lib/nginx.libsonnet. nsip := strings.TrimSpace(r.Header.Get("Hscloud-Nic-Source-IP")) nsport := strings.TrimSpace(r.Header.Get("Hscloud-Nic-Source-Port")) if nsip == "" || nsport == "" { return nil, 0, fmt.Errorf("Hscloud-Nic-* headers not set") } ip := net.ParseIP(nsip) if ip == nil { return nil, 0, fmt.Errorf("Invalid Hscloud-Nix-Source-IP %q", nsip) } port, err := parsePort(nsport) if err != nil { return nil, 0, fmt.Errorf("Invalid Hscloud-Nix-Source-Port: %v", err) } return ip, port, nil }