| 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 |
| } |