ry's Tech blog

Cloud Native技術などについて書いていきます。

Kubenews #extra-001

2021-09-**

配信なし

monday.com’s Multi-Regional Architecture: A Deep Dive (click here to source)

マルチリージョンに移行することを決定するときは、パフォーマンス優先、復元力優先、プライバシー優先等の優先事項に対する設計間で作業が大きく異なるため、主な動機を理解する必要があります。

プラットフォームを複数の地域で実行するに至る3つの主な動機

  • パフォーマンス

    • サーバーを顧客の近くで実行すると、待ち時間が短くなり、接続品質が向上し(特にモバイルで)、一般的にエンドユーザーのエクスペリエンスが向上します。
  • レジリエンス

    • 任意のリージョンにおいて任意の時点でユーザーにサービスを提供できる場合、複数のリージョンでシステムを同時に実行することは、システムの全体的なフォールトトレランスの向上、ダウンタイムの短縮につながる。
  • プライバシーとコンプライアンス

    • 顧客が世界のどこにデータを保存するかを選択できるようにすることで、明確な競争上の優位性が生まれ、PIIや機密情報を処理するヘルスケア、銀行、企業などの厳しく規制された業界との連携への扉が開かれます。

2番目のリージョンを追加する際の課題

【課題1】 サブドメインは基本的にスラッグであるが、完全ではない。

ユニークなサブドメインを使えるものもあれば、すべての顧客が使用する共有サブドメインがも存在してしまう。

【課題2】ユーザーが誰であるかさえわからない場合がある。

ログインフローにおいて、匿名の訪問者がプラットフォームに到着し、電子メールとパスワードを入力して、最終的にアカウントにルーティングされる際に、すべてのユーザーが同じサブドメインにアクセスします。

この時、どのリージョンがログインフローを処理するのか。

【課題3】可能な限りベンダーに中立を保つ

アーキテクチャを単一のネットワークサービスプロバイダーにチェーンした場合、いざ大規模な停止が起こった際に、インフラストラクチャ全体を再構築せずに回復することが難しい。

【課題4】再現性

簡単に複製できる方法で新しいリージョンを構築したかった。

monday.comのマルチリージョンデザイン

設計の重要なポイントとして以下を上げている。

  • 各リージョンの独立
  • ユーザーは各リージョンに直接アクセス可能
  • 認証はグローバル
  • ベンダー中立性

そのほか、処理の流れの説明が書かれています。

マルチリージョンをサポートするためのアンバサダーの構成

前で説明したことを基に、マルチリージョンネットワーク、認証サービス、API構造を結び付ける構成について、深く掘り下げて説明がされています。

マルチリージョンでのアプリケーションの展開

次の3つのデプロイトポロジをサポートしているようです。

  • Regional

    • 地域ごとに分けて展開されるアプリケーションは、独立したコンポーネントとしてあらゆるリージョンに展開されます。

    • 各デプロイメントは、地域ごとに独立していて、データを共有せず、互いを認識しないようになっています。

  • Global

    • グローバルなアプリケーションは米国で一度展開され、すべての地域から内部ネットワークを介してアクセスできるようになっている。
  • Replicated

    • レプリケートされたアプリケーションはすべてのリージョンに独立してデプロイされますが、データベースに関しては単一のレプリケートされたものを共有し、それらの状態はすべてのリージョンで同じものになっている。

ロギングとモニタリング

  • Envoy アクセスログ

    • すべてのリージョンからのすべてのアクセスログを一元化されたログシステムにストリーミングします。
  • Prometheus / Grafana

  • AWS CloudWatch

    • インフラストラクチャ全体がAWSクラウド上に構築されているため、トランジットゲートウェイVPC、NLBメトリックを使用して、内部ネットワークアクティビティを追跡します。

Kubernetes Supply Chain Policy Management with Cosign and Kyverno (click here to source)

以前は、connaseiurというツールを紹介しました。

ryo-xjsbx.hatenablog.com

今回はみんな大好きkyverno!

Image Signing with Cosign

  • cosign (github)

  • Generate a keypair

$ cosign generate-key-pair
Enter password for private key:
Enter again:
Private key written to cosign.key
Public key written to cosign.pub
  • Sign a container and store the signature in the registry
$ cosign sign -key cosign.key dlorenc/demo
Enter password for private key:
Pushing signature to: index.docker.io/dlorenc/demo:sha256-87ef60f558bad79beea6425a3b28989f01dd417164150ab3baab98dcbf04def8.sig
  • Verify a container against a public key
