From bb7649b362bcd8e221afeb39e221afe4b213c8ff Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Thu, 30 Apr 2020 08:37:36 +0100 Subject: [PATCH] Use hostnames in redirects This allows usage of HTTPS, IPs won't work well in that scenario. --- cmd/radiod/radiod.go | 10 +++++----- node/debug.go | 3 +-- node/http.go | 23 +++++++++-------------- node/loadbalancing.go | 25 ------------------------- 4 files changed, 15 insertions(+), 46 deletions(-) diff --git a/cmd/radiod/radiod.go b/cmd/radiod/radiod.go index 325fdacc..f7fb319d 100644 --- a/cmd/radiod/radiod.go +++ b/cmd/radiod/radiod.go @@ -23,7 +23,7 @@ import ( ) var ( - name = flag.String("name", shortHostname(), "Name for this node") + name = flag.String("name", hostname(), "Name for this node (FQDN)") publicIPs = util.IPListFlag("public-ip", "Public IP for this machine (may be specified more than once). If unset, the program will try to resolve the local hostname, or it will fall back to inspecting network devices.") peerIP = util.IPFlag("peer-ip", "Internal IP for this machine (within the cluster), if different from --ip") httpPort = flag.Int("http-port", 80, "HTTP port") @@ -47,11 +47,8 @@ var ( sessionTTL = 5 ) -func shortHostname() string { +func hostname() string { hostname, _ := os.Hostname() - if r := strings.Index(hostname, "."); r >= 0 { - return hostname[:r] - } return hostname } @@ -84,6 +81,9 @@ func main() { if *name == "" { log.Fatal("--name must be set") } + if !strings.Contains(*name, ".") && *httpsPort > 0 { + log.Fatal("--name must be a FQDN when SSL is enabled") + } if *domain == "" { log.Fatal("--domain must be set") } diff --git a/node/debug.go b/node/debug.go index 23826503..0b9b4104 100644 --- a/node/debug.go +++ b/node/debug.go @@ -2,7 +2,6 @@ package node import ( "bytes" - "html/template" "log" "net/http" "sort" @@ -93,7 +92,7 @@ func mountsToStatus(mounts []*pb.Mount, nodes []*nodeInfo, icecastMounts map[str return msl } -func serveStatusPage(n *Node, w http.ResponseWriter, r *http.Request, tpl *template.Template, domain string) { +func serveStatusPage(n *Node, w http.ResponseWriter, r *http.Request, domain string) { // Convert the list of nodes to just the status. While we're // at it, build a map of mount path -> exemplary IcecastMount, // which we use to show the current song artist / title. diff --git a/node/http.go b/node/http.go index 558a2f15..49559346 100644 --- a/node/http.go +++ b/node/http.go @@ -29,12 +29,17 @@ import ( var ( disableDebugHandlers = flag.Bool("http-disable-debug", false, "disable HTTP /debug handlers") restrictDebugHandlers = flag.Bool("http-restrict-debug", false, "restrict access to /debug from localhost only") + + tpl *template.Template ) +func init() { + tpl = mustParseEmbeddedTemplates() +} + // Build the HTTP handler for the public HTTP endpoint. func newHTTPHandler(n *Node, icecastPort int, domain string) http.Handler { mux := http.NewServeMux() - tpl := mustParseEmbeddedTemplates() // Serve /static/ from builtin assets. Also serve directly // /favicon.ico using the same mechanism. @@ -117,7 +122,7 @@ func newHTTPHandler(n *Node, icecastPort int, domain string) http.Handler { case r.Method == "SOURCE" || r.Method == "PUT": sourceHandler.ServeHTTP(w, r) case r.Method == "GET" && (r.URL.Path == "" || r.URL.Path == "/"): - serveStatusPage(n, w, r, tpl, domain) + serveStatusPage(n, w, r, domain) case r.Method == "GET": redirectHandler.ServeHTTP(w, r) default: @@ -184,20 +189,10 @@ func serveRedirect(lb *loadBalancer, mount *pb.Mount, w http.ResponseWriter, r * return } - // Pick an IP address to redirect to. - // TODO: replace with an explicit hostname. - targetAddr := randomIPWithMatchingProtocol(targetNode.parsedAddrs, r.RemoteAddr) - if targetAddr == "" { - // This should not happen if the protocol filter in - // the load balancer evaluation did its job properly. - log.Printf("http: %s: protocol unavailable", mount.Path) - http.Error(w, "No nodes available with the right IP protocol", http.StatusServiceUnavailable) - return - } - + // Use the node hostname for the redirect (compatible with using SSL certs). targetURL := url.URL{ Scheme: schemeFromRequest(r), - Host: targetAddr, + Host: targetNode.ep.Name, Path: autoradio.MountPathToIcecastPath(mount.Path), } diff --git a/node/loadbalancing.go b/node/loadbalancing.go index 0c4363fe..6f5df0e7 100644 --- a/node/loadbalancing.go +++ b/node/loadbalancing.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "log" - "math/rand" "net" "net/http" "strconv" @@ -309,27 +308,3 @@ func filterIPByProto(ips []ipPort, v6 bool) []ipPort { } return candidates } - -// Pick a random IP for the specified proto. -func randomIPByProto(ips []ipPort, v6 bool) ipPort { - candidates := filterIPByProto(ips, v6) - if len(candidates) > 0 { - return candidates[rand.Intn(len(candidates))] - } - return ipPort{} -} - -// Select a random IP address from ips, with an IP protocol that -// matches remoteAddr. -func randomIPWithMatchingProtocol(ips []ipPort, remoteAddr string) string { - var isV6 bool - if host, _, err := net.SplitHostPort(remoteAddr); err == nil { - addr := net.ParseIP(host) - isV6 = (addr != nil && addr.To4() == nil) - } - ipp := randomIPByProto(ips, isV6) - if ipp.ip == nil { - return "" - } - return ipp.String() -} -- GitLab