From 1dc0d118229e0742554f9de464dd2d96dd33ac3a Mon Sep 17 00:00:00 2001 From: Hongliang Liu Date: Thu, 4 May 2023 21:08:12 +0800 Subject: [PATCH 1/2] Enhance L7 NetworkPolicy to support TLS protocol Signed-off-by: Hongliang Liu --- .../antrea/crds/clusternetworkpolicy.yaml | 12 + build/charts/antrea/crds/networkpolicy.yaml | 12 + build/yamls/antrea-aks.yml | 24 + build/yamls/antrea-crds.yml | 24 + build/yamls/antrea-eks.yml | 24 + build/yamls/antrea-gke.yml | 24 + build/yamls/antrea-ipsec.yml | 24 + build/yamls/antrea.yml | 24 + docs/antrea-l7-network-policy.md | 101 +++- .../antrea-multicluster-leader-global.yml | 44 ++ ...cluster.crd.antrea.io_resourceexports.yaml | 22 + ...cluster.crd.antrea.io_resourceimports.yaml | 22 + .../networkpolicy/l7engine/reconciler.go | 16 + .../networkpolicy/l7engine/reconciler_test.go | 26 + pkg/apis/controlplane/types.go | 8 + pkg/apis/controlplane/v1beta2/generated.pb.go | 564 ++++++++++++------ pkg/apis/controlplane/v1beta2/generated.proto | 9 + pkg/apis/controlplane/v1beta2/types.go | 8 + .../v1beta2/zz_generated.conversion.go | 32 + .../v1beta2/zz_generated.deepcopy.go | 21 + .../controlplane/zz_generated.deepcopy.go | 21 + pkg/apis/crd/v1alpha1/types.go | 8 + .../crd/v1alpha1/zz_generated.deepcopy.go | 21 + pkg/apiserver/openapi/zz_generated.openapi.go | 28 +- pkg/controller/networkpolicy/crd_utils.go | 1 + .../networkpolicy/crd_utils_test.go | 8 + test/e2e/l7networkpolicy_test.go | 74 ++- 27 files changed, 1015 insertions(+), 187 deletions(-) diff --git a/build/charts/antrea/crds/clusternetworkpolicy.yaml b/build/charts/antrea/crds/clusternetworkpolicy.yaml index 459f2ea3c79..3c9308b985f 100644 --- a/build/charts/antrea/crds/clusternetworkpolicy.yaml +++ b/build/charts/antrea/crds/clusternetworkpolicy.yaml @@ -267,6 +267,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -278,6 +279,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -522,6 +528,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -533,6 +540,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/build/charts/antrea/crds/networkpolicy.yaml b/build/charts/antrea/crds/networkpolicy.yaml index 08180b5aa97..424c056ffe3 100644 --- a/build/charts/antrea/crds/networkpolicy.yaml +++ b/build/charts/antrea/crds/networkpolicy.yaml @@ -203,6 +203,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -214,6 +215,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -431,6 +437,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -442,6 +449,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/build/yamls/antrea-aks.yml b/build/yamls/antrea-aks.yml index d2a864182d9..0173886c028 100644 --- a/build/yamls/antrea-aks.yml +++ b/build/yamls/antrea-aks.yml @@ -646,6 +646,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -657,6 +658,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -901,6 +907,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -912,6 +919,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: @@ -1858,6 +1870,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -1869,6 +1882,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -2086,6 +2104,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -2097,6 +2116,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/build/yamls/antrea-crds.yml b/build/yamls/antrea-crds.yml index d6b73970b5e..38dabe85810 100644 --- a/build/yamls/antrea-crds.yml +++ b/build/yamls/antrea-crds.yml @@ -639,6 +639,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -650,6 +651,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -894,6 +900,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -905,6 +912,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: @@ -1839,6 +1851,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -1850,6 +1863,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -2067,6 +2085,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -2078,6 +2097,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/build/yamls/antrea-eks.yml b/build/yamls/antrea-eks.yml index 551d538313d..8a1d37f12d2 100644 --- a/build/yamls/antrea-eks.yml +++ b/build/yamls/antrea-eks.yml @@ -646,6 +646,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -657,6 +658,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -901,6 +907,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -912,6 +919,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: @@ -1858,6 +1870,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -1869,6 +1882,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -2086,6 +2104,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -2097,6 +2116,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/build/yamls/antrea-gke.yml b/build/yamls/antrea-gke.yml index c0d8fe46367..b7e0d2182e5 100644 --- a/build/yamls/antrea-gke.yml +++ b/build/yamls/antrea-gke.yml @@ -646,6 +646,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -657,6 +658,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -901,6 +907,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -912,6 +919,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: @@ -1858,6 +1870,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -1869,6 +1882,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -2086,6 +2104,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -2097,6 +2116,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/build/yamls/antrea-ipsec.yml b/build/yamls/antrea-ipsec.yml index 717ebdf02bb..b48327b34c9 100644 --- a/build/yamls/antrea-ipsec.yml +++ b/build/yamls/antrea-ipsec.yml @@ -646,6 +646,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -657,6 +658,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -901,6 +907,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -912,6 +919,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: @@ -1858,6 +1870,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -1869,6 +1882,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -2086,6 +2104,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -2097,6 +2116,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/build/yamls/antrea.yml b/build/yamls/antrea.yml index 6c7de8c1d10..acba0e99ea3 100644 --- a/build/yamls/antrea.yml +++ b/build/yamls/antrea.yml @@ -646,6 +646,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -657,6 +658,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -901,6 +907,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -912,6 +919,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: @@ -1858,6 +1870,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -1869,6 +1882,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string from: type: array items: @@ -2086,6 +2104,7 @@ spec: type: object oneOf: - required: [http] + - required: [tls] properties: http: type: object @@ -2097,6 +2116,11 @@ spec: enum: ['GET', 'POST', 'PUT', 'HEAD', 'DELETE', 'TRACE', 'OPTIONS', 'CONNECT', 'PATCH'] path: type: string + tls: + type: object + properties: + sni: + type: string to: type: array items: diff --git a/docs/antrea-l7-network-policy.md b/docs/antrea-l7-network-policy.md index 558c53eee19..660a9144767 100644 --- a/docs/antrea-l7-network-policy.md +++ b/docs/antrea-l7-network-policy.md @@ -8,6 +8,8 @@ - [Usage](#usage) - [HTTP](#http) - [More examples](#more-examples) + - [TLS](#tls) + - [More examples](#more-examples-1) - [Logs](#logs) - [Limitations](#limitations) @@ -75,7 +77,7 @@ welcome feature requests for protocols that you are interested in. ### HTTP -A typical layer 7 NetworkPolicy for HTTP protocol is as below: +An example of layer 7 NetworkPolicy for the HTTP protocol is like below: ```yaml apiVersion: crd.antrea.io/v1alpha1 @@ -90,7 +92,7 @@ spec: matchLabels: app: web ingress: - - name: allow-http # Allow inbound HTTP GET requests to "/api/v2" from Pods with app=client label. + - name: allow-http # Allow inbound HTTP GET requests to "/api/v2" from Pods with label "app=client". action: Allow # All other traffic from these Pods will be automatically dropped, and subsequent rules will not be considered. from: - podSelector: @@ -101,7 +103,7 @@ spec: path: "/api/v2/*" host: "foo.bar.com" method: "GET" - - name: drop-other # Drop all other inbound traffic (i.e., from Pods without the app=client label or from external clients). + - name: drop-other # Drop all other inbound traffic (i.e., from Pods without label "app=client" or from external clients). action: Drop ``` @@ -133,7 +135,7 @@ spec: matchLabels: app: web ingress: - - name: for-admin # Allow inbound HTTP GET requests to "/admin" and "/public" from Pods with role=admin label. + - name: for-admin # Allow inbound HTTP GET requests to "/admin" and "/public" from Pods with label "role=admin". action: Allow from: - podSelector: @@ -144,7 +146,7 @@ spec: path: "/admin/*" - http: path: "/public/*" - - name: for-public # Allow inbound HTTP GET requests to "/public" from Pods with app=client label. + - name: for-public # Allow inbound HTTP GET requests to "/public" from Pods with label "app=client". action: Allow # All other inbound traffic will be automatically dropped. l7Protocols: - http: @@ -173,14 +175,14 @@ spec: port: 53 - protocol: UDP port: 53 - - name: allow-http-only # Allow outbound HTTP requests towards foo.bar.com. + - name: allow-http-only # Allow outbound HTTP requests towards "*.bar.com". action: Allow # As the rule's "to" and "ports" are empty, which means it selects traffic to any network l7Protocols: # peer's any port using any transport protocol, all outbound HTTP requests towards other - http: # domains and non-HTTP requests will be automatically dropped, and subsequent rules will host: "*.bar.com" # not be considered. ``` -The following NetworkPolicy blocks network traffic using an unauthorized application protocol regardless of port used. +The following NetworkPolicy blocks network traffic using an unauthorized application protocol regardless of the port used. ```yaml apiVersion: crd.antrea.io/v1alpha1 @@ -201,6 +203,91 @@ spec: - http: {} # automatically dropped, and subsequent rules will not be considered. ``` +### TLS + +An example layer 7 NetworkPolicy for the TLS protocol is like below: + +```yaml +apiVersion: crd.antrea.io/v1alpha1 +kind: NetworkPolicy +metadata: + name: ingress-allow-tls-handshake +spec: + priority: 5 + tier: application + appliedTo: + - podSelector: + matchLabels: + app: web + ingress: + - name: allow-tls # Allow inbound TLS/SSL handshake packets to server name "foo.bar.com" from Pods with label "app=client". + action: Allow # All other traffic from these Pods will be automatically dropped, and subsequent rules will not be considered. + from: + - podSelector: + matchLabels: + app: client + l7Protocols: + - tls: + sni: "foo.bar.com" + - name: drop-other # Drop all other inbound traffic (i.e., from Pods without label "app=client" or from external clients). + action: Drop +``` + +**sni**: The `sni` field matches the TLS/SSL Server Name Indication (SNI) field in the TLS/SSL handshake process. Both +exact matches and wildcards are supported, e.g. `*.foo.com`, `*.foo.*`, `foo.bar.com`. If not set, the rule matches all names. + +#### More examples + +The following NetworkPolicy prevents applications from accessing unauthorized SSL/TLS server names: + +```yaml +apiVersion: crd.antrea.io/v1alpha1 +kind: ClusterNetworkPolicy +metadata: + name: allow-tls-handshake-to-internal +spec: + priority: 5 + tier: securityops + appliedTo: + - podSelector: + matchLabels: + egress-restriction: internal-tls-only + egress: + - name: allow-dns # Allow outbound DNS requests. + action: Allow + ports: + - protocol: TCP + port: 53 + - protocol: UDP + port: 53 + - name: allow-tls-only # Allow outbound SSL/TLS handshake packets towards "*.bar.com". + action: Allow # As the rule's "to" and "ports" are empty, which means it selects traffic to any network + l7Protocols: # peer's any port of any transport protocol, all outbound SSL/TLS handshake packet towards + - tls: # other server names and non-SSL/non-TLS handshake packets will be automatically dropped, + sni: "*.bar.com" # and subsequent rules will not be considered. +``` + +The following NetworkPolicy blocks network traffic using an unauthorized application protocol regardless of the port used. + +```yaml +apiVersion: crd.antrea.io/v1alpha1 +kind: NetworkPolicy +metadata: + name: allow-tls-only +spec: + priority: 5 + tier: application + appliedTo: + - podSelector: + matchLabels: + app: web + ingress: + - name: tls-only # Allow inbound SSL/TLS handshake packets only. + action: Allow # As the rule's "from" and "ports" are empty, which means it selects traffic from any network + l7Protocols: # peer to any port of the Pods this policy applies to, all inbound non-SSL/non-TLS handshake + - tls: {} # packets will be automatically dropped, and subsequent rules will not be considered. +``` + ### Logs Layer 7 traffic that matches the NetworkPolicy will be logged in an event diff --git a/multicluster/build/yamls/antrea-multicluster-leader-global.yml b/multicluster/build/yamls/antrea-multicluster-leader-global.yml index 69a4cad0829..23b67841126 100644 --- a/multicluster/build/yamls/antrea-multicluster-leader-global.yml +++ b/multicluster/build/yamls/antrea-multicluster-leader-global.yml @@ -1203,6 +1203,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: @@ -2107,6 +2118,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: @@ -3948,6 +3970,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: @@ -4852,6 +4885,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: diff --git a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml index 317313f61c6..02c770a7990 100644 --- a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml +++ b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml @@ -793,6 +793,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: @@ -1697,6 +1708,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: diff --git a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml index f6ec3fb3240..5959412449e 100644 --- a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml +++ b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml @@ -791,6 +791,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: @@ -1695,6 +1706,17 @@ spec: (Ex. "/index.html", "/admin"). type: string type: object + tls: + description: TLSProtocol matches TLS handshakes with + specific SNI. If the field is not provided, this + matches all TLS handshakes. + properties: + sni: + description: SNI (Server Name Indication) indicates + the server domain name in the TLS/SSL hello + message. + type: string + type: object type: object type: array logLabel: diff --git a/pkg/agent/controller/networkpolicy/l7engine/reconciler.go b/pkg/agent/controller/networkpolicy/l7engine/reconciler.go index b2e9b012ee5..b669cfadb80 100644 --- a/pkg/agent/controller/networkpolicy/l7engine/reconciler.go +++ b/pkg/agent/controller/networkpolicy/l7engine/reconciler.go @@ -44,6 +44,7 @@ const ( suricataCommandSocket = "/var/run/suricata/suricata-command.socket" protocolHTTP = "http" + protocolTLS = "tls" scCmdOK = "OK" ) @@ -195,6 +196,14 @@ func convertProtocolHTTP(http *v1beta.HTTPProtocol) string { return strings.Join(keywords, " ") } +func convertProtocolTLS(tls *v1beta.TLSProtocol) string { + var keywords []string + if tls.SNI != "" { + keywords = append(keywords, fmt.Sprintf("tls.sni; %s", convertContent(tls.SNI))) + } + return strings.Join(keywords, " ") +} + func (r *Reconciler) AddRule(ruleID, policyName string, vlanID uint32, l7Protocols []v1beta.L7Protocol, enableLogging bool) error { start := time.Now() defer func() { @@ -215,6 +224,13 @@ func (r *Reconciler) AddRule(ruleID, policyName string, vlanID uint32, l7Protoco } protoKeywords[protocolHTTP].Insert(httpKeywords) } + if protocol.TLS != nil { + tlsKeywords := convertProtocolTLS(protocol.TLS) + if _, ok := protoKeywords[protocolTLS]; !ok { + protoKeywords[protocolTLS] = sets.New[string]() + } + protoKeywords[protocolTLS].Insert(tlsKeywords) + } } klog.InfoS("Reconciling L7 rule", "RuleID", ruleID, "PolicyName", policyName) diff --git a/pkg/agent/controller/networkpolicy/l7engine/reconciler_test.go b/pkg/agent/controller/networkpolicy/l7engine/reconciler_test.go index 790f26bfff5..f5b960a1233 100644 --- a/pkg/agent/controller/networkpolicy/l7engine/reconciler_test.go +++ b/pkg/agent/controller/networkpolicy/l7engine/reconciler_test.go @@ -89,6 +89,32 @@ func TestConvertProtocolHTTP(t *testing.T) { } } +func TestConvertProtocolTLS(t *testing.T) { + testCases := []struct { + name string + tls *v1beta.TLSProtocol + expected string + }{ + { + name: "without SNI", + tls: &v1beta.TLSProtocol{}, + expected: "", + }, + { + name: "with SNI", + tls: &v1beta.TLSProtocol{ + SNI: "google.com", + }, + expected: `tls.sni; content:"google.com"; startswith; endswith;`, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.expected, convertProtocolTLS(tc.tls)) + }) + } +} + func TestStartSuricata(t *testing.T) { defaultFS = afero.NewMemMapFs() defer func() { diff --git a/pkg/apis/controlplane/types.go b/pkg/apis/controlplane/types.go index 9ceeddba421..941ddee8690 100644 --- a/pkg/apis/controlplane/types.go +++ b/pkg/apis/controlplane/types.go @@ -314,6 +314,7 @@ type Service struct { // L7Protocol defines application layer protocol to match. type L7Protocol struct { HTTP *HTTPProtocol + TLS *TLSProtocol } // HTTPProtocol matches HTTP requests with specific host, method, and path. All @@ -330,6 +331,13 @@ type HTTPProtocol struct { Path string } +// TLSProtocol matches TLS handshake packets with specific SNI. If the field is not provided, this +// matches all TLS handshake packets. +type TLSProtocol struct { + // SNI (Server Name Indication) indicates the server domain name in the TLS/SSL hello message. + SNI string `json:"sni,omitempty" protobuf:"bytes,1,opt,name=sni"` +} + // NetworkPolicyPeer describes a peer of NetworkPolicyRules. // It could contain one of the subfields or a combination of them. type NetworkPolicyPeer struct { diff --git a/pkg/apis/controlplane/v1beta2/generated.pb.go b/pkg/apis/controlplane/v1beta2/generated.pb.go index 4f5dd916dac..65c23caa957 100644 --- a/pkg/apis/controlplane/v1beta2/generated.pb.go +++ b/pkg/apis/controlplane/v1beta2/generated.pb.go @@ -1223,6 +1223,34 @@ func (m *SupportBundleCollectionStatus) XXX_DiscardUnknown() { var xxx_messageInfo_SupportBundleCollectionStatus proto.InternalMessageInfo +func (m *TLSProtocol) Reset() { *m = TLSProtocol{} } +func (*TLSProtocol) ProtoMessage() {} +func (*TLSProtocol) Descriptor() ([]byte, []int) { + return fileDescriptor_fbaa7d016762fa1d, []int{42} +} +func (m *TLSProtocol) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TLSProtocol) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *TLSProtocol) XXX_Merge(src proto.Message) { + xxx_messageInfo_TLSProtocol.Merge(m, src) +} +func (m *TLSProtocol) XXX_Size() int { + return m.Size() +} +func (m *TLSProtocol) XXX_DiscardUnknown() { + xxx_messageInfo_TLSProtocol.DiscardUnknown(m) +} + +var xxx_messageInfo_TLSProtocol proto.InternalMessageInfo + func init() { proto.RegisterType((*AddressGroup)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.AddressGroup") proto.RegisterType((*AddressGroupList)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.AddressGroupList") @@ -1266,6 +1294,7 @@ func init() { proto.RegisterType((*SupportBundleCollectionList)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.SupportBundleCollectionList") proto.RegisterType((*SupportBundleCollectionNodeStatus)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.SupportBundleCollectionNodeStatus") proto.RegisterType((*SupportBundleCollectionStatus)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.SupportBundleCollectionStatus") + proto.RegisterType((*TLSProtocol)(nil), "antrea_io.antrea.pkg.apis.controlplane.v1beta2.TLSProtocol") } func init() { @@ -1273,183 +1302,186 @@ func init() { } var fileDescriptor_fbaa7d016762fa1d = []byte{ - // 2804 bytes of a gzipped FileDescriptorProto + // 2849 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x3a, 0xcd, 0x6f, 0x24, 0x47, 0xf5, 0xdb, 0x9e, 0x19, 0xdb, 0xf3, 0xc6, 0xf6, 0x7a, 0xcb, 0x49, 0xd6, 0xbf, 0x24, 0x6b, 0x6f, 0x3a, 0x3f, 0xa2, 0x05, 0x85, 0x99, 0xd8, 0x24, 0xd9, 0x25, 0x5f, 0xe0, 0xf1, 0x7a, 0x9d, 0x21, - 0xb6, 0x33, 0x29, 0x3b, 0x8a, 0x94, 0x90, 0x90, 0x76, 0x77, 0xcd, 0x4c, 0xb3, 0x3d, 0x5d, 0x9d, - 0xea, 0x1a, 0x67, 0x9d, 0x03, 0x0a, 0x0a, 0x1c, 0xc2, 0x57, 0x10, 0x17, 0xc4, 0x8d, 0x1b, 0x17, - 0xfe, 0x82, 0xdc, 0x40, 0x42, 0xca, 0x31, 0x08, 0x21, 0x72, 0xb2, 0x88, 0x11, 0x41, 0x39, 0x70, - 0xe1, 0xc6, 0x22, 0x24, 0x54, 0xd5, 0xd5, 0xdd, 0xd5, 0x3d, 0x9e, 0xf5, 0x8e, 0xed, 0x35, 0x12, - 0xc9, 0x69, 0xa6, 0xdf, 0x77, 0x55, 0xbd, 0x57, 0xef, 0xa3, 0x1b, 0x9e, 0xb1, 0x7c, 0xce, 0x88, - 0x55, 0x75, 0x69, 0x2d, 0xfa, 0x57, 0x0b, 0xae, 0xb7, 0x6b, 0x56, 0xe0, 0x86, 0x35, 0x9b, 0xfa, - 0x9c, 0x51, 0x2f, 0xf0, 0x2c, 0x9f, 0xd4, 0x76, 0x16, 0xb6, 0x09, 0xb7, 0x16, 0x6b, 0x6d, 0xe2, - 0x13, 0x66, 0x71, 0xe2, 0x54, 0x03, 0x46, 0x39, 0x45, 0xd5, 0x88, 0xeb, 0x5b, 0x2e, 0x55, 0xff, - 0xaa, 0xc1, 0xf5, 0x76, 0x55, 0xf0, 0x57, 0x75, 0xfe, 0xaa, 0xe2, 0xbf, 0xf7, 0xca, 0x60, 0x7d, - 0x21, 0xb7, 0x78, 0x58, 0xdb, 0x59, 0xb0, 0xbc, 0xa0, 0x63, 0x2d, 0xe4, 0x35, 0xdd, 0xfb, 0xe5, - 0xb6, 0xcb, 0x3b, 0xbd, 0xed, 0xaa, 0x4d, 0xbb, 0xb5, 0x36, 0x6d, 0xd3, 0x9a, 0x04, 0x6f, 0xf7, - 0x5a, 0xf2, 0x49, 0x3e, 0xc8, 0x7f, 0x8a, 0xfc, 0xd1, 0xeb, 0x57, 0x42, 0xa9, 0x25, 0x70, 0xbb, - 0x96, 0xdd, 0x71, 0x7d, 0xc2, 0x76, 0x53, 0x5d, 0x5d, 0xc2, 0xad, 0xda, 0x4e, 0xbf, 0x92, 0xda, - 0x20, 0x2e, 0xd6, 0xf3, 0xb9, 0xdb, 0x25, 0x7d, 0x0c, 0x8f, 0x1f, 0xc6, 0x10, 0xda, 0x1d, 0xd2, - 0xb5, 0xfa, 0xf8, 0xbe, 0x32, 0x88, 0xaf, 0xc7, 0x5d, 0xaf, 0xe6, 0xfa, 0x3c, 0xe4, 0x2c, 0xcf, - 0x64, 0xfe, 0xcd, 0x80, 0x89, 0x25, 0xc7, 0x61, 0x24, 0x0c, 0x57, 0x19, 0xed, 0x05, 0xe8, 0x75, - 0x18, 0x17, 0x2b, 0x71, 0x2c, 0x6e, 0xcd, 0x1a, 0x17, 0x8d, 0x4b, 0x95, 0xc5, 0x47, 0xaa, 0x91, - 0xe0, 0xaa, 0x2e, 0x38, 0x3d, 0x13, 0x41, 0x5d, 0xdd, 0x59, 0xa8, 0x3e, 0xbf, 0xfd, 0x6d, 0x62, - 0xf3, 0x75, 0xc2, 0xad, 0x3a, 0xfa, 0x60, 0x6f, 0xfe, 0xcc, 0xfe, 0xde, 0x3c, 0xa4, 0x30, 0x9c, - 0x48, 0x45, 0x3d, 0x98, 0x68, 0x0b, 0x55, 0xeb, 0xa4, 0xbb, 0x4d, 0x58, 0x38, 0x3b, 0x72, 0xb1, - 0x70, 0xa9, 0xb2, 0xf8, 0xe4, 0x90, 0xc7, 0x5e, 0x5d, 0x4d, 0x65, 0xd4, 0xef, 0x52, 0x0a, 0x27, - 0x34, 0x60, 0x88, 0x33, 0x6a, 0xcc, 0x3f, 0x18, 0x30, 0xad, 0xaf, 0x74, 0xcd, 0x0d, 0x39, 0xfa, - 0x66, 0xdf, 0x6a, 0xab, 0xb7, 0xb7, 0x5a, 0xc1, 0x2d, 0xd7, 0x3a, 0xad, 0x54, 0x8f, 0xc7, 0x10, - 0x6d, 0xa5, 0x16, 0x94, 0x5c, 0x4e, 0xba, 0xf1, 0x12, 0x9f, 0x1a, 0x76, 0x89, 0xba, 0xb9, 0xf5, - 0x49, 0xa5, 0xa8, 0xd4, 0x10, 0x22, 0x71, 0x24, 0xd9, 0x7c, 0xb7, 0x00, 0xe7, 0x74, 0xb2, 0xa6, - 0xc5, 0xed, 0xce, 0x29, 0x1c, 0xe2, 0xf7, 0x0c, 0x38, 0x67, 0x39, 0x0e, 0x71, 0x56, 0x4f, 0xf8, - 0x28, 0xff, 0x4f, 0xa9, 0x15, 0xab, 0xca, 0x4a, 0xc7, 0xfd, 0x0a, 0xd1, 0x0f, 0x0c, 0x98, 0x61, - 0xa4, 0x4b, 0x77, 0x72, 0x86, 0x14, 0x8e, 0x6f, 0xc8, 0x7d, 0xca, 0x90, 0x19, 0xdc, 0x2f, 0x1f, - 0x1f, 0xa4, 0xd4, 0xfc, 0xd4, 0x80, 0xa9, 0xa5, 0x20, 0xf0, 0x5c, 0xe2, 0x6c, 0xd1, 0xff, 0xf1, - 0x68, 0xfa, 0x93, 0x01, 0x28, 0xbb, 0xd6, 0x53, 0x88, 0x27, 0x3b, 0x1b, 0x4f, 0xcf, 0x0c, 0x1d, - 0x4f, 0x19, 0x83, 0x07, 0x44, 0xd4, 0x0f, 0x0b, 0x30, 0x93, 0x25, 0xfc, 0x3c, 0xa6, 0xfe, 0x7b, - 0x31, 0xf5, 0x06, 0xcc, 0xd4, 0xad, 0xd0, 0xb5, 0x97, 0x7a, 0xbc, 0x43, 0x7c, 0xee, 0xda, 0x16, - 0x77, 0xa9, 0x8f, 0x1e, 0x86, 0xf1, 0x5e, 0x48, 0x98, 0x6f, 0x75, 0x89, 0x3c, 0x8c, 0x72, 0xea, - 0x37, 0x2f, 0x2a, 0x38, 0x4e, 0x28, 0x04, 0x75, 0x60, 0x85, 0xe1, 0x9b, 0x94, 0x39, 0xb3, 0x23, - 0x59, 0xea, 0xa6, 0x82, 0xe3, 0x84, 0xc2, 0x5c, 0x80, 0xe9, 0x7a, 0xcf, 0x77, 0x3c, 0x72, 0xcd, - 0xf5, 0xc8, 0x26, 0x61, 0x3b, 0x84, 0xa1, 0x0b, 0x50, 0xe8, 0x31, 0x4f, 0xa9, 0xaa, 0x28, 0xe6, - 0xc2, 0x8b, 0x78, 0x0d, 0x0b, 0xb8, 0xf9, 0xde, 0x08, 0x5c, 0x88, 0x78, 0x22, 0x7a, 0x61, 0xed, - 0x32, 0xf5, 0x5b, 0x6e, 0xbb, 0xc7, 0x22, 0x83, 0x1f, 0x83, 0xca, 0x36, 0xb1, 0x18, 0x61, 0x5b, - 0xf4, 0x3a, 0xf1, 0x95, 0xa0, 0x19, 0x25, 0xa8, 0x52, 0x4f, 0x51, 0x58, 0xa7, 0x43, 0x0f, 0xc1, - 0xa8, 0x15, 0xb8, 0xcf, 0x91, 0x5d, 0x65, 0xf7, 0x94, 0xe2, 0x18, 0x5d, 0x6a, 0x36, 0x9e, 0x23, - 0xbb, 0x58, 0x61, 0xd1, 0x4f, 0x0c, 0x98, 0xd9, 0xee, 0xdf, 0xa7, 0xd9, 0x82, 0x74, 0xd4, 0xe5, - 0x61, 0xcf, 0xec, 0x80, 0x2d, 0xaf, 0x9f, 0x17, 0xe7, 0x76, 0x00, 0x02, 0x1f, 0xa4, 0xd8, 0xfc, - 0x65, 0x11, 0x66, 0x96, 0xbd, 0x5e, 0xc8, 0x09, 0xcb, 0x38, 0xd7, 0x9d, 0x8f, 0xa2, 0xef, 0x1a, - 0x30, 0x4d, 0x5a, 0x2d, 0x62, 0x73, 0x77, 0x87, 0x9c, 0x60, 0x10, 0xcd, 0x2a, 0xad, 0xd3, 0x2b, - 0x39, 0xe1, 0xb8, 0x4f, 0x1d, 0xfa, 0x0e, 0x9c, 0x4b, 0x60, 0x8d, 0x66, 0xdd, 0xa3, 0xf6, 0xf5, - 0x38, 0x7e, 0x1e, 0x1b, 0xd6, 0x86, 0x46, 0x73, 0x83, 0xf0, 0x34, 0x84, 0x57, 0xf2, 0x72, 0x71, - 0xbf, 0x2a, 0x74, 0x05, 0x26, 0x38, 0xe5, 0x96, 0x17, 0x2f, 0xbf, 0x78, 0xd1, 0xb8, 0x54, 0x48, - 0xef, 0xf5, 0x2d, 0x0d, 0x87, 0x33, 0x94, 0x68, 0x11, 0x40, 0x3e, 0x37, 0xad, 0x36, 0x09, 0x67, - 0x4b, 0x92, 0x2f, 0xd9, 0xef, 0xad, 0x04, 0x83, 0x35, 0x2a, 0xe1, 0xdb, 0x76, 0x8f, 0x31, 0xe2, - 0x73, 0xf1, 0x3c, 0x3b, 0x2a, 0x99, 0x12, 0xdf, 0x5e, 0x4e, 0x51, 0x58, 0xa7, 0x33, 0x3f, 0x31, - 0xa0, 0xb2, 0xd2, 0xfe, 0x0c, 0x54, 0x9e, 0xbf, 0x37, 0xe0, 0xac, 0xb6, 0xd0, 0x53, 0x48, 0x94, - 0xaf, 0x67, 0x13, 0xe5, 0xd0, 0x2b, 0xd4, 0xac, 0x1d, 0x90, 0x25, 0x7f, 0x54, 0x80, 0x69, 0x8d, - 0x2a, 0x4a, 0x91, 0x0e, 0x00, 0x4d, 0xf6, 0xfd, 0x44, 0xcf, 0x50, 0x93, 0xfb, 0x79, 0x9a, 0x3c, - 0x20, 0x4d, 0x7a, 0x70, 0x7e, 0xe5, 0x06, 0x17, 0xe9, 0xce, 0x5b, 0xf1, 0xb9, 0xcb, 0x77, 0x31, - 0x69, 0x11, 0x46, 0x7c, 0x9b, 0xa0, 0x8b, 0x50, 0xd4, 0xd2, 0xe4, 0x84, 0x12, 0x5d, 0xdc, 0x10, - 0x29, 0x52, 0x62, 0x50, 0x0d, 0xca, 0xe2, 0x37, 0x0c, 0x2c, 0x9b, 0xa8, 0x3c, 0x73, 0x4e, 0x91, - 0x95, 0x37, 0x62, 0x04, 0x4e, 0x69, 0xcc, 0x7f, 0x19, 0x30, 0x2d, 0xd5, 0x2f, 0x85, 0x21, 0xb5, - 0xdd, 0x28, 0xc3, 0x9d, 0x4a, 0x7d, 0x34, 0x6d, 0x29, 0x8d, 0x6a, 0xfd, 0x47, 0x2e, 0x05, 0x25, - 0x77, 0xb2, 0x49, 0xe9, 0xe5, 0xbe, 0x94, 0x93, 0x8f, 0xfb, 0x34, 0x9a, 0xef, 0x17, 0xa1, 0xa2, - 0x6d, 0x3e, 0x7a, 0x09, 0x0a, 0x01, 0x75, 0xd4, 0x9a, 0x87, 0xee, 0xf1, 0x9a, 0xd4, 0x49, 0xcd, - 0x18, 0x13, 0x55, 0x85, 0x80, 0x08, 0x89, 0xe8, 0x1d, 0x03, 0xa6, 0x48, 0xe6, 0x54, 0xe5, 0xe9, - 0x54, 0x16, 0x57, 0x87, 0x8e, 0xe7, 0x83, 0x7d, 0xa3, 0x8e, 0xf6, 0xf7, 0xe6, 0xa7, 0x72, 0xc8, - 0x9c, 0x4a, 0xf4, 0x10, 0x14, 0xdc, 0x20, 0x72, 0xeb, 0x89, 0xfa, 0x5d, 0xc2, 0xc0, 0x46, 0x33, - 0xbc, 0xb9, 0x37, 0x5f, 0x6e, 0x34, 0x55, 0xe3, 0x89, 0x05, 0x01, 0x7a, 0x0d, 0x4a, 0x01, 0x65, - 0x5c, 0x24, 0x1b, 0x71, 0x22, 0x5f, 0x1d, 0xd6, 0x46, 0xe1, 0x69, 0x4e, 0x93, 0x32, 0x9e, 0xde, - 0x38, 0xe2, 0x29, 0xc4, 0x91, 0x58, 0xf4, 0x0a, 0x14, 0x7d, 0xea, 0x10, 0x99, 0x93, 0x2a, 0x8b, - 0x4f, 0x0f, 0x2d, 0x9e, 0x3a, 0x24, 0x5d, 0xf8, 0xb8, 0x0c, 0x01, 0x01, 0x92, 0x42, 0x51, 0x1b, - 0xc6, 0x42, 0xc2, 0x76, 0x5c, 0x3b, 0x4a, 0x5f, 0x95, 0xc5, 0xaf, 0x0f, 0x2b, 0x7f, 0x33, 0x62, - 0x4f, 0x55, 0x54, 0xf6, 0xf7, 0xe6, 0xc7, 0x62, 0x68, 0x2c, 0xdd, 0xfc, 0x95, 0x01, 0x53, 0x59, - 0xdf, 0xcb, 0x86, 0x9f, 0x71, 0x78, 0xf8, 0x25, 0x11, 0x3d, 0x32, 0x30, 0xa2, 0xeb, 0x50, 0xe8, - 0xb9, 0x8e, 0xac, 0xfe, 0xca, 0xf5, 0x47, 0x92, 0x72, 0xb5, 0x71, 0xf5, 0xe6, 0xde, 0xfc, 0x03, - 0x83, 0xc6, 0x44, 0x7c, 0x37, 0x20, 0x61, 0xf5, 0xc5, 0xc6, 0x55, 0x2c, 0x98, 0xcd, 0xb7, 0x60, - 0xe2, 0xd9, 0xad, 0xad, 0x66, 0x93, 0x51, 0x4e, 0x6d, 0xea, 0x09, 0xad, 0x1d, 0x1a, 0xf2, 0xfc, - 0x3d, 0xf2, 0x2c, 0x0d, 0x39, 0x96, 0x18, 0x51, 0xac, 0x76, 0x09, 0xef, 0x50, 0x27, 0x5f, 0xac, - 0xae, 0x4b, 0x28, 0x56, 0x58, 0x21, 0x29, 0xb0, 0x78, 0x47, 0x99, 0x97, 0x48, 0x6a, 0x5a, 0xbc, - 0x83, 0x25, 0xc6, 0xfc, 0x8d, 0x01, 0x63, 0xaa, 0x98, 0x41, 0x2f, 0x41, 0xd1, 0x76, 0x1d, 0xa6, - 0xe2, 0xeb, 0x88, 0xe5, 0x53, 0xa2, 0x64, 0xb9, 0x71, 0x15, 0x63, 0x29, 0x10, 0xbd, 0x0a, 0xa3, - 0xe4, 0x86, 0x4d, 0x02, 0xae, 0xee, 0x90, 0x23, 0x8a, 0x4e, 0x56, 0xb9, 0x22, 0x85, 0x61, 0x25, - 0xd4, 0xfc, 0xb7, 0x01, 0xa8, 0xd1, 0xfc, 0xec, 0x5e, 0x93, 0x2d, 0x28, 0xc9, 0x0d, 0x42, 0x0f, - 0xc2, 0x88, 0x1b, 0xc8, 0xb5, 0x4e, 0xd4, 0x67, 0xf6, 0xf7, 0xe6, 0x47, 0x1a, 0xcd, 0xec, 0xf5, - 0x31, 0xe2, 0x06, 0xa2, 0x62, 0x0d, 0x18, 0x69, 0xb9, 0x37, 0xd6, 0x88, 0xdf, 0xe6, 0x1d, 0xe9, - 0x41, 0xa5, 0xb4, 0xba, 0x6a, 0x6a, 0x38, 0x9c, 0xa1, 0x34, 0x3b, 0x00, 0x6b, 0x97, 0x13, 0x2f, - 0x7d, 0x19, 0x8a, 0x1d, 0xce, 0x83, 0xa3, 0xde, 0xc6, 0xba, 0xc7, 0x47, 0x97, 0x84, 0x80, 0x60, - 0x29, 0xd3, 0xfc, 0x85, 0x01, 0x68, 0xbd, 0xe7, 0x89, 0x1e, 0x27, 0xe4, 0x72, 0x95, 0x0d, 0xbf, - 0x45, 0xd1, 0x83, 0x50, 0x92, 0xe5, 0x9e, 0x8a, 0x8c, 0xe4, 0xf6, 0x8a, 0xf6, 0x2e, 0xc2, 0xa1, - 0xd7, 0xa0, 0x18, 0x50, 0xe7, 0xc8, 0x93, 0xc0, 0x4c, 0x96, 0x48, 0x23, 0x86, 0x3a, 0x21, 0x96, - 0x72, 0xcd, 0x77, 0x0d, 0x28, 0x27, 0x37, 0xa8, 0x8c, 0x30, 0xca, 0xa2, 0x58, 0x2d, 0xe9, 0xf4, - 0x8c, 0x63, 0x89, 0xb9, 0x8d, 0x3b, 0xe4, 0x0a, 0x8c, 0x07, 0x6a, 0x27, 0x54, 0xa4, 0xde, 0x9f, - 0x34, 0xcd, 0x0a, 0x7e, 0x53, 0xfb, 0x8f, 0x13, 0x6a, 0xf3, 0xef, 0x05, 0x98, 0xdc, 0x20, 0xfc, - 0x4d, 0xca, 0xae, 0x37, 0xa9, 0xe7, 0xda, 0xbb, 0xa7, 0xe0, 0xf4, 0x2d, 0x28, 0xb1, 0x9e, 0x47, - 0xe2, 0x0d, 0x5e, 0x1a, 0x3a, 0x3d, 0xe8, 0xf6, 0xe2, 0x9e, 0x47, 0xd2, 0x73, 0x14, 0x4f, 0x21, - 0x8e, 0xc4, 0xa3, 0xa7, 0xe1, 0xac, 0x95, 0x19, 0x0e, 0x45, 0x99, 0xb1, 0x2c, 0x3d, 0xfb, 0x6c, - 0x76, 0x6e, 0x14, 0xe2, 0x3c, 0x2d, 0xba, 0x24, 0x36, 0xd5, 0xa5, 0x4c, 0xe4, 0x72, 0xd1, 0x94, - 0x19, 0xf5, 0x89, 0x68, 0x43, 0x23, 0x18, 0x4e, 0xb0, 0xe8, 0x51, 0x98, 0xe0, 0x2e, 0x61, 0x31, - 0x46, 0xa6, 0xbd, 0x52, 0x7d, 0x5a, 0xb6, 0x6f, 0x1a, 0x1c, 0x67, 0xa8, 0x50, 0x08, 0xe5, 0x90, - 0xf6, 0x98, 0xcc, 0x43, 0x2a, 0x93, 0x5d, 0x3b, 0xde, 0x56, 0x24, 0x5e, 0x37, 0x29, 0xf2, 0xd1, - 0x66, 0x2c, 0x1c, 0xa7, 0x7a, 0xcc, 0x3f, 0x1a, 0x70, 0x2e, 0xc3, 0x74, 0x0a, 0x1d, 0xce, 0x76, - 0xb6, 0xc3, 0x79, 0xfa, 0x58, 0x8b, 0x1c, 0xd0, 0xe3, 0xfc, 0xc3, 0x80, 0xf3, 0x19, 0x3a, 0x51, - 0x30, 0x6c, 0x72, 0x8b, 0xf7, 0x42, 0xf4, 0x30, 0x8c, 0x8b, 0xc2, 0x61, 0xe3, 0x80, 0x01, 0xd4, - 0x86, 0x82, 0xe3, 0x84, 0x42, 0x74, 0xd5, 0xea, 0xc5, 0x8b, 0x4b, 0x7d, 0x19, 0x73, 0x5a, 0x57, - 0xbd, 0x9a, 0x60, 0xb0, 0x46, 0x85, 0xbe, 0x01, 0x88, 0x11, 0xcb, 0x73, 0xdf, 0x92, 0x8f, 0xd7, - 0x2c, 0xd7, 0xeb, 0x31, 0x22, 0x23, 0x71, 0xbc, 0x7e, 0xaf, 0xe2, 0x45, 0xb8, 0x8f, 0x02, 0x1f, - 0xc0, 0x85, 0xbe, 0x08, 0x63, 0x5d, 0x12, 0x86, 0xa2, 0x3b, 0x2f, 0x4a, 0x63, 0xcf, 0x2a, 0x01, - 0x63, 0xeb, 0x11, 0x18, 0xc7, 0x78, 0xf9, 0x42, 0x21, 0xb3, 0xe8, 0x26, 0x21, 0x0c, 0x5d, 0x86, - 0x49, 0x4b, 0x7b, 0xcb, 0x10, 0xce, 0x1a, 0xd2, 0xe9, 0xcf, 0xed, 0xef, 0xcd, 0x4f, 0xea, 0xaf, - 0x1f, 0x42, 0x9c, 0xa5, 0x43, 0x04, 0xc6, 0xdd, 0x40, 0x0d, 0x40, 0xa2, 0xa3, 0xba, 0x3c, 0x7c, - 0x9a, 0x95, 0xfc, 0xe9, 0x06, 0x27, 0x93, 0x8f, 0x44, 0x34, 0x9a, 0x87, 0x52, 0xeb, 0x0d, 0xc7, - 0x8f, 0x83, 0xb1, 0x2c, 0xce, 0xf2, 0xda, 0x0b, 0x57, 0x37, 0x42, 0x1c, 0xc1, 0x11, 0x07, 0xe0, - 0x54, 0x55, 0x63, 0x71, 0x89, 0x7a, 0xfc, 0x1a, 0x4f, 0x9b, 0x8c, 0xc4, 0xb2, 0xb1, 0xa6, 0x47, - 0xdc, 0x16, 0x9e, 0xb5, 0x4d, 0xbc, 0x86, 0x43, 0x44, 0x31, 0xed, 0xca, 0x91, 0x4a, 0xe1, 0xd2, - 0x64, 0x74, 0x5b, 0xac, 0x65, 0x51, 0x38, 0x4f, 0x6b, 0x7e, 0x62, 0xc0, 0x3d, 0x07, 0x47, 0x23, - 0x7a, 0x0c, 0x8a, 0xa2, 0x5e, 0x53, 0xbe, 0xf7, 0x40, 0x7c, 0x7f, 0x6f, 0xed, 0x06, 0xe4, 0xe6, - 0xde, 0x7c, 0xf6, 0x04, 0x05, 0x10, 0x4b, 0xf2, 0xa1, 0x5b, 0xbd, 0x24, 0x4f, 0x14, 0x0e, 0xab, - 0x35, 0x8b, 0xc7, 0xa9, 0x35, 0x7f, 0x3b, 0x9a, 0x73, 0x3a, 0x71, 0xe7, 0xa2, 0xa7, 0xa0, 0xec, - 0xb8, 0x8c, 0xd8, 0x32, 0x68, 0xa2, 0x85, 0xce, 0xc5, 0xc6, 0x5e, 0x8d, 0x11, 0x37, 0xf5, 0x07, - 0x9c, 0x32, 0x20, 0x1b, 0x8a, 0x2d, 0x46, 0xbb, 0xaa, 0x65, 0x3a, 0x5e, 0x42, 0x10, 0x31, 0x90, - 0x2e, 0xfe, 0x1a, 0xa3, 0x5d, 0x2c, 0x85, 0xa3, 0x57, 0x61, 0x84, 0x53, 0x35, 0x65, 0x3d, 0x01, - 0x15, 0xa0, 0x54, 0x8c, 0x6c, 0x51, 0x3c, 0xc2, 0xa9, 0x88, 0x9e, 0x30, 0xeb, 0xb3, 0x97, 0x8f, - 0xe8, 0xb3, 0x69, 0xf4, 0x24, 0x8e, 0x9a, 0x88, 0x96, 0xf3, 0xf1, 0x5c, 0x9e, 0x49, 0x53, 0x7d, - 0x5f, 0x66, 0x7a, 0x09, 0x46, 0xad, 0xe8, 0x4c, 0x46, 0xe5, 0x99, 0x7c, 0x4d, 0xce, 0xa3, 0xe3, - 0xc3, 0x58, 0xb8, 0xc5, 0xdb, 0x7f, 0xe6, 0x24, 0xef, 0xe2, 0xab, 0xe2, 0x84, 0x23, 0x26, 0xac, - 0xc4, 0xa1, 0x27, 0x61, 0x92, 0xf8, 0xd6, 0xb6, 0x47, 0xd6, 0x68, 0xbb, 0xed, 0xfa, 0xed, 0xd9, - 0x31, 0x79, 0xd9, 0xdd, 0xad, 0x6c, 0x99, 0x5c, 0xd1, 0x91, 0x38, 0x4b, 0x7b, 0x50, 0x62, 0x1e, - 0x1f, 0x22, 0x31, 0xc7, 0x7e, 0x5e, 0x1e, 0xe8, 0xe7, 0x6f, 0x40, 0xc5, 0x4b, 0xea, 0xcc, 0x70, - 0x16, 0xe4, 0x71, 0x3c, 0x31, 0xec, 0x71, 0xa4, 0xa5, 0x6a, 0x3a, 0x21, 0x4d, 0x61, 0x21, 0xd6, - 0x75, 0x88, 0x73, 0xf1, 0x68, 0x5b, 0x5e, 0x13, 0xb3, 0x95, 0x6c, 0x92, 0x59, 0x53, 0x70, 0x9c, - 0x50, 0x98, 0xef, 0x15, 0x00, 0x65, 0x5c, 0x4a, 0xa4, 0xaa, 0x10, 0xbd, 0x63, 0xc0, 0xa4, 0xaf, - 0x83, 0x55, 0x36, 0x3e, 0xa9, 0xba, 0x20, 0x39, 0x9e, 0x2c, 0x3e, 0xab, 0x13, 0x05, 0x30, 0xc1, - 0x99, 0xd5, 0x6a, 0xb9, 0xb6, 0xb4, 0x4a, 0x45, 0xe5, 0xe3, 0xb7, 0xb0, 0x41, 0x7e, 0xbb, 0x51, - 0x4d, 0xfc, 0x65, 0x4b, 0xe3, 0xd6, 0x26, 0xd9, 0x1a, 0x14, 0x67, 0x34, 0xa0, 0xb7, 0x0d, 0x98, - 0x16, 0x35, 0x9b, 0x4e, 0xa2, 0x86, 0x73, 0x4f, 0xdc, 0xbe, 0x5a, 0x9c, 0x93, 0x90, 0xb6, 0x40, - 0x79, 0x0c, 0xee, 0xd3, 0x66, 0xfe, 0xd5, 0x80, 0x99, 0xbe, 0x13, 0xe9, 0x9d, 0xc6, 0x4b, 0x10, - 0x0f, 0x4a, 0xa2, 0xf8, 0x88, 0x73, 0xee, 0xea, 0xb1, 0xce, 0x3a, 0x2d, 0x7b, 0xd2, 0x42, 0x49, - 0xc0, 0x42, 0x1c, 0x29, 0x31, 0x17, 0x60, 0x32, 0x33, 0x5e, 0x39, 0x7c, 0xe6, 0x68, 0xbe, 0x5f, - 0x82, 0xe9, 0x58, 0x6e, 0xb8, 0xd9, 0xeb, 0x76, 0x2d, 0x76, 0x1a, 0x6d, 0xc2, 0xf7, 0x0d, 0x38, - 0xab, 0x3b, 0xa6, 0x9b, 0x6c, 0x51, 0xfd, 0x58, 0x5b, 0x14, 0xf9, 0xc6, 0x79, 0xa5, 0xfb, 0xec, - 0x46, 0x56, 0x05, 0xce, 0xeb, 0x44, 0xbf, 0x36, 0xe0, 0xfe, 0x48, 0x8b, 0x7a, 0x49, 0x96, 0xe3, - 0x50, 0x8e, 0x7a, 0x12, 0x46, 0xfd, 0xbf, 0x32, 0xea, 0xfe, 0xa5, 0x5b, 0xe8, 0xc3, 0xb7, 0xb4, - 0x06, 0xfd, 0xdc, 0x80, 0xbb, 0x23, 0x82, 0xbc, 0x9d, 0xc5, 0x13, 0xb3, 0xf3, 0x82, 0xb2, 0xf3, - 0xee, 0xa5, 0x83, 0x14, 0xe1, 0x83, 0xf5, 0x8b, 0x86, 0xa7, 0x1b, 0xb7, 0xe4, 0xb2, 0xb6, 0x3a, - 0x82, 0x31, 0xfd, 0x3d, 0x7d, 0x5a, 0x14, 0x25, 0x38, 0x9c, 0xea, 0x31, 0x5f, 0x85, 0xbb, 0x9a, - 0x56, 0xdb, 0xf5, 0x65, 0x8d, 0xbd, 0x4a, 0xf8, 0xf3, 0x81, 0xf8, 0x13, 0x46, 0x83, 0xad, 0x76, - 0xe4, 0xf6, 0x05, 0x7d, 0xb0, 0xd5, 0x26, 0x58, 0x62, 0xd0, 0x83, 0x50, 0xf2, 0xdc, 0xae, 0xcb, - 0x55, 0x0f, 0x90, 0x84, 0xd3, 0x9a, 0x00, 0xe2, 0x08, 0x67, 0x5a, 0x30, 0xa1, 0xf7, 0xfb, 0x77, - 0x62, 0x82, 0xff, 0xbb, 0x02, 0xc4, 0xb3, 0x49, 0xf4, 0xa8, 0xd6, 0xe8, 0x47, 0x2a, 0x66, 0x0f, - 0x6f, 0xf2, 0xd1, 0x86, 0x1a, 0x31, 0x8c, 0x1c, 0x12, 0xa7, 0x3d, 0xee, 0x7a, 0xd5, 0xe8, 0xe3, - 0xb3, 0x6a, 0xc3, 0xe7, 0xcf, 0xb3, 0x4d, 0xce, 0x5c, 0xbf, 0x1d, 0x0d, 0x57, 0xb4, 0x81, 0xc4, - 0x17, 0x60, 0x8c, 0xf8, 0x72, 0x7a, 0x21, 0xcb, 0xa9, 0x52, 0x34, 0x3f, 0x5d, 0x89, 0x40, 0x38, - 0xc6, 0x89, 0x06, 0xda, 0xb5, 0xbb, 0x81, 0x28, 0x69, 0x65, 0xc9, 0x59, 0x8a, 0x1a, 0xe8, 0xc6, - 0xf2, 0x7a, 0x53, 0x96, 0xb9, 0x09, 0x36, 0xa6, 0x5c, 0x8e, 0x67, 0xc6, 0x1a, 0xa5, 0x80, 0xe1, - 0x04, 0x2b, 0x29, 0xdb, 0x4a, 0xe6, 0xa8, 0x46, 0xb9, 0x9a, 0xc8, 0x54, 0x58, 0x74, 0x45, 0xbd, - 0x40, 0x54, 0x2d, 0x8f, 0x2c, 0x50, 0xca, 0xb9, 0x77, 0x80, 0xf1, 0x54, 0x2b, 0x43, 0x29, 0x96, - 0x17, 0x32, 0x5b, 0x2e, 0x6f, 0x3c, 0x5d, 0xde, 0x66, 0x04, 0xc2, 0x31, 0x0e, 0x55, 0x01, 0x42, - 0x66, 0xab, 0x55, 0xcb, 0x62, 0xa4, 0x54, 0x9f, 0x12, 0xb7, 0xd9, 0x66, 0x02, 0xc5, 0x1a, 0x85, - 0x49, 0x60, 0x3a, 0xdf, 0x94, 0xdc, 0x09, 0x77, 0x79, 0xaf, 0x08, 0xe7, 0x37, 0x7b, 0x81, 0x38, - 0xa8, 0xe8, 0x33, 0x87, 0x65, 0xea, 0x79, 0xaa, 0xce, 0xbe, 0xf3, 0x97, 0xf6, 0x2b, 0x50, 0x26, - 0x37, 0x02, 0x97, 0x11, 0x67, 0x29, 0xf6, 0xb7, 0x2f, 0xdd, 0x9e, 0x8a, 0x2d, 0xb7, 0x4b, 0xd2, - 0xa5, 0xad, 0xc4, 0x42, 0x70, 0x2a, 0x4f, 0xec, 0x45, 0xe8, 0xfa, 0x36, 0x11, 0xa4, 0xaa, 0xcb, - 0x49, 0x18, 0x36, 0x63, 0x04, 0x4e, 0x69, 0x44, 0x27, 0xd9, 0x4a, 0x3e, 0x0c, 0x91, 0x3e, 0x78, - 0x84, 0x4e, 0x32, 0xff, 0x81, 0x49, 0xba, 0x03, 0x29, 0x0c, 0x6b, 0x7a, 0xd0, 0x8f, 0x0d, 0x98, - 0xb2, 0xb2, 0xdf, 0x76, 0x44, 0x2f, 0x42, 0xd6, 0x8f, 0xa6, 0x7a, 0xc0, 0x77, 0x2a, 0xf5, 0x7b, - 0x94, 0x1d, 0x53, 0xb9, 0x8f, 0x3c, 0x72, 0xca, 0xcd, 0x4f, 0x0d, 0xb8, 0x6f, 0x80, 0x47, 0x9c, - 0xc2, 0xf4, 0xc7, 0xcb, 0x4e, 0x7f, 0x86, 0x2e, 0x6f, 0x06, 0x58, 0x3e, 0x60, 0x0e, 0xf4, 0xb3, - 0x11, 0x78, 0x60, 0x00, 0xc7, 0x91, 0x27, 0x42, 0x4f, 0xc2, 0x64, 0xfc, 0x5f, 0x0f, 0xc3, 0xb4, - 0x98, 0xd6, 0x91, 0x38, 0x4b, 0x1b, 0xab, 0x92, 0x17, 0x56, 0xa1, 0x5f, 0x55, 0x74, 0x69, 0xc5, - 0x14, 0xc2, 0xc3, 0x6d, 0xda, 0x0d, 0x3c, 0xc2, 0x49, 0xd4, 0xa6, 0x8f, 0xa7, 0x1e, 0xbe, 0x1c, - 0x23, 0x70, 0x4a, 0x23, 0x92, 0x14, 0x61, 0x8c, 0x32, 0xe9, 0x61, 0xda, 0x40, 0x7b, 0x45, 0x00, - 0x71, 0x84, 0x33, 0xff, 0x69, 0xc0, 0x85, 0x01, 0x9b, 0x72, 0x6a, 0x55, 0xee, 0x4e, 0xb6, 0xca, - 0x7d, 0xe1, 0x84, 0xdc, 0xe0, 0xb0, 0x7a, 0xb7, 0xbe, 0xf5, 0xc1, 0xc7, 0x73, 0x67, 0x3e, 0xfc, - 0x78, 0xee, 0xcc, 0x47, 0x1f, 0xcf, 0x9d, 0x79, 0x7b, 0x7f, 0xce, 0xf8, 0x60, 0x7f, 0xce, 0xf8, - 0x70, 0x7f, 0xce, 0xf8, 0x68, 0x7f, 0xce, 0xf8, 0xf3, 0xfe, 0x9c, 0xf1, 0xd3, 0xbf, 0xcc, 0x9d, - 0x79, 0xb9, 0x3a, 0xdc, 0x77, 0xf0, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x22, 0xe8, 0xa6, - 0x38, 0x2f, 0x00, 0x00, + 0xb6, 0x33, 0xa9, 0x99, 0x10, 0x29, 0x21, 0x21, 0xed, 0xee, 0x9a, 0x71, 0xe3, 0x9e, 0xae, 0x4e, + 0x75, 0x8d, 0xb3, 0xce, 0x01, 0x05, 0x05, 0x0e, 0xe1, 0x2b, 0x88, 0x0b, 0xe2, 0xc6, 0x8d, 0x0b, + 0x7f, 0x41, 0x4e, 0x80, 0x84, 0x94, 0x63, 0x10, 0x42, 0xe4, 0x64, 0xb1, 0x46, 0x04, 0xe5, 0xc0, + 0x85, 0x1b, 0x8b, 0x90, 0x50, 0x55, 0x57, 0x77, 0x57, 0xcf, 0x78, 0xd6, 0x3b, 0xb6, 0xd7, 0x48, + 0x24, 0xa7, 0x99, 0x7e, 0xdf, 0x55, 0xf5, 0x5e, 0xbd, 0x8f, 0x6e, 0x78, 0xc6, 0xf2, 0x39, 0x23, + 0x56, 0xd9, 0xa5, 0x95, 0xe8, 0x5f, 0x25, 0xd8, 0x6e, 0x57, 0xac, 0xc0, 0x0d, 0x2b, 0x36, 0xf5, + 0x39, 0xa3, 0x5e, 0xe0, 0x59, 0x3e, 0xa9, 0xec, 0x2c, 0x6c, 0x12, 0x6e, 0x2d, 0x56, 0xda, 0xc4, + 0x27, 0xcc, 0xe2, 0xc4, 0x29, 0x07, 0x8c, 0x72, 0x8a, 0xca, 0x11, 0xd7, 0x37, 0x5d, 0xaa, 0xfe, + 0x95, 0x83, 0xed, 0x76, 0x59, 0xf0, 0x97, 0x75, 0xfe, 0xb2, 0xe2, 0xbf, 0xf7, 0xca, 0x60, 0x7d, + 0x21, 0xb7, 0x78, 0x58, 0xd9, 0x59, 0xb0, 0xbc, 0x60, 0xcb, 0x5a, 0xe8, 0xd5, 0x74, 0xef, 0x17, + 0xdb, 0x2e, 0xdf, 0xea, 0x6e, 0x96, 0x6d, 0xda, 0xa9, 0xb4, 0x69, 0x9b, 0x56, 0x24, 0x78, 0xb3, + 0xdb, 0x92, 0x4f, 0xf2, 0x41, 0xfe, 0x53, 0xe4, 0x8f, 0x6e, 0x5f, 0x09, 0xa5, 0x96, 0xc0, 0xed, + 0x58, 0xf6, 0x96, 0xeb, 0x13, 0xb6, 0x9b, 0xea, 0xea, 0x10, 0x6e, 0x55, 0x76, 0xfa, 0x95, 0x54, + 0x06, 0x71, 0xb1, 0xae, 0xcf, 0xdd, 0x0e, 0xe9, 0x63, 0x78, 0xfc, 0x30, 0x86, 0xd0, 0xde, 0x22, + 0x1d, 0xab, 0x8f, 0xef, 0x4b, 0x83, 0xf8, 0xba, 0xdc, 0xf5, 0x2a, 0xae, 0xcf, 0x43, 0xce, 0x7a, + 0x99, 0xcc, 0xbf, 0x19, 0x30, 0xb1, 0xe4, 0x38, 0x8c, 0x84, 0xe1, 0x2a, 0xa3, 0xdd, 0x00, 0xbd, + 0x0e, 0xe3, 0x62, 0x25, 0x8e, 0xc5, 0xad, 0x59, 0xe3, 0xa2, 0x71, 0xa9, 0xb4, 0xf8, 0x48, 0x39, + 0x12, 0x5c, 0xd6, 0x05, 0xa7, 0x67, 0x22, 0xa8, 0xcb, 0x3b, 0x0b, 0xe5, 0xe7, 0x37, 0xbf, 0x45, + 0x6c, 0xbe, 0x4e, 0xb8, 0x55, 0x45, 0x1f, 0xec, 0xcd, 0x9f, 0xd9, 0xdf, 0x9b, 0x87, 0x14, 0x86, + 0x13, 0xa9, 0xa8, 0x0b, 0x13, 0x6d, 0xa1, 0x6a, 0x9d, 0x74, 0x36, 0x09, 0x0b, 0x67, 0x47, 0x2e, + 0xe6, 0x2e, 0x95, 0x16, 0x9f, 0x1c, 0xf2, 0xd8, 0xcb, 0xab, 0xa9, 0x8c, 0xea, 0x5d, 0x4a, 0xe1, + 0x84, 0x06, 0x0c, 0x71, 0x46, 0x8d, 0xf9, 0x07, 0x03, 0xa6, 0xf5, 0x95, 0xae, 0xb9, 0x21, 0x47, + 0xdf, 0xe8, 0x5b, 0x6d, 0xf9, 0xf6, 0x56, 0x2b, 0xb8, 0xe5, 0x5a, 0xa7, 0x95, 0xea, 0xf1, 0x18, + 0xa2, 0xad, 0xd4, 0x82, 0x82, 0xcb, 0x49, 0x27, 0x5e, 0xe2, 0x53, 0xc3, 0x2e, 0x51, 0x37, 0xb7, + 0x3a, 0xa9, 0x14, 0x15, 0x6a, 0x42, 0x24, 0x8e, 0x24, 0x9b, 0xef, 0xe6, 0xe0, 0x9c, 0x4e, 0x56, + 0xb7, 0xb8, 0xbd, 0x75, 0x0a, 0x87, 0xf8, 0x5d, 0x03, 0xce, 0x59, 0x8e, 0x43, 0x9c, 0xd5, 0x13, + 0x3e, 0xca, 0xff, 0x53, 0x6a, 0xc5, 0xaa, 0xb2, 0xd2, 0x71, 0xbf, 0x42, 0xf4, 0x7d, 0x03, 0x66, + 0x18, 0xe9, 0xd0, 0x9d, 0x1e, 0x43, 0x72, 0xc7, 0x37, 0xe4, 0x3e, 0x65, 0xc8, 0x0c, 0xee, 0x97, + 0x8f, 0x0f, 0x52, 0x6a, 0x7e, 0x62, 0xc0, 0xd4, 0x52, 0x10, 0x78, 0x2e, 0x71, 0x9a, 0xf4, 0x7f, + 0x3c, 0x9a, 0xfe, 0x64, 0x00, 0xca, 0xae, 0xf5, 0x14, 0xe2, 0xc9, 0xce, 0xc6, 0xd3, 0x33, 0x43, + 0xc7, 0x53, 0xc6, 0xe0, 0x01, 0x11, 0xf5, 0x83, 0x1c, 0xcc, 0x64, 0x09, 0x3f, 0x8b, 0xa9, 0xff, + 0x5e, 0x4c, 0xbd, 0x01, 0x33, 0x55, 0x2b, 0x74, 0xed, 0xa5, 0x2e, 0xdf, 0x22, 0x3e, 0x77, 0x6d, + 0x8b, 0xbb, 0xd4, 0x47, 0x0f, 0xc3, 0x78, 0x37, 0x24, 0xcc, 0xb7, 0x3a, 0x44, 0x1e, 0x46, 0x31, + 0xf5, 0x9b, 0x17, 0x15, 0x1c, 0x27, 0x14, 0x82, 0x3a, 0xb0, 0xc2, 0xf0, 0x4d, 0xca, 0x9c, 0xd9, + 0x91, 0x2c, 0x75, 0x5d, 0xc1, 0x71, 0x42, 0x61, 0x2e, 0xc0, 0x74, 0xb5, 0xeb, 0x3b, 0x1e, 0xb9, + 0xe6, 0x7a, 0xa4, 0x41, 0xd8, 0x0e, 0x61, 0xe8, 0x02, 0xe4, 0xba, 0xcc, 0x53, 0xaa, 0x4a, 0x8a, + 0x39, 0xf7, 0x22, 0x5e, 0xc3, 0x02, 0x6e, 0xbe, 0x37, 0x02, 0x17, 0x22, 0x9e, 0x88, 0x5e, 0x58, + 0xbb, 0x4c, 0xfd, 0x96, 0xdb, 0xee, 0xb2, 0xc8, 0xe0, 0xc7, 0xa0, 0xb4, 0x49, 0x2c, 0x46, 0x58, + 0x93, 0x6e, 0x13, 0x5f, 0x09, 0x9a, 0x51, 0x82, 0x4a, 0xd5, 0x14, 0x85, 0x75, 0x3a, 0xf4, 0x10, + 0x8c, 0x5a, 0x81, 0xfb, 0x1c, 0xd9, 0x55, 0x76, 0x4f, 0x29, 0x8e, 0xd1, 0xa5, 0x7a, 0xed, 0x39, + 0xb2, 0x8b, 0x15, 0x16, 0xfd, 0xd8, 0x80, 0x99, 0xcd, 0xfe, 0x7d, 0x9a, 0xcd, 0x49, 0x47, 0x5d, + 0x1e, 0xf6, 0xcc, 0x0e, 0xd8, 0xf2, 0xea, 0x79, 0x71, 0x6e, 0x07, 0x20, 0xf0, 0x41, 0x8a, 0xcd, + 0x5f, 0xe4, 0x61, 0x66, 0xd9, 0xeb, 0x86, 0x9c, 0xb0, 0x8c, 0x73, 0xdd, 0xf9, 0x28, 0xfa, 0x8e, + 0x01, 0xd3, 0xa4, 0xd5, 0x22, 0x36, 0x77, 0x77, 0xc8, 0x09, 0x06, 0xd1, 0xac, 0xd2, 0x3a, 0xbd, + 0xd2, 0x23, 0x1c, 0xf7, 0xa9, 0x43, 0xdf, 0x86, 0x73, 0x09, 0xac, 0x56, 0xaf, 0x7a, 0xd4, 0xde, + 0x8e, 0xe3, 0xe7, 0xb1, 0x61, 0x6d, 0xa8, 0xd5, 0x37, 0x08, 0x4f, 0x43, 0x78, 0xa5, 0x57, 0x2e, + 0xee, 0x57, 0x85, 0xae, 0xc0, 0x04, 0xa7, 0xdc, 0xf2, 0xe2, 0xe5, 0xe7, 0x2f, 0x1a, 0x97, 0x72, + 0xe9, 0xbd, 0xde, 0xd4, 0x70, 0x38, 0x43, 0x89, 0x16, 0x01, 0xe4, 0x73, 0xdd, 0x6a, 0x93, 0x70, + 0xb6, 0x20, 0xf9, 0x92, 0xfd, 0x6e, 0x26, 0x18, 0xac, 0x51, 0x09, 0xdf, 0xb6, 0xbb, 0x8c, 0x11, + 0x9f, 0x8b, 0xe7, 0xd9, 0x51, 0xc9, 0x94, 0xf8, 0xf6, 0x72, 0x8a, 0xc2, 0x3a, 0x9d, 0xf9, 0xb1, + 0x01, 0xa5, 0x95, 0xf6, 0xa7, 0xa0, 0xf2, 0xfc, 0xbd, 0x01, 0x67, 0xb5, 0x85, 0x9e, 0x42, 0xa2, + 0x7c, 0x3d, 0x9b, 0x28, 0x87, 0x5e, 0xa1, 0x66, 0xed, 0x80, 0x2c, 0xf9, 0xc3, 0x1c, 0x4c, 0x6b, + 0x54, 0x51, 0x8a, 0x74, 0x00, 0x68, 0xb2, 0xef, 0x27, 0x7a, 0x86, 0x9a, 0xdc, 0xcf, 0xd2, 0xe4, + 0x01, 0x69, 0xd2, 0x83, 0xf3, 0x2b, 0xd7, 0xb9, 0x48, 0x77, 0xde, 0x8a, 0xcf, 0x5d, 0xbe, 0x8b, + 0x49, 0x8b, 0x30, 0xe2, 0xdb, 0x04, 0x5d, 0x84, 0xbc, 0x96, 0x26, 0x27, 0x94, 0xe8, 0xfc, 0x86, + 0x48, 0x91, 0x12, 0x83, 0x2a, 0x50, 0x14, 0xbf, 0x61, 0x60, 0xd9, 0x44, 0xe5, 0x99, 0x73, 0x8a, + 0xac, 0xb8, 0x11, 0x23, 0x70, 0x4a, 0x63, 0xfe, 0xcb, 0x80, 0x69, 0xa9, 0x7e, 0x29, 0x0c, 0xa9, + 0xed, 0x46, 0x19, 0xee, 0x54, 0xea, 0xa3, 0x69, 0x4b, 0x69, 0x54, 0xeb, 0x3f, 0x72, 0x29, 0x28, + 0xb9, 0x93, 0x4d, 0x4a, 0x2f, 0xf7, 0xa5, 0x1e, 0xf9, 0xb8, 0x4f, 0xa3, 0xf9, 0x7e, 0x1e, 0x4a, + 0xda, 0xe6, 0xa3, 0x97, 0x20, 0x17, 0x50, 0x47, 0xad, 0x79, 0xe8, 0x1e, 0xaf, 0x4e, 0x9d, 0xd4, + 0x8c, 0x31, 0x51, 0x55, 0x08, 0x88, 0x90, 0x88, 0xde, 0x31, 0x60, 0x8a, 0x64, 0x4e, 0x55, 0x9e, + 0x4e, 0x69, 0x71, 0x75, 0xe8, 0x78, 0x3e, 0xd8, 0x37, 0xaa, 0x68, 0x7f, 0x6f, 0x7e, 0xaa, 0x07, + 0xd9, 0xa3, 0x12, 0x3d, 0x04, 0x39, 0x37, 0x88, 0xdc, 0x7a, 0xa2, 0x7a, 0x97, 0x30, 0xb0, 0x56, + 0x0f, 0x6f, 0xee, 0xcd, 0x17, 0x6b, 0x75, 0xd5, 0x78, 0x62, 0x41, 0x80, 0x5e, 0x83, 0x42, 0x40, + 0x19, 0x17, 0xc9, 0x46, 0x9c, 0xc8, 0x97, 0x87, 0xb5, 0x51, 0x78, 0x9a, 0x53, 0xa7, 0x8c, 0xa7, + 0x37, 0x8e, 0x78, 0x0a, 0x71, 0x24, 0x16, 0xbd, 0x02, 0x79, 0x9f, 0x3a, 0x44, 0xe6, 0xa4, 0xd2, + 0xe2, 0xd3, 0x43, 0x8b, 0xa7, 0x0e, 0x49, 0x17, 0x3e, 0x2e, 0x43, 0x40, 0x80, 0xa4, 0x50, 0xd4, + 0x86, 0xb1, 0x90, 0xb0, 0x1d, 0xd7, 0x8e, 0xd2, 0x57, 0x69, 0xf1, 0xab, 0xc3, 0xca, 0x6f, 0x44, + 0xec, 0xa9, 0x8a, 0xd2, 0xfe, 0xde, 0xfc, 0x58, 0x0c, 0x8d, 0xa5, 0x9b, 0xbf, 0x34, 0x60, 0x2a, + 0xeb, 0x7b, 0xd9, 0xf0, 0x33, 0x0e, 0x0f, 0xbf, 0x24, 0xa2, 0x47, 0x06, 0x46, 0x74, 0x15, 0x72, + 0x5d, 0xd7, 0x91, 0xd5, 0x5f, 0xb1, 0xfa, 0x48, 0x52, 0xae, 0xd6, 0xae, 0xde, 0xdc, 0x9b, 0x7f, + 0x60, 0xd0, 0x98, 0x88, 0xef, 0x06, 0x24, 0x2c, 0xbf, 0x58, 0xbb, 0x8a, 0x05, 0xb3, 0xf9, 0x16, + 0x4c, 0x3c, 0xdb, 0x6c, 0xd6, 0xeb, 0x8c, 0x72, 0x6a, 0x53, 0x4f, 0x68, 0xdd, 0xa2, 0x21, 0xef, + 0xbd, 0x47, 0x9e, 0xa5, 0x21, 0xc7, 0x12, 0x23, 0x8a, 0xd5, 0x0e, 0xe1, 0x5b, 0xd4, 0xe9, 0x2d, + 0x56, 0xd7, 0x25, 0x14, 0x2b, 0xac, 0x90, 0x14, 0x58, 0x7c, 0x4b, 0x99, 0x97, 0x48, 0xaa, 0x5b, + 0x7c, 0x0b, 0x4b, 0x8c, 0xf9, 0x1b, 0x03, 0xc6, 0x54, 0x31, 0x83, 0x5e, 0x82, 0xbc, 0xed, 0x3a, + 0x4c, 0xc5, 0xd7, 0x11, 0xcb, 0xa7, 0x44, 0xc9, 0x72, 0xed, 0x2a, 0xc6, 0x52, 0x20, 0x7a, 0x15, + 0x46, 0xc9, 0x75, 0x9b, 0x04, 0x5c, 0xdd, 0x21, 0x47, 0x14, 0x9d, 0xac, 0x72, 0x45, 0x0a, 0xc3, + 0x4a, 0xa8, 0xf9, 0x6f, 0x03, 0x50, 0xad, 0xfe, 0xe9, 0xbd, 0x26, 0x5b, 0x50, 0x90, 0x1b, 0x84, + 0x1e, 0x84, 0x11, 0x37, 0x90, 0x6b, 0x9d, 0xa8, 0xce, 0xec, 0xef, 0xcd, 0x8f, 0xd4, 0xea, 0xd9, + 0xeb, 0x63, 0xc4, 0x0d, 0x44, 0xc5, 0x1a, 0x30, 0xd2, 0x72, 0xaf, 0xaf, 0x11, 0xbf, 0xcd, 0xb7, + 0xa4, 0x07, 0x15, 0xd2, 0xea, 0xaa, 0xae, 0xe1, 0x70, 0x86, 0xd2, 0xfc, 0xb5, 0x01, 0xb0, 0x76, + 0x39, 0x71, 0xd3, 0x97, 0x21, 0xbf, 0xc5, 0x79, 0x70, 0xd4, 0xeb, 0x58, 0x77, 0xf9, 0xe8, 0x96, + 0x10, 0x10, 0x2c, 0x65, 0xa2, 0xaf, 0x43, 0x8e, 0x7b, 0xa1, 0xba, 0x84, 0x87, 0xce, 0xf0, 0xcd, + 0xb5, 0x46, 0x22, 0x59, 0x5e, 0xf4, 0xcd, 0xb5, 0x06, 0x16, 0x02, 0xcd, 0x9f, 0x1b, 0x80, 0xd6, + 0xbb, 0x9e, 0x68, 0x9e, 0x42, 0x2e, 0xb7, 0xaf, 0xe6, 0xb7, 0x28, 0x7a, 0x10, 0x0a, 0xb2, 0x8e, + 0x54, 0x21, 0x97, 0x5c, 0x8b, 0xd1, 0xa1, 0x44, 0x38, 0xf4, 0x1a, 0xe4, 0x03, 0xea, 0x1c, 0x79, + 0xc4, 0x98, 0x49, 0x3f, 0x69, 0x28, 0x52, 0x27, 0xc4, 0x52, 0xae, 0xf9, 0xae, 0x01, 0xc5, 0xe4, + 0x6a, 0x96, 0xa1, 0x4b, 0x59, 0x74, 0x09, 0x14, 0x74, 0x7a, 0xc6, 0xb1, 0xc4, 0xdc, 0xc6, 0xe5, + 0x74, 0x05, 0xc6, 0x03, 0xb5, 0x0f, 0xea, 0x0a, 0xb8, 0x3f, 0xe9, 0xc6, 0x15, 0xfc, 0xa6, 0xf6, + 0x1f, 0x27, 0xd4, 0xe6, 0xdf, 0x73, 0x30, 0xb9, 0x41, 0xf8, 0x9b, 0x94, 0x6d, 0xd7, 0xa9, 0xe7, + 0xda, 0xbb, 0xa7, 0x10, 0x4d, 0x2d, 0x28, 0xb0, 0xae, 0x47, 0xe2, 0x0d, 0x5e, 0x1a, 0x3a, 0xef, + 0xe8, 0xf6, 0xe2, 0xae, 0x47, 0xd2, 0x73, 0x14, 0x4f, 0x21, 0x8e, 0xc4, 0xa3, 0xa7, 0xe1, 0xac, + 0x95, 0x99, 0x3a, 0x45, 0x29, 0xb7, 0x28, 0x43, 0xe6, 0x6c, 0x76, 0x20, 0x15, 0xe2, 0x5e, 0x5a, + 0x74, 0x49, 0x6c, 0xaa, 0x4b, 0x99, 0x28, 0x12, 0x44, 0xb7, 0x67, 0x54, 0x27, 0xa2, 0x0d, 0x8d, + 0x60, 0x38, 0xc1, 0xa2, 0x47, 0x61, 0x82, 0xbb, 0x84, 0xc5, 0x18, 0x99, 0x4f, 0x0b, 0xd5, 0x69, + 0xd9, 0x17, 0x6a, 0x70, 0x9c, 0xa1, 0x42, 0x21, 0x14, 0x43, 0xda, 0x65, 0x32, 0xc1, 0xa9, 0x14, + 0x79, 0xed, 0x78, 0x5b, 0x91, 0x78, 0xdd, 0xa4, 0x48, 0x74, 0x8d, 0x58, 0x38, 0x4e, 0xf5, 0x98, + 0x7f, 0x34, 0xe0, 0x5c, 0x86, 0xe9, 0x14, 0x5a, 0xa7, 0xcd, 0x6c, 0xeb, 0xf4, 0xf4, 0xb1, 0x16, + 0x39, 0xa0, 0x79, 0xfa, 0x87, 0x01, 0xe7, 0x33, 0x74, 0xa2, 0x12, 0x69, 0x70, 0x8b, 0x77, 0x43, + 0xf4, 0x30, 0x8c, 0x8b, 0x8a, 0x64, 0xe3, 0x80, 0xc9, 0xd6, 0x86, 0x82, 0xe3, 0x84, 0x42, 0xb4, + 0xeb, 0xea, 0x8d, 0x8e, 0x4b, 0x7d, 0x19, 0x73, 0x5a, 0xbb, 0xbe, 0x9a, 0x60, 0xb0, 0x46, 0x85, + 0xbe, 0x06, 0x88, 0x11, 0xcb, 0x73, 0xdf, 0x92, 0x8f, 0xd7, 0x2c, 0xd7, 0xeb, 0x32, 0x22, 0x23, + 0x71, 0xbc, 0x7a, 0xaf, 0xe2, 0x45, 0xb8, 0x8f, 0x02, 0x1f, 0xc0, 0x85, 0x3e, 0x0f, 0x63, 0x1d, + 0x12, 0x86, 0xa2, 0xed, 0xcf, 0x4b, 0x63, 0xcf, 0x2a, 0x01, 0x63, 0xeb, 0x11, 0x18, 0xc7, 0x78, + 0xf9, 0xa6, 0x22, 0xb3, 0xe8, 0x3a, 0x21, 0x0c, 0x5d, 0x86, 0x49, 0x4b, 0x7b, 0x7d, 0x11, 0xce, + 0x1a, 0xd2, 0xe9, 0xcf, 0xed, 0xef, 0xcd, 0x4f, 0xea, 0xef, 0x35, 0x42, 0x9c, 0xa5, 0x43, 0x04, + 0xc6, 0xdd, 0x40, 0x4d, 0x56, 0xa2, 0xa3, 0xba, 0x3c, 0x7c, 0xfe, 0x96, 0xfc, 0xe9, 0x06, 0x27, + 0x23, 0x95, 0x44, 0x34, 0x9a, 0x87, 0x42, 0xeb, 0x0d, 0xc7, 0x8f, 0x83, 0xb1, 0x28, 0xce, 0xf2, + 0xda, 0x0b, 0x57, 0x37, 0x42, 0x1c, 0xc1, 0x11, 0x07, 0xe0, 0x54, 0x95, 0x79, 0x71, 0xed, 0x7b, + 0xfc, 0xe2, 0x51, 0x1b, 0xb9, 0xc4, 0xb2, 0xb1, 0xa6, 0x47, 0xdc, 0x16, 0x9e, 0xb5, 0x49, 0xbc, + 0x9a, 0x43, 0x44, 0x95, 0xee, 0xca, 0x59, 0x4d, 0xee, 0xd2, 0x64, 0x74, 0x5b, 0xac, 0x65, 0x51, + 0xb8, 0x97, 0xd6, 0xfc, 0xd8, 0x80, 0x7b, 0x0e, 0x8e, 0x46, 0xf4, 0x18, 0xe4, 0x45, 0x21, 0xa8, + 0x7c, 0xef, 0x81, 0xf8, 0xfe, 0x6e, 0xee, 0x06, 0xe4, 0xe6, 0xde, 0x7c, 0xf6, 0x04, 0x05, 0x10, + 0x4b, 0xf2, 0xa1, 0x7b, 0xc8, 0x24, 0x4f, 0xe4, 0x0e, 0x2b, 0x62, 0xf3, 0xc7, 0x29, 0x62, 0x7f, + 0x3b, 0xda, 0xe3, 0x74, 0xe2, 0xce, 0x45, 0x4f, 0x41, 0xd1, 0x71, 0x19, 0xb1, 0x65, 0xd0, 0x44, + 0x0b, 0x9d, 0x8b, 0x8d, 0xbd, 0x1a, 0x23, 0x6e, 0xea, 0x0f, 0x38, 0x65, 0x40, 0x36, 0xe4, 0x5b, + 0x8c, 0x76, 0x54, 0x19, 0x70, 0xbc, 0x84, 0x20, 0x62, 0x20, 0x5d, 0xfc, 0x35, 0x46, 0x3b, 0x58, + 0x0a, 0x47, 0xaf, 0xc2, 0x08, 0xa7, 0x6a, 0x7c, 0x7b, 0x02, 0x2a, 0x40, 0xa9, 0x18, 0x69, 0x52, + 0x3c, 0xc2, 0xa9, 0x88, 0x9e, 0x30, 0xeb, 0xb3, 0x97, 0x8f, 0xe8, 0xb3, 0x69, 0xf4, 0x24, 0x8e, + 0x9a, 0x88, 0x96, 0x83, 0xf7, 0x9e, 0x3c, 0x93, 0xa6, 0xfa, 0xbe, 0xcc, 0xf4, 0x12, 0x8c, 0x5a, + 0xd1, 0x99, 0x8c, 0xca, 0x33, 0xf9, 0x8a, 0x1c, 0x74, 0xc7, 0x87, 0xb1, 0x70, 0x8b, 0xcf, 0x0a, + 0x98, 0x93, 0xbc, 0xe4, 0x2f, 0x8b, 0x13, 0x8e, 0x98, 0xb0, 0x12, 0x87, 0x9e, 0x84, 0x49, 0xe2, + 0x5b, 0x9b, 0x1e, 0x59, 0xa3, 0xed, 0xb6, 0xeb, 0xb7, 0x67, 0xc7, 0xe4, 0x65, 0x77, 0xb7, 0xb2, + 0x65, 0x72, 0x45, 0x47, 0xe2, 0x2c, 0xed, 0x41, 0x89, 0x79, 0x7c, 0x88, 0xc4, 0x1c, 0xfb, 0x79, + 0x71, 0xa0, 0x9f, 0xbf, 0x01, 0x25, 0x2f, 0xa9, 0x5f, 0xc3, 0x59, 0x90, 0xc7, 0xf1, 0xc4, 0xb0, + 0xc7, 0x91, 0x96, 0xc0, 0xe9, 0xe8, 0x35, 0x85, 0x85, 0x58, 0xd7, 0x21, 0xce, 0xc5, 0xa3, 0x6d, + 0x79, 0x4d, 0xcc, 0x96, 0xb2, 0x49, 0x66, 0x4d, 0xc1, 0x71, 0x42, 0x61, 0xbe, 0x97, 0x03, 0x94, + 0x71, 0x29, 0x91, 0xaa, 0x42, 0xf4, 0x8e, 0x01, 0x93, 0xbe, 0x0e, 0x56, 0xd9, 0xf8, 0xa4, 0xea, + 0x82, 0xe4, 0x78, 0xb2, 0xf8, 0xac, 0x4e, 0x14, 0xc0, 0x04, 0x67, 0x56, 0xab, 0xe5, 0xda, 0xd2, + 0x2a, 0x15, 0x95, 0x8f, 0xdf, 0xc2, 0x06, 0xf9, 0x51, 0x48, 0x39, 0xf1, 0x97, 0xa6, 0xc6, 0xad, + 0x8d, 0xc8, 0x35, 0x28, 0xce, 0x68, 0x40, 0x6f, 0x1b, 0x30, 0x2d, 0x6a, 0x36, 0x9d, 0x44, 0x4d, + 0xfd, 0x9e, 0xb8, 0x7d, 0xb5, 0xb8, 0x47, 0x42, 0xda, 0x5b, 0xf5, 0x62, 0x70, 0x9f, 0x36, 0xf3, + 0xaf, 0x06, 0xcc, 0xf4, 0x9d, 0x48, 0xf7, 0x34, 0xde, 0xae, 0x78, 0x50, 0x10, 0xc5, 0x47, 0x9c, + 0x73, 0x57, 0x8f, 0x75, 0xd6, 0x69, 0xd9, 0x93, 0x16, 0x4a, 0x02, 0x16, 0xe2, 0x48, 0x89, 0xb9, + 0x00, 0x93, 0x99, 0xb9, 0xcd, 0xe1, 0xc3, 0x4c, 0xf3, 0xfd, 0x02, 0x4c, 0xc7, 0x72, 0xc3, 0x46, + 0xb7, 0xd3, 0xb1, 0xd8, 0x69, 0xb4, 0x09, 0xdf, 0x33, 0xe0, 0xac, 0xee, 0x98, 0x6e, 0xb2, 0x45, + 0xd5, 0x63, 0x6d, 0x51, 0xe4, 0x1b, 0xe7, 0x95, 0xee, 0xb3, 0x1b, 0x59, 0x15, 0xb8, 0x57, 0x27, + 0xfa, 0x95, 0x01, 0xf7, 0x47, 0x5a, 0xd4, 0xdb, 0xb7, 0x1e, 0x0e, 0xe5, 0xa8, 0x27, 0x61, 0xd4, + 0xff, 0x2b, 0xa3, 0xee, 0x5f, 0xba, 0x85, 0x3e, 0x7c, 0x4b, 0x6b, 0xd0, 0xcf, 0x0c, 0xb8, 0x3b, + 0x22, 0xe8, 0xb5, 0x33, 0x7f, 0x62, 0x76, 0x5e, 0x50, 0x76, 0xde, 0xbd, 0x74, 0x90, 0x22, 0x7c, + 0xb0, 0x7e, 0xd1, 0xf0, 0x74, 0xe2, 0x96, 0x5c, 0xd6, 0x56, 0x47, 0x30, 0xa6, 0xbf, 0xa7, 0x4f, + 0x8b, 0xa2, 0x04, 0x87, 0x53, 0x3d, 0xe6, 0xab, 0x70, 0x57, 0xdd, 0x6a, 0xbb, 0xbe, 0xac, 0xb1, + 0x57, 0x09, 0x7f, 0x3e, 0x10, 0x7f, 0xc2, 0x68, 0x62, 0xd6, 0x8e, 0xdc, 0x3e, 0xa7, 0x4f, 0xcc, + 0xda, 0x04, 0x4b, 0x0c, 0x7a, 0x10, 0x0a, 0x9e, 0xdb, 0x71, 0xb9, 0xea, 0x01, 0x92, 0x70, 0x5a, + 0x13, 0x40, 0x1c, 0xe1, 0x4c, 0x0b, 0x26, 0xf4, 0x7e, 0xff, 0x4e, 0xbc, 0x1a, 0xf8, 0x5d, 0x0e, + 0xe2, 0xa1, 0x27, 0x7a, 0x54, 0x6b, 0xf4, 0x23, 0x15, 0xb3, 0x87, 0x37, 0xf9, 0x68, 0x43, 0x8d, + 0x18, 0x46, 0x0e, 0x89, 0xd3, 0x2e, 0x77, 0xbd, 0x72, 0xf4, 0x55, 0x5b, 0xb9, 0xe6, 0xf3, 0xe7, + 0x59, 0x83, 0x33, 0xd7, 0x6f, 0x47, 0x43, 0x1b, 0x6d, 0x20, 0xf1, 0x39, 0x18, 0x23, 0xbe, 0x9c, + 0x5e, 0xc8, 0x72, 0xaa, 0x10, 0x0d, 0x66, 0x57, 0x22, 0x10, 0x8e, 0x71, 0xa2, 0x81, 0x76, 0xed, + 0x4e, 0x20, 0x4a, 0x5a, 0x59, 0x72, 0x16, 0xa2, 0x06, 0xba, 0xb6, 0xbc, 0x5e, 0x97, 0x65, 0x6e, + 0x82, 0x8d, 0x29, 0x97, 0xe3, 0x61, 0xb4, 0x46, 0x29, 0x60, 0x38, 0xc1, 0x4a, 0xca, 0xb6, 0x92, + 0x39, 0xaa, 0x51, 0xae, 0x26, 0x32, 0x15, 0x16, 0x5d, 0x51, 0x6f, 0x26, 0x55, 0xcb, 0x23, 0x0b, + 0x94, 0x62, 0xcf, 0xcb, 0xc5, 0x78, 0x5c, 0x96, 0xa1, 0x14, 0xcb, 0x0b, 0x99, 0x2d, 0x97, 0x37, + 0x9e, 0x2e, 0xaf, 0x11, 0x81, 0x70, 0x8c, 0x43, 0x65, 0x80, 0x90, 0xd9, 0x6a, 0xd5, 0xb2, 0x18, + 0x29, 0x54, 0xa7, 0xc4, 0x6d, 0xd6, 0x48, 0xa0, 0x58, 0xa3, 0x30, 0x09, 0x4c, 0xf7, 0x36, 0x25, + 0x77, 0xc2, 0x5d, 0xde, 0xcb, 0xc3, 0xf9, 0x46, 0x37, 0x10, 0x07, 0x15, 0x7d, 0x3f, 0xb1, 0x4c, + 0x3d, 0x4f, 0xd5, 0xd9, 0x77, 0xfe, 0xd2, 0x7e, 0x05, 0x8a, 0xe4, 0x7a, 0xe0, 0x32, 0xe2, 0x2c, + 0xc5, 0xfe, 0xf6, 0x85, 0xdb, 0x53, 0xd1, 0x74, 0x3b, 0x24, 0x5d, 0xda, 0x4a, 0x2c, 0x04, 0xa7, + 0xf2, 0xc4, 0x5e, 0x84, 0xae, 0x6f, 0x13, 0x41, 0xaa, 0xba, 0x9c, 0x84, 0xa1, 0x11, 0x23, 0x70, + 0x4a, 0x23, 0x3a, 0xc9, 0x56, 0xf2, 0xc5, 0x89, 0xf4, 0xc1, 0x23, 0x74, 0x92, 0xbd, 0x5f, 0xae, + 0xa4, 0x3b, 0x90, 0xc2, 0xb0, 0xa6, 0x07, 0xfd, 0xc8, 0x80, 0x29, 0x2b, 0xfb, 0xd1, 0x48, 0xf4, + 0x86, 0x65, 0xfd, 0x68, 0xaa, 0x07, 0x7c, 0x00, 0x53, 0xbd, 0x47, 0xd9, 0x31, 0xd5, 0xf3, 0xf5, + 0x48, 0x8f, 0x72, 0xf3, 0x13, 0x03, 0xee, 0x1b, 0xe0, 0x11, 0xa7, 0x30, 0xfd, 0xf1, 0xb2, 0xd3, + 0x9f, 0xa1, 0xcb, 0x9b, 0x01, 0x96, 0x0f, 0x98, 0x03, 0xfd, 0x74, 0x04, 0x1e, 0x18, 0xc0, 0x71, + 0xe4, 0x89, 0xd0, 0x93, 0x30, 0x19, 0xff, 0xd7, 0xc3, 0x30, 0x2d, 0xa6, 0x75, 0x24, 0xce, 0xd2, + 0xc6, 0xaa, 0xe4, 0x85, 0x95, 0xeb, 0x57, 0x15, 0x5d, 0x5a, 0x31, 0x85, 0xf0, 0x70, 0x9b, 0x76, + 0x02, 0x8f, 0x70, 0x12, 0xb5, 0xe9, 0xe3, 0xa9, 0x87, 0x2f, 0xc7, 0x08, 0x9c, 0xd2, 0x88, 0x24, + 0x45, 0x18, 0xa3, 0x4c, 0x7a, 0x98, 0x36, 0xd0, 0x5e, 0x11, 0x40, 0x1c, 0xe1, 0xcc, 0x7f, 0x1a, + 0x70, 0x61, 0xc0, 0xa6, 0x9c, 0x5a, 0x95, 0xbb, 0x93, 0xad, 0x72, 0x5f, 0x38, 0x21, 0x37, 0x38, + 0xb4, 0xde, 0x7d, 0x18, 0x4a, 0xda, 0x5b, 0x02, 0x74, 0x01, 0x72, 0xa1, 0xef, 0xf6, 0x7e, 0x75, + 0xd6, 0xd8, 0xa8, 0x61, 0x01, 0xaf, 0x36, 0x3f, 0xb8, 0x31, 0x77, 0xe6, 0xc3, 0x1b, 0x73, 0x67, + 0x3e, 0xba, 0x31, 0x77, 0xe6, 0xed, 0xfd, 0x39, 0xe3, 0x83, 0xfd, 0x39, 0xe3, 0xc3, 0xfd, 0x39, + 0xe3, 0xa3, 0xfd, 0x39, 0xe3, 0xcf, 0xfb, 0x73, 0xc6, 0x4f, 0xfe, 0x32, 0x77, 0xe6, 0xe5, 0xf2, + 0x70, 0x9f, 0xe3, 0xff, 0x27, 0x00, 0x00, 0xff, 0xff, 0x5c, 0xc7, 0x62, 0xe3, 0xbf, 0x2f, 0x00, + 0x00, } func (m *AddressGroup) Marshal() (dAtA []byte, err error) { @@ -2490,6 +2522,18 @@ func (m *L7Protocol) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.TLS != nil { + { + size, err := m.TLS.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if m.HTTP != nil { { size, err := m.HTTP.MarshalToSizedBuffer(dAtA[:i]) @@ -3591,6 +3635,34 @@ func (m *SupportBundleCollectionStatus) MarshalToSizedBuffer(dAtA []byte) (int, return len(dAtA) - i, nil } +func (m *TLSProtocol) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TLSProtocol) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TLSProtocol) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + i -= len(m.SNI) + copy(dAtA[i:], m.SNI) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.SNI))) + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int { offset -= sovGenerated(v) base := offset @@ -3995,6 +4067,10 @@ func (m *L7Protocol) Size() (n int) { l = m.HTTP.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.TLS != nil { + l = m.TLS.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -4419,6 +4495,17 @@ func (m *SupportBundleCollectionStatus) Size() (n int) { return n } +func (m *TLSProtocol) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SNI) + n += 1 + l + sovGenerated(uint64(l)) + return n +} + func sovGenerated(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -4765,6 +4852,7 @@ func (this *L7Protocol) String() string { } s := strings.Join([]string{`&L7Protocol{`, `HTTP:` + strings.Replace(this.HTTP.String(), "HTTPProtocol", "HTTPProtocol", 1) + `,`, + `TLS:` + strings.Replace(this.TLS.String(), "TLSProtocol", "TLSProtocol", 1) + `,`, `}`, }, "") return s @@ -5101,6 +5189,16 @@ func (this *SupportBundleCollectionStatus) String() string { }, "") return s } +func (this *TLSProtocol) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&TLSProtocol{`, + `SNI:` + fmt.Sprintf("%v", this.SNI) + `,`, + `}`, + }, "") + return s +} func valueToStringGenerated(v interface{}) string { rv := reflect.ValueOf(v) if rv.IsNil() { @@ -8003,6 +8101,42 @@ func (m *L7Protocol) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TLS", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.TLS == nil { + m.TLS = &TLSProtocol{} + } + if err := m.TLS.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -11381,6 +11515,88 @@ func (m *SupportBundleCollectionStatus) Unmarshal(dAtA []byte) error { } return nil } +func (m *TLSProtocol) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TLSProtocol: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TLSProtocol: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SNI", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SNI = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenerated(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenerated + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipGenerated(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/pkg/apis/controlplane/v1beta2/generated.proto b/pkg/apis/controlplane/v1beta2/generated.proto index 30537d49d45..017eba18c9d 100644 --- a/pkg/apis/controlplane/v1beta2/generated.proto +++ b/pkg/apis/controlplane/v1beta2/generated.proto @@ -226,6 +226,8 @@ message IPNet { // L7Protocol defines application layer protocol to match. message L7Protocol { optional HTTPProtocol http = 1; + + optional TLSProtocol tls = 2; } // MulticastGroupInfo contains the list of Pods that have joined a multicast group, for a given Node. @@ -525,3 +527,10 @@ message SupportBundleCollectionStatus { repeated SupportBundleCollectionNodeStatus nodes = 2; } +// TLSProtocol matches TLS handshake packets with specific SNI. If the field is not provided, this +// matches all TLS handshake packets. +message TLSProtocol { + // SNI (Server Name Indication) indicates the server domain name in the TLS/SSL hello message. + optional string sni = 1; +} + diff --git a/pkg/apis/controlplane/v1beta2/types.go b/pkg/apis/controlplane/v1beta2/types.go index fb45dbae753..27c00607b45 100644 --- a/pkg/apis/controlplane/v1beta2/types.go +++ b/pkg/apis/controlplane/v1beta2/types.go @@ -312,6 +312,7 @@ type Service struct { // L7Protocol defines application layer protocol to match. type L7Protocol struct { HTTP *HTTPProtocol `json:"http,omitempty" protobuf:"bytes,1,opt,name=http"` + TLS *TLSProtocol `json:"tls,omitempty" protobuf:"bytes,2,opt,name=tls"` } // HTTPProtocol matches HTTP requests with specific host, method, and path. All fields could be used alone or together. @@ -327,6 +328,13 @@ type HTTPProtocol struct { Path string `json:"path,omitempty" protobuf:"bytes,3,opt,name=path"` } +// TLSProtocol matches TLS handshake packets with specific SNI. If the field is not provided, this +// matches all TLS handshake packets. +type TLSProtocol struct { + // SNI (Server Name Indication) indicates the server domain name in the TLS/SSL hello message. + SNI string `json:"sni,omitempty" protobuf:"bytes,1,opt,name=sni"` +} + // NetworkPolicyPeer describes a peer of NetworkPolicyRules. // It could be a list of names of AddressGroups and/or a list of IPBlock. type NetworkPolicyPeer struct { diff --git a/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go b/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go index ea5bbdf83b1..2f96026c1df 100644 --- a/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go +++ b/pkg/apis/controlplane/v1beta2/zz_generated.conversion.go @@ -459,6 +459,16 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddGeneratedConversionFunc((*TLSProtocol)(nil), (*controlplane.TLSProtocol)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta2_TLSProtocol_To_controlplane_TLSProtocol(a.(*TLSProtocol), b.(*controlplane.TLSProtocol), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*controlplane.TLSProtocol)(nil), (*TLSProtocol)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_controlplane_TLSProtocol_To_v1beta2_TLSProtocol(a.(*controlplane.TLSProtocol), b.(*TLSProtocol), scope) + }); err != nil { + return err + } if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*PaginationGetOptions)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_url_Values_To_v1beta2_PaginationGetOptions(a.(*url.Values), b.(*PaginationGetOptions), scope) }); err != nil { @@ -1221,6 +1231,7 @@ func Convert_controlplane_IPNet_To_v1beta2_IPNet(in *controlplane.IPNet, out *IP func autoConvert_v1beta2_L7Protocol_To_controlplane_L7Protocol(in *L7Protocol, out *controlplane.L7Protocol, s conversion.Scope) error { out.HTTP = (*controlplane.HTTPProtocol)(unsafe.Pointer(in.HTTP)) + out.TLS = (*controlplane.TLSProtocol)(unsafe.Pointer(in.TLS)) return nil } @@ -1231,6 +1242,7 @@ func Convert_v1beta2_L7Protocol_To_controlplane_L7Protocol(in *L7Protocol, out * func autoConvert_controlplane_L7Protocol_To_v1beta2_L7Protocol(in *controlplane.L7Protocol, out *L7Protocol, s conversion.Scope) error { out.HTTP = (*HTTPProtocol)(unsafe.Pointer(in.HTTP)) + out.TLS = (*TLSProtocol)(unsafe.Pointer(in.TLS)) return nil } @@ -1857,3 +1869,23 @@ func autoConvert_controlplane_SupportBundleCollectionStatus_To_v1beta2_SupportBu func Convert_controlplane_SupportBundleCollectionStatus_To_v1beta2_SupportBundleCollectionStatus(in *controlplane.SupportBundleCollectionStatus, out *SupportBundleCollectionStatus, s conversion.Scope) error { return autoConvert_controlplane_SupportBundleCollectionStatus_To_v1beta2_SupportBundleCollectionStatus(in, out, s) } + +func autoConvert_v1beta2_TLSProtocol_To_controlplane_TLSProtocol(in *TLSProtocol, out *controlplane.TLSProtocol, s conversion.Scope) error { + out.SNI = in.SNI + return nil +} + +// Convert_v1beta2_TLSProtocol_To_controlplane_TLSProtocol is an autogenerated conversion function. +func Convert_v1beta2_TLSProtocol_To_controlplane_TLSProtocol(in *TLSProtocol, out *controlplane.TLSProtocol, s conversion.Scope) error { + return autoConvert_v1beta2_TLSProtocol_To_controlplane_TLSProtocol(in, out, s) +} + +func autoConvert_controlplane_TLSProtocol_To_v1beta2_TLSProtocol(in *controlplane.TLSProtocol, out *TLSProtocol, s conversion.Scope) error { + out.SNI = in.SNI + return nil +} + +// Convert_controlplane_TLSProtocol_To_v1beta2_TLSProtocol is an autogenerated conversion function. +func Convert_controlplane_TLSProtocol_To_v1beta2_TLSProtocol(in *controlplane.TLSProtocol, out *TLSProtocol, s conversion.Scope) error { + return autoConvert_controlplane_TLSProtocol_To_v1beta2_TLSProtocol(in, out, s) +} diff --git a/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go b/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go index 0e70afea3f5..68e7b231e2a 100644 --- a/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go +++ b/pkg/apis/controlplane/v1beta2/zz_generated.deepcopy.go @@ -672,6 +672,11 @@ func (in *L7Protocol) DeepCopyInto(out *L7Protocol) { *out = new(HTTPProtocol) **out = **in } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSProtocol) + **out = **in + } return } @@ -1270,3 +1275,19 @@ func (in *SupportBundleCollectionStatus) DeepCopyObject() runtime.Object { } return nil } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSProtocol) DeepCopyInto(out *TLSProtocol) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSProtocol. +func (in *TLSProtocol) DeepCopy() *TLSProtocol { + if in == nil { + return nil + } + out := new(TLSProtocol) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/controlplane/zz_generated.deepcopy.go b/pkg/apis/controlplane/zz_generated.deepcopy.go index 187e4056a5f..3d1f309b2a0 100644 --- a/pkg/apis/controlplane/zz_generated.deepcopy.go +++ b/pkg/apis/controlplane/zz_generated.deepcopy.go @@ -672,6 +672,11 @@ func (in *L7Protocol) DeepCopyInto(out *L7Protocol) { *out = new(HTTPProtocol) **out = **in } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSProtocol) + **out = **in + } return } @@ -1270,3 +1275,19 @@ func (in *SupportBundleCollectionStatus) DeepCopyObject() runtime.Object { } return nil } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSProtocol) DeepCopyInto(out *TLSProtocol) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSProtocol. +func (in *TLSProtocol) DeepCopy() *TLSProtocol { + if in == nil { + return nil + } + out := new(TLSProtocol) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/crd/v1alpha1/types.go b/pkg/apis/crd/v1alpha1/types.go index 8aa641950cd..86b432e3302 100644 --- a/pkg/apis/crd/v1alpha1/types.go +++ b/pkg/apis/crd/v1alpha1/types.go @@ -919,6 +919,7 @@ type BundleServerAuthConfiguration struct { type L7Protocol struct { HTTP *HTTPProtocol `json:"http,omitempty"` + TLS *TLSProtocol `json:"tls,omitempty"` } // HTTPProtocol matches HTTP requests with specific host, method, and path. All fields could be used alone or together. @@ -933,3 +934,10 @@ type HTTPProtocol struct { // Path represents the URI path to match (Ex. "/index.html", "/admin"). Path string `json:"path,omitempty"` } + +// TLSProtocol matches TLS handshake packets with specific SNI. If the field is not provided, this +// matches all TLS handshake packets. +type TLSProtocol struct { + // SNI (Server Name Indication) indicates the server domain name in the TLS/SSL hello message. + SNI string `json:"sni,omitempty"` +} diff --git a/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go index 5d637d04d43..f80a99a2c64 100644 --- a/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/crd/v1alpha1/zz_generated.deepcopy.go @@ -493,6 +493,11 @@ func (in *L7Protocol) DeepCopyInto(out *L7Protocol) { *out = new(HTTPProtocol) **out = **in } + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLSProtocol) + **out = **in + } return } @@ -1120,6 +1125,22 @@ func (in *TCPHeader) DeepCopy() *TCPHeader { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLSProtocol) DeepCopyInto(out *TLSProtocol) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSProtocol. +func (in *TLSProtocol) DeepCopy() *TLSProtocol { + if in == nil { + return nil + } + out := new(TLSProtocol) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Tier) DeepCopyInto(out *Tier) { *out = *in diff --git a/pkg/apiserver/openapi/zz_generated.openapi.go b/pkg/apiserver/openapi/zz_generated.openapi.go index dadc564dc97..6f6e76d5a8d 100644 --- a/pkg/apiserver/openapi/zz_generated.openapi.go +++ b/pkg/apiserver/openapi/zz_generated.openapi.go @@ -72,6 +72,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "antrea.io/antrea/pkg/apis/controlplane/v1beta2.SupportBundleCollectionList": schema_pkg_apis_controlplane_v1beta2_SupportBundleCollectionList(ref), "antrea.io/antrea/pkg/apis/controlplane/v1beta2.SupportBundleCollectionNodeStatus": schema_pkg_apis_controlplane_v1beta2_SupportBundleCollectionNodeStatus(ref), "antrea.io/antrea/pkg/apis/controlplane/v1beta2.SupportBundleCollectionStatus": schema_pkg_apis_controlplane_v1beta2_SupportBundleCollectionStatus(ref), + "antrea.io/antrea/pkg/apis/controlplane/v1beta2.TLSProtocol": schema_pkg_apis_controlplane_v1beta2_TLSProtocol(ref), "antrea.io/antrea/pkg/apis/crd/v1beta1.AgentCondition": schema_pkg_apis_crd_v1beta1_AgentCondition(ref), "antrea.io/antrea/pkg/apis/crd/v1beta1.AntreaAgentInfo": schema_pkg_apis_crd_v1beta1_AntreaAgentInfo(ref), "antrea.io/antrea/pkg/apis/crd/v1beta1.AntreaAgentInfoList": schema_pkg_apis_crd_v1beta1_AntreaAgentInfoList(ref), @@ -1333,11 +1334,16 @@ func schema_pkg_apis_controlplane_v1beta2_L7Protocol(ref common.ReferenceCallbac Ref: ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.HTTPProtocol"), }, }, + "tls": { + SchemaProps: spec.SchemaProps{ + Ref: ref("antrea.io/antrea/pkg/apis/controlplane/v1beta2.TLSProtocol"), + }, + }, }, }, }, Dependencies: []string{ - "antrea.io/antrea/pkg/apis/controlplane/v1beta2.HTTPProtocol"}, + "antrea.io/antrea/pkg/apis/controlplane/v1beta2.HTTPProtocol", "antrea.io/antrea/pkg/apis/controlplane/v1beta2.TLSProtocol"}, } } @@ -2407,6 +2413,26 @@ func schema_pkg_apis_controlplane_v1beta2_SupportBundleCollectionStatus(ref comm } } +func schema_pkg_apis_controlplane_v1beta2_TLSProtocol(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TLSProtocol matches TLS handshake packets with specific SNI. If the field is not provided, this matches all TLS handshake packets.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "sni": { + SchemaProps: spec.SchemaProps{ + Description: "SNI (Server Name Indication) indicates the server domain name in the TLS/SSL hello message.", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + } +} + func schema_pkg_apis_crd_v1beta1_AgentCondition(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/controller/networkpolicy/crd_utils.go b/pkg/controller/networkpolicy/crd_utils.go index e58aeee2e30..5a240436a6c 100644 --- a/pkg/controller/networkpolicy/crd_utils.go +++ b/pkg/controller/networkpolicy/crd_utils.go @@ -119,6 +119,7 @@ func toAntreaL7ProtocolsForCRD(l7Protocols []v1alpha1.L7Protocol) []controlplane for _, l7p := range l7Protocols { antreaL7Protocols = append(antreaL7Protocols, controlplane.L7Protocol{ HTTP: (*controlplane.HTTPProtocol)(l7p.HTTP), + TLS: (*controlplane.TLSProtocol)(l7p.TLS), }) } return antreaL7Protocols diff --git a/pkg/controller/networkpolicy/crd_utils_test.go b/pkg/controller/networkpolicy/crd_utils_test.go index 8b4242162ab..73714175ada 100644 --- a/pkg/controller/networkpolicy/crd_utils_test.go +++ b/pkg/controller/networkpolicy/crd_utils_test.go @@ -219,6 +219,14 @@ func TestToAntreaL7ProtocolsForCRD(t *testing.T) { {HTTP: &controlplane.HTTPProtocol{Host: "test.com", Method: "GET", Path: "/admin"}}, }, }, + { + []crdv1alpha1.L7Protocol{ + {TLS: &crdv1alpha1.TLSProtocol{SNI: "test.com"}}, + }, + []controlplane.L7Protocol{ + {TLS: &controlplane.TLSProtocol{SNI: "test.com"}}, + }, + }, } for _, table := range tables { gotValue := toAntreaL7ProtocolsForCRD(table.l7Protocol) diff --git a/test/e2e/l7networkpolicy_test.go b/test/e2e/l7networkpolicy_test.go index 698d15d19ee..c35c40dcc5e 100644 --- a/test/e2e/l7networkpolicy_test.go +++ b/test/e2e/l7networkpolicy_test.go @@ -57,6 +57,9 @@ func TestL7NetworkPolicy(t *testing.T) { t.Run("HTTP", func(t *testing.T) { testL7NetworkPolicyHTTP(t, data) }) + t.Run("TLS", func(t *testing.T) { + testL7NetworkPolicyTLS(t, data) + }) } func createL7NetworkPolicy(t *testing.T, @@ -121,7 +124,7 @@ func createL7NetworkPolicy(t *testing.T, assert.NoError(t, err) } -func probeL7NetworkPolicy(t *testing.T, data *TestData, serverPodName, clientPodName string, serverIPs []*net.IP, allowHTTPPathHostname, allowHTTPPathClientIP bool) { +func probeL7NetworkPolicyHTTP(t *testing.T, data *TestData, serverPodName, clientPodName string, serverIPs []*net.IP, allowHTTPPathHostname, allowHTTPPathClientIP bool) { for _, ip := range serverIPs { baseURL := net.JoinHostPort(ip.String(), "8080") @@ -166,6 +169,19 @@ func probeL7NetworkPolicy(t *testing.T, data *TestData, serverPodName, clientPod } } +func probeL7NetworkPolicyTLS(t *testing.T, data *TestData, clientPodName string, serverName string, canAccess bool) { + url := fmt.Sprintf("https://%s", serverName) + assert.NoError(t, wait.PollImmediate(time.Second, 5*time.Second, func() (bool, error) { + _, _, err := data.runWgetCommandFromTestPodWithRetry(clientPodName, data.testNamespace, agnhostContainerName, url, 5) + if canAccess && err != nil { + return false, err + } else if !canAccess && err == nil { + return false, fmt.Errorf("should not be able to access to the server") + } + return true, nil + })) +} + func testL7NetworkPolicyHTTP(t *testing.T, data *TestData) { clientPodName := "test-l7-http-client-selected" clientPodLabels := map[string]string{"test-l7-http-e2e": "client"} @@ -227,7 +243,7 @@ func testL7NetworkPolicyHTTP(t *testing.T, data *TestData) { // the first L7 NetworkPolicy has higher priority, matched packets will be only matched by the first L7 NetworkPolicy. // As a result, only HTTP path 'hostname' is allowed by the first L7 NetworkPolicy, other HTTP path like 'clientip' // will be rejected. - probeL7NetworkPolicy(t, data, serverPodName, clientPodName, serverIPs, true, false) + probeL7NetworkPolicyHTTP(t, data, serverPodName, clientPodName, serverIPs, true, false) // Delete the first L7 NetworkPolicy that only allows HTTP path 'hostname'. data.crdClient.CrdV1alpha1().NetworkPolicies(data.testNamespace).Delete(context.TODO(), policyAllowPathHostname, metav1.DeleteOptions{}) @@ -235,7 +251,7 @@ func testL7NetworkPolicyHTTP(t *testing.T, data *TestData) { // Since the fist L7 NetworkPolicy has been deleted, corresponding packets will be matched by the second L7 NetworkPolicy, // and the second L7 NetworkPolicy allows any HTTP path, then both path 'hostname' and 'clientip' are allowed. - probeL7NetworkPolicy(t, data, serverPodName, clientPodName, serverIPs, true, true) + probeL7NetworkPolicyHTTP(t, data, serverPodName, clientPodName, serverIPs, true, true) data.crdClient.CrdV1alpha1().NetworkPolicies(data.testNamespace).Delete(context.TODO(), policyAllowAnyPath, metav1.DeleteOptions{}) }) @@ -254,7 +270,7 @@ func testL7NetworkPolicyHTTP(t *testing.T, data *TestData) { // the first L7 NetworkPolicy has higher priority, matched packets will be only matched by the first L7 NetworkPolicy. // As a result, only HTTP path 'hostname' is allowed by the first L7 NetworkPolicy, other HTTP path like 'clientip' // will be rejected. - probeL7NetworkPolicy(t, data, serverPodName, clientPodName, serverIPs, true, false) + probeL7NetworkPolicyHTTP(t, data, serverPodName, clientPodName, serverIPs, true, false) // Delete the first L7 NetworkPolicy that only allows HTTP path 'hostname'. data.crdClient.CrdV1alpha1().NetworkPolicies(data.testNamespace).Delete(context.TODO(), policyAllowPathHostname, metav1.DeleteOptions{}) @@ -262,6 +278,54 @@ func testL7NetworkPolicyHTTP(t *testing.T, data *TestData) { // Since the fist L7 NetworkPolicy has been deleted, corresponding packets will be matched by the second L7 NetworkPolicy, // and the second L7 NetworkPolicy allows any HTTP path, then both path 'hostname' and 'clientip' are allowed. - probeL7NetworkPolicy(t, data, serverPodName, clientPodName, serverIPs, true, true) + probeL7NetworkPolicyHTTP(t, data, serverPodName, clientPodName, serverIPs, true, true) }) } + +func testL7NetworkPolicyTLS(t *testing.T, data *TestData) { + clientPodName := "test-l7-tls-client-selected" + clientPodLabels := map[string]string{"test-l7-tls-e2e": "client"} + cmd := []string{"bash", "-c", "sleep 3600"} + + // Create a client Pod which will be selected by test L7 NetworkPolices. + require.NoError(t, NewPodBuilder(clientPodName, data.testNamespace, agnhostImage).OnNode(nodeName(0)).WithCommand(cmd).WithLabels(clientPodLabels).Create(data)) + if _, err := data.podWaitForIPs(defaultTimeout, clientPodName, data.testNamespace); err != nil { + t.Fatalf("Error when waiting for IP for Pod '%s': %v", clientPodName, err) + } + require.NoError(t, data.podWaitForRunning(defaultTimeout, clientPodName, data.testNamespace)) + + l7ProtocolAllowsGoogle := []crdv1alpha1.L7Protocol{ + { + TLS: &crdv1alpha1.TLSProtocol{ + SNI: "*.google.com", + }, + }, + } + l7ProtocolAllowsFacebook := []crdv1alpha1.L7Protocol{ + { + TLS: &crdv1alpha1.TLSProtocol{ + SNI: "*.facebook.com", + }, + }, + } + + policyAllowSNI1 := "test-l7-tls-allow-sni-google" + policyAllowSNI2 := "test-l7-tls-allow-sni-facebook" + + // Create two L7 NetworkPolicies, one allows server name '*.google.com', the other allows '*.facebook.com'. Note + // that, the priority of the first one is higher than the second one, and they have the same appliedTo labels and Pod + // selector labels. + createL7NetworkPolicy(t, data, false, policyAllowSNI1, 1, nil, clientPodLabels, ProtocolTCP, 443, l7ProtocolAllowsGoogle) + createL7NetworkPolicy(t, data, false, policyAllowSNI2, 2, nil, clientPodLabels, ProtocolTCP, 443, l7ProtocolAllowsFacebook) + time.Sleep(networkPolicyDelay) + + probeL7NetworkPolicyTLS(t, data, clientPodName, "apis.google.com", true) + probeL7NetworkPolicyTLS(t, data, clientPodName, "www.facebook.com", false) + + // Delete the first L7 NetworkPolicy that allows server name '*.google.com'. + data.crdClient.CrdV1alpha1().NetworkPolicies(data.testNamespace).Delete(context.TODO(), policyAllowSNI1, metav1.DeleteOptions{}) + time.Sleep(networkPolicyDelay) + + probeL7NetworkPolicyTLS(t, data, clientPodName, "apis.google.com", false) + probeL7NetworkPolicyTLS(t, data, clientPodName, "www.facebook.com", true) +} From 20630595540d1f4c15d9721860841cbe1697f68e Mon Sep 17 00:00:00 2001 From: Hongliang Liu Date: Thu, 20 Jul 2023 14:47:11 +0800 Subject: [PATCH 2/2] Enhance L7 NetworkPolicy to support TLS protocol Signed-off-by: Hongliang Liu --- docs/antrea-l7-network-policy.md | 4 ++-- .../antrea-multicluster-leader-global.yml | 24 +++++++++---------- ...cluster.crd.antrea.io_resourceexports.yaml | 12 +++++----- ...cluster.crd.antrea.io_resourceimports.yaml | 12 +++++----- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/docs/antrea-l7-network-policy.md b/docs/antrea-l7-network-policy.md index 660a9144767..ab1831d2b12 100644 --- a/docs/antrea-l7-network-policy.md +++ b/docs/antrea-l7-network-policy.md @@ -77,7 +77,7 @@ welcome feature requests for protocols that you are interested in. ### HTTP -An example of layer 7 NetworkPolicy for the HTTP protocol is like below: +An example layer 7 NetworkPolicy for the HTTP protocol is like below: ```yaml apiVersion: crd.antrea.io/v1alpha1 @@ -262,7 +262,7 @@ spec: port: 53 - name: allow-tls-only # Allow outbound SSL/TLS handshake packets towards "*.bar.com". action: Allow # As the rule's "to" and "ports" are empty, which means it selects traffic to any network - l7Protocols: # peer's any port of any transport protocol, all outbound SSL/TLS handshake packet towards + l7Protocols: # peer's any port of any transport protocol, all outbound SSL/TLS handshake packets towards - tls: # other server names and non-SSL/non-TLS handshake packets will be automatically dropped, sni: "*.bar.com" # and subsequent rules will not be considered. ``` diff --git a/multicluster/build/yamls/antrea-multicluster-leader-global.yml b/multicluster/build/yamls/antrea-multicluster-leader-global.yml index 23b67841126..fdd9566627f 100644 --- a/multicluster/build/yamls/antrea-multicluster-leader-global.yml +++ b/multicluster/build/yamls/antrea-multicluster-leader-global.yml @@ -1204,9 +1204,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates @@ -2119,9 +2119,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates @@ -3971,9 +3971,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates @@ -4886,9 +4886,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates diff --git a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml index 02c770a7990..9b44e3db695 100644 --- a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml +++ b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceexports.yaml @@ -794,9 +794,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates @@ -1709,9 +1709,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates diff --git a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml index 5959412449e..155dfeb9901 100644 --- a/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml +++ b/multicluster/config/crd/bases/multicluster.crd.antrea.io_resourceimports.yaml @@ -792,9 +792,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates @@ -1707,9 +1707,9 @@ spec: type: string type: object tls: - description: TLSProtocol matches TLS handshakes with - specific SNI. If the field is not provided, this - matches all TLS handshakes. + description: TLSProtocol matches TLS handshake packets + with specific SNI. If the field is not provided, + this matches all TLS handshake packets. properties: sni: description: SNI (Server Name Indication) indicates