iT邦幫忙

2023 iThome 鐵人賽

DAY 28
0

在介紹 Pulumi 測試時,有提到一種測試為「屬性測試」,可以用來測試所要建立的資源參數是否符合規範,也就是 Policy as Code 的功能。今天就來介紹 Pulumi 中,怎麼做 Policy as Code。

什麼是 Policy as Code ?

Policy as Code 是指將檢查資源符不符合某種規範變成以程式碼撰寫。例如檢查是否有將權限設定為 public read 的 s3 bucket、或是設定監控的 EC2 Instance。可以用程式碼的驗證規則來檢查這些資源是否符合特定的政策。如果不符合的話,可以回報哪些資源違反了政策。

而 Pulumi 也為了這個需求,製作了 Policy as Code 的工具——CrossGuard。

CrossGuard 可以使用跨程式語言撰寫 Policy as Code 的程式。不過雖說是跨語言,目前只支援了 JavaScript/TypeScript 與 Python。另外還支援使用 OPA (Open Policy Agent) 撰寫 Policy as Code。

Policy as Code 的程式碼是與 Pulumi 的 IaC 程式碼分開成不同專案,這樣可以讓我們定義一組 Policy,並套用至多個專案中重復使用。

而 Policy as Code 所使用的程式語言不需要與 IaC 專案的語言相同。也就是說,可以使用 Python 撰寫 Policy as Code,並套用到 Java 撰寫的 IaC Project 中。

開始撰寫 Policy as Code

首先建立一個全新的 Policy as Code 專案,使用 Pulumi CLI 就可以建立 Policy as Code 的專案了。與建立普通的 IaC 專案的區別在於,是使用 policy 子指令建立專案。

$ mkdir my-policy-as-code
$ cd my-policy-as-code
$ pulumi policy new aws-typescript

專案建立好後結構與一般的 IaC 專案很像,差別在於專案的檔名從 Pulumi.yaml 變成了 PulumiPolicy.yaml。

在 index.ts 中,會提供範例的 Policy as Code 的程式碼,該程式碼定義了 S3 bucket 中,不能有 public-read 的 Policy。

.
├── PulumiPolicy.yaml
├── index.ts
├── node_modules
├── package-lock.json
├── package.json
└── tsconfig.json

PolicyPack

PolicyPack 是一組 Policy 的集合,做為一個可重複套用檢查的最小單元。一個 Pulumi Policy as Code 專案中只能有一個 PolicyPack。但一個 IaC 專案可以套用很多 PolicyPack 的檢查。

例如我可以在專案中套用 資安 相關的 PolicyPack、 成本管控 相關的 PolicyPack、合規 的 PolicyPack。

  • 資安 相關的 PolicyPack 會檢查是否有不安全的 ACL、Security Group、IAM 權限是否有限制可存取的資源等
  • 成本控管 相關的 PolicyPack 可能可以檢查 stage 環境與 production 環境所使用的機器 Instance 是否符合規範
  • 合規 相關的 PolicyPack 可能會檢查是否可以開啟 Log 的服務都有開啟 Log 以供稽核等

接著我們修改專案的 index.ts 的 PolickPack 的名稱為 my-aws-policies

new PolicyPack("my-aws-policies", {
    policies: [
       ...
    ]
});

Policy

在 Pulumi 中,PolicyPack 內可以放很多 Policy。而 Policy 又分為兩種。ResourcePolicy 與 StackPolicy。

  • ResourcePolicy 是針對每個資源的獨立檢查,會在資源建立或更新檢查是否有違反 Policy。每次檢查時只能知道自己這個資源的資訊,沒辦法取得其他資源的資訊做交叉檢查。由於是在建立資源之前的檢查,因此只能針對資源的 Input 資料做檢查。
  • StackPolicy 是針對整個 Stack 部署結束後做的檢查,可以一次性的檢查整個 Stack 中的所有資源內容是否符合規範。如果要做到跨資源的檢查也只能在 StackPolicy 中做,因為 StackPolicy 才可以一次存取所有資源。由於是在資源建立後做檢查,因此是可以存取到資源建立後的狀態。

這裡的範例為檢查 VPC 是否有在 Tag 中設定 Name,因為沒有牽扯到其他資源,Tag 也是在建立資源時就會傳入的資訊屬於 Input 資料,因此適用 ResourcePolicy。

一個 Policy 的內容,需要填寫名稱、描述、驗證規則以及 enforcement level (政策遵從的等級)。

Enforcement Level

設定 Enforcement Level 的主要目的是在於告訴使用者違反這條規則的危害性高不高,主要分三種

  • advisory: 違反規則只會出現警告訊息,使用者可以選擇不修正。
  • mandatory: 違反規則時是無法正常部署雲端資源的,使用者必須要修正到沒有違反規則為止。
  • remediate: 除了違反規則時,可能會執行自動修補程式,將狀態矯正成符合 Policy 的樣子。

驗證規則

驗證規則為一個函式,用來檢查資源是否符合規範,如果發現違反規範,則回報違反。

