Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ REACT_DEV ?= false
.PHONY: all help fmt lint test build vulncheck check-deps kubectl-unbounded kubectl-unbounded-build install-tools install-protoc generate kubectl-unbounded forge unbounded-agent machina machina-build machina-oci machina-oci-push machina-manifests machine-ops-controller machine-ops-controller-build machine-ops-controller-oci machine-ops-controller-oci-push machine-ops-manifests metalman metalman-build metalman-oci metalman-oci-push gomod docs-serve unbounded-net-controller unbounded-net-node unbounded-net-routeplan-debug unping unroute notice notice-check
.PHONY: net-frontend net-frontend-clean net-build-ebpf net-manifests release-manifests
.PHONY: image-machina-local image-machine-ops-controller-local image-metalman-local image-net-controller-local image-net-node-local images-local
.PHONY: image-net-controller-push image-net-node-push images-net-all images-net-all-push
.PHONY: unbounded-storage unbounded-storage-build unbounded-storage-test

##@ General
Expand Down Expand Up @@ -190,7 +191,11 @@ help: ## Show this help
@echo " image-machine-ops-controller-local Build machine-ops-controller image"
@echo " image-metalman-local Build metalman image"
@echo " image-net-controller-local Build unbounded-net-controller image"
@echo " image-net-controller-push Build and push unbounded-net-controller image"
@echo " image-net-node-local Build unbounded-net-node image"
@echo " image-net-node-push Build and push unbounded-net-node image"
@echo " images-net-all Build all unbounded-net images"
@echo " images-net-all-push Build and push all unbounded-net images"
@echo " images-local Build all local images"
@echo " machina-oci-push Build machina image and push"
@echo " machine-ops-controller-oci-push Build machine-ops-controller image and push"
Expand Down Expand Up @@ -690,6 +695,16 @@ image-net-node-local: resources/cni-plugins-linux-$(HOST_GOARCH)-$(CNI_PLUGINS_V
-f ./images/net-node/Dockerfile .
$(call trivy-maybe,$(NET_NODE_IMAGE))

image-net-controller-push: image-net-controller-local ## Build and push the unbounded-net-controller image
$(CONTAINER_ENGINE) push $(NET_CONTROLLER_IMAGE)

image-net-node-push: image-net-node-local ## Build and push the unbounded-net-node image
$(CONTAINER_ENGINE) push $(NET_NODE_IMAGE)

images-net-all: image-net-controller-local image-net-node-local ## Build all unbounded-net container images locally

images-net-all-push: image-net-controller-push image-net-node-push ## Build and push all unbounded-net container images

images-local: image-machina-local image-machine-ops-controller-local image-metalman-local image-net-controller-local image-net-node-local ## Build all container images locally

##@ Net Frontend
Expand Down
31 changes: 28 additions & 3 deletions cmd/unbounded-net-controller/dashboard_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,19 +176,44 @@ func authorizeDashboardRequest(requireDashboardAuth bool, tokenIssuer *authn.Tok
return authorizer.authorize(r.Context(), claims.Subject, claims.Groups)
}

// authorizeDashboardOrAggregated allows a request if it arrives via the
// aggregated API server (verified front-proxy client certificate) or if it
// passes dashboard authentication and authorization.
// authorizeDashboardOrAggregated allows a request if it passes dashboard
// authentication and authorization (HMAC viewer Bearer token) or if it
// arrives via the aggregated API server (verified front-proxy client
// certificate).
//
// When the request carries a Bearer token, dashboard auth is attempted
// first to avoid logging spurious "no client certificate" rejections from
// the local kubectl-unbounded proxy, which never presents a client cert.
// Aggregator-style client-cert auth is still attempted as a fallback so
// requests proxied through the kube-aggregator continue to work.
func authorizeDashboardOrAggregated(
requireDashboardAuth bool,
tokenIssuer *authn.TokenIssuer,
authorizer *dashboardAuthorizer,
webhookServer *webhookpkg.Server,
r *http.Request,
) bool {
if hasBearerToken(r) && authorizeDashboardRequest(requireDashboardAuth, tokenIssuer, authorizer, r) {
return true
}

if webhookServer.IsTrustedAggregatedRequest(r) {
return true
}

// Final attempt: dashboard auth path for requests that did not carry a
// Bearer token (e.g. requireDashboardAuth=false).
return authorizeDashboardRequest(requireDashboardAuth, tokenIssuer, authorizer, r)
}

// hasBearerToken reports whether the request has an Authorization: Bearer
// header.
func hasBearerToken(r *http.Request) bool {
if r == nil {
return false
}

auth := r.Header.Get("Authorization")

return strings.HasPrefix(auth, "Bearer ")
}
105 changes: 56 additions & 49 deletions cmd/unbounded-net-node/ebpf_geneve_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strings"

Expand Down Expand Up @@ -230,7 +229,14 @@ func configureEBPFTunnelPeers(
// SyncAddresses, SetLinkAddress, EnsureMTU). Some netlink operations
// can cause the kernel to reset interface sysctls.
disableRPFilter(ifName)
ensureTunnelForwardAccept(ifName)

if state.forwardManager != nil {
state.forwardManager.EnsureInterface(ifName)
}

if state.isGatewayNode && state.notrackManager != nil {
state.notrackManager.EnsureInterface(ifName)
}

// Get tunnel interface index for BPF map entries (redirect target).
geneveIface, err := net.InterfaceByName(ifName)
Expand All @@ -249,7 +255,13 @@ func configureEBPFTunnelPeers(
klog.V(2).Infof("eBPF: failed to remove %s: %v", ifName, err)
}

removeTunnelForwardAccept(ifName)
if state.forwardManager != nil {
state.forwardManager.RemoveInterface(ifName)
}

if state.notrackManager != nil {
state.notrackManager.RemoveInterface(ifName)
}
}
}

