Layer 4 정책
Layer 4 정책 (Layer 4 Policies)
엔드포인트가 특정 포트와 프로토콜로 패킷을 보내거나 받는 능력을 제한하는 Layer 4 정책을 설명하는 문서예요. 포트 범위, ICMP/ICMPv6 타입, TLS SNI 제한까지 다뤄요.
출처: Layer 4 Policies
본문
ingress/egress 포트 제한 (Limit ingress/egress ports)
Layer 4 정책은 Layer 3 정책에 추가로 또는 독립적으로 지정할 수 있어요. 이는 엔드포인트가 특정 프로토콜로 특정 포트에서 패킷을 보내거나/받는 능력을 제한해요. 엔드포인트에 Layer 4 정책이 지정되지 않으면, ICMP를 포함한 모든 Layer 4 포트와 프로토콜에서 보내고 받는 것이 허용돼요. Layer 4 정책이 하나라도 지정되면, 정책이 달리 허용하는 연결과 관련되지 않은 ICMP는 차단돼요. Layer 4 정책은 서비스 포트 매핑이 적용된 후의 포트에 적용돼요.
Layer 4 정책은 toPorts 필드를 사용해 ingress와 egress 모두에서 지정할 수 있어요. toPorts 필드는 다음과 같이 정의되는 PortProtocol 구조를 받아요.
// PortProtocol specifies an L4 port with an optional transport protocol
type PortProtocol struct {
// Port can be an L4 port number, or a name in the form of "http"
// or "http-8080". EndPort is ignored if Port is a named port.
Port string `json:"port"`
// EndPort can only be an L4 port number. It is ignored when
// Port is a named port.
//
// +optional
EndPort int32 `json:"endPort,omitempty"`
// Protocol is the L4 protocol. If omitted or empty, any protocol
// matches. Accepted values: "TCP", "UDP", ""/"ANY"
//
// Matching on ICMP is not supported.
//
// +optional
Protocol string `json:"protocol,omitempty"`
}
예제 (L4)
다음 규칙은 app=myService 라벨의 모든 엔드포인트를 80 포트에서 TCP로만 패킷을 보낼 수 있도록 제한해요(어떤 Layer 3 목적지든).
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l4-rule"
spec:
endpointSelector:
matchLabels:
app: myService
egress:
- toPorts:
- ports:
- port: "80"
protocol: TCP
예제: 포트 범위 (Example Port Ranges)
다음 규칙은 app=myService 라벨의 모든 엔드포인트를 80~444 포트에서 TCP로만 패킷을 보낼 수 있도록 제한해요(어떤 Layer 3 목적지든).
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l4-port-range-rule"
spec:
endpointSelector:
matchLabels:
app: myService
egress:
- toPorts:
- ports:
- port: "80"
endPort: 444
protocol: TCP
참고: Layer 7 규칙은 DNS 규칙을 제외하고 포트 범위를 지원해요.
라벨 의존 Layer 4 규칙 (Labels-dependent Layer 4 rule)
이 예제는 role=frontend 라벨의 모든 엔드포인트가 role=backend 라벨의 모든 엔드포인트와 통신할 수 있게 하지만, 반드시 TCP 80 포트로 통신해야 한다는 것을 허용해요. 다른 라벨의 엔드포인트는 role=backend 라벨의 엔드포인트와 통신할 수 없고, role=frontend 라벨의 엔드포인트는 80이 아닌 포트로 role=backend와 통신할 수 없어요.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l4-rule"
spec:
endpointSelector:
matchLabels:
role: backend
ingress:
- fromEndpoints:
- matchLabels:
role: frontend
toPorts:
- ports:
- port: "80"
protocol: TCP
CIDR 의존 Layer 4 규칙 (CIDR-dependent Layer 4 Rule)
이 예제는 role=crawler 라벨의 모든 엔드포인트가 CIDR 192.0.2.0/24 안의 모든 원격 목적지와 통신할 수 있게 하지만, 반드시 TCP 80 포트로 통신해야 한다는 것을 허용해요. 이 정책은 role=crawler 라벨이 없는 엔드포인트가 CIDR 192.0.2.0/24의 목적지와 통신하는 것을 허용하지 않아요. 또한 role=crawler 라벨의 엔드포인트는 80이 아닌 포트로 CIDR 192.0.2.0/24의 목적지와 통신할 수 없어요.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "cidr-l4-rule"
spec:
endpointSelector:
matchLabels:
role: crawler
egress:
- toCIDR:
- 192.0.2.0/24
toPorts:
- ports:
- port: "80"
protocol: TCP
ICMP/ICMPv6 타입 제한 (Limit ICMP/ICMPv6 types)
ICMP 정책은 Layer 3 정책에 추가로 또는 독립적으로 지정할 수 있어요. 이는 엔드포인트가 특정 ICMP/ICMPv6 타입(정수 타입과 해당 CamelCase 메시지 문자열 모두 지원)에서 패킷을 보내거나/받는 능력을 제한해요. ICMP 정책이 하나라도 지정되면, 정책이 달리 허용하는 연결과 관련되지 않은 Layer 4 및 ICMP 통신이 차단돼요.
ICMP 정책은 icmps 필드를 사용해 ingress와 egress 모두에서 지정할 수 있어요. icmps 필드는 다음과 같이 정의되는 ICMPField 구조를 받아요.
// ICMPField is a ICMP field.
//
// +deepequal-gen=true
// +deepequal-gen:private-method=true
type ICMPField struct {
// Family is a IP address version.
// Currently, we support `IPv4` and `IPv6`.
// `IPv4` is set as default.
//
// +kubebuilder:default=IPv4
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Enum=IPv4;IPv6
Family string `json:"family,omitempty"`
// Type is a ICMP-type.
// It should be an 8bit code (0-255), or it's CamelCase name (for example, "EchoReply").
// Allowed ICMP types are:
// Ipv4: EchoReply | DestinationUnreachable | Redirect | Echo | EchoRequest |
// RouterAdvertisement | RouterSelection | TimeExceeded | ParameterProblem |
// Timestamp | TimestampReply | Photuris | ExtendedEcho Request | ExtendedEcho Reply
// Ipv6: DestinationUnreachable | PacketTooBig | TimeExceeded | ParameterProblem |
// EchoRequest | EchoReply | MulticastListenerQuery| MulticastListenerReport |
// MulticastListenerDone | RouterSolicitation | RouterAdvertisement | NeighborSolicitation |
// NeighborAdvertisement | RedirectMessage | RouterRenumbering | ICMPNodeInformationQuery |
// ICMPNodeInformationResponse | InverseNeighborDiscoverySolicitation | InverseNeighborDiscoveryAdvertisement |
// HomeAgentAddressDiscoveryRequest | HomeAgentAddressDiscoveryReply | MobilePrefixSolicitation |
// MobilePrefixAdvertisement | DuplicateAddressRequestCodeSuffix | DuplicateAddressConfirmationCodeSuffix |
// ExtendedEchoRequest | ExtendedEchoReply
//
// +deepequal-gen=false
// +kubebuilder:validation:XIntOrString
// +kubebuilder:validation:Pattern="^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]|EchoReply|DestinationUnreachable|Redirect|Echo|RouterAdvertisement|RouterSelection|TimeExceeded|ParameterProblem|Timestamp|TimestampReply|Photuris|ExtendedEchoRequest|ExtendedEcho Reply|PacketTooBig|ParameterProblem|EchoRequest|MulticastListenerQuery|MulticastListenerReport|MulticastListenerDone|RouterSolicitation|RouterAdvertisement|NeighborSolicitation|NeighborAdvertisement|RedirectMessage|RouterRenumbering|ICMPNodeInformationQuery|ICMPNodeInformationResponse|InverseNeighborDiscoverySolicitation|InverseNeighborDiscoveryAdvertisement|HomeAgentAddressDiscoveryRequest|HomeAgentAddressDiscoveryReply|MobilePrefixSolicitation|MobilePrefixAdvertisement|DuplicateAddressRequestCodeSuffix|DuplicateAddressConfirmationCodeSuffix)$"
Type *intstr.IntOrString `json:"type"`
}
예제 (ICMP/ICMPv6)
다음 규칙은 app=myService 라벨의 모든 엔드포인트를 ICMP 타입 8과 ICMPv6 메시지 EchoRequest로만 패킷을 보낼 수 있도록 제한해요(어떤 Layer 3 목적지든).
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "icmp-rule"
spec:
endpointSelector:
matchLabels:
app: myService
egress:
- icmps:
- fields:
- type: 8
family: IPv4
- type: EchoRequest
family: IPv6
TLS SNI(Server Name Indication) 제한 (Limit TLS Server Name Indication (SNI))
여러 웹사이트가 공유 IP 주소를 가진 같은 서버에 호스팅될 때, TLS 프로토콜의 확장인 SNI(Server Name Indication)는 클라이언트가 접근하려는 웹사이트에 대한 올바른 SSL 인증서를 받도록 보장해요. SNI는 HTTP 연결이 수립되는 핸드셰이크 후가 아니라 TLS 핸드셰이크 중에 웹사이트의 호스트 이름이나 도메인 이름을 지정할 수 있게 해 줘요.
Cilium 네트워크 정책은 엔드포인트가 지정된 SNI 목록으로 TLS 핸드셰이크를 수립하는 능력을 제한할 수 있어요. SNI 정책은 항상 egress 수준에서 구성되며, 보통 포트 정책과 함께 설정돼요.
예제 (TLS SNI)
참고: TLS SNI 정책 시행에는 L7 프록시가 활성화되어 있어야 해요.
다음 규칙은 app=myService 라벨의 모든 엔드포인트가 one.one.one.one SNI로만 TLS 연결을 수립할 수 있도록 제한해요. 다른 SNI(예: cilium.io)로의 시도는 거부돼요.
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: "l4-sni-rule"
spec:
endpointSelector:
matchLabels:
app: myService
egress:
- toPorts:
- ports:
- port: "443"
protocol: TCP
serverNames:
- one.one.one.one
다음은 curl로 cilium.io에 연결하려 할 때 나타나는 SSL 오류예요.
$ kubectl exec <my-service-pod> -- curl -v https://cilium.io
* Host cilium.io:443 was resolved.
* IPv6: (none)
* IPv4: 104.198.14.52
* Trying 104.198.14.52:443...
* Connected to cilium.io (104.198.14.52) port 443
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* Recv failure: Connection reset by peer
* OpenSSL SSL_connect: Connection reset by peer in connection to cilium.io:443
* Closing connection
curl: (35) Recv failure: Connection reset by peer
command terminated with exit code 35