Tek Düğümlü k3s Kümesi Nasıl Güvenli Hale Getirilir?
VPS üzerinde k3s kurulumu sonrası açık kalan 6443 ve 10250 portlarını, kubeconfig izinlerini ve güvensiz NodePort yapılandırmasını kapatmak için gereken teknik adımları öğrenin.
Tek düğümlü bir k3s kümesinin ilk gününde dışa açtığı noktalar
Genel bir VPS üzerindeki tek düğümlü k3s kümesi, tek satırlık kurulum tamamlandıktan sonraki gün beş belirli noktada dışa açıktır: TCP 6443 üzerinde Kubernetes API sunucusu, TCP 10250 üzerinde kubelet, diskte duran kubeconfig dosyası, güvenlik duvarınızın göremediği NodePort aralığı ve privileged veya hostPath talep etmesine izin verilen herhangi bir pod. Bunların her birinin dakikalar içinde uygulanabilecek bir çözümü vardır. Bu kılavuz k3s'in halihazırda çalıştığını varsayar; bu nedenle eğer çalışmıyorsa, bir VPS üzerinde tek düğümlü k3s kurulumu ile başlayıp daha sonra geri dönün.
Herhangi bir değişiklik yapmadan önce neyin dinlemede olduğuna bakın.
sudo ss -tulpn | grep -E '6443|10250|10256|8472'Varsayılan bir kurulumda 6443 (API sunucusu), 10250 (kubelet), 10256 (kube-proxy sağlık kontrolü) ve 8472/udp (VXLAN yani sanal genişletilebilir LAN kullanan flannel katmanı) görünür. k3s varsayılan olarak 0.0.0.0 adresine bağlandığından, bunların her biri yalnızca loopback üzerinde değil, genel IP adresiniz üzerinde de yer alır.
Neden 6443 numaralı port tüm kümedir
6443 numaralı porta yönetici haklarıyla kimlik doğrulaması yapabilen herhangi bir yapı, bir pod oluşturabilir ve bir pod, ana makinede root yetkilerine sahip olabilir. 6443 numaralı port, makineye açılan kapıdır.
Açık bir 6443 portu anında bir ihlal anlamına gelmez, çünkü Kubernetes parola kabul etmez. İstemci sertifikası veya bir taşıyıcı token (bearer token) gerektirir. Ancak iki gerçek hala geçerlidir.
Birincisi, API sunucusu bazı isteklere hiçbir kimlik bilgisi olmadan yanıt verir. Varsayılan Kubernetes RBAC (rol tabanlı erişim denetimi), system:unauthenticated grubunu system:public-info-viewer adlı bir role bağlar; bu rol /version, /healthz, /livez ve /readyz işlemlerine izin verir. Başka bir makineden şu komut çalıştırıldığında:
curl -sk https://YOUR_SERVER_IP:6443/versionBu komut, tam Kubernetes sürümünüzü döndürür. Bu bilgi, bir CVE (ortak güvenlik açıkları ve maruziyetler) aramasının girdisidir ve bir tarayıcının sunucunuzu neden hedef olarak seçtiğinin sebebidir. Bu yolların dışındaki her şey reddedilir ve ret yanıtı sizi tanımlar:
forbidden: User "system:anonymous" cannot get path "/api"İkincisi, bu port açık olduğu sürece her API sunucusu hatasına uzaktan erişilebilir. Bu noktada yamalama yapmak isteğe bağlı olmaktan çıkar.
En basit çözüm bir güvenlik duvarı kuralıdır. ufw burada tek satırdan fazlasına ihtiyaç duyar, çünkü k3s küme trafiğini aynı çekirdek üzerinden yönlendirir.
sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow from 203.0.113.10 to any port 6443 proto tcp
sudo ufw allow from 10.42.0.0/16 to any
sudo ufw allow from 10.43.0.0/16 to any
sudo ufw enableSon iki kural doğrudan k3s dokümantasyonundan alınmıştır. 10.42.0.0/16 varsayılan pod ağıdır ve 10.43.0.0/16 varsayılan servis ağıdır; bunlar olmadan ufw küme içi trafiği düşürür, bu nedenle podlar API sunucusuyla ve birbirleriyle olan bağlantılarını kaybeder. k3s örneği 6443 portuna her yerden erişime izin verir; bunu kendi adresinizle değiştirmek yapılması gereken önemli bir değişikliktir. ufw sizin için yeniyse, VPS için ufw güvenlik duvarı temelleri rehberi, bu yapılandırmanın dayandığı varsayılan politikaları kapsar.
k3s dokümanları overlay portu konusunda nettir: "Düğümler üzerindeki VXLAN portu dünyaya açılmamalıdır, aksi takdirde küme ağınız herkesin erişimine açık hale gelir." Gelen trafik için varsayılan bir reddetme politikası, portu ayrıca belirtmeye gerek kalmadan bu durumu yönetir.
Daha güçlü bir çözüm, API'ye genel adres üzerinden erişmeyi tamamen bırakmak ve bunun yerine bir VPN veya mesh adresi kullanmaktır. Sunucu sertifikası bağlandığınız adresi listelemelidir, bu yüzden onu /etc/rancher/k3s/config.yaml içinde bir SAN (konu alternatif adı) olarak ekleyin:
tls-san:
- 10.8.0.1
- k3s.example.com
secrets-encryption: truesudo systemctl restart k3s
sudo k3s secrets-encrypt statussecrets-encryption: true, veri deposundaki Secret nesnelerini şifreler. k3s dokümanları, "Secrets-encryption özelliği, sunucu yeniden başlatılmadan mevcut bir sunucuda etkinleştirilemez" notunu düşer ve değişiklikten önce yazılan Secret'lar, siz sudo k3s secrets-encrypt reencrypt komutunu çalıştırana kadar eski biçimlerini korur. Bu işlemin ne sağladığı konusunda net olun. Bu işlem, yedekten kopyalanan bir veri deposu dosyasını korur. API sunucusuyla iletişim kurabilen birine karşı hiçbir koruma sağlamaz, çünkü API sunucusu Secret'ları, onları okumaya yetkili olan herkes için şifresini çözerek sunar. Aynı ayrım tüm self-hosted secret depoları için geçerlidir; bu yüzden Vaultwarden üzerinde güvenlik sıkılaştırma rehberi, şifrelemenin kendisinden ziyade yönetici token'ı ve yedek dosyası üzerinde durur.
10250 numaralı porttaki kubelet neden önemlidir
kubelet, container'ları başlatan aracıdır. 10250 numaralı port üzerindeki API'si, pod'ları listeler ve içlerinde komut çalıştırır. Anonim istekleri kabul eden bir kubelet, makinedeki her iş yüküne uzaktan erişim sağlayan bir kabuk (shell) görevi görür.
Kendi yapılandırmanızı kontrol edin:
curl -sk https://127.0.0.1:10250/pods | head -c 60Güncel bir k3s, Unauthorized yanıtını verir; çünkü kubelet, API sunucusundan her çağrıyı doğrulamasını ve yetkilendirmesini ister. Eğer bunun yerine bir JSON pod listesi dönüyorsa, anonim erişim açıktır ve 10250 numaralı porta erişebilen herkes container'larınızdan veri okuyabilir ve içlerinde komut çalıştırabilir.
Her iki durumda da bu portu dış dünyaya kapatın. Tek düğümlü bir yapıda kubelet'in tek istemcisi aynı makinedeki control plane'dir ve bu trafik, ufw tarafından varsayılan olarak kabul edilen loopback arayüzü üzerinden girer. 10250 numaralı portu internete kapatmanın size bir maliyeti yoktur. Buraya bozuk bir kubectl top veya çalışmayan bir metrics-server nedeniyle geldiyseniz, nedenleri kubelet 10250 numaralı port hataları altında toplanmıştır.
kubeconfig dosyanız bir küme yöneticisi kimlik bilgisidir
k3s, /etc/rancher/k3s/k3s.yaml dosyasını root sahipliğinde ve 600 izin moduyla yazar. Belgeler, bu izni değiştirmenin sonuçlarını şu şekilde açıklar: "kubeconfig dosyası root sahipliğindedir ve varsayılan olarak 600 moduyla yazılır. Modu 644 olarak değiştirmek, dosyanın sunucudaki diğer yetkisiz kullanıcılar tarafından okunmasına olanak tanır."
Bunu şu şekilde yorumlayın: 644 modu, yerel her hesabı bir küme yöneticisi yapar. Birçok rehber, genellikle --write-kubeconfig-mode 644 kullanarak ve sudo gereksinimini ortadan kaldırarak kubectl komutunun çalışması için tam olarak bunu önerir. Bu yöntem, yönetici kimlik bilgisini kabuk erişimi olan herkese vererek çalışır.
Bunun yerine dosyayı tek bir kullanıcıya kopyalayın.
mkdir -p ~/.kube
sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config
kubectl get nodesArdından orijinal dosyanın hala kısıtlı olduğunu doğrulayın:
stat -c '%a %U:%G' /etc/rancher/k3s/k3s.yamlİstediğiniz cevap 600 root:root dosyasıdır. Bu dosya, API sunucusunun koşulsuz olarak izin verdiği ve RBAC kurallarının asla sorgulanmadığı system:masters grubunun bir üyesi için istemci sertifikası tutar. Kubernetes'te sertifika iptal listesi bulunmaz; bu da sızdırılan bir kopyanın, küme sertifika yetkilisini yenileyene kadar geçerli kalacağı anlamına gelir. Dosyayı bir SSH özel anahtarı gibi değerlendirin ve ona erişebilecek hesap sayısını sınırlı tutun; bu, VPS üzerinde en az yetkili kullanıcı hesapları ile aynı mantıktır.
Güvenlik duvarınız NodePort trafiğini görmüyor
Bir type: NodePort servisi, düğümün sahip olduğu her adres üzerinde (genel IP dahil) 30000 ile 32767 arasında bir port açar. Bir type: LoadBalancer servisi ise k3s üzerinde daha ileri gider: Paketle gelen yük dengeleyici ServiceLB, her servis için kube-system içerisinde küçük bir pod zamanlar ve bu pod, servis portunu doğrudan ana makine üzerinde talep eder.
kubectl -n kube-system get pods | grep svclb
sudo ss -tulpn | grep -E ':3[0-2][0-9]{3}'Şimdi insanları şaşırtan kısma gelelim. Bu portu ufw ile engelleseniz bile yanıt vermeye devam eder.
sudo ufw deny 30080/tcp
curl http://YOUR_SERVER_IP:30080Sayfa hala yüklenir, çünkü paketin izlediği yol farklıdır. kube-proxy, DNAT (hedef ağ adresi çevirisi) kurallarını nat tablosunun PREROUTING zincirine yazar ve PREROUTING, herhangi bir filtreleme kararı verilmeden önce çalışır. Hedef, ana makine değil bir pod adresi haline gelir; bu nedenle çekirdek paketi FORWARD zincirine gönderir ve INPUT zincirine asla uğratmaz. ufw kuralları ise INPUT zincirinde yaşar. Paket bu kurallarla asla karşılaşmaz. Atlama sırası şu şekilde görülebilir:
sudo iptables -S PREROUTING -t nat | head
sudo iptables -S FORWARD | headKUBE-SERVICES, PREROUTING'in en üstünde yer alır ve FORWARD içindeki Kubernetes atlamaları, ufw'nin kendi zincirlerinin üzerinde bulunur. Bu, Docker'ın portları ufw'yi atlayarak yayınlamasına izin veren mekanizmanın aynısıdır ve çözümler de aynıdır.
- Servis sağlayıcınızın ağ güvenlik duvarında filtreleme yapın. Bu güvenlik duvarı makinenin önünde çalışır ve çekirdeğinizin trafiği nasıl yönlendirdiğiyle ilgilenmez.
- NodePort ve LoadBalancer kullanmaktan kaçının. Servisleri
ClusterIPüzerinde bırakın ve onlara halihazırda sahip olduğunuz SSH oturumu üzerindenkubectl port-forwardile erişin. - Yalnızca 80 ve 443 portlarında bir ingress yayınlayın, başka hiçbir şey açmayın.
kube-apiserver-argaltındakiservice-node-port-rangeayarıyla aralığı daraltın; böylece yanlışlıkla açılan bir NodePort, izlediğiniz bir aralığa denk gelir.
ufw yine de çalıştırılmaya değerdir. Doğrudan ana makineye yönelik trafiği (SSH ve API sunucusu gibi) yönetir. Sadece pod trafiğini denetlemez; ufw'nin bunu yapmasını beklemek, bir veritabanının dışarıdan erişilebilir hale gelmesine neden olur.
hostPath veya privileged içeren bir pod, VPS üzerinde root yetkisine sahiptir
Container'lar, çekirdek üzerinde kısıtlı bir görünüme sahip sıradan süreçlerdir. Bazı pod alanları bu kısıtlamayı ortadan kaldırır.
securityContext.privileged: true, container'a tüm Linux yeteneklerini (capabilities) ve ana makine aygıtlarına erişim izni verir.hostPath, bir ana makine dizinini pod içerisine bağlar./dizinini okuma-yazma yetkisiyle bağlayan bir pod,/root/.ssh/authorized_keysdosyasına bir anahtar ekleyebilir.hostPID: true, container'ı ana makine süreç ad alanına (process namespace) yerleştirir; burada PID 1'e karşı çalıştırılannsenter, ana makinede bir kabuk (shell) açar.hostNetwork: true, container'ı ana makine ağ yığınına yerleştirir; burada ana makine portlarını dinleyebilir ve loopback arayüzüne bağlı servislere erişebilir.
Bu nedenle "burada kim pod oluşturabilir" sorusu, "bu VPS üzerinde kim root yetkisine sahip" sorusuyla aynıdır. Bir pod'u reddeden bir mekanizma olmadığı sürece, herhangi bir namespace içinde pod oluşturma yetkisine (create) sahip her ServiceAccount, root ile eşdeğerdir.
Bu mekanizma, API server içerisine yerleşik olan Pod Security admission'dır. Hızlı kurulumu, her namespace için bir etiket (label) tanımlamaktan ibarettir ve yeniden başlatma gerektirmez.
kubectl label namespace default \
pod-security.kubernetes.io/enforce=baseline \
pod-security.kubernetes.io/enforce-version=latest \
pod-security.kubernetes.io/warn=restrictedbaseline, yukarıdaki dört alanı da reddeder. restricted ise daha ileri giderek root olmayan bir kullanıcı, bir seccomp (secure computing mode) profili, ayrıcalık yükseltme yasağı ve ALL seviyesine düşürülmüş yetenekler gerektirir; bu durum birçok yayınlanmış chart'ın çalışmasını engeller. baseline politikasını zorunlu tutarken restricted için yalnızca uyarı modunu kullanmak, değişiklikleri kalıcı hale getirmeden önce nelerin bozulacağını görmenizi sağlar.
Çalıştığını doğrulayın:
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pstest
spec:
containers:
- name: app
image: busybox
command: ["sleep", "60"]
securityContext:
privileged: true
EOFAPI server bunu reddeder ve hangi alan nedeniyle reddettiğini belirtir:
Error from server (Forbidden): error when creating "STDIN": pods "pstest" is forbidden: violates PodSecurity "baseline:latest": privileged (container "app" must not set securityContext.privileged=true)Her namespace'e ayrı etiket eklemek yerine küme genelinde bir varsayılan ayar için k3s, /var/lib/rancher/k3s/server/psa.yaml konumunda bir admission yapılandırma dosyası belgelendirmiştir:
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1beta1
kind: PodSecurityConfiguration
defaults:
enforce: "baseline"
enforce-version: "latest"
warn: "restricted"
warn-version: "latest"
exemptions:
namespaces: [kube-system]Bu dosyayı /etc/rancher/k3s/config.yaml içerisinde API server'a tanıtın ve ardından k3s'i yeniden başlatın:
kube-apiserver-arg:
- 'admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml'kube-system muafiyeti isteğe bağlı değildir. k3s'in kendi ServiceLB pod'ları ana makine portlarını kullanır; baseline bunu yasakladığı için kube-system değerini listeden çıkarmak, bu pod'lar yeniden oluşturulduğunda reddedilmelerine neden olur. Admission değişikliğinden sonra k3s'i yeniden başlatırken ikinci bir SSH oturumunu açık tutun.
Buradayken bir ek bilgi: k3s, bir ağ politikası denetleyicisi (network policy controller) ile gelir ve bunu varsayılan olarak etkinleştirir; bu nedenle NetworkPolicy nesneleri, başka hiçbir şey kurmanıza gerek kalmadan bu kümede etkili olur. Bu durum her Kubernetes dağıtımı için geçerli değildir ve ele geçirilmiş bir pod'un diğerlerine erişmesini engellemek için kullanılan temel araçtır.
Kullanmadığınız paketlenmiş bileşenleri kaldırın
Yükleyici, bir dizi eklenti dağıtır. Bunların her biri bir dinleyici ve yamalanması gereken bir başka unsur ekler. --disable şu değerleri kabul eder: coredns, servicelb, traefik, local-storage, metrics-server, runtimes.
coredns bileşenini tutun. Küme içindeki hiçbir şey, bu bileşen olmadan bir ismi çözümleyemez. Geri kalanlar tercihe bağlıdır. /etc/rancher/k3s/config.yaml içinde:
disable:
- traefik
- servicelb
disable-helm-controller: truesudo systemctl restart k3s
kubectl get pods -Ak3s, devre dışı bıraktığınız bileşenleri siler; bu nedenle traefik pod'ları ve svclb- pod'ları kendiliğinden kaybolur. Öncelikle sonuçlarını bilin. servicelb kaldırıldığında, hiçbir şey ona bir adres atamadığı için her type: LoadBalancer servisi sonsuza kadar <pending> durumunda kalır. traefik kaldırıldığında bir ingress controller kalmaz, bu nedenle Ingress nesneleri hiçbir işlev görmez. Bunları, ana makine üzerinde bir reverse proxy gibi başka bir yöntemle trafik sunduğunuzda devre dışı bırakın; kullandığınız durumlarda ise olduğu gibi bırakın. disable-helm-controller: true, HelmChart kaynaklarını izleyen denetleyiciyi kaldırır; bu, helm uygulamasını kendiniz çalıştırıyorsanız kullanmadığınız ayrıcalıklı bir bileşendir.
Varsayılan ServiceAccount token'ının otomatik bağlanmasını durdurma
Aksi belirtilmedikçe her pod, /var/run/secrets/kubernetes.io/serviceaccount/token konumunda bir ServiceAccount token'ı alır. default ServiceAccount'u herhangi bir RBAC iznine sahip değildir, bu nedenle token tek başına pek bir işe yaramaz. Ele geçirilmiş bir container içindeki saldırgana sağladığı şey, geçerli bir kimlik bilgisi ve erişilebilir bir API sunucusudur; bu da çoğu küme yükseltme senaryosunun ilk adımıdır.
k3s sıkılaştırma kılavuzu, bunu namespace bazında devre dışı bırakır:
kubectl patch serviceaccount --namespace default default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-node-lease default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-public default --patch '{"automountServiceAccountToken": false}'Ayarların uygulandığını doğrulayın. Pod'un başlaması için birkaç saniye bekleyin.
kubectl run t --image=busybox --restart=Never --command -- sleep 30
kubectl exec t -- ls /var/run/secrets/kubernetes.io/serviceaccountYol artık mevcut değildir, bu nedenle ls komutu No such file or directory çıktısını verir. API erişimine gerçekten ihtiyaç duyan bir iş yükü, kendi pod tanımında automountServiceAccountToken: true ayarını yapar; böylece hiçbir şey kalıcı olarak kilitlenmiş olmaz. Bu tek başına küçük bir kazanımdır. Daha büyük kazanç ise bir iş yüküne gerçek izinleri olan bir ServiceAccount vermemektir; bir token'ın şu anki değerini şu şekilde görebilirsiniz:
kubectl auth can-i --list --as=system:serviceaccount:default:defaultKaynak sınırları: bir pod'un tüm kümenin çökmesine neden olmasını engelleme
Tek bir düğüm üzerinde kontrol düzlemi ve iş yükleriniz aynı çekirdeği ve aynı bellek havuzunu paylaşır. Bellek sızıntısı olan bir pod her zaman tek başına ölmez. Çekirdeğin OOM (bellek yetersizliği) sonlandırıcısı, kurbanını büyük süreçlere ağırlık veren bir puanlama sistemiyle seçer. k3s uzun ömürlü ve büyük bir süreç olduğundan, pod yerine kümenin kendisi devre dışı kalabilir. Bu durumda iş yüklerinizi yeniden başlatacak bir mekanizma kalmaz, çünkü iş yüklerini yeniden başlatan süreç zaten ölmüştür.
Bir LimitRange, sınır belirtilmemiş pod'lar için varsayılan değerleri doldurur:
apiVersion: v1
kind: LimitRange
metadata:
name: defaults
namespace: default
spec:
limits:
- type: Container
default:
cpu: 500m
memory: 512Mi
defaultRequest:
cpu: 50m
memory: 128MiBir ResourceQuota, tüm namespace'in talep edebileceği toplam kaynağı sınırlar:
apiVersion: v1
kind: ResourceQuota
metadata:
name: cap
namespace: default
spec:
hard:
limits.cpu: "3"
limits.memory: 3Gi
pods: "20"Ardından /etc/rancher/k3s/config.yaml içerisinde k3s için yer ayırın:
kubelet-arg:
- 'system-reserved=cpu=250m,memory=512Mi'Aradaki fark iki noktada ortaya çıkar. Kendi sınırını aştığı için sonlandırılan bir container, kubectl describe pod içerisinde Last State altında Reason: OOMKilled rapor eder ve düğümün geri kalanı çalışmaya devam eder. Belleği tamamen tükenen bir düğüm ise dmesg içerisinde bir Killed process satırı bırakır ve genellikle komşu süreçleri de beraberinde götürür. Birincisi sınırlarınızın görevini yaptığını gösterir. İkincisi ise sınırların var olma nedenidir.
Manifestleri CI sürecinde ve k3s kümesinde düzenli olarak tarayın
Tarama işlemi iki farklı noktada yapılmalıdır; bu noktalar birbirinden farklı sorunları tespit eder. Öncelikle Trivy aracını kurun. Proje, her sürümle birlikte bir Debian paketi yayınlamaktadır. Ağustos 2026 itibarıyla 0.74.0 sürümü günceldi; bu nedenle otomasyonunuza sabitlemeden önce daha yeni bir sürüm olup olmadığını kontrol etmek için sürümler sayfasına bakın.
sudo apt-get install -y wget
wget -q https://github.com/aquasecurity/trivy/releases/download/v0.74.0/trivy_0.74.0_Linux-64bit.deb
sudo dpkg -i trivy_0.74.0_Linux-64bit.deb
trivy --versionİlk nokta, manifestlerinizin kümeye ulaşmadan önceki halidir.
trivy fs --scanners misconfig --severity HIGH,CRITICAL --exit-code 1 ./deploy--exit-code 1, bir bulgu durumunda CI (sürekli entegrasyon) işini başarısız kılar. Her sonuç, başarısızlığa neden olan alanı ve önem derecesini belirtir; böylece commit etmeyi amaçlamadığınız bir privileged: true, API sunucusuna ulaşmak yerine derleme aşamasında başarısız olur. Kabul etmeye karar verdiğiniz bir bulgu ise, bu kararı git üzerinde ilgili manifestin yanında tutan bir .trivyignore dosyasına kaydedilir.
İkinci nokta ise çalışan kümedir ve bu işlem bir zaman çizelgesine göre yapılır.
trivy k8s --compliance=k8s-cis-1.23 --report summaryBu komutla ilgili dürüst bir not: trivy k8s, düğüm düzeyindeki ayarları incelemek için ana makine erişimine ihtiyaç duyan bir düğüm toplayıcı pod dağıtır. Ayrıcalıklı podları reddetmeye yeni başladığınız bir kümede, bu durumu aşmaya çalışmak yerine farkında olmak önemlidir. trivy k8s --report summary --disable-node-collector, toplayıcıyı atlar ve bununla birlikte düğüm düzeyindeki kontrolleri de devre dışı bırakır.
kube-bench, ana makine tarafını kapsar: CIS (Center for Internet Security) kriterlerinin büyük kısmının aslında bulunduğu dosya izinleri ve süreç bayrakları. Araç, bir k3s profili ile gelir. Yukarı akış (upstream) Job manifestini alın ve düzenleyin.
curl -sfL -o kube-bench-job.yaml https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yamlKapsayıcı komutunu ["kube-bench", "--benchmark", "k3s-cis-1.7"] olarak değiştirin ve /etc/kubernetes ile /var/lib/etcd mount noktalarını /etc/rancher ve /var/lib/rancher ile değiştirin; çünkü k3s dosyalarını bu dizinlerde tutar. Ardından:
kubectl apply -f kube-bench-job.yaml
kubectl logs -l app=kube-bench --tail=-1Bu Job'ın ne olduğuna dikkat edin: hostPID: true ve ana makine dizin mount'ları; yani bir bölüm önce reddetmeye başladığınız pod yapısının aynısı. Bunu muaf tutulan kube-system namespace içinde çalıştırın, çıktıyı okuyun ve ardından kubectl delete job kube-bench komutunu uygulayın. Ayrıcalıklı olması gereken bir tarayıcı, ayrıcalıklı iş yüklerini yasaklamayı bırakmak için bir neden değildir.
Kapsayıcı içeriği ayrı bir konudur. trivy image ghcr.io/example/app:1.4, bir kapsayıcı içindeki paket veritabanını okur ve bilinen güvenlik açıklarını listeler; bu, sunucunuzu bilinen CVE'ler için kontrol etme işlemiyle aynıdır.
Şimdi tarayıcı çıktısı hakkındaki dürüst kısma gelelim. Tek düğümlü bir hobi kümesi, uzun bir CIS kontrol listesinde başarısız olacaktır ve bu başarısızlıkların çoğu hem doğru hem de sizin için alakasızdır. Bu kriterler; etcd'nin ayrı ana makinelerde olduğu, denetim günlüklerinin makine dışına gönderildiği, ayrı bir kubelet sertifika yetkilisinin bulunduğu ve tabi olmadığınız bir uyumluluk rejimi için kabul eklentilerinin kullanıldığı çok düğümlü, çok kiracılı kümeler için yazılmıştır. k3s, kontrol düzlemini tek bir yapılandırma dosyasıyla tek bir süreç olarak çalıştırdığından, bir kube-scheduler manifest dosyasının izinlerini kontrol eden kriterler geçemez; çünkü böyle bir dosya mevcut değildir.
Başarısızlıkları şu sırayla okuyun ve değer üretmediğini düşündüğünüz noktada durun: /etc/rancher ve /var/lib/rancher altındaki dosya modları ve sahiplik bilgileri, anonim veya kimlik doğrulaması gerektirmeyen erişimden bahseden her şey, 0.0.0.0 adresine bağlı bir bileşeni raporlayan her şey ve hiçbir neden yokken UID 0 ile çalışan tüm kapsayıcılar. Geri kalanı ikinci bir düğüm veya erişimi olan ikinci bir kişi gelene kadar bekleyebilir. Göz ardı ettiğiniz yüz satırlık bir rapor, üzerinde işlem yaptığınız beş satırlık bir rapordan daha değersizdir.
Tek düğümlü k3s için sıkılaştırma sırası
- ufw varsayılan gelen trafik politikasını reddetme (deny) olarak ayarlayın, SSH'a izin verin, kendi adresinizden 6443 numaralı porta erişime izin verin ve pod ile servis ağlarına izin verin.
- kubeconfig dosyasını 600 modunda kullanıcınıza kopyalayın ve asla
--write-kubeconfig-mode 644ayarını yapmayın. - 10250 numaralı porttaki kubelet'in anonim istekleri reddettiğini doğrulayın ve bu portu internete kapalı tutun.
- Kullanmadığınız paketlenmiş bileşenleri devre dışı bırakın, ardından k3s'i yeniden başlatın.
- Namespace'lerinizi Pod Security admission için
enforce=baselinevewarn=restrictedile etiketleyin. - Varsayılan ServiceAccount token otomatik bağlama (automounting) özelliğini kapatın.
- Bir LimitRange ve ResourceQuota ekleyin, k3s için CPU ve bellek ayırın.
trivy fs --scanners misconfigdeğerini CI sürecine ekleyin ve aylık olarak CIS taraması çalıştırın.
Altındaki ana makine, diğer tüm sunucularla aynı özeni gerektirir ve k3s burada ek bir katman oluşturur. apt yerine bir betik ile kurulduğu için apt upgrade ona hiçbir zaman müdahale etmez. İşletim sistemini kendi zamanlamasına göre Ubuntu üzerinde unattended upgrades ile güncel tutun ve k3s'i istediğiniz kanal veya sürümle yükleyicisini yeniden çalıştırarak bilinçli bir şekilde yükseltin. Tek bir makinede iki farklı güncelleme yolu unutulmaya meyillidir, bu nedenle hangi bileşenin hangi yöntemi izlediğini not edin.
FAQ
k3s API sunucusunu 6443 numaralı port üzerinden internete açmak güvenli midir?
API sunucusu bir istemci sertifikası veya token gerektirdiği ve diğer tüm istekleri forbidden: User "system:anonymous" ile reddettiği için bu tek başına açık bir kapı değildir. Ancak iki risk devam eder. Anonim çağrılar, bir tarayıcıya tam olarak hangi Kubernetes sürümünün hedefleneceğini söyleyen /version verisini okuyabilir. Ayrıca, port açık olduğu sürece gelecekte ortaya çıkabilecek her API sunucusu zafiyeti uzaktan erişilebilir hale gelir. Tek düğümlü bir kümede, kendi kubectl aracınız dışında 6443 numaralı porta ihtiyaç duyan bir dış kaynak yoktur; bu nedenle erişime yalnızca kendi adresinizden sudo ufw allow from YOUR_IP to any port 6443 proto tcp ile izin verin ve geri kalanını varsayılan reddetme politikasına bırakın.
ufw kuralım neden bir NodePort servisini engellemiyor?
Çünkü paket, kuralınızın bulunduğu zincire asla ulaşmaz. kube-proxy, nat tablosunun PREROUTING zincirine DNAT kuralları ekler; bu zincir ilk sırada çalışır ve hedefi bir pod adresine yeniden yazar. Paket yerel olarak teslim edilmek yerine iletildiği için FORWARD zincirinden geçer ve ufw kurallarının bulunduğu INPUT zincirini atlar. Bunu sudo iptables -S PREROUTING -t nat | head ile doğrulayabilirsiniz. NodePort trafiğini servis sağlayıcınızın ağ güvenlik duvarında filtreleyin veya type: NodePort kullanımından kaçınarak servislere kubectl port-forward üzerinden erişin.
k3s'i --write-kubeconfig-mode 644 ile çalıştırmalı mıyım?
Hayır. k3s dokümantasyonu bunun ne yaptığını açıkça belirtir: "Modu 644 olarak değiştirmek, dosyanın ana makinedeki diğer yetkisiz kullanıcılar tarafından okunmasına izin verir." Bu dosya system:masters için bir istemci sertifikası barındırır; bu nedenle 644 modu, yerel her hesabı bir küme yöneticisi yapar. Bunun yerine dosyayı sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config ile tek bir kullanıcıya kopyalayın ve orijinalini 600 root:root konumunda bırakın.
k3s için hangi kube-bench testini kullanmalıyım?
k3s-cis-1.7 kullanın. kube-bench dokümantasyonu şunu belirtir: "kube-bench, Rancher K3S platformu için testler içerir. Bunu çalıştırmak için kube-bench komutunu kullanırken --benchmark k3s-cis-1.7 parametresini belirtmeniz gerekir." Otomatik algılama bir kubeadm düzenini varsaydığı, k3s ise dosyalarını /etc/rancher ve /var/lib/rancher altında tuttuğu için bu parametreyi açıkça belirtin. Tek bir düğüm için geçerli olmayan başarısızlıklar bekleyin ve öncelikle dosya izinleri ile anonim erişim konularına odaklanın.
Pod Security admission, k3s ile gelen bileşenleri bozar mı?
Eğer kube-system üzerinde zorunlu tutarsanız bozar. k3s'in type: LoadBalancer servisleri için oluşturduğu ServiceLB pod'ları ana makinedeki portları talep eder ve baseline ana makine portlarını yasakladığı için bu pod'lar yeniden oluşturulduklarında reddedilirler. Kabul yapılandırma dosyasında kube-system muaf tutun veya Pod Security'yi yalnızca kendi oluşturduğunuz namespace'lere etiket olarak uygulayın. enforce=baseline ve warn=restricted ile başlayın; böylece özelliği etkinleştirmeden önce restricted nelerin bozulacağını görebilirsiniz.