Expand Down Expand Up @@ -283,7 +295,15 @@ func configureEBPFTunnelPeers(
}

disableRPFilter(ipipIfName)
ensureTunnelForwardAccept(ipipIfName)

if state.forwardManager != nil {
state.forwardManager.EnsureInterface(ipipIfName)
}

if state.isGatewayNode && state.notrackManager != nil {
state.notrackManager.EnsureInterface(ipipIfName)
}

// IPIP is layer 3 and does not support MAC addresses; skip
// SetLinkAddress (the kernel returns ENOTSUP).
}
Expand All @@ -297,7 +317,13 @@ func configureEBPFTunnelPeers(
klog.V(2).Infof("eBPF: failed to remove %s: %v", ipipIfName, err)
}

removeTunnelForwardAccept(ipipIfName)
if state.forwardManager != nil {
state.forwardManager.RemoveInterface(ipipIfName)
}

if state.notrackManager != nil {
state.notrackManager.RemoveInterface(ipipIfName)
}
}
}

Expand Down Expand Up @@ -469,7 +495,14 @@ func configureEBPFVXLANPeers(
// SyncAddresses, SetLinkAddress, EnsureMTU). Some netlink operations
// can cause the kernel to reset interface sysctls.
disableRPFilter(ifName)
ensureTunnelForwardAccept(ifName)

if state.forwardManager != nil {
state.forwardManager.EnsureInterface(ifName)
}

if state.isGatewayNode && state.notrackManager != nil {
state.notrackManager.EnsureInterface(ifName)
}

iface, err := net.InterfaceByName(ifName)
if err != nil {
Expand Down Expand Up @@ -842,48 +875,9 @@ func disableRPFilter(ifName string) {
}
}

// ensureTunnelForwardAccept adds an iptables FORWARD chain rule that accepts
// forwarded traffic arriving on the specified tunnel interface. This must be
// before KUBE-FORWARD (which drops ctstate INVALID packets) so that transit
// overlay traffic through gateway nodes is not dropped.
func ensureTunnelForwardAccept(ifName string) {
check := exec.Command("nsenter", "-t", "1", "-n", "--",
"iptables", "-C", "FORWARD",
"-i", ifName, "-j", "ACCEPT",
"-m", "comment", "--comment", "unbounded-net: accept tunnel traffic")
if check.Run() == nil {
return
}

out, err := exec.Command("nsenter", "-t", "1", "-n", "--",
"iptables", "-I", "FORWARD", "1",
"-i", ifName, "-j", "ACCEPT",
"-m", "comment", "--comment", "unbounded-net: accept tunnel traffic").CombinedOutput()
if err != nil {
klog.Warningf("failed to add FORWARD accept rule for %s: %v (%s)", ifName, err, strings.TrimSpace(string(out)))
} else {
klog.V(2).Infof("added FORWARD accept rule for %s", ifName)
}
}

// removeTunnelForwardAccept removes the FORWARD chain ACCEPT rule for
// the specified tunnel interface. Called when an interface is deleted.
func removeTunnelForwardAccept(ifName string) {
out, err := exec.Command("nsenter", "-t", "1", "-n", "--",
"iptables", "-D", "FORWARD",
"-i", ifName, "-j", "ACCEPT",
"-m", "comment", "--comment", "unbounded-net: accept tunnel traffic").CombinedOutput()
if err != nil {
// Rule may not exist if the interface was never set up -- ignore.
klog.V(4).Infof("removeTunnelForwardAccept %s: %v (%s)", ifName, err, strings.TrimSpace(string(out)))
} else {
klog.V(2).Infof("removed FORWARD accept rule for %s", ifName)
}
}

// using. This avoids leaving stale geneve0, vxlan0, or ipip0 interfaces
// when the tunnel protocol has been changed or when all peers use WireGuard.
func cleanupUnusedTunnelDevices(meshPeers []meshPeerInfo, gatewayPeers, wgGatewayPeers []gatewayPeerInfo) {
func cleanupUnusedTunnelDevices(meshPeers []meshPeerInfo, gatewayPeers, wgGatewayPeers []gatewayPeerInfo, fwdMgr *unboundednetnetlink.ForwardManager, notrackMgr *unboundednetnetlink.NotrackManager) {
usesGeneve := false
usesVXLAN := false
usesIPIP := false
Expand Down Expand Up @@ -944,7 +938,13 @@ func cleanupUnusedTunnelDevices(meshPeers []meshPeerInfo, gatewayPeers, wgGatewa
klog.V(2).Infof("eBPF: failed to remove unused device %s: %v", d.name, err)
}

removeTunnelForwardAccept(d.name)
if fwdMgr != nil {
fwdMgr.RemoveInterface(d.name)
}

if notrackMgr != nil {
notrackMgr.RemoveInterface(d.name)
}
}
}
}
Expand All @@ -953,7 +953,7 @@ func cleanupUnusedTunnelDevices(meshPeers []meshPeerInfo, gatewayPeers, wgGatewa
// are still in use. This must be called after cleanupUnusedTunnelDevices
// because deleting interfaces can cause the kernel to reset rp_filter on
// remaining interfaces.
func reapplyRPFilterOnActiveTunnels(meshPeers []meshPeerInfo, gatewayPeers, wgGatewayPeers []gatewayPeerInfo) {
func reapplyRPFilterOnActiveTunnels(meshPeers []meshPeerInfo, gatewayPeers, wgGatewayPeers []gatewayPeerInfo, fwdMgr *unboundednetnetlink.ForwardManager, isGateway bool, notrackMgr *unboundednetnetlink.NotrackManager) {
usesGeneve := false
usesVXLAN := false
usesIPIP := false
Expand Down Expand Up @@ -1002,7 +1002,14 @@ func reapplyRPFilterOnActiveTunnels(meshPeers []meshPeerInfo, gatewayPeers, wgGa
} {
if d.used {
disableRPFilter(d.name)
ensureTunnelForwardAccept(d.name)

if fwdMgr != nil {
fwdMgr.EnsureInterface(d.name)
}

if isGateway && notrackMgr != nil {
notrackMgr.EnsureInterface(d.name)
}
}
}
}
26 changes: 23 additions & 3 deletions cmd/unbounded-net-node/geneve_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,14 @@ func configureTunnelPeers(ctx context.Context, cfg *config, meshPeers []meshPeer
// SyncAddresses, EnsureMTU). Some netlink operations can cause
// the kernel to reset interface sysctls.
disableRPFilter(ifName)
ensureTunnelForwardAccept(ifName)

if state.forwardManager != nil {
state.forwardManager.EnsureInterface(ifName)
}

if state.isGatewayNode && state.notrackManager != nil {
state.notrackManager.EnsureInterface(ifName)
}

iface, err := net.InterfaceByName(ifName)
if err != nil {
Expand Down Expand Up @@ -287,7 +294,14 @@ func removeStaleGeneveInterfaces(state *wireGuardState, desired map[string]bool)
klog.Infof("Tunnel: removed stale interface %s", ifName)
}

removeTunnelForwardAccept(ifName)
if state.forwardManager != nil {
state.forwardManager.RemoveInterface(ifName)
}

if state.notrackManager != nil {
state.notrackManager.RemoveInterface(ifName)
}

delete(state.geneveInterfaces, ifName)
}

Expand Down Expand Up @@ -331,7 +345,13 @@ func removeStaleGeneveInterfaces(state *wireGuardState, desired map[string]bool)
klog.Infof("Tunnel: removed unmanaged interface %s", name)
}

removeTunnelForwardAccept(name)
if state.forwardManager != nil {
state.forwardManager.RemoveInterface(name)
}

if state.notrackManager != nil {
state.notrackManager.RemoveInterface(name)
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/unbounded-net-node/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ then annotates the node with the public key.`,
// WireGuard configuration flags
flags.StringVar(&cfg.WireGuardDir, "wireguard-dir", "/etc/wireguard", "Directory to store WireGuard keys")
flags.IntVar(&cfg.WireGuardPort, "wireguard-port", 51820, "WireGuard listen port")
flags.BoolVar(&cfg.EnablePolicyRouting, "enable-policy-routing", false, "Enable policy-based routing on gateway interfaces (deprecated, per-interface FORWARD rules replace PBR)")
flags.BoolVar(&cfg.EnablePolicyRouting, "enable-policy-routing", false, "Enable policy-based routing on gateway interfaces (deprecated, UNBOUNDED-FORWARD chain rules replace PBR)")

// GENEVE configuration flags
flags.IntVar(&cfg.GenevePort, "geneve-port", 6081, "GENEVE UDP destination port")
Expand Down
6 changes: 6 additions & 0 deletions cmd/unbounded-net-node/node_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ type wireGuardState struct {
// MSS clamp manager for TCP MSS clamping on WireGuard interfaces
mssClampManager *unboundednetnetlink.MSSClampManager

// Forward manager for tunnel-to-tunnel iptables FORWARD rules
forwardManager *unboundednetnetlink.ForwardManager

// Notrack manager for skipping conntrack on transit traffic (gateway nodes only)
notrackManager *unboundednetnetlink.NotrackManager

// GENEVE tunnel managers -- per-peer interfaces with fixed Remote IP
geneveInterfaces map[string]*unboundednetnetlink.LinkManager // per-peer GENEVE interface managers keyed by iface name

Expand Down
8 changes: 8 additions & 0 deletions cmd/unbounded-net-node/reconciliation_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,14 @@ func removeUnmanagedWireGuardInterfaces(cfg *config, state *wireGuardState, desi

klog.Infof("Removed unmanaged WireGuard interface %s", name)

if state.forwardManager != nil {
state.forwardManager.RemoveInterface(name)
}

if state.notrackManager != nil {
state.notrackManager.RemoveInterface(name)
}

delete(state.gatewayLinkManagers, name)
delete(state.gatewayWireguardManagers, name)
delete(state.gatewayHealthEndpoints, name)
Expand Down
Loading
Loading