diff --git a/cmd/k8s-operator/ingress.go b/cmd/k8s-operator/ingress.go index 8335506bc..f8d21c29c 100644 --- a/cmd/k8s-operator/ingress.go +++ b/cmd/k8s-operator/ingress.go @@ -155,6 +155,16 @@ func (a *IngressReconciler) maybeProvision(ctx context.Context, logger *zap.Suga // magic443 is a fake hostname that we can use to tell containerboot to swap // out with the real hostname once it's known. const magic443 = "${TS_CERT_DOMAIN}:443" + + tlsHostname := "" + if ing.Spec.TLS != nil && len(ing.Spec.TLS) > 0 && len(ing.Spec.TLS[0].Hosts) > 0 { + tlsHostname = ing.Spec.TLS[0].Hosts[0] + } + tlsHost := ipn.HostPort(fmt.Sprintf("%s:443", tlsHostname)) + if tlsHostname == "" { + tlsHost = magic443 + } + sc := &ipn.ServeConfig{ TCP: map[uint16]*ipn.TCPPortHandler{ 443: { @@ -162,18 +172,18 @@ func (a *IngressReconciler) maybeProvision(ctx context.Context, logger *zap.Suga }, }, Web: map[ipn.HostPort]*ipn.WebServerConfig{ - magic443: { + tlsHost: { Handlers: map[string]*ipn.HTTPHandler{}, }, }, } if opt.Bool(ing.Annotations[AnnotationFunnel]).EqualBool(true) { sc.AllowFunnel = map[ipn.HostPort]bool{ - magic443: true, + tlsHost: true, } } - web := sc.Web[magic443] + web := sc.Web[tlsHost] addIngressBackend := func(b *networkingv1.IngressBackend, path string) { if b == nil { return @@ -216,14 +226,10 @@ func (a *IngressReconciler) maybeProvision(ctx context.Context, logger *zap.Suga } addIngressBackend(ing.Spec.DefaultBackend, "/") - var tlsHost string // hostname or FQDN or empty - if ing.Spec.TLS != nil && len(ing.Spec.TLS) > 0 && len(ing.Spec.TLS[0].Hosts) > 0 { - tlsHost = ing.Spec.TLS[0].Hosts[0] - } for _, rule := range ing.Spec.Rules { // Host is optional, but if it's present it must match the TLS host // otherwise we ignore the rule. - if rule.Host != "" && rule.Host != tlsHost { + if rule.Host != "" && rule.Host != tlsHostname { a.recorder.Eventf(ing, corev1.EventTypeWarning, "InvalidIngressBackend", "rule with host %q ignored, unsupported", rule.Host) continue } @@ -253,10 +259,9 @@ func (a *IngressReconciler) maybeProvision(ctx context.Context, logger *zap.Suga tags = strings.Split(tstr, ",") } hostname := ing.Namespace + "-" + ing.Name + "-ingress" - if tlsHost != "" { - hostname, _, _ = strings.Cut(tlsHost, ".") - } - + // if tlsHost != "" { + // hostname, _, _ = strings.Cut(tlsHost, ".") + // } sts := &tailscaleSTSConfig{ Hostname: hostname, ParentResourceName: ing.Name, @@ -265,6 +270,7 @@ func (a *IngressReconciler) maybeProvision(ctx context.Context, logger *zap.Suga Tags: tags, ChildResourceLabels: crl, ProxyClass: proxyClass, + TSVIP: a.tailnetVIPForIngress(ing), } if val := ing.GetAnnotations()[AnnotationExperimentalForwardClusterTrafficViaL7IngresProxy]; val == "true" { @@ -307,6 +313,13 @@ func (a *IngressReconciler) maybeProvision(ctx context.Context, logger *zap.Suga return nil } +func (a *IngressReconciler) tailnetVIPForIngress(ing *networkingv1.Ingress) string { + if !a.shouldExpose(ing) || ing.Annotations == nil { + return "" + } + return ing.GetAnnotations()[AnnotationTSVIP] +} + func (a *IngressReconciler) shouldExpose(ing *networkingv1.Ingress) bool { return ing != nil && ing.Spec.IngressClassName != nil && diff --git a/cmd/k8s-operator/sts.go b/cmd/k8s-operator/sts.go index d3538a565..d82647085 100644 --- a/cmd/k8s-operator/sts.go +++ b/cmd/k8s-operator/sts.go @@ -428,7 +428,7 @@ var userspaceProxyYaml []byte func (a *tailscaleSTSReconciler) reconcileSTS(ctx context.Context, logger *zap.SugaredLogger, sts *tailscaleSTSConfig, headlessSvc *corev1.Service, proxySecret, tsConfigHash string) (*appsv1.StatefulSet, error) { ss := new(appsv1.StatefulSet) - if sts.ServeConfig != nil && sts.ForwardClusterTrafficViaL7IngressProxy != true { // If forwarding cluster traffic via is required we need non-userspace + NET_ADMIN + forwarding + if sts.ServeConfig != nil && sts.ForwardClusterTrafficViaL7IngressProxy != true && sts.TSVIP == "" { // If forwarding cluster traffic via is required we need non-userspace + NET_ADMIN + forwarding if err := yaml.Unmarshal(userspaceProxyYaml, &ss); err != nil { return nil, fmt.Errorf("failed to unmarshal userspace proxy spec: %v", err) } diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index b8aa769a1..681fb687d 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -3299,9 +3299,13 @@ func (b *LocalBackend) handlePeerAPIConn(remote, local netip.AddrPort, c net.Con return } -func (b *LocalBackend) isLocalIP(ip netip.Addr) bool { +func (b *LocalBackend) isLocallyAvailable(ip netip.Addr) bool { nm := b.NetMap() - return nm != nil && views.SliceContains(nm.GetAddresses(), netip.PrefixFrom(ip, ip.BitLen())) + if nm == nil { + return false + } + pfx := netip.PrefixFrom(ip, ip.BitLen()) + return views.SliceContains(nm.SelfNode.AllowedIPs(), pfx) } var ( @@ -3319,7 +3323,7 @@ func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c } return b.HandleQuad100Port80Conn, opts } - if !b.isLocalIP(dst.Addr()) { + if !b.isLocallyAvailable(dst.Addr()) { return nil, nil } if dst.Port() == 22 && b.ShouldRunSSH() {