$ cosign verify -key cosign.pub dlorenc/demo
The following checks were performed on these signatures:
  - The cosign claims were validated
  - The signatures were verified against the specified public key
{"Critical":{"Identity":{"docker-reference":""},"Image":{"Docker-manifest-digest":"sha256:87ef60f558bad79beea6425a3b28989f01dd417164150ab3baab98dcbf04def8"},"Type":"cosign container image signature"},"Optional":null}

Cosign Image Signature Verification with Kyverno

「ghcr.io/kyverno/」リポジトリの「test-verify-image」という名前で始まるのすべてのイメージを対象として、cosignの共通鍵で複合化できるのかを確認してくれる。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-image
spec:
  validationFailureAction: enforce
  background: false
  rules:
    - name: check-image
      match:
        resources:
          kinds:
            - Pod
      verifyImages:
      - image: "ghcr.io/kyverno/test-verify-image:*"
        key: |-
          – ---BEGIN PUBLIC KEY-----
          MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE8nXRh950IZbRj8Ra/N9sbqOPZrfM
          5/KAQN0/KjHcorm/J5yctVd7iEcnessRQjU917hmKO6JWVGHpDguIyakZA==
          – ---END PUBLIC KEY--- –  

署名されたimageを使ってdeployすると通ります。

kubectl create deployment signed \
--image=ghcr.io/kyverno/test-verify-image:signed

署名されてないimageを使った場合、kyvernoによって弾かれる。

kubectl create deployment unsigned \
--image=ghcr.io/kyverno/test-verify-image:unsigned

error: failed to create deployment: admission webhook "mutate.kyverno.svc" denied the request:

resource Deployment/default/unsigned was blocked due to the following policies

verify-image:
  autogen-verify-image: 'image verification failed for ghcr.io/kyverno/test-verify-image:unsigned:
    signature not found'

別のキーペアにおける鍵で署名されたimageも通りません。

kubectl create deployment signed-other \
--image=ghcr.io/kyverno/test-verify-image:signed-by-someone-else

error: failed to create deployment: admission webhook "mutate.kyverno.svc" denied the request:

resource Deployment/default/signed-other was blocked due to the following policies

verify-image:
  autogen-verify-image: 'image verification failed for ghcr.io/kyverno/test-verify-image:signed-by-someone-else:
    invalid signature'

Audit Logging in Clusters (click here to source)

Falcoを使用した監査ログの検索について書かれています。

監査ポリシー

監査ポリシーは、監査ログの構成を定義します。どのイベントが監査の対象に入るのかを制御します。

パラメータ「omitStages」を使用して、リクエストがログに記録されないようにする対象を定義できます。

監査ポリシーのYAMLファイル

apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
  - "RequestReceived"
rules:
  # Log pod changes at RequestResponse level
  - level: RequestResponse
    resources:
    - group: ""
      # Resource "pods" doesn't match requests to any subresource of pods,
      # which is consistent with the RBAC policy.
      resources: ["pods"]
  # Log "pods/log", "pods/status" at Metadata level
  - level: Metadata
    resources:
    - group: ""
      resources: ["pods/log", "pods/status"]

  # Don't log requests to a configmap called "controller-leader"
  - level: None
    resources:
    - group: ""
      resources: ["configmaps"]
      resourceNames: ["controller-leader"]

リクエストのステージ

リクエストをログに記録できる4つの段階は、次のように定義されています。

  • RequestReceived

    • サーバーが応答の発行を開始する前の段階。
  • ResponseStarted

    • リクエストのヘッダーの作成は完了したが、レスポンスの本文は送信されていない段階。
  • ResponseComplete

    • リクエストに対するレスポンスボディが完了した段階。
  • Panic

    • パニックが発生した段階。

イベントのレベル

  • None

    • このルールに一致するイベントはログに記録されません。
  • Metadata

    • リクエストのメタデータのみをログに記録し、リクエストまたはレスポンスのbodyはログに記録しません。
  • Request

    • イベントのメタデータとリクエストbodyをログに記録しますが、レスポンスbodyはログに記録しません。
  • RequestResponse

    • イベントメタデータ、リクエストおよびレスポンスのbodyをログに記録します。
    • これは最も有益なレベルですが、クラスターに高い負荷をかける可能性があります

パラメータ「rules」は、監査ポリシーYAMLファイルで必要な唯一のパラメータです。

ここで、どのリクエストをどのレベルでログに記録するかを定義できます。

たとえば、先ほどの例の最初のruleは、Podのすべての変更がRequestResponseレベルでログに記録されることを示しています。

監査のバックエンド

監査ログを外部ストレージに書き込むことに関して、2つの方法があります。

  • ログバックエンド

  • webhookバックエンド

    • イベントを外部HTTPAPIに送信します

kubescape (click here to source)

Kubernetes Hardening Guidance by NSA and CISAに基づいて、Cluster内のリソースをチェックしてくれます。

$ kubescape scan framework nsa --exclude-namespaces kube-system,kube-public
ARMO security scanner starting
[progress] Downloading framework definitions
[success] Downloaded framework
[progress] Accessing Kubernetes objects
W0831 18:12:29.875935     241 warnings.go:70] batch/v1beta1 CronJob is deprecated in v1.21+, unavailable in v1.25+; use batch/v1 CronJob
[success] Accessed successfully to Kubernetes objects, let’s start!!!
[progress] Scanning cluster
◑ [success] Done scanning cluster
[control: Allow privilege escalation] passed 👍
Description: Attackers may gain access to a container and uplift its privilege to enable excessive capabilities.
Summary - Passed:1   Failed:0   Total:1

・・・

[control: Control plane hardening] passed 👍
Description: Kubernetes control plane API is running with non-secure port enabled which allows attackers to gain unprotected access to the cluster.
Summary - Passed:1   Failed:0   Total:1

[control: Dangerous capabilities] passed 👍
Description: Giving dangerous and unnecessary capabilities for a container can increase the impact of a container compromise.
Summary - Passed:1   Failed:0   Total:1

・・・

[control: Privileged container] failed 😥
Description: Potential attackers may gain access to privileged containers and inherit access to the host resources. Therefore, it is not recommended to deploy privileged containers unless it is absolutely necessary.
   Namespace kube-image
      DaemonSet - builder
Summary - Passed:0   Failed:1   Total:1
Remediation: Change the deployment and/or pod definition to unprivileged. The securityContext.privileged should be false.

・・・

+-------------------------------------------------+------------------+---------------+-----------+
|                  CONTROL NAME                   | FAILED RESOURCES | ALL RESOURCES | % SUCCESS |
+-------------------------------------------------+------------------+---------------+-----------+
| Allow privilege escalation                      | 0                | 1             | 100%      |
| Allowed hostPath                                | 1                | 1             | 0%        |
| Anonymous requests                              | 0                | 0             | NaN       |
| Applications credentials in configuration files | 3                | 4             | 25%       |
| Automatic mapping of service account            | 3                | 3             | 0%        |
| Cluster-admin binding                           | 4                | 122           | 96%       |
| Container hostPort                              | 1                | 1             | 0%        |
| Control plane hardening                         | 0                | 1             | 100%      |
| Dangerous capabilities                          | 0                | 1             | 100%      |
| Exec into container                             | 4                | 122           | 96%       |
| Exposed dashboard                               | 0                | 2             | 100%      |
| Host PID/IPC privileges                         | 1                | 1             | 0%        |
| Immutable container filesystem                  | 1                | 1             | 0%        |
| Ingress and Egress blocked                      | 1                | 1             | 0%        |
| Insecure capabilities                           | 0                | 1             | 100%      |
| Linux hardening                                 | 1                | 1             | 0%        |
| Non-root containers                             | 0                | 1             | 100%      |
| Privileged container                            | 1                | 1             | 0%        |
| Resource policies                               | 1                | 1             | 0%        |
| hostNetwork access                              | 1                | 1             | 0%        |
+-------------------------------------------------+------------------+---------------+-----------+
|                       20                        |        23        |      267      |    91%    |
+-------------------------------------------------+------------------+---------------+-----------+

Writing a Kubernetes Validating Webhook using Python (click here to source)

Validating Webhookを実行するコンテナをPythonを用いて作成する方法について書かれています。

How to Secure Containers with Cosign and Distroless Images (click here to source)

Distroless Container Imagesとは何か。

Distroless Container Imagesは、「言語に焦点を合わせたDockerイメージからオペレーティングシステムを除いたもの」です。つまり、アプリケーションとそのランタイム依存関係のみが含まれ、他の通常のOSパッケージマネージャー、Linuxシェル、または標準のLinuxディストリビューションで通常期待されるようなプログラムは含まれません。

ソース

ディストロレスコンテナイメージと一緒にCosignが必要な理由

Cosignの必要性は、ディストロレスイメージを使用しても、タイポスクワッティング攻撃や悪意のあるイメージの受信などのセキュリティ上の脅威に直面する可能性があるためです。

Cosign検証を使用した、ディストロレスコンテナのベースイメージの検証。

こちらでは、実際にディストロレスコンテナイメージとcosignを使用したDemoを紹介してくれています。

Easy, Secure Kubernetes Authentication With Pinniped (click here to source)

Pinnipedについて、Demoを交えて説明してくれています。