惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy International News Feed
WordPress大学
WordPress大学
Security Latest
Security Latest
Cyberwarzone
Cyberwarzone
K
Kaspersky official blog
Cisco Talos Blog
Cisco Talos Blog
Microsoft Security Blog
Microsoft Security Blog
G
GRAHAM CLULEY
N
News | PayPal Newsroom
Apple Machine Learning Research
Apple Machine Learning Research
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
V
Visual Studio Blog
美团技术团队
J
Java Code Geeks
I
Intezer
The Cloudflare Blog
SecWiki News
SecWiki News
S
Secure Thoughts
Microsoft Azure Blog
Microsoft Azure Blog
V2EX - 技术
V2EX - 技术
C
Cyber Attacks, Cyber Crime and Cyber Security
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Spread Privacy
Spread Privacy
D
DataBreaches.Net
S
Security Affairs
Help Net Security
Help Net Security
S
Securelist
F
Full Disclosure
C
Check Point Blog
F
Fortinet All Blogs
Know Your Adversary
Know Your Adversary
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Y
Y Combinator Blog
云风的 BLOG
云风的 BLOG
阮一峰的网络日志
阮一峰的网络日志
The Register - Security
The Register - Security
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
人人都是产品经理
人人都是产品经理
博客园_首页
G
Google Developers Blog
Google Online Security Blog
Google Online Security Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
酷 壳 – CoolShell
酷 壳 – CoolShell
I
InfoQ
Application and Cybersecurity Blog
Application and Cybersecurity Blog
H
Hacker News: Front Page
L
LINUX DO - 热门话题
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LangChain Blog

Vegard S. Hagen on Stonegarden

Direct Server Return with Cilium Direct Server Return with Cilium BGP with Cilium and UniFi BGP with Cilium and UniFi Custom OIDC claims with Argo CD and Audiobookshelf Custom OIDC claims with Argo CD and Audiobookshelf OpenID Connect with Authelia on Kubernetes OpenID Connect with Authelia on Kubernetes LLDAP — Declarative Selfhosted Lightweight Authentication LLDAP — Declarative Selfhosted Lightweight Authentication Demystifying Kubernetes RBAC and OIDC Auth Demystifying Kubernetes RBAC and OIDC Auth Postgres databases in Kubernetes Postgres databases in Kubernetes TalosCon & SREDay London 2024 TalosCon & SREDay London 2024 Talos Kubernetes on Proxmox using OpenTofu Talos Kubernetes on Proxmox using OpenTofu Kubernetes Proxmox Container Storage Interface Kubernetes Proxmox Container Storage Interface Intel Quick Sync Video with Kubernetes Intel Quick Sync Video with Kubernetes External services with Gateway API External services with Gateway API Kubernetes on Proxmox Kubernetes on Proxmox Bootstrapping K3s with Cilium Bootstrapping K3s with Cilium CUDA on Kubernetes CUDA on Kubernetes Archived: Gateway API with Cilium and Cert-manager Gateway API v1.3.0 with Cilium and cert-manager Archived: Gateway API with Cilium and Cert-manager Gateway API v1.3.0 with Cilium and cert-manager Wildcard Certificates with Traefik Wildcard Certificates with Traefik Migrating from MetaLB to Cilium Theme Dynamic Images Theme Dynamic Images mo.unit wiring mo.unit wiring Bio Bio Browser rendered terminal Browser rendered terminal Argo CD Kustomize with Helm Argo CD Kustomize with Helm Cloudflared SSH tunneling Cloudflared SSH tunneling Stonegarden Stonegarden
Migrating from MetaLB to Cilium
Vegard S. Hagen · 2023-12-01 · via Vegard S. Hagen on Stonegarden

For my homelab I’m running an over-engineered one-node Kubernetes “cluster” using Cilium as the Container Network Interface (CNI). Up until recently I used MetalLB for LoadBalancer IP Address Management (LB-IPAM) and L2 announcements for Address Resolution Protocol (ARP) requests over the local network, but Cilium has now replaced this functionality.

Personally I really like Cilium because of their use of the eBPF 🐝 protocol and frankly also for their logo since hexagons are bestagons.

Cilium 1.13 introduced LB-IPAM support and 1.14 added L2 announcement capabilities, making MetalLB superfluous for my setup. Cilium also has support for Border Gateway Protocol (BGP) if you need that.

LB-IPAM#

LB-IPAM functionality is enabled by default, and you only need to create an IP-pool to allow Cilium to start assigning IPs to LoadBalancer services

apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: default-pool
spec:
  blocks:
    - cidr: 192.168.1.128/25

If you need more control over which services are assigned from which IP-pool you can use e.g. label matching as written in the Cilium LB-IPAM documentation.

To avoid having to change my network settings I wanted to Cilium to assign the same IPs as MetalLB did to some of my services. This was easily done by changing the Service annotations from

kind: Service
apiVersion: v1
metadata:
  annotations:
    metallb.universe.tf/loadBalancerIPs: 192.168.1.153
    metallb.universe.tf/allow-shared-ip: pi-hole

to

kind: Service
apiVersion: v1
metadata:
  annotations:
    io.cilium/lb-ipam-ips: 192.168.1.153
    io.cilium/lb-ipam-sharing-key: pi-hole

Avoid using .spec.loadBalancerIP which has been deprecated since Kubernetes v1.24 and will be removed in a future release.

Here I’ve also included an annotation to allow two services to share the same IP. This allowed me to circumvent an issue with mixed protocol services which has now been fixed and stable since Kubernetes v1.26.

