blob: 46f22d426785e10ec0408dbeabda579c4a1593df [file] [log] [blame]
Serge Bazanski13c90f02021-05-22 19:10:13 +00001package mirko
2
3import (
4 "fmt"
5 "net"
6 "net/http"
7 "strconv"
8 "strings"
9)
10
11// parsePort parses a string as a port number from 1 to 65535.
12func parsePort(s string) (uint16, error) {
13 port, err := strconv.ParseUint(s, 10, 16)
14 if err != nil {
15 return 0, fmt.Errorf("could not parse port %q: %v", s, err)
16 }
17 if port < 1 || port > 65535 {
18 return 0, fmt.Errorf("port %d out of range", port)
19 }
20 return uint16(port), nil
21}
22
23// GetHTTPRemoteClient returns the IP address and source port of the client
24// initiating the given HTTP request. This will either interpret the remote
25// side of the HTTP connection if not running within a cluster, or the source
26// IP/port as reported by the cluster reverse proxy (nginx-ingress-controller).
27// An error will be returned if the request is unparseable for this data. In
28// this case, the caller should assume that the environment is misconfigured,
29// and that the client source cannot be deduced.
30func GetHTTPRemoteClient(r *http.Request) (net.IP, uint16, error) {
31 if KubernetesClient() == nil {
32 // We're not running inside a cluster (we're probably running on a dev
33 // machine), so just return whatever net/http says.
34
35 host, portStr, err := net.SplitHostPort(r.RemoteAddr)
36 if err != nil {
37 return nil, 0, fmt.Errorf("could not split hostport: %v", err)
38 }
39 ip := net.ParseIP(host)
40 if ip == nil {
41 return nil, 0, fmt.Errorf("could not parse host %q to IP address", host)
42 }
43 port, err := parsePort(portStr)
44 if err != nil {
45 return nil, 0, err
46 }
47 return ip, uint16(port), nil
48 }
49
50 // We are running in a cluster, so we can expect Hscloud-* headers.
51 // These are configured in the nginx-ingress-controller, //cluster/kube/lib/nginx.libsonnet.
52 nsip := strings.TrimSpace(r.Header.Get("Hscloud-Nic-Source-IP"))
53 nsport := strings.TrimSpace(r.Header.Get("Hscloud-Nic-Source-Port"))
54 if nsip == "" || nsport == "" {
55 return nil, 0, fmt.Errorf("Hscloud-Nic-* headers not set")
56 }
57 ip := net.ParseIP(nsip)
58 if ip == nil {
59 return nil, 0, fmt.Errorf("Invalid Hscloud-Nix-Source-IP %q", nsip)
60 }
61 port, err := parsePort(nsport)
62 if err != nil {
63 return nil, 0, fmt.Errorf("Invalid Hscloud-Nix-Source-Port: %v", err)
64 }
65 return ip, port, nil
66}