例如以下為一個驗證規則,如果傳入的資源 Type 為 VPC,且沒有在 Tag 中設定 Name 的話,就會使用 reportViolation 回報違反規則。

function (args: ResourceValidationArgs, reportViolation) {
    if (args.type === "aws:ec2/vpc:Vpc") {
        if ((args.props.tags || {})["Name"] === undefined) {
            reportViolation("A 'Name' tag is required.");
        }
    }
},

以下為一個範例,會檢查 VPC 與 Subnet 是否都有在 Tag 中設定 Name。

{
    name: "required-name-tag",
    description: "A 'Name' tag is required for VPC, Subnet",
    enforcementLevel: "advisory",
    validateResource: [
        function (args: ResourceValidationArgs, reportViolation) {
            if (args.type === "aws:ec2/vpc:Vpc") {
                if ((args.props.tags || {})["Name"] === undefined) {
                    reportViolation("A 'Name' tag is required.");
                }
            }
        },
        function (args: ResourceValidationArgs, reportViolation) {
            if (args.type === "aws:ec2/subnet:Subnet") {
                if ((args.props.tags || {})["Name"] === undefined) {
                    reportViolation("A 'Name' tag is required.");
                }
            }
        },
    ]
},

另外 Pulumi 有提供一個型別友善的 Helper 函式,讓我們檢查資源不需要再去找他的 Type 名稱。除了可以使用型別的方式比對資源是否是某個型別,還會將 args 轉型成對應的資源型別。

{
    name: "required-name-tag",
    description: "A 'Name' tag is required for VPC, Subnet and EC2 instance",
    enforcementLevel: "advisory",
    validateResource: [
        validateResourceOfType(aws.ec2.Vpc, (vpc: UnwrappedObject<aws.ec2.Vpc>, args, reportViolation) => {
            if ((args.props.tags || {})["Name"] === undefined) {
                reportViolation("A 'Name' tag is required.");
            }
        }),
        validateResourceOfType(aws.ec2.Subnet, (vpc: UnwrappedObject<aws.ec2.Subnet>, args, reportViolation) => {
            if ((args.props.tags || {})["Name"] === undefined) {
                reportViolation("A 'Name' tag is required.");
            }
        }),
    ]
},

執行 Policy as Code 的檢查

撰寫完了 Policy as Code 後,就可以將其套用至專案中了。在 Pulumi 中,是在 IaC 專案中執行 pulumi up 或是 pulumi preview 時,傳入要檢查的 PolicyPack 至該專案中。

這邊我就挑一個 Java 撰寫的 Pulumi 專案來執行 TypeScript 撰寫的 Policy。

$ cd day06-aws-vpc-java
$ pulumi up --policy-pack ../my-aws-policy


     Type                 Name                    Plan
     pulumi:pulumi:Stack  day06-aws-vpc-java-dev


Policy Violations:
    [advisory]  my-aws-policies v0.0.1  required-name-tag (aws:ec2/vpc:Vpc: my-vpc)
    A 'Name' tag is required for VPC, Subnet and EC2 instance
    A 'Name' tag is required.

Resources:
    19 unchanged

Policy Packs run:
    Name                 Version
     (../my-aws-policy)  (local)

Do you want to perform this update?  [Use arrows to move, type to filter]
  yes
> no
  details

可以從執行結果中看到,my-vpc 違反了 required-name-tag policy,不過因為這個 Policy 是 advisory 等級的,所以下方還是可以選擇 yes 執行資源的更新。

如果 enforcement level 設為 mandatory 的話,就會執行錯誤不給繼續執行資源更新了!

Diagnostics:
  pulumi:pulumi:Stack (day06-aws-vpc-java-dev):
    error: preview failed

Policy Violations:
    [mandatory]  my-aws-policies v0.0.1  required-name-tag (aws:ec2/vpc:Vpc: my-vpc)
    A 'Name' tag is required for VPC, Subnet and EC2 instance
    A 'Name' tag is required.

預定義的 Policy

為了方便使用,Pulumi 官方有預先定義了很多常用的 Policy。主要是合規與 AWS 最佳實務的 Policy。只要安裝後,並選擇要用的 Policy,包裝成 PolicyPack,就可以直接使用。省去撰寫驗證程式的麻煩。

合規有關的 Polices 可以參考這篇文章:
Compliance Ready Policies。AWS 最佳實務可以參考 AWSGuard Policies 的文件內容。

組織層級的 Policy

最後,如果有購買 Pulumi Cloud 最高等級的方案「Business Critical」的話,可以將 Policy as Code 上傳至 Pulumi Cloud。經啟用後,組織內的專案都會自動套用啟用的 PolicyPack,不需要手動指定。

不過這是屬於最高級方案才有的功能... 平民版就還是要在 pulumi up 時自己指定 policy pack。

Reference


上一篇
[Day 27] 使用 Automation API 進行整合測試
下一篇
[Day 29] AWSx (Pulumi Crosswalk for AWS) 介紹
系列文
30 天學習 Pulumi:用各種程式語言控制雲端資源30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言