Istio를 사용한 게이트키퍼

7571 단어 opagatekeeperistio
마이크로 서비스를 관리하기 위해 Istio을 사용해 온 사람들은 그 강력한 기능에 대해 너무 놀라지 않을 것입니다. 엔터프라이즈 내에서 다양한 팀 및 조직과 작업하는 동안 Istio가 때때로 매우 복잡할 수 있음을 인정해야 합니다. 다행스럽게도 GoogleSolo 및 기타 여러 회사가 이 분야에서 계속해서 혁신을 주도하여 궁극적으로 최종 사용자로서 이점을 얻을 수 있습니다.

ZTA에 따르면 절대 신뢰하지 말고 항상 확인해야 합니다.

이 기사에서는 Istio의 올바른 구현을 시행하기 위해 PaC(일명 Policy-As-Code)를 사용하는 방법을 다루고자 합니다(절대적인 옳고 그름은 없지만 달성한 모범 사례를 따름으로써). 당분간 정확성), 예를 들어 Protocol Selection . 기본적으로 Istio는 HTTP(/2) 트래픽을 자동으로 감지할 수 있으며 그렇지 않으면 일반 TCP 트래픽으로 처리됩니다. Zen of Python에서 명시적인 것이 암시적인 것보다 낫다는 것을 가르쳐줍니다. 우리는 항상 코드의 가독성과 유지보수성을 위해 노력해야 합니다. 따라서 디자인 타임과 런타임에 이 규칙을 적용해 보겠습니다.

이 기사는 Rego에 대한 심층 분석을 위한 것이 아니며 독자에게 맡깁니다.

Istio Explicit Protocol Selection에 따르면:

This can be configured in two ways:

By the name of the port: name: <protocol>[-<suffix>].
In Kubernetes 1.18+, by the appProtocol field: appProtocol: <protocol>.



해부해보자.
  • 포트: 이름

  • 빌트인 함수re_match를 확실히 사용할 수 있지만 잠시 후에 이야기할 Gatekeeper의 ConstraintTemplate이 복잡해질 수 있습니다. 대신 split에 의해 port_name을 -하고 구성원을 확인할 수 있습니다.

    protocol := split(port_name, "-")[0]
    protocol in protocols
    


  • 포트: appProtocol

  • 멤버십을 확인하는 몇 가지 방법이 있습니다.

    #1 Unification, which is different from `:=` and `==` in Rego.
    
    protocol = protocols[_]
    
    #2 `in`, which requires to `import future.keywords`
    
    protocol in protocols
    
    #3 Set
    
    protocol_set := { p | p := input.parameters.protocols[_] }
    protocol_set[protocol]
    
    


    StyraRego Style Guide을 따르면 옵션 #2가 선호되는 방법이 됩니다.

    우리가 아직 이야기하지 않은 작은 미묘함이 하나 있습니다. Service에 포트 이름과 포트 appProtocol이 모두 있는 경우 후자는 Istio보다 우선합니다. 그렇다면 레고에서는 어떻게 표현할까요?

    규칙 내의 레고에서 AND 조건은 이 규칙을 참으로 만들려면 모든 규칙 본문이 참이어야 함을 의미합니다. OR 조건은 동일한 규칙 이름을 가진 여러 규칙에 의해 달성됩니다.

    # when port.appProtocl exists just use it and ignore port name altogether.
    _is_valid(port, protocols) {
      port.appProtocol
    
      _match_app_protocol(port.appProtocol, protocols)
    }
    
    # when port.appProtocol doesn't exit port name has to exist and match the protocols we specified.
    _is_valid(port, protocols) {
      not port.appProtocol
      port.name
    
      _match_port_name(port.name, protocols)
    }
    


    모두 합치자:

    package istio.security.protocolselection
    
    import future.keywords
    
    violation[{"msg": msg}] {
      protocols := input.parameters.protocols
    
      some port in input.review.object.spec.ports
      not _is_valid(port, protocols)
    
      msg := sprintf("port: %v name or appProtocol is invalid", [port])
    }
    
    _is_valid(port, protocols) {
      port.appProtocol
    
      _match_app_protocol(port.appProtocol, protocols)
    }
    
    _is_valid(port, protocols) {
      not port.appProtocol
      port.name
    
      _match_port_name(port.name, protocols)
    }
    
    _match_app_protocol(protocol, protocols) {
      protocol in protocols
    }
    
    _match_port_name(port_name, protocols) {
      protocol := split(port_name, "-")[0]
    
      protocol in protocols
    }
    


    이제 가장 어려운 부분이 해결되었으며 OPA Gatekeeper 으로 관심을 돌려 보겠습니다. Gatekeeper는 OPA Constraint Framework을 사용하여 정책을 설명하고 적용합니다. 현재 우리가 주목해야 할 부분은 주로 3가지입니다.
  • ContraintTemplate: 제약 조건을 적용하는 Rego와 제약 조건의 스키마를 모두 설명합니다.
  • 제약 조건: 적용해야 하는 ContraintTemplate의 내용과 방법을 설명합니다.
  • 구성: 특정 프로세스에 대한 동작을 설명합니다.

  • 1: ConstraintTemplate




    apiVersion: templates.gatekeeper.sh/v1
    kind: ConstraintTemplate
    metadata:
      annotations:
        description: Explicit protocol selection either by name or appProtocol
      name: istioexplicitprotocolselection
    spec:
      crd:
        spec:
          names:
            kind: IstioExplicitProtocolSelection
          validation:
            openAPIV3Schema:
              type: object
              properties:
                prefixes:
                  type: string
                protocols:
                  type: array
                  items:
                    type: string
      targets:
      - target: admission.k8s.gatekeeper.sh
        rego: |-
          package istio.security.protocolselection
    
          import future.keywords
    
          violation[{"msg": msg}] {
            protocols := input.parameters.protocols
    
            some port in input.review.object.spec.ports
            not _is_valid(port, protocols)
    
            msg := sprintf("port: %v name or appProtocol is invalid", [port])
          }
    
          _is_valid(port, protocols) {
            port.appProtocol
    
            _match_app_protocol(port.appProtocol, protocols)
          }
    
          _is_valid(port, protocols) {
            not port.appProtocol
            port.name
    
            _match_port_name(port.name, protocols)
          }
    
          _match_app_protocol(protocol, protocols) {
            protocol in protocols
          }
    
          _match_port_name(port_name, protocols) {
            protocol := split(port_name, "-")[0]
    
            protocol in protocols
          }
    


    2: 제약




    apiVersion: constraints.gatekeeper.sh/v1beta1
    kind: IstioExplicitProtocolSelection
    metadata:
      name: explicitprotocolselection
    spec:
      enforcementAction: deny
      match:
        kinds:
        - apiGroups:
          - ""
          kinds:
          - Service
      parameters:
        protocols:
        - http
        - https
        - http2
        - grpc
        - grpc-web
        - tcp
        - tls
    


    3: 구성



    정책을 적용하지 않으려는 모든 네임스페이스를 무시하는 데 사용할 것입니다.

    apiVersion: config.gatekeeper.sh/v1alpha1
    kind: Config
    metadata:
      name: config
      namespace: "gatekeeper-system"
    spec:
      match:
        - excludedNamespaces: ["kube-*", "istio-*"]
          processes: ["*"]
    


    짜잔!

    OPA Gatekeeper는 여기서 보여드린 것보다 더 많은 것을 가지고 있습니다. Gator, Mutation, Audit, Replication 등을 탐색하도록 남겨 두겠습니다.

    참조: https://github.com/feiyao/gatekeeper-istio

    좋은 웹페이지 즐겨찾기