บันทึก. แปล: ผู้เขียนบทความ Reuven Harrison มีประสบการณ์มากกว่า 20 ปีในการพัฒนาซอฟต์แวร์ และปัจจุบันเป็น CTO และผู้ร่วมก่อตั้ง Tufin ซึ่งเป็นบริษัทที่สร้างโซลูชันการจัดการนโยบายความปลอดภัย แม้ว่าเขาจะมองว่านโยบายเครือข่าย Kubernetes เป็นเครื่องมือที่ทรงพลังพอสมควรในการแบ่งส่วนเครือข่ายในคลัสเตอร์ แต่เขาก็เชื่อว่านโยบายดังกล่าวในทางปฏิบัตินั้นไม่ใช่เรื่องง่ายนัก เนื้อหานี้ (ค่อนข้างมาก) มีวัตถุประสงค์เพื่อปรับปรุงการรับรู้ของผู้เชี่ยวชาญเกี่ยวกับปัญหานี้ และช่วยพวกเขาสร้างการกำหนดค่าที่จำเป็น
ในปัจจุบัน บริษัทหลายแห่งหันมาเลือก Kubernetes เพื่อใช้งานแอปพลิเคชันของตนมากขึ้น ความสนใจในซอฟต์แวร์นี้สูงมากจนบางคนเรียก Kubernetes ว่า "ระบบปฏิบัติการใหม่สำหรับศูนย์ข้อมูล" ทีละน้อย Kubernetes (หรือ k8s) เริ่มถูกมองว่าเป็นส่วนสำคัญของธุรกิจ ซึ่งจำเป็นต้องมีการจัดกระบวนการทางธุรกิจที่สมบูรณ์ รวมถึงความปลอดภัยของเครือข่าย
สำหรับผู้เชี่ยวชาญด้านความปลอดภัยที่สับสนกับการทำงานร่วมกับ Kubernetes การเปิดเผยที่แท้จริงอาจเป็นนโยบายเริ่มต้นของแพลตฟอร์ม: อนุญาตทุกอย่าง
คู่มือนี้จะช่วยให้คุณเข้าใจโครงสร้างภายในของนโยบายเครือข่าย ทำความเข้าใจว่ากฎเหล่านี้แตกต่างจากกฎของไฟร์วอลล์ทั่วไปอย่างไร นอกจากนี้ยังจะครอบคลุมข้อผิดพลาดบางประการและให้คำแนะนำเพื่อช่วยรักษาความปลอดภัยแอปพลิเคชันบน Kubernetes
นโยบายเครือข่าย Kubernetes
กลไกนโยบายเครือข่าย Kubernetes ช่วยให้คุณจัดการการโต้ตอบของแอปพลิเคชันที่ใช้งานบนแพลตฟอร์มที่เลเยอร์เครือข่าย (ตัวที่สามในรุ่น OSI) นโยบายเครือข่ายขาดคุณสมบัติขั้นสูงบางประการของไฟร์วอลล์สมัยใหม่ เช่น การบังคับใช้ OSI Layer 7 และการตรวจจับภัยคุกคาม แต่มีความปลอดภัยเครือข่ายขั้นพื้นฐานซึ่งเป็นจุดเริ่มต้นที่ดี
นโยบายเครือข่ายควบคุมการสื่อสารระหว่างพ็อด
ปริมาณงานใน Kubernetes จะกระจายไปตามพ็อด ซึ่งประกอบด้วยคอนเทนเนอร์อย่างน้อย XNUMX คอนเทนเนอร์ที่ใช้งานร่วมกัน Kubernetes กำหนดที่อยู่ IP ให้แต่ละพ็อดที่สามารถเข้าถึงได้จากพ็อดอื่น นโยบายเครือข่าย Kubernetes กำหนดสิทธิ์การเข้าถึงสำหรับกลุ่มพ็อดในลักษณะเดียวกับที่ใช้กลุ่มความปลอดภัยในระบบคลาวด์เพื่อควบคุมการเข้าถึงอินสแตนซ์เครื่องเสมือน
การกำหนดนโยบายเครือข่าย
เช่นเดียวกับทรัพยากร Kubernetes อื่นๆ นโยบายเครือข่ายจะระบุไว้ใน YAML ในตัวอย่างด้านล่าง แอปพลิเคชัน balance
การเข้าถึง postgres
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- podSelector:
matchLabels:
app: balance
policyTypes:
- Ingress
(บันทึก. แปล: ภาพหน้าจอนี้เช่นเดียวกับภาพที่คล้ายกันในเวลาต่อมาถูกสร้างขึ้นโดยไม่ใช้เครื่องมือ Kubernetes ดั้งเดิม แต่ใช้เครื่องมือ Tufin Orca ซึ่งพัฒนาโดย บริษัท ของผู้เขียนบทความต้นฉบับและมีการกล่าวถึงในตอนท้ายของเนื้อหา)
หากต้องการกำหนดนโยบายเครือข่ายของคุณเอง คุณจะต้องมีความรู้พื้นฐานเกี่ยวกับ YAML ภาษานี้มีพื้นฐานมาจากการเยื้อง (ระบุด้วยช่องว่างมากกว่าแท็บ) องค์ประกอบที่เยื้องเป็นขององค์ประกอบเยื้องที่ใกล้ที่สุดที่อยู่ด้านบน องค์ประกอบรายการใหม่เริ่มต้นด้วยยัติภังค์ องค์ประกอบอื่นๆ ทั้งหมดจะมีแบบฟอร์ม คีย์-ค่า.
ต้องอธิบายนโยบายใน YAML แล้วให้ใช้
kubectl create -f policy.yaml
ข้อกำหนดนโยบายเครือข่าย
ข้อกำหนดนโยบายเครือข่าย Kubernetes ประกอบด้วยองค์ประกอบสี่ประการ:
-
podSelector
: กำหนดพ็อดที่ได้รับผลกระทบจากนโยบายนี้ (เป้าหมาย) - จำเป็น -
policyTypes
: ระบุประเภทของนโยบายที่รวมอยู่ในนี้: ทางเข้าและ/หรือทางออก - เป็นทางเลือก แต่ฉันแนะนำให้ระบุอย่างชัดเจนในทุกกรณี -
ingress
: กำหนดว่าได้รับอนุญาต ขาเข้า การรับส่งข้อมูลไปยังพ็อดเป้าหมาย - ทางเลือก; -
egress
: กำหนดว่าได้รับอนุญาต ขาออก การรับส่งข้อมูลจากพ็อดเป้าหมายเป็นทางเลือก
ตัวอย่างที่นำมาจากเว็บไซต์ Kubernetes (ฉันแทนที่ role
บน app
) แสดงให้เห็นว่าองค์ประกอบทั้งสี่ถูกนำมาใช้อย่างไร:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector: # <<<
matchLabels:
app: db
policyTypes: # <<<
- Ingress
- Egress
ingress: # <<<
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress: # <<<
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
โปรดทราบว่าไม่จำเป็นต้องรวมองค์ประกอบทั้งสี่ไว้ด้วย เป็นเพียงข้อบังคับเท่านั้น podSelector
สามารถใช้พารามิเตอร์อื่นๆ ได้ตามต้องการ
หากคุณละเว้น policyTypes
นโยบายจะตีความดังนี้:
- โดยค่าเริ่มต้น จะถือว่ากำหนดด้านทางเข้า หากนโยบายไม่ได้ระบุสิ่งนี้อย่างชัดเจน ระบบจะถือว่าการรับส่งข้อมูลทั้งหมดเป็นสิ่งต้องห้าม
- ลักษณะการทำงานในด้านทางออกจะถูกกำหนดโดยการมีหรือไม่มีพารามิเตอร์ทางออกที่เกี่ยวข้อง
เพื่อหลีกเลี่ยงข้อผิดพลาดฉันแนะนำ ทำให้ชัดเจนอยู่เสมอ policyTypes
.
ตามตรรกะข้างต้นหากพารามิเตอร์ ingress
และ / หรือ egress
หากไม่ระบุ นโยบายจะปฏิเสธการรับส่งข้อมูลทั้งหมด (ดู "กฎการแยก" ด้านล่าง)
นโยบายเริ่มต้นคืออนุญาต
หากไม่มีการกำหนดนโยบาย Kubernetes จะอนุญาตการรับส่งข้อมูลทั้งหมดตามค่าเริ่มต้น พ็อดทั้งหมดสามารถแลกเปลี่ยนข้อมูลระหว่างกันได้อย่างอิสระ นี้อาจดูเหมือนขัดกับสัญชาตญาณจากมุมมองด้านความปลอดภัย แต่โปรดจำไว้ว่า Kubernetes เดิมได้รับการออกแบบโดยนักพัฒนาเพื่อให้แอปพลิเคชันสามารถทำงานร่วมกันได้ มีการเพิ่มนโยบายเครือข่ายในภายหลัง
เนมสเปซ
เนมสเปซเป็นกลไกการทำงานร่วมกันของ Kubernetes ได้รับการออกแบบมาเพื่อแยกสภาพแวดล้อมลอจิคัลออกจากกัน ในขณะที่การสื่อสารระหว่างช่องว่างได้รับอนุญาตตามค่าเริ่มต้น
เช่นเดียวกับส่วนประกอบ Kubernetes ส่วนใหญ่ นโยบายเครือข่ายอยู่ในเนมสเปซเฉพาะ ในบล็อก metadata
คุณสามารถระบุพื้นที่ของนโยบายได้:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: my-namespace # <<<
spec:
...
หากเนมสเปซไม่ได้ระบุไว้อย่างชัดเจนในข้อมูลเมตา ระบบจะใช้เนมสเปซที่ระบุใน kubectl (โดยค่าเริ่มต้น namespace=default
):
kubectl apply -n my-namespace -f namespace.yaml
ฉันแนะนำ ระบุเนมสเปซอย่างชัดเจนเว้นแต่ว่าคุณกำลังเขียนนโยบายที่กำหนดเป้าหมายเนมสเปซหลายรายการพร้อมกัน
ตัวหลัก ธาตุ podSelector
ในนโยบายจะเลือกพ็อดจากเนมสเปซที่เป็นของนโยบาย (ถูกปฏิเสธการเข้าถึงพ็อดจากเนมสเปซอื่น)
ในทำนองเดียวกัน podSelectors ในบล็อกทางเข้าและออก สามารถเลือกได้เฉพาะพ็อดจากเนมสเปซของตนเอง เว้นแต่คุณจะรวมเข้าด้วยกัน namespaceSelector
(ซึ่งจะกล่าวถึงในส่วน “กรองตามเนมสเปซและพ็อด”)
กฎการตั้งชื่อนโยบาย
ชื่อนโยบายจะไม่ซ้ำกันภายในเนมสเปซเดียวกัน ไม่สามารถมีนโยบายสองนโยบายที่มีชื่อเดียวกันในพื้นที่เดียวกันได้ แต่สามารถมีนโยบายที่มีชื่อเดียวกันในพื้นที่ที่แตกต่างกันได้ สิ่งนี้มีประโยชน์เมื่อคุณต้องการใช้นโยบายเดียวกันซ้ำในหลายพื้นที่
ฉันชอบวิธีการตั้งชื่อวิธีใดวิธีหนึ่งเป็นพิเศษ ประกอบด้วยการรวมชื่อเนมสเปซเข้ากับพ็อดเป้าหมาย ตัวอย่างเช่น:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres # <<<
namespace: default
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- podSelector:
matchLabels:
app: admin
policyTypes:
- Ingress
ป้าย
คุณแนบป้ายกำกับที่กำหนดเองกับออบเจ็กต์ Kubernetes ได้ เช่น พ็อดและเนมสเปซ ป้ายกำกับ (ฉลาก - แท็ก) เทียบเท่ากับแท็กในระบบคลาวด์ นโยบายเครือข่าย Kubernetes ใช้ป้ายกำกับเพื่อเลือก ฝักที่พวกเขาใช้:
podSelector:
matchLabels:
role: db
… หรือ เนมสเปซที่พวกเขาสมัคร ตัวอย่างนี้เลือกพ็อดทั้งหมดในเนมสเปซที่มีป้ายกำกับที่เกี่ยวข้อง:
namespaceSelector:
matchLabels:
project: myproject
ข้อควรระวังประการหนึ่ง: เมื่อใช้ namespaceSelector
ตรวจสอบให้แน่ใจว่าเนมสเปซที่คุณเลือกมีป้ายกำกับที่ถูกต้อง. โปรดทราบว่าเนมสเปซในตัวเช่น default
и kube-system
โดยค่าเริ่มต้นจะไม่มีป้ายกำกับ
คุณสามารถเพิ่มป้ายกำกับลงในช่องว่างได้ดังนี้:
kubectl label namespace default namespace=default
ในเวลาเดียวกัน เนมสเปซในส่วน metadata
ควรอ้างอิงถึงชื่อพื้นที่จริง ไม่ใช่ป้ายกำกับ:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default # <<<
spec:
...
ต้นทางและปลายทาง
นโยบายไฟร์วอลล์ประกอบด้วยกฎที่มีแหล่งที่มาและปลายทาง นโยบายเครือข่าย Kubernetes ได้รับการกำหนดไว้สำหรับเป้าหมาย ซึ่งเป็นชุดของพ็อดที่ใช้ จากนั้นจึงตั้งกฎสำหรับการรับส่งข้อมูลขาเข้าและ/หรือขาออก ในตัวอย่างของเรา เป้าหมายของนโยบายจะเป็นพ็อดทั้งหมดในเนมสเปซ default
มีป้ายพร้อมกุญแจ app
และมีความหมาย db
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
app: db # <<<
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
ส่วนย่อย ingress
ในนโยบายนี้ เปิดการรับส่งข้อมูลขาเข้าไปยังพ็อดเป้าหมาย กล่าวอีกนัยหนึ่ง ทางเข้าคือต้นทางและเป้าหมายคือปลายทางที่สอดคล้องกัน ในทำนองเดียวกัน ทางออกคือจุดหมายปลายทาง และเป้าหมายคือต้นทาง
ซึ่งเทียบเท่ากับกฎไฟร์วอลล์สองกฎ: Ingress → Target; เป้าหมาย → ทางออก
ทางออกและ DNS (สำคัญ!)
ด้วยการจำกัดการรับส่งข้อมูลขาออก ให้ความสนใจเป็นพิเศษกับ DNS - Kubernetes ใช้บริการนี้เพื่อแมปบริการกับที่อยู่ IP ตัวอย่างเช่น นโยบายต่อไปนี้จะไม่ทำงานเนื่องจากคุณไม่อนุญาตแอปพลิเคชัน balance
เข้าถึง DNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
policyTypes:
- Egress
คุณสามารถแก้ไขได้โดยการเปิดการเข้าถึงบริการ DNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
- to: # <<<
ports: # <<<
- protocol: UDP # <<<
port: 53 # <<<
policyTypes:
- Egress
องค์ประกอบสุดท้าย to
ว่างเปล่าจึงเลือกทางอ้อม พ็อดทั้งหมดในเนมสเปซทั้งหมดอนุญาต balance
ส่งการสืบค้น DNS ไปยังบริการ Kubernetes ที่เหมาะสม (โดยปกติจะทำงานในพื้นที่ kube-system
).
อย่างไรก็ตามวิธีนี้ใช้ได้ผล อนุญาตและไม่ปลอดภัยมากเกินไปเนื่องจากช่วยให้สามารถส่งการสืบค้น DNS ไปนอกคลัสเตอร์ได้
คุณสามารถปรับปรุงได้ในสามขั้นตอนติดต่อกัน
1. อนุญาตการสืบค้น DNS เท่านั้น ภายใน คลัสเตอร์โดยการเพิ่ม namespaceSelector
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
- to:
- namespaceSelector: {} # <<<
ports:
- protocol: UDP
port: 53
policyTypes:
- Egress
2. อนุญาตการสืบค้น DNS ภายในเนมสเปซเท่านั้น kube-system
.
ในการดำเนินการนี้ คุณจะต้องเพิ่มป้ายกำกับในเนมสเปซ kube-system
: kubectl label namespace kube-system namespace=kube-system
- และจดบันทึกไว้ในนโยบายการใช้ namespaceSelector
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
egress:
- to:
- podSelector:
matchLabels:
app: postgres
- to:
- namespaceSelector: # <<<
matchLabels: # <<<
namespace: kube-system # <<<
ports:
- protocol: UDP
port: 53
policyTypes:
- Egress
3. คนที่หวาดระแวงสามารถไปไกลกว่านี้และจำกัดการสืบค้น DNS ไปยังบริการ DNS ที่เฉพาะเจาะจง kube-system
. ส่วน “กรองตามเนมสเปซและพ็อด” จะบอกวิธีดำเนินการดังกล่าว
อีกทางเลือกหนึ่งคือแก้ไข DNS ในระดับเนมสเปซ ในกรณีนี้ ไม่จำเป็นต้องเปิดสำหรับแต่ละบริการ:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.dns
namespace: default
spec:
podSelector: {} # <<<
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: UDP
port: 53
policyTypes:
- Egress
ว่างเปล่า podSelector
เลือกพ็อดทั้งหมดในเนมสเปซ
นัดแรกและลำดับกฎ
ในไฟร์วอลล์ทั่วไป การดำเนินการ (อนุญาตหรือปฏิเสธ) บนแพ็กเก็ตจะถูกกำหนดตามกฎข้อแรกที่ตอบสนอง ใน Kubernetes ลำดับของนโยบายไม่สำคัญ
ตามค่าเริ่มต้น เมื่อไม่มีการตั้งค่านโยบาย ระบบจะอนุญาตให้มีการสื่อสารระหว่างพ็อดและแลกเปลี่ยนข้อมูลได้อย่างอิสระ เมื่อคุณเริ่มกำหนดนโยบาย แต่ละพ็อดที่ได้รับผลกระทบจากอย่างน้อยหนึ่งรายการจะถูกแยกออกตามการแยกกัน (ตรรกะหรือ) ของนโยบายทั้งหมดที่เลือก พ็อดที่ไม่ได้รับผลกระทบจากนโยบายใดๆ ยังคงเปิดอยู่
คุณสามารถเปลี่ยนลักษณะการทำงานนี้ได้โดยใช้กฎการปอก
กฎการปอก (“ปฏิเสธ”)
โดยทั่วไปนโยบายไฟร์วอลล์จะปฏิเสธการรับส่งข้อมูลที่ไม่ได้รับอนุญาตอย่างชัดเจน
ไม่มีการปฏิเสธการดำเนินการใน Kubernetesอย่างไรก็ตาม คุณสามารถบรรลุผลที่คล้ายกันได้ด้วยนโยบายปกติ (อนุญาต) โดยการเลือกกลุ่มพ็อดต้นทางที่ว่างเปล่า (ทางเข้า):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: default
spec:
podSelector: {}
policyTypes:
- Ingress
นโยบายนี้จะเลือกพ็อดทั้งหมดในเนมสเปซและปล่อยให้ทางเข้าไม่ได้กำหนดไว้ ซึ่งจะปฏิเสธการรับส่งข้อมูลขาเข้าทั้งหมด
ในทำนองเดียวกัน คุณสามารถจำกัดการรับส่งข้อมูลขาออกทั้งหมดจากเนมสเปซได้:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
โปรดทราบว่า นโยบายเพิ่มเติมใดๆ ที่อนุญาตให้รับส่งข้อมูลไปยังพ็อดในเนมสเปซจะมีความสำคัญเหนือกว่ากฎนี้ (คล้ายกับการเพิ่มกฎการอนุญาตก่อนกฎการปฏิเสธในการกำหนดค่าไฟร์วอลล์)
อนุญาตทุกอย่าง (Any-Any-Any-Allow)
หากต้องการสร้างนโยบายอนุญาตทั้งหมด คุณต้องเสริมนโยบายปฏิเสธด้านบนด้วยองค์ประกอบว่าง ingress
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
namespace: default
spec:
podSelector: {}
ingress: # <<<
- {} # <<<
policyTypes:
- Ingress
โดยจะอนุญาตให้เข้าถึงได้จาก พ็อดทั้งหมดในเนมสเปซทั้งหมด (และ IP ทั้งหมด) ไปยังพ็อดใดๆ ในเนมสเปซ default
. ลักษณะการทำงานนี้ถูกเปิดใช้งานตามค่าเริ่มต้น ดังนั้นจึงไม่จำเป็นต้องกำหนดเพิ่มเติม อย่างไรก็ตาม บางครั้งคุณอาจต้องปิดการใช้งานสิทธิ์บางอย่างชั่วคราวเพื่อวินิจฉัยปัญหา
กฎสามารถจำกัดให้แคบลงเพื่อให้เข้าถึงได้เท่านั้น ชุดพ็อดเฉพาะ (app:balance
) ในเนมสเปซ default
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-to-balance
namespace: default
spec:
podSelector:
matchLabels:
app: balance
ingress:
- {}
policyTypes:
- Ingress
นโยบายต่อไปนี้อนุญาตการรับส่งข้อมูลขาเข้าและขาออกทั้งหมด รวมถึงการเข้าถึง IP ใดๆ ภายนอกคลัสเตอร์:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
ingress:
- {}
egress:
- {}
policyTypes:
- Ingress
- Egress
การรวมหลายนโยบายเข้าด้วยกัน
นโยบายถูกรวมเข้าด้วยกันโดยใช้ตรรกะหรือในสามระดับ การอนุญาตของแต่ละพ็อดได้รับการตั้งค่าตามการแยกนโยบายทั้งหมดที่ส่งผลต่อ:
1. ในทุ่งนา from
и to
สามารถกำหนดองค์ประกอบได้สามประเภท (ซึ่งทั้งหมดรวมกันโดยใช้ OR):
-
namespaceSelector
— เลือกเนมสเปซทั้งหมด -
podSelector
— เลือกพ็อด; -
ipBlock
— เลือกซับเน็ต
ยิ่งไปกว่านั้น จำนวนองค์ประกอบ (แม้จะเหมือนกันก็ตาม) ในส่วนย่อย from
/to
ไม่ จำกัด. ทั้งหมดนี้จะรวมกันโดยใช้ตรรกะ OR
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
- podSelector:
matchLabels:
app: admin
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
2. ภายในส่วนนโยบาย ingress
สามารถมีได้หลายองค์ประกอบ from
(รวมด้วยตรรกะ OR) ในทำนองเดียวกันมาตรา egress
อาจมีหลายองค์ประกอบ to
(รวมเข้าด้วยกันโดยการแยกทาง):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
- from:
- podSelector:
matchLabels:
app: admin
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
3. นโยบายที่แตกต่างกันจะรวมเข้ากับตรรกะหรือด้วย
แต่เมื่อรวมเข้าด้วยกันก็มีข้อจำกัดประการหนึ่งคือ policyTypes
(Ingress
หรือ Egress
). นโยบายที่กำหนดทางเข้า (หรือทางออก) จะเขียนทับซึ่งกันและกัน
ความสัมพันธ์ระหว่างเนมสเปซ
ตามค่าเริ่มต้น อนุญาตให้ใช้ข้อมูลร่วมกันระหว่างเนมสเปซได้ ซึ่งสามารถเปลี่ยนแปลงได้โดยใช้นโยบายการปฏิเสธที่จะจำกัดการรับส่งข้อมูลขาออกและ/หรือขาเข้าในเนมสเปซ (ดู "กฎการแยกส่วน" ด้านบน)
เมื่อคุณบล็อกการเข้าถึงเนมสเปซแล้ว (ดู "กฎการแยกส่วน" ด้านบน) คุณสามารถสร้างข้อยกเว้นสำหรับนโยบายการปฏิเสธได้โดยการอนุญาตการเชื่อมต่อจากเนมสเปซเฉพาะโดยใช้ namespaceSelector
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database.postgres
namespace: database
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- namespaceSelector: # <<<
matchLabels:
namespace: default
policyTypes:
- Ingress
เป็นผลให้พ็อดทั้งหมดในเนมสเปซ default
จะสามารถเข้าถึงพ็อดได้ postgres
ในเนมสเปซ database
. แต่ถ้าคุณต้องการเปิดการเข้าถึง postgres
เฉพาะพ็อดเฉพาะในเนมสเปซ default
?
กรองตามเนมสเปซและพ็อด
Kubernetes เวอร์ชัน 1.11 ขึ้นไปช่วยให้คุณรวมโอเปอเรเตอร์ได้ namespaceSelector
и podSelector
ใช้ตรรกะ AND ดูเหมือนว่านี้:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database.postgres
namespace: database
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- namespaceSelector:
matchLabels:
namespace: default
podSelector: # <<<
matchLabels:
app: admin
policyTypes:
- Ingress
เหตุใดจึงตีความว่าเป็น AND แทนที่จะเป็นแบบปกติหรือ
โปรดทราบว่า podSelector
ไม่ได้ขึ้นต้นด้วยยัติภังค์ ใน YAML นี่หมายความว่า podSelector
และยืนอยู่ตรงหน้าเขา namespaceSelector
อ้างถึงองค์ประกอบรายการเดียวกัน ดังนั้นจึงรวมเข้ากับตรรกะ AND
เพิ่มยัติภังค์ก่อน podSelector
จะส่งผลให้เกิดองค์ประกอบรายการใหม่ซึ่งจะรวมกับองค์ประกอบก่อนหน้า namespaceSelector
โดยใช้ตรรกะหรือ
เพื่อเลือกพ็อดที่มีป้ายกำกับเฉพาะ ในเนมสเปซทั้งหมด, กรอกช่องว่าง namespaceSelector
:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database.postgres
namespace: database
spec:
podSelector:
matchLabels:
app: postgres
ingress:
- from:
- namespaceSelector: {}
podSelector:
matchLabels:
app: admin
policyTypes:
- Ingress
ค่ายเพลงหลายค่ายร่วมมือกับ I
กฎสำหรับไฟร์วอลล์ที่มีหลายออบเจ็กต์ (โฮสต์ เครือข่าย กลุ่ม) จะรวมกันโดยใช้ตรรกะ OR กฎต่อไปนี้จะใช้ได้หากแหล่งแพ็กเก็ตตรงกัน Host_1
หรือ Host_2
:
| Source | Destination | Service | Action |
| ----------------------------------------|
| Host_1 | Subnet_A | HTTPS | Allow |
| Host_2 | | | |
| ----------------------------------------|
ในทางตรงกันข้าม ใน Kubernetes จะมีป้ายกำกับต่างๆ อยู่ในนั้น podSelector
หรือ namespaceSelector
รวมกับตรรกะ AND ตัวอย่างเช่น กฎต่อไปนี้จะเลือกพ็อดที่มีป้ายกำกับทั้งสอง role=db
И version=v2
:
podSelector:
matchLabels:
role: db
version: v2
ตรรกะเดียวกันนี้ใช้กับตัวดำเนินการทุกประเภท: ตัวเลือกเป้าหมายนโยบาย ตัวเลือกพ็อด และตัวเลือกเนมสเปซ
ซับเน็ตและที่อยู่ IP (IPBlocks)
ไฟร์วอลล์ใช้ VLAN, ที่อยู่ IP และซับเน็ตเพื่อแบ่งกลุ่มเครือข่าย
ใน Kubernetes ที่อยู่ IP จะถูกกำหนดให้กับพ็อดโดยอัตโนมัติและสามารถเปลี่ยนแปลงได้บ่อยครั้ง ดังนั้นจึงใช้ป้ายกำกับเพื่อเลือกพ็อดและเนมสเปซในนโยบายเครือข่าย
ซับเน็ต (ipBlocks
) ถูกใช้เมื่อจัดการการเชื่อมต่อขาเข้า (ทางเข้า) หรือขาออก (ทางออก) ภายนอก (เหนือ-ใต้) เช่น นโยบายนี้เปิดแก่พ็อดทั้งหมดจากเนมสเปซ default
การเข้าถึงบริการ Google DNS:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-dns
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 8.8.8.8/32
ports:
- protocol: UDP
port: 53
ตัวเลือกพ็อดว่างในตัวอย่างนี้หมายถึง “เลือกพ็อดทั้งหมดในเนมสเปซ”
นโยบายนี้อนุญาตให้เข้าถึงเฉพาะ 8.8.8.8; ห้ามเข้าถึง IP อื่นใด โดยพื้นฐานแล้ว คุณได้บล็อกการเข้าถึงบริการ Kubernetes DNS ภายใน หากคุณยังคงต้องการเปิด ให้ระบุสิ่งนี้อย่างชัดเจน
มักจะ ipBlocks
и podSelectors
เป็นแบบเอกสิทธิ์เฉพาะบุคคล เนื่องจากไม่ได้ใช้ที่อยู่ IP ภายในของพ็อด ipBlocks
. โดยแสดง พ็อด IP ภายในคุณจะอนุญาตการเชื่อมต่อไปยัง/จากพ็อดด้วยที่อยู่เหล่านี้จริงๆ ในทางปฏิบัติ คุณจะไม่รู้ว่าควรใช้ที่อยู่ IP ใด ซึ่งเป็นเหตุผลว่าทำไมจึงไม่ควรใช้เพื่อเลือกพ็อด
เพื่อเป็นตัวอย่างในการโต้แย้ง นโยบายต่อไปนี้จะรวม IP ทั้งหมด ดังนั้นจึงอนุญาตให้เข้าถึงพ็อดอื่นๆ ทั้งหมดได้
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-any
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
คุณสามารถเปิดการเข้าถึงได้เฉพาะ IP ภายนอกเท่านั้น ยกเว้นที่อยู่ IP ภายในของพ็อด ตัวอย่างเช่น หากซับเน็ตของพ็อดของคุณคือ 10.16.0.0/14:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-any
namespace: default
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 10.16.0.0/14
พอร์ตและโปรโตคอล
โดยทั่วไปแล้ว พ็อดจะฟังพอร์ตเดียว ซึ่งหมายความว่าคุณไม่สามารถระบุหมายเลขพอร์ตในนโยบายและปล่อยให้ทุกอย่างเป็นค่าเริ่มต้นได้ อย่างไรก็ตาม ขอแนะนำให้กำหนดนโยบายที่เข้มงวดที่สุดเท่าที่จะเป็นไปได้ ดังนั้นในบางกรณี คุณยังคงระบุพอร์ตได้:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
- podSelector:
matchLabels:
app: admin
ports: # <<<
- port: 443 # <<<
protocol: TCP # <<<
- port: 80 # <<<
protocol: TCP # <<<
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
โปรดทราบว่าตัวเลือก ports
ใช้กับองค์ประกอบทั้งหมดในบล็อก to
หรือ from
, ซึ่งประกอบด้วย. หากต้องการระบุพอร์ตที่แตกต่างกันสำหรับชุดองค์ประกอบต่างๆ ให้แยกออก ingress
หรือ egress
ออกเป็นหลายส่วนย่อยด้วย to
หรือ from
และในแต่ละการลงทะเบียนพอร์ตของคุณ:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default.postgres
namespace: default
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: indexer
ports: # <<<
- port: 443 # <<<
protocol: TCP # <<<
- from:
- podSelector:
matchLabels:
app: admin
ports: # <<<
- port: 80 # <<<
protocol: TCP # <<<
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
การทำงานของพอร์ตเริ่มต้น:
- หากคุณละเว้นคำจำกัดความของพอร์ตโดยสิ้นเชิง (
ports
) ซึ่งหมายถึงโปรโตคอลทั้งหมดและพอร์ตทั้งหมด - หากคุณละเว้นคำจำกัดความของโปรโตคอล (
protocol
) นี่หมายถึง TCP; - หากคุณละเว้นข้อกำหนดพอร์ต (
port
) นี่หมายถึงพอร์ตทั้งหมด
แนวปฏิบัติที่ดีที่สุด: อย่าพึ่งพาค่าเริ่มต้น ระบุสิ่งที่คุณต้องการอย่างชัดเจน
โปรดทราบว่าคุณต้องใช้พอร์ตพ็อด ไม่ใช่พอร์ตบริการ (เพิ่มเติมเกี่ยวกับเรื่องนี้ในย่อหน้าถัดไป)
มีการกำหนดนโยบายสำหรับพ็อดหรือบริการหรือไม่
โดยทั่วไปแล้ว พ็อดใน Kubernetes จะเข้าถึงกันผ่านบริการ ซึ่งเป็นตัวจัดสรรภาระงานเสมือนที่เปลี่ยนเส้นทางการรับส่งข้อมูลไปยังพ็อดที่ใช้บริการ คุณอาจคิดว่านโยบายเครือข่ายควบคุมการเข้าถึงบริการ แต่ไม่ใช่กรณีนี้ นโยบายเครือข่าย Kubernetes ทำงานบนพอร์ตพ็อด ไม่ใช่พอร์ตบริการ
ตัวอย่างเช่น หากบริการรับฟังพอร์ต 80 แต่เปลี่ยนเส้นทางการรับส่งข้อมูลไปยังพอร์ต 8080 ของพ็อด คุณต้องระบุ 8080 ให้ตรงกันทุกประการในนโยบายเครือข่าย
กลไกดังกล่าวควรได้รับการพิจารณาว่าไม่เหมาะสม: หากโครงสร้างภายในของบริการ (พอร์ตที่พ็อดฟัง) เปลี่ยนแปลง นโยบายเครือข่ายจะต้องได้รับการอัปเดต
แนวทางสถาปัตยกรรมใหม่โดยใช้ Service Mesh (เช่น ดูเกี่ยวกับ Istio ด้านล่าง - การแปลโดยประมาณ) ช่วยให้คุณสามารถรับมือกับปัญหานี้ได้
จำเป็นต้องลงทะเบียนทั้ง Ingress และ Egress หรือไม่?
คำตอบสั้นๆ คือ ใช่ เพื่อให้พ็อด A สื่อสารกับพ็อด B ได้ จะต้องได้รับอนุญาตให้สร้างการเชื่อมต่อขาออก (สำหรับสิ่งนี้ คุณต้องกำหนดค่านโยบายทางออก) และพ็อด B จะต้องสามารถยอมรับการเชื่อมต่อขาเข้าได้ ( ด้วยเหตุนี้ คุณจำเป็นต้องมีนโยบายทางเข้า) นโยบาย)
อย่างไรก็ตาม ในทางปฏิบัติ คุณสามารถใช้นโยบายเริ่มต้นเพื่ออนุญาตการเชื่อมต่อในทิศทางเดียวหรือทั้งสองทิศทางได้
หากฝักบาง-แหล่ง จะถูกเลือกโดยหนึ่งหรือมากกว่านั้น ออก-นักการเมือง ข้อจำกัดที่กำหนดไว้จะถูกกำหนดโดยการแยกทางของพวกเขา ในกรณีนี้ คุณจะต้องอนุญาตการเชื่อมต่อกับพ็อดอย่างชัดเจน -ถึงผู้รับ. หากไม่ได้เลือกพ็อดตามนโยบายใดๆ ระบบจะอนุญาตการรับส่งข้อมูลขาออก (ทางออก) ของพ็อดตามค่าเริ่มต้น
ในทำนองเดียวกันชะตากรรมของพ็อดก็คือผู้รับเลือกโดยหนึ่งหรือหลายรายการ สิทธิในการเข้า-นักการเมืองจะถูกกำหนดโดยการแยกทางของพวกเขา ในกรณีนี้ คุณต้องอนุญาตให้รับการเข้าชมจากพ็อดต้นทางอย่างชัดเจน หากนโยบายใดๆ ไม่ได้เลือกพ็อด การรับส่งข้อมูลขาเข้าทั้งหมดจะได้รับอนุญาตตามค่าเริ่มต้น
ดูสถานะไร้สถานะหรือไร้สัญชาติด้านล่าง
บันทึก
นโยบายเครือข่าย Kubernetes ไม่สามารถบันทึกการรับส่งข้อมูลได้ ทำให้ยากต่อการพิจารณาว่านโยบายทำงานตามที่ตั้งใจไว้หรือไม่ และทำให้การวิเคราะห์ความปลอดภัยมีความซับซ้อนอย่างมาก
การควบคุมการรับส่งข้อมูลไปยังบริการภายนอก
นโยบายเครือข่าย Kubernetes ไม่อนุญาตให้คุณระบุชื่อโดเมนแบบเต็ม (DNS) ในส่วนขาออก ข้อเท็จจริงนี้ทำให้เกิดความไม่สะดวกอย่างมากเมื่อพยายามจำกัดการรับส่งข้อมูลไปยังปลายทางภายนอกที่ไม่มีที่อยู่ IP คงที่ (เช่น aws.com)
การตรวจสอบนโยบาย
ไฟร์วอลล์จะเตือนคุณหรือแม้กระทั่งปฏิเสธที่จะยอมรับนโยบายที่ไม่ถูกต้อง Kubernetes ยังทำการตรวจสอบบางอย่างด้วย เมื่อตั้งค่านโยบายเครือข่ายผ่าน kubectl Kubernetes อาจประกาศว่าไม่ถูกต้องและปฏิเสธที่จะยอมรับ ในกรณีอื่นๆ Kubernetes จะนำนโยบายไปกรอกรายละเอียดที่ขาดหายไป สามารถดูได้โดยใช้คำสั่ง:
kubernetes get networkpolicy <policy-name> -o yaml
โปรดทราบว่าระบบตรวจสอบความถูกต้องของ Kubernetes นั้นไม่มีข้อผิดพลาดและอาจพลาดข้อผิดพลาดบางประเภทไป
การปฏิบัติ
Kubernetes ไม่ได้ใช้นโยบายเครือข่าย แต่เป็นเพียงเกตเวย์ API ที่มอบหมายภาระในการควบคุมให้กับระบบพื้นฐานที่เรียกว่า Container Networking Interface (CNI) การตั้งค่านโยบายบนคลัสเตอร์ Kubernetes โดยไม่กำหนด CNI ที่เหมาะสมจะเหมือนกับการสร้างนโยบายบนเซิร์ฟเวอร์การจัดการไฟร์วอลล์โดยไม่ต้องติดตั้งบนไฟร์วอลล์ ขึ้นอยู่กับคุณเพื่อให้แน่ใจว่าคุณมี CNI ที่เหมาะสม หรือโฮสต์ในระบบคลาวด์ในกรณีของแพลตฟอร์ม Kubernetes (คุณสามารถดูรายชื่อผู้ให้บริการได้
โปรดทราบว่า Kubernetes จะไม่เตือนคุณหากคุณตั้งค่านโยบายเครือข่ายโดยไม่มี CNI ตัวช่วยที่เหมาะสม
มีสถานะหรือไร้สัญชาติ?
CNI ของ Kubernetes ทั้งหมดที่ฉันพบนั้นมีสถานะ (เช่น Calico ใช้ Linux conntrack) ซึ่งช่วยให้พ็อดสามารถรับการตอบสนองต่อการเชื่อมต่อ TCP ที่เริ่มต้นโดยไม่ต้องสร้างใหม่ อย่างไรก็ตาม ฉันไม่ทราบถึงมาตรฐาน Kubernetes ที่จะรับประกันความเป็นรัฐ
การจัดการนโยบายความปลอดภัยขั้นสูง
ต่อไปนี้เป็นวิธีปรับปรุงการบังคับใช้นโยบายความปลอดภัยใน Kubernetes:
- รูปแบบสถาปัตยกรรม Service Mesh ใช้คอนเทนเนอร์ไซด์คาร์เพื่อให้การวัดและส่งข้อมูลทางไกลโดยละเอียดและการควบคุมการรับส่งข้อมูลในระดับบริการ เป็นตัวอย่างที่เราสามารถทำได้
อิสติโอ . - ผู้จำหน่าย CNI บางรายได้ขยายเครื่องมือของตนให้นอกเหนือไปจากนโยบายเครือข่าย Kubernetes
-
ทูฟิน ออร์ก้า ให้การมองเห็นและการทำงานอัตโนมัติของนโยบายเครือข่าย Kubernetes
แพ็คเกจ Tufin Orca จัดการนโยบายเครือข่าย Kubernetes (และเป็นที่มาของภาพหน้าจอด้านบน)
ข้อมูลเพิ่มเติม
-
ตัวอย่างนโยบายเครือข่ายที่จัดทำโดย Ahmet Alp Balkan จาก GKE ; -
เอกสารจากเว็บไซต์ Kubernetes อย่างเป็นทางการ ; -
คำแนะนำเกี่ยวกับโมเดลเครือข่าย Kubernetes ; -
สคริปต์สำหรับตรวจสอบนโยบายเครือข่าย .
ข้อสรุป
นโยบายเครือข่าย Kubernetes นำเสนอชุดเครื่องมือที่ดีสำหรับการแบ่งกลุ่มคลัสเตอร์ แต่นโยบายเหล่านี้ไม่เป็นไปตามสัญชาตญาณและยังมีรายละเอียดปลีกย่อยมากมาย เนื่องจากความซับซ้อนนี้ ฉันจึงเชื่อว่านโยบายคลัสเตอร์ที่มีอยู่จำนวนมากมีปัญหา วิธีแก้ไขที่เป็นไปได้สำหรับปัญหานี้ ได้แก่ การกำหนดนโยบายอัตโนมัติหรือการใช้เครื่องมือการแบ่งส่วนอื่นๆ
ฉันหวังว่าคู่มือนี้จะช่วยตอบคำถามบางข้อและแก้ไขปัญหาที่คุณอาจพบได้
ปล.จากผู้แปล
อ่านเพิ่มเติมในบล็อกของเรา:
- "กลับสู่ไมโครเซอร์วิสด้วย Istio":
ตอนที่ 1 (แนะนำคุณสมบัติหลัก) ,ตอนที่ 2 (การกำหนดเส้นทาง, การควบคุมการจราจร) ,ส่วนที่ 3 (ความปลอดภัย) ; - "ภาพประกอบคู่มือการสร้างเครือข่ายใน Kubernetes":
ส่วนที่ 1 และ 2 (โมเดลเครือข่าย, เครือข่ายโอเวอร์เลย์) ,ส่วนที่ 3 (บริการและการประมวลผลการรับส่งข้อมูล) ; - «
Docker และ Kubernetes ในสภาพแวดล้อมที่คำนึงถึงความปลอดภัย "; - «
9 แนวทางปฏิบัติที่ดีที่สุดสำหรับความปลอดภัยของ Kubernetes "; - «
11 วิธีในการ (ไม่) ตกเป็นเหยื่อของการแฮ็ก Kubernetes '
ที่มา: will.com