Limitations#

Update 2024-03-09: This is no longer an issue using Cilium 1.15.1 and Kubernetes 1.29.1. Thanks to Reddit user u/spooge_mcnubbins for informing me!

Using MetalLB I was able to expose both UDP and TCP connections on the same port, but unfortunately this isn’t supported by Cilium (yet) according to a GitHub issue.

Running both UDP and TCP on port 53 is the standard for DNS. Since I run a Pi-hole DNS instance in the cluster I relied on both UDP and TCP to be exposed on the same port. Doing some research however reveals that even though DNS is mostly UDP it has fallback to TCP if either the packet size is too big or UDP isn’t available (RFC 1034). With this in mind I decided it’s acceptable to take the (negligible) overhead hit running DNS only over TCP.

L2 Announcements#

For Cilium to perform L2 announcements and reply to ARP requests it must run in Kube-proxy replacement mode. We also need to explicitly enable the feature in the Helm chart values.yaml file

kubeProxyReplacement: true

l2announcements:
  enabled: true

externalIPs:
  enabled: true

The above config enables Cilium to announce IPs from services’ .status.loadbalancer.ingress field, i.e. the IPs assigned by LB-IPAM, and external IPs from a service’s manually assigned .spec.externalIPs field.

Next we also need to define a CiliumL2AnnouncementPolicy to actually announce the IPs

apiVersion: cilium.io/v2alpha1
kind: CiliumL2AnnouncementPolicy
metadata:
  name: default-l2-announcement-policy
  namespace: kube-system
spec:
  interfaces:
    - enp0s25
  externalIPs: true
  loadBalancerIPs: true

Here I’ve explicitly set it to only announce over the enp0s25 interface which is the physical network card. You can list all network interfaces by running

Leaving .spec.interfaces empty announces over all interfaces. For more details read the documentation here.

Caveats#

Enabling L2 announcements can significantly increase API traffic depending on you configuration as explained in the Cilium L2 announcements documentation.

I already have quite a few services running in my homelab and skimmed over this important detail to begin with, which resulted in connectivity/discovery issues.

Looking at the logs of the cilium-agent container by running

kubectl logs -l app.kubernetes.io/name=cilium-agent -c cilium-agent -n kube-system --tail 128

I saw multiple error- and info-level messages indicating I hit some kind of rate limit

error retrieving resource lock kube-system/cilium-l2announce-...: client rate limiter Wait returned an error: rate: Wait(n=1) would exceed context deadline
level=error msg="error retrieving resource lock kube-system/cilium-l2announce-...: client rate limiter Wait returned an error: rate: Wait(n=1) would exceed context deadline" subsys=klog
error retrieving resource lock kube-system/cilium-l2announce-...: client rate limiter Wait returned an error: rate: Wait(n=1) would exceed context deadline
level=error msg="error retrieving resource lock kube-system/cilium-l2announce-...: client rate limiter Wait returned an error: rate: Wait(n=1) would exceed context deadline" subsys=klog
level=info msg="failed to renew lease kube-system/cilium-l2announce-...: timed out waiting for the condition" subsys=klog
level=info msg="failed to renew lease kube-system/cilium-l2announce-...: timed out waiting for the condition" subsys=klog
...
level=info msg="Waited for 7.593379216s due to client-side throttling, not priority and fairness, request: PUT:https://...:6443/apis/coordination.k8s.io/v1/namespaces/kube-system/leases/cilium-l2announce-kube-system-hubble-peer" subsys=klog

This was easily remedied by increasing the k8sClientRateLimit tenfold

k8sClientRateLimit:
  qps: 50
  burst: 100

Summary#

My current Cilium configuration can be found on GitHub, but for posterity the below configuration should enable Cilium with LB-IPAM and L2 announcements using ArgoCD with Kustomize + Helm

# kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ip-pool.yaml
  - announce.yaml

helmCharts:
  - name: cilium
    repo: https://helm.cilium.io
    version: 1.14.4
    releaseName: "cilium"
    namespace: kube-system
    valuesFile: values.yaml
#values.yaml
k8sServiceHost: <-- HOST IP -->
k8sServicePort: 6443

kubeProxyReplacement: true

# Roll out cilium agent pods automatically when ConfigMap is updated.
rollOutCiliumPods: true

# Increase rate limit when doing L2 announcements
k8sClientRateLimit:
  qps: 50
  burst: 100

# Announce IPs from services' `.status.loadbalancer.ingress` field (automatically assigned by LB-IPAM).
l2announcements:
  enabled: true

# Announce manually assigned IPs from services' `.spec.externalIPs` field
externalIPs:
  enabled: true

operator:
  # Can't have more replicas than nodes
  replicas: 1
# ip-pool.yaml
apiVersion: cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: default-pool
  namespace: kube-system
spec:
  blocks:
    - cidr: <-- VALID CIDR RANGE FOR YOUR NETWORK -->
    - start: <-- START IP -->
      stop: <-- END IP -->
# announce.yaml
apiVersion: cilium.io/v2alpha1
kind: CiliumL2AnnouncementPolicy
metadata:
  name: default-l2-announcement-policy
  namespace: kube-system
spec:
  externalIPs: true
  loadBalancerIPs: true

Hands-on lab#

If you’re interested in more details, Isovalent — the company behind Cilium, have created multiple hands-on labs for getting started with Cilium, including one about implementing LB-IPAM and L2 service announcements which is helpful for better understanding what’s happening under the hood.