feat(kilo): update to v0.7.0 and add configurable MTU (#2003)

## What this PR does

Updates kilo to v0.7.0 from the cozystack fork, replacing the local
image build (with patches applied via Dockerfile) with a pre-built image
from `ghcr.io/cozystack/cozystack/kilo`. This simplifies the build
process and removes the need for maintaining local patches.

Additionally, exposes the `--mtu` flag in kilo's `values.yaml` to allow
overriding the WireGuard interface MTU. The default value is set to
`auto`, letting kilo determine the optimal MTU automatically.

### Release note

```release-note
[kilo] Update kilo to v0.7.0 from cozystack fork; add configurable `mtu` parameter (default: auto) for WireGuard interface
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added configurable MTU support with automatic detection
* Added internal CIDR filtering capability for network mesh
configuration

* **Updates**
  * Updated kilo image version to v0.7.0
  * Transitioned to pre-built remote images

* **Improvements**
* Refined IP overlap and containment warning logic for better
diagnostics

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Andrei Kvapil 2026-02-10 20:20:55 +01:00 committed by GitHub
commit 24c8b9c7a5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 8 additions and 342 deletions

View file

@ -27,7 +27,6 @@ build: build-deps
make -C packages/system/dashboard image
make -C packages/system/metallb image
make -C packages/system/kamaji image
make -C packages/system/kilo image
make -C packages/system/bucket image
make -C packages/system/objectstorage-controller image
make -C packages/system/grafana-operator image

View file

@ -5,21 +5,9 @@ include ../../../hack/common-envs.mk
include ../../../hack/package.mk
update:
wget https://raw.githubusercontent.com/squat/kilo/refs/heads/main/manifests/crds.yaml -O templates/crds.yaml
wget https://raw.githubusercontent.com/squat/kilo/refs/heads/main/manifests/kilo-typhoon-flannel.yaml -O templates/kilo.yaml
sed -i 's|kube-system|cozy-kilo|g' templates/kilo.yaml
sed -i 's|--compatibility=flannel|--compatibility=cilium|' templates/kilo.yaml
sed -i '/- --local=false/a \ - --mesh-granularity=full\n - --service-cidr=10.244.0.0/24\n - --service-cidr=10.96.0.0/24' templates/kilo.yaml
image:
docker buildx build images/kilo \
--tag $(REGISTRY)/kilo:$(call settag,$(TAG)) \
--cache-from type=registry,ref=$(REGISTRY)/kilo:latest \
--cache-to type=inline \
--metadata-file images/kilo.json \
$(BUILDX_ARGS)
REPOSITORY="$(REGISTRY)/kilo" \
yq -i '.kilo.image.repository = strenv(REPOSITORY)' values.yaml
TAG=$(TAG)@$$(yq e '."containerimage.digest"' images/kilo.json -o json -r) \
tag=$$(git ls-remote --tags --sort="v:refname" https://github.com/cozystack/kilo | awk -F'[/^]' 'END{print $$3}') && \
digest=$$(skopeo inspect --override-os linux --format '{{.Digest}}' docker://ghcr.io/cozystack/cozystack/kilo:$${tag}) && \
REPOSITORY="ghcr.io/cozystack/cozystack/kilo" \
yq -i '.kilo.image.repository = strenv(REPOSITORY)' values.yaml && \
TAG="$${tag}@$${digest}" \
yq -i '.kilo.image.tag = strenv(TAG)' values.yaml
rm -f images/kilo.json

View file

@ -1,47 +0,0 @@
# Build the manager binary
ARG FROM=alpine
FROM $FROM AS cni
ARG GOARCH=amd64
ARG CNI_PLUGINS_VERSION=v1.1.1
RUN apk add --no-cache curl && \
curl -Lo cni.tar.gz https://github.com/containernetworking/plugins/releases/download/$CNI_PLUGINS_VERSION/cni-plugins-linux-$GOARCH-$CNI_PLUGINS_VERSION.tgz && \
tar -xf cni.tar.gz
FROM golang:1.19.0 as builder
ARG VERSION=0.6.0
ARG TARGETOS
ARG TARGETARCH
WORKDIR /workspace
RUN curl -sSL https://github.com/squat/kilo/archive/refs/tags/${VERSION}.tar.gz | tar -xzvf- --strip=1
COPY patches /patches
RUN git apply /patches/*.diff
RUN set -eux; \
GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 \
go build -mod=vendor \
-ldflags "-X github.com/squat/kilo/pkg/version.Version=$VERSION" \
-o /out/kg \
./cmd/kg/... ; \
GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 \
go build -mod=vendor \
-ldflags "-X github.com/squat/kilo/pkg/version.Version=$VERSION" \
-o /out/kgctl \
./cmd/kgctl/...
FROM alpine:3.20
ARG GOARCH
ARG ALPINE_VERSION=v3.20
LABEL maintainer="squat <lserven@gmail.com>"
RUN echo -e "https://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/main\nhttps://alpine.global.ssl.fastly.net/alpine/$ALPINE_VERSION/community" > /etc/apk/repositories && \
apk add --no-cache ipset iptables ip6tables graphviz font-noto
COPY --from=cni bridge host-local loopback portmap /opt/cni/bin/
ADD https://raw.githubusercontent.com/kubernetes-sigs/iptables-wrappers/e139a115350974aac8a82ec4b815d2845f86997e/iptables-wrapper-installer.sh /
RUN chmod 700 /iptables-wrapper-installer.sh && /iptables-wrapper-installer.sh --no-sanity-check
COPY --from=builder /out/kg /opt/bin/
COPY --from=builder /out/kgctl /opt/bin/
ENTRYPOINT ["/opt/bin/kg"]

View file

@ -1,246 +0,0 @@
diff --git a/cmd/kg/main.go b/cmd/kg/main.go
index c2ad6d5..301819f 100644
--- a/cmd/kg/main.go
+++ b/cmd/kg/main.go
@@ -120,6 +120,7 @@ var (
topologyLabel string
port int
serviceCIDRsRaw []string
+ internalCIDRsRaw []string
subnet string
resyncPeriod time.Duration
iptablesForwardRule bool
@@ -152,6 +153,7 @@ func init() {
cmd.Flags().StringVar(&topologyLabel, "topology-label", k8s.RegionLabelKey, "Kubernetes node label used to group nodes into logical locations.")
cmd.Flags().IntVar(&port, "port", mesh.DefaultKiloPort, "The port over which WireGuard peers should communicate.")
cmd.Flags().StringSliceVar(&serviceCIDRsRaw, "service-cidr", nil, "The service CIDR for the Kubernetes cluster. Can be provided optionally to avoid masquerading packets sent to service IPs. Can be specified multiple times.")
+ cmd.Flags().StringSliceVar(&internalCIDRsRaw, "internal-cidr", nil, "CIDRs to consider for internal IP auto-detection. If specified, only IPs within these CIDRs will be used. Can be specified multiple times.")
cmd.Flags().StringVar(&subnet, "subnet", mesh.DefaultKiloSubnet.String(), "CIDR from which to allocate addresses for WireGuard interfaces.")
cmd.Flags().DurationVar(&resyncPeriod, "resync-period", 30*time.Second, "How often should the Kilo controllers reconcile?")
cmd.Flags().BoolVar(&iptablesForwardRule, "iptables-forward-rules", false, "Add default accept rules to the FORWARD chain in iptables. Warning: this may break firewalls with a deny all policy and is potentially insecure!")
@@ -266,7 +268,16 @@ func runRoot(_ *cobra.Command, _ []string) error {
serviceCIDRs = append(serviceCIDRs, s)
}
- m, err := mesh.New(b, enc, gr, hostname, port, s, local, cni, cniPath, iface, cleanUp, cleanUpIface, createIface, mtu, resyncPeriod, prioritisePrivateAddr, iptablesForwardRule, serviceCIDRs, log.With(logger, "component", "kilo"), registry)
+ var internalCIDRs []*net.IPNet
+ for _, internalCIDR := range internalCIDRsRaw {
+ _, s, err := net.ParseCIDR(internalCIDR)
+ if err != nil {
+ return fmt.Errorf("failed to parse %q as CIDR: %v", internalCIDR, err)
+ }
+ internalCIDRs = append(internalCIDRs, s)
+ }
+
+ m, err := mesh.New(b, enc, gr, hostname, port, s, local, cni, cniPath, iface, cleanUp, cleanUpIface, createIface, mtu, resyncPeriod, prioritisePrivateAddr, iptablesForwardRule, internalCIDRs, serviceCIDRs, log.With(logger, "component", "kilo"), registry)
if err != nil {
return fmt.Errorf("failed to create Kilo mesh: %v", err)
}
diff --git a/manifests/kilo-bootkube-flannel.yaml b/manifests/kilo-bootkube-flannel.yaml
index 64e3500..fb3d25f 100644
--- a/manifests/kilo-bootkube-flannel.yaml
+++ b/manifests/kilo-bootkube-flannel.yaml
@@ -74,11 +74,16 @@ spec:
- --cni=false
- --compatibility=flannel
- --local=false
+ - --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
+ - name: NODE_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.hostIP
ports:
- containerPort: 1107
name: metrics
diff --git a/manifests/kilo-k3s-cilium.yaml b/manifests/kilo-k3s-cilium.yaml
index d75c93c..c98e518 100644
--- a/manifests/kilo-k3s-cilium.yaml
+++ b/manifests/kilo-k3s-cilium.yaml
@@ -106,11 +106,16 @@ spec:
- --encapsulate=crosssubnet
- --clean-up-interface=true
- --log-level=all
+ - --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
+ - name: NODE_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.hostIP
ports:
- containerPort: 1107
name: metrics
diff --git a/manifests/kilo-k3s-flannel.yaml b/manifests/kilo-k3s-flannel.yaml
index 612cb11..07b4c87 100644
--- a/manifests/kilo-k3s-flannel.yaml
+++ b/manifests/kilo-k3s-flannel.yaml
@@ -103,11 +103,16 @@ spec:
- --cni=false
- --compatibility=flannel
- --local=false
+ - --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
+ - name: NODE_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.hostIP
ports:
- containerPort: 1107
name: metrics
diff --git a/manifests/kilo-kubeadm-cilium.yaml b/manifests/kilo-kubeadm-cilium.yaml
index 5bf065a..ac0bf90 100644
--- a/manifests/kilo-kubeadm-cilium.yaml
+++ b/manifests/kilo-kubeadm-cilium.yaml
@@ -79,11 +79,16 @@ spec:
- --clean-up-interface=true
- --subnet=172.31.254.0/24
- --log-level=all
+ - --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
+ - name: NODE_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.hostIP
ports:
- containerPort: 1107
name: metrics
diff --git a/manifests/kilo-kubeadm-flannel-userspace.yaml b/manifests/kilo-kubeadm-flannel-userspace.yaml
index c4ce25b..5d1824e 100644
--- a/manifests/kilo-kubeadm-flannel-userspace.yaml
+++ b/manifests/kilo-kubeadm-flannel-userspace.yaml
@@ -88,11 +88,16 @@ spec:
- --cni=false
- --compatibility=flannel
- --local=false
+ - --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
+ - name: NODE_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.hostIP
ports:
- containerPort: 1107
name: metrics
diff --git a/manifests/kilo-kubeadm-flannel.yaml b/manifests/kilo-kubeadm-flannel.yaml
index fea35dc..ff6bb25 100644
--- a/manifests/kilo-kubeadm-flannel.yaml
+++ b/manifests/kilo-kubeadm-flannel.yaml
@@ -74,11 +74,16 @@ spec:
- --cni=false
- --compatibility=flannel
- --local=false
+ - --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
+ - name: NODE_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.hostIP
ports:
- containerPort: 1107
name: metrics
diff --git a/manifests/kilo-typhoon-flannel.yaml b/manifests/kilo-typhoon-flannel.yaml
index 0e4d9b2..cbd467a 100644
--- a/manifests/kilo-typhoon-flannel.yaml
+++ b/manifests/kilo-typhoon-flannel.yaml
@@ -74,11 +74,16 @@ spec:
- --cni=false
- --compatibility=flannel
- --local=false
+ - --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
+ - name: NODE_IP
+ valueFrom:
+ fieldRef:
+ fieldPath: status.hostIP
ports:
- containerPort: 1107
name: metrics
diff --git a/pkg/mesh/discoverips.go b/pkg/mesh/discoverips.go
index c8991d9..4598472 100644
--- a/pkg/mesh/discoverips.go
+++ b/pkg/mesh/discoverips.go
@@ -40,7 +40,8 @@ import (
// - private IP assigned to interface of default route
// - private IP assigned to local interface
// - if no IP was found, return nil and an error.
-func getIP(hostname string, ignoreIfaces ...int) (*net.IPNet, *net.IPNet, error) {
+// If allowedCIDRs is not empty, only IPs within these CIDRs will be considered for private IP selection.
+func getIP(hostname string, allowedCIDRs []*net.IPNet, ignoreIfaces ...int) (*net.IPNet, *net.IPNet, error) {
ignore := make(map[string]struct{})
for i := range ignoreIfaces {
if ignoreIfaces[i] == 0 {
@@ -144,6 +145,10 @@ func getIP(hostname string, ignoreIfaces ...int) (*net.IPNet, *net.IPNet, error)
if _, ok := ignore[tmpPriv[i].String()]; ok {
continue
}
+ // If allowedCIDRs is specified, filter private IPs by these CIDRs.
+ if len(allowedCIDRs) > 0 && !isInCIDRs(tmpPriv[i].IP, allowedCIDRs) {
+ continue
+ }
priv = append(priv, tmpPriv[i])
}
for i := range tmpPub {
@@ -290,3 +295,13 @@ func defaultInterface() (*net.Interface, error) {
return nil, errors.New("failed to find default route")
}
+
+// isInCIDRs checks if the given IP is within any of the provided CIDRs.
+func isInCIDRs(ip net.IP, cidrs []*net.IPNet) bool {
+ for _, cidr := range cidrs {
+ if cidr.Contains(ip) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/pkg/mesh/mesh.go b/pkg/mesh/mesh.go
index 3057d2a..e042467 100644
--- a/pkg/mesh/mesh.go
+++ b/pkg/mesh/mesh.go
@@ -89,7 +89,7 @@ type Mesh struct {
}
// New returns a new Mesh instance.
-func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularity, hostname string, port int, subnet *net.IPNet, local, cni bool, cniPath, iface string, cleanup bool, cleanUpIface bool, createIface bool, mtu uint, resyncPeriod time.Duration, prioritisePrivateAddr, iptablesForwardRule bool, serviceCIDRs []*net.IPNet, logger log.Logger, registerer prometheus.Registerer) (*Mesh, error) {
+func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularity, hostname string, port int, subnet *net.IPNet, local, cni bool, cniPath, iface string, cleanup bool, cleanUpIface bool, createIface bool, mtu uint, resyncPeriod time.Duration, prioritisePrivateAddr, iptablesForwardRule bool, allowedInternalCIDRs []*net.IPNet, serviceCIDRs []*net.IPNet, logger log.Logger, registerer prometheus.Registerer) (*Mesh, error) {
if err := os.MkdirAll(kiloPath, 0700); err != nil {
return nil, fmt.Errorf("failed to create directory to store configuration: %v", err)
}
@@ -134,7 +134,7 @@ func New(backend Backend, enc encapsulation.Encapsulator, granularity Granularit
}
kiloIface = link.Attrs().Index
}
- privateIP, publicIP, err := getIP(hostname, kiloIface, enc.Index(), cniIndex)
+ privateIP, publicIP, err := getIP(hostname, allowedInternalCIDRs, kiloIface, enc.Index(), cniIndex)
if err != nil {
return nil, fmt.Errorf("failed to find public IP: %v", err)
}

View file

@ -1,30 +0,0 @@
diff --git a/pkg/mesh/topology.go b/pkg/mesh/topology.go
index ca22bf6..1de8ae4 100644
--- a/pkg/mesh/topology.go
+++ b/pkg/mesh/topology.go
@@ -263,17 +263,22 @@ CheckIPs:
}
}
// Check if allowed location IPs intersect with the allowed IPs.
+ // If the allowed location IP fully contains an allowed IP, that's fine -
+ // the more specific route will be used. Only warn if it's a partial overlap
+ // or if the allowed IP contains the allowed location IP.
for _, i := range s.allowedIPs {
- if intersect(ip, i) {
+ if intersect(ip, i) && !ip.Contains(i.IP) {
level.Warn(t.logger).Log("msg", "overlapping allowed location IPnet with allowed IPnets", "IP", ip.String(), "IP2", i.String(), "segment-location", s.location)
continue CheckIPs
}
}
// Check if allowed location IPs intersect with the private IPs of the segment.
+ // If the allowed location IP fully contains a private IP, that's fine.
for _, i := range s.privateIPs {
if ip.Contains(i) {
- level.Warn(t.logger).Log("msg", "overlapping allowed location IPnet with privateIP", "IP", ip.String(), "IP2", i.String(), "segment-location", s.location)
- continue CheckIPs
+ // This is OK - the allowed location IP contains the private IP,
+ // so the more specific route to the private IP will still work.
+ level.Debug(t.logger).Log("msg", "allowed location IPnet contains privateIP", "IP", ip.String(), "IP2", i.String(), "segment-location", s.location)
}
}
}

View file

@ -41,6 +41,7 @@ spec:
{{- with .Values.kilo.transitCIDR }}
- --subnet={{ . }}
{{- end }}
- --mtu={{ .Values.kilo.mtu | default "auto" }}
- --internal-cidr=$(NODE_IP)/32
env:
- name: NODE_NAME

View file

@ -1,10 +1,11 @@
kilo:
image:
pullPolicy: IfNotPresent
tag: v1.0.0-beta.2@sha256:45ad01a89ebb5311735660a0d1a1df36eda4b6f960be1b6319d2b94d2a7db701
tag: v0.7.0@sha256:69c14b8292c0dc9045fd646d332811619a2d93bcc1f9e19e11da9a9a172c988c
repository: ghcr.io/cozystack/cozystack/kilo
podCIDR: 10.244.0.0/16
serviceCIDR: 10.96.0.0/16
transitCIDR: 100.66.0.0/16
meshGranularity: location
cleanUpInterface: false
mtu: auto