Files
photoprism/internal/config/cluster_defaults.go
2025-09-24 08:28:38 +02:00

102 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package config
import (
"net"
"os"
"regexp"
"strings"
)
// getHostname is a var to allow tests to stub os.Hostname.
var getHostname = os.Hostname
// NonUniqueHostnames lists hostnames that must never be used as node name or to derive a cluster domain.
// It is a package variable on purpose so operators/tests can adjust in the future without spec changes.
var NonUniqueHostnames = map[string]struct{}{
"localhost": {},
"localhost.localdomain": {},
"localdomain": {},
}
// ReservedDomains lists special/reserved domains that must not be used as cluster domains.
var ReservedDomains = map[string]struct{}{
"example.com": {},
"example.net": {},
"example.org": {},
"invalid": {},
"test": {},
}
var dnsLabelRe = regexp.MustCompile(`^[a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])?$`)
// isDNSLabel returns true if s is a valid DNS label per our rules: lowercase, [a-z0-9-], 132 chars, starts/ends alnum.
func isDNSLabel(s string) bool {
if s == "" || len(s) > 32 {
return false
}
return dnsLabelRe.MatchString(s)
}
// isLocalSuffix returns true for .local mDNS or similar local-only suffixes we want to ignore.
func isLocalSuffix(suffix string) bool {
return suffix == "local" || strings.HasSuffix(suffix, ".local")
}
// isDNSDomain validates a DNS domain (FQDN or single label not allowed here). It must have at least one dot.
// Each label must match isDNSLabel (except overall length and hyphen rules already covered by regex logic).
func isDNSDomain(d string) bool {
d = strings.TrimSuffix(strings.ToLower(strings.TrimSpace(d)), ".")
if d == "" || strings.Count(d, ".") < 1 || len(d) > 253 {
return false
}
if _, bad := ReservedDomains[d]; bad {
return false
}
if isLocalSuffix(d) {
return false
}
parts := strings.Split(d, ".")
for _, p := range parts {
if !isDNSLabel(p) {
return false
}
}
return true
}
// deriveSystemDomain tries to determine a usable cluster domain from system configuration.
// It uses the system hostname and returns the domain (everything after the first dot) when valid and not reserved.
func deriveSystemDomain() string {
hn, _ := getHostname()
hn = strings.ToLower(strings.TrimSpace(hn))
if hn == "" {
return ""
}
if _, bad := NonUniqueHostnames[hn]; bad {
return ""
}
// If hostname contains a dot, take the domain part.
if i := strings.IndexByte(hn, '.'); i > 0 && i < len(hn)-1 {
dom := hn[i+1:]
if isDNSDomain(dom) {
return dom
}
}
// Try reverse lookup to get FQDN domain, then validate.
if addrs, err := net.LookupAddr(hn); err == nil {
for _, fqdn := range addrs {
fqdn = strings.TrimSuffix(strings.ToLower(fqdn), ".")
if fqdn == "" || fqdn == hn {
continue
}
if i := strings.IndexByte(fqdn, '.'); i > 0 && i < len(fqdn)-1 {
dom := fqdn[i+1:]
if isDNSDomain(dom) {
return dom
}
}
}
}
return ""
}