summaryrefslogtreecommitdiff
path: root/internal/config/bind.go
blob: f117385d6ec8f9dcb584fad87486bef0c024c6aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package config

import (
	"fmt"
	"net"
)

// ValidateBindAddr enforces loopback-by-default binding. It returns an error
// when addr resolves to a non-loopback interface and externalAllowed is false,
// so the server fails loud rather than silently exposing itself. Loopback
// addresses (127.0.0.1, ::1, localhost) always pass.
func ValidateBindAddr(addr string, externalAllowed bool) error {
	host, _, err := net.SplitHostPort(addr)
	if err != nil {
		// No port (or malformed): treat the whole string as the host.
		host = addr
	}
	if isLoopbackHost(host) {
		return nil
	}
	if externalAllowed {
		return nil
	}
	return fmt.Errorf(
		"refusing to bind non-loopback address %q: set external_bind_allowed = true to override, "+
			"but prefer fronting external access with a reverse proxy (Tailscale / Cloudflare Access / Caddy)",
		addr,
	)
}

// isLoopbackHost reports whether host is a loopback target. An empty host
// (e.g. ":8484") or 0.0.0.0/:: binds all interfaces and is NOT loopback.
func isLoopbackHost(host string) bool {
	switch host {
	case "localhost":
		return true
	case "":
		return false
	}
	if ip := net.ParseIP(host); ip != nil {
		return ip.IsLoopback()
	}
	return false
}