internal/names/names.go
Ref: Size: 3.5 KiB History
// Package names validates the DNS-label shape shared across planes: a VM's
// name doubles as its guest hostname, so it must be a valid RFC-1123 label.
// It also validates the distinct, narrower grammar of a named network's name
// (see IsNetworkName), and holds the one cross-plane fact about which host OS
// can serve a named network at all (see OSServesNamedNetworks). This package
// is a dependency-free leaf, which is exactly why both facts live here rather
// than beside either plane.
package names
import "regexp"
// rfc1123Label matches valid RFC-1123 DNS label names.
// Rules: lowercase alphanum start/end, lowercase alphanum or hyphen in between,
// max 63 characters total.
var rfc1123Label = regexp.MustCompile(`^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$`)
// IsRFC1123Label reports whether s is a valid RFC-1123 DNS label.
func IsRFC1123Label(s string) bool { return rfc1123Label.MatchString(s) }
// sha256Hex matches a lowercase hex SHA-256 digest: exactly 64 hex characters.
var sha256Hex = regexp.MustCompile(`^[a-f0-9]{64}$`)
// IsSHA256Hex reports whether s is a lowercase hex SHA-256 digest (exactly 64
// hex characters) — the shape of an image content digest and of the cert
// fingerprint on a join blob. Both planes check it before building a filesystem
// path or trusting the value, so it lives in this dependency-free leaf.
func IsSHA256Hex(s string) bool { return sha256Hex.MatchString(s) }
// IsNetworkName reports whether s is a legal named-network name: 1–32 chars
// of [a-z0-9-], no leading/trailing hyphen, and not the reserved word "nat"
// (the anonymous default underlay's name in prose; a network named "nat"
// would make every conversation about it ambiguous). One home for the
// grammar — the API validates with it and the agent flag parser refuses
// with it, so the two cannot drift. The byte-wise loop below is sound for
// multi-byte UTF-8 input too: every legal character here is single-byte
// ASCII, so any byte of a multi-byte rune falls outside the allowed ranges
// and rejects.
func IsNetworkName(s string) bool {
if s == "" || s == "nat" || len(s) > 32 {
return false
}
if s[0] == '-' || s[len(s)-1] == '-' {
return false
}
for i := 0; i < len(s); i++ {
switch c := s[i]; {
case c >= 'a' && c <= 'z', c >= '0' && c <= '9', c == '-':
// allowed
default:
return false
}
}
return true
}
// OSServesNamedNetworks reports whether an agent running on goos (the value
// runtime.GOOS reports) can serve a named network at all: only a Linux host
// owns a bridge to attach a named network's taps to. This is the one home
// for that fact — the agent's flag parser refuses --host-network at startup
// with it, and the server words a create's network refusal from it using
// store.Host.OS (set once, from this same runtime.GOOS, at the host's
// enrollment — see internal/server/api/networks.go for why that source is
// trustworthy without a live report), so a third OS gaining support is a
// one-line change made once, here, rather than found and fixed twice.
func OSServesNamedNetworks(goos string) bool { return goos == "linux" }
// ConnectName returns a VM's connect name, "<tenant>.<name>" — the form the SSH
// gate resolves and the VM's host certificate is issued for. It splits back
// unambiguously on the first dot only because tenant ids and VM names are both
// dot-free (IsRFC1123Label rejects dots), so the leaf that enforces that shape
// is also the one place that composes the name from it.
func ConnectName(tenant, name string) string { return tenant + "." + name }