查看原文
其他

火线安全——云端火线上的安全

大可不加冰 大可不加冰 2022-12-19

这是《HashiStack 在中国》系列的第二期访谈,今天访谈的对象出乎我的意料,并不是我们常见的 IaC 或是 HashiCorp 产品的典型用户,因为国庆前工作比较繁忙,并且本期的话题其实并不是我非常擅长的领域,所以写起来并不是那么容易,用了比较多的时间,也难免还是会有一些错漏,敬请读者与受访的朋友包涵。

今天我们分享的是与北京火线安全团队的一次交流。火线是由北京安全共识科技有限公司创立的实名制安全平台,主要的业务有安全众测服务、企业SRC运营服务以及洞态 IAST 检测工具。

火线敏锐地察觉到了 IaC 这股潮流,并且在国内率先开始研发相关的工具与产品。

本文介绍的工具和引用的材料比较多,相关链接也添加在文章的最末尾,有需要的读者可以自取。

云端基础设施安全面临的挑战

企业在面临要不要上云的选择时,一个很重要的因素就是安全。我们不能武断地说公有云的安全性不如私有云,或者是反过来说私有云的安全性不如公有云。大部分云厂商采用的是一种名为“责任共担模式”的责任划分:

  • 云厂商负责“云本身的安全” – 公有云厂商负责保护运行所有云服务的基础设施。该基础实施由云厂商提供的硬件、软件、网络和设备组成。
  • 客户负责“云内部的安全” – 客户责任由客户所选的公有云服务确定。这决定了客户在履行安全责任时必须完成的配置工作量。例如,部署虚拟机实例的客户需要负责虚拟机运行的操作系统(包括更新和安全补丁)的管理、客户在实例上安装的任何应用程序软件或实用工具,以及为每个实例配置合适的防火墙(又称为安全组)。如果使用的是对象存储这样的服务,客户需要负责管理其数据(包括加密选项),以及分配适当的读写权限。
AWS 的责任共担模式

事实上自云计算这个概念诞生以来,由于公有云厂商的问题引发的安全事故并不多见,公有云上最常见的安全问题是:配置错误(Misconfiguration)。

配置错误看起来非常简单,也很容易预防,例如存有机密客户信息的对象存储桶打开了匿名公网访问权限,就是最常见的配置错误。配置错误也不单单是公有云上才会有的问题。例如在使用私有云的时候,我们使用了容器,但容器的镜像来自于不可信的第三方仓库,一旦该容器的供应链遭到入侵,latest 版本的镜像被植入了恶意代码,我们很可能会将恶意代码释放到生产环境中。我们有没有办法强制只能使用来自可信来源的镜像?

根据趋势科技的调查[1],公有云平台上发生的安全事故中约有 65-70% 是由配置错误引发的。如此简单的问题,却又引发了如此多的严重事故,我们能不能做的更好?

安全的左右

在这里要先费些篇幅为非技术背景的读者介绍一下左移和右移的概念。我们可以把软件团队看作是一条流水线,左侧输入的是收集到的各种需求,经过分类、筛选、加工后成为开发团队的工作项,然后经由开发工程师编码实现,测试工程师对其进行测试确保正确性,然后交由运维团队部署至生产环境,并且持续对其进行监控和维护。越是接近于这个流程开始的阶段我们就称之为左侧,越是接近于生产环境的阶段我们就称之为右侧。

很多团队的开发模型中是没有特别的安全设计的,有时安全团队会像测试团队一样,在开发团队自认为完成编码工作实现功能以后,对其进行审计和测试。由于开发一次交付的代码可能很多,也很复杂,所以人工对其进行安全审计很多时候并不现实。不少安全公司和团队提供了各种自动化工具,号称可以对代码进行扫描,找出不安全的编码实践。如果我们不是在完成了所有的编码工作后才对代码进行扫描,而是一边编码一边同时扫描的话,就可以确保在引入安全风险的最初时刻就将之消灭在萌芽中,减少返工带来的浪费。这种将安全从测试阶段前移至编码阶段的实践我们称之为安全的左移。

但光是在编码阶段确保没有引入安全风险还是不够的,因为一旦软件被部署到生产环境,特别是云服务这样的持续运行的在线服务,它的配置依然有被错误修改的风险,或者一个新的漏洞刚刚被披露,所有尚未完成相关安全加固的系统都处于攻击风险之中。所以我们需要对已经部署到生产环境的软件和基础设施进行持续的监控,确保它们始终是处于保护之中。

总体来说云服务的安全与个人电脑防护非常不同,并不是安装一套杀毒软件,边关网络设备上安装一套防火墙就可以高枕无忧的,云服务的安全需要的全生命周期的防护。

火线的解法

火线分析了许多业界比较知名的工具和产品,例如 BridgeCrew 的 Checkov[2](正好这个我也在用)、TfSec[3]Wiz[4]Orca[5]等等(我还可以补充几个:Snyk[6]HashiCorp Sentinel[7]Opa Terraform[8]ConfTest[9]等等),然后火线把着眼点放在了右侧安全上,即如何确保云端生产环境中没有错误配置。

这其实是一个相当符合国情的决策,因为目前在国内完全践行 IaC 的团队并不多见,假如你的基础设施并不是用代码来定义和创建的,那么安全就无法左移,既然不能左移,那么就试试右移。

头部公有云厂商已经提供了右移的安全检测功能,例如 AWS Config,就可以针对云端资源编写和应用策略,找出违例资源告警

<<< 左右滑动见更多 >>>

Azure 也有 Azure Policy 可以做到类似的事,例如编写如下 Policy 文件:

{
  "if": {
    "allOf": [
      {
        "field""type",
        "equals""Microsoft.Storage/storageAccounts"
      },
      {
        "not": {
          "field":"Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
          "equals""false"
        }
      }
    ]
  },
  "then": {
    "effect""deny"
  }
}

就可以阻止用户创建允许匿名公网访问的存储桶。

既然公有云厂商已经有了类似的功能,为什么火线还要做一遍?这与 Terraform 逐渐取代了 CloudFormation、ArmTemplate 的原因一样,因为我们处在一个多云的时代,越来越多的客户选择同时使用多个云平台,他们需要一个一致的右侧安全策略管理接口。此外,还会有许多不是云厂商提供的服务,例如数据库的配置,例如 Kafka 的配置,都要纳入到这个管理中。

如何实现?

要实现类似的右侧安全,首先要做的是采集云端资源的现状,然后根据现状进行分析,寻找是否有已知的违例状态。这个问题就被分解为两个部分:如何采集云端资源的现状,以及如何在这些数据上运行用户定义的策略。

公有云厂商都提供了全面的 API,可以检索各种所需要的数据,但是各家厂商的 API 调用规范不同,数据格式不同,要提供一个多云统一的分析工具,首先要做的就是统一对各家 API 的调用和数据的格式,这可是一项很庞大的工程。

另外以刚才 Azure Policy 的例子里,Policy 是以 Json 格式编写的,而我们知道 Json 并不是一种适合人类直接编写的格式。需要寻找一种足够简单的让用户自定义什么叫“违例资源”的方式。

Terraform Provider As Infrastructure

火线对于问题一的解决方案是,既然 Terraform 社区已经有 2000+ 个 Provider,这些 Provider 可以把对应平台的云资源转化为一个 Terraform State 文件中的 Resource,能否利用这一点?

说到这里,我们就要介绍一点 Terraform 工作的原理了。

Terraform 分为 Terraform Core、Terraform Provider 两部分,Terraform Provider 类似一个驱动程序,但是以一个独立进程的形式运行的,两者之间通过 RPC 通信实现调用,这是通过 HashiCorp 的 go-plugin[10] 项目实现的。

一个 Terraform Provider 的开发者需要实现几点:

  1. 定义该 Provider 实现了哪些 resourcedata
  2. resource 实现一组 Create、Read、Update、Delete 方法,为 data 实现 Read 方法

Terraform 在执行 Plan 时会调用 Read 方法,读取云端资源的状态,更新到本地的 State 中,然后和用户代码定义的状态进行比对,再调用 Create / Update / Delete 方法,使得云端资源的状态符合用户 Terraform 代码所描述的状态。

一个 resource 的状态在 Terraform 运行时是用以下的一个 go 结构体描述的:

type ResourceData struct {
 schema       map[string]*Schema
 config       *terraform.ResourceConfig
 state        *terraform.InstanceState
 diff         *terraform.InstanceDiff
 meta         map[string]interface{}
 timeouts     *ResourceTimeout
 providerMeta cty.Value
 multiReader *MultiLevelFieldReader
 setWriter   *MapFieldWriter
 newState    *terraform.InstanceState
 partial     bool
 once        sync.Once
 isNew       bool
 panicOnError bool
}

其中最关键的就是类型为 map[string]*SchemaschemaSchema 类型的定义如下:

type Schema struct {
    Type ValueType
    ConfigMode SchemaConfigMode
    Required bool
    Optional bool
    Computed bool
    ForceNew bool
    DiffSuppressFunc SchemaDiffSuppressFunc
    DiffSuppressOnRefresh bool
    Default interface{}
    DefaultFunc SchemaDefaultFunc
    Description string
    InputDefault string
    StateFunc SchemaStateFunc
    Elem interface{}
    MaxItems int
    MinItems int
    Set SchemaSetFunc
    ComputedWhen []string
    ConflictsWith []string
    ExactlyOneOf []string
    AtLeastOneOf []string
    RequiredWith []string
    Deprecated string
    ValidateFunc SchemaValidateFunc
    ValidateDiagFunc SchemaValidateDiagFunc
    Sensitive bool
}

如果我们把一个 resource 类型看作是一张 SQL 的表,那么 ResourceData 下的 schema:

type ResourceData struct {
 schema       map[string]*Schema // 就是这玩意

这个 schema 就对应了表的列,Schema 描述了列的类型、约束等元信息。我们以 HashiCorp 用以示范如何编写一个 Provider 的示例 HashiCup 里的一个例子来说明:

func resourceOrderRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
 c := m.(*hc.Client)
 var diags diag.Diagnostics
 orderID := d.Id()
 order, err := c.GetOrder(orderID, &c.Token)
 if err != nil {
  return diag.FromErr(err)
 }
 orderItems := flattenOrderItems(&order.Items)
 if err := d.Set("items", orderItems); err != nil {
  return diag.FromErr(err)
 }
 return diags
}

在这个 Read 函数的例子里,参数 d 的类型是 *schema.ResourceData,也就代表了这个 resource 在 Terraform State 文件里存储的值。通过 d.Id() 方法可以读取到 Terraform State 文件里记录的我们曾经创建过的资源的 ID,然后通过对应平台的 Go SDK Client(代码中就是 c := m.(*hc.Client) 里的 c)查询云端资源实际的值,然后写入本地 Terraform State 文件里。当我们在执行 terraform import 命令时,多数资源实际上也是在调用对应的 Read 方法,获取云端的状态生成本地 State 文件里的记录。

以上是对 Terraform 工作原理的一个非常简单的描述。由于 Go 语言的特性,所有的库都是以代码静态链接的形式被下载后一起编译的,最终只会有一个可执行文件,这也就意味着,实际上我们可以把任意的 Terraform Provider 插件当作库来引用,就可以复用任意 resource 或是 dataRead 方法的代码了。实际上已经有一些项目采用这种方式来构建他们自己的 Provider 了,比如:

  • pulumi-tf-provider-boilerplate[11] —— Pulumi 提供的用以将一个 Terraform Provider 转化为 Pulumi Provider 的工具
  • crossplane-terrajet[12] —— Crossplane 提供的用以将一个 Terraform Provider 转化为 Crossplane Provider 的工具

这种做法我称之为寄生战略,借助 Terraform Provider 和 Go 语言的特性,我们可以把 Terraform Provider 生态本身作为一种基础设施,来构建新的工具和生态,这样 Terraform 负责解决多云适配的工作,而社区则可以坐享其成。

火线的这种做法有一个缺陷,那就是 Terraform 的 Read 方法只能读取单个资源的信息,并且前提是我们知道资源的 ID。假如用户的基础设施不是用 IaC 创建的,那么就需要先枚举出有多少资源,它们的类型和 ID 是什么,才能调用对应的 Read 方法。这方面目前社区也有一些工具,例如:

  • Google 的 Terraformer[13] —— 支持读取主流云平台上的云资源,并生成相关的 Terraform 代码以及 State 文件
  • Azure 的 Aztfy[14] —— 支持读取 Azure 上的资源,并生成相关的 Terraform 代码以及 State 文件

利用相关工具的代码,就可以实现对云端资源的检索。当然,火线安全应该是采用了自己的一些实现,但检索现存资源的问题的确可以参照 Terraform Provider 的做法,也采用插件式设计,为不同云平台编写不同的检索代码。

用户如何定义策略?

如何定义策略,我们在上面的文章中介绍过 AWS Config 和 Azure Policy 的做法,业界还有其他的实现,例如:

Checkov 的做法

Checkov[15] 是由 Bridgecrew 公司开发的一款开源的策略即代码(Policy as Code)工具,用以扫描 IaC 代码中可能存在的错误配置。例如:

class S3RestrictPublicBuckets(BaseResourceValueCheck):
    def __init__(self):
        name = "Ensure S3 bucket has 'restrict_public_bucket' enabled"
        id = "CKV_AWS_56"
        supported_resources = ['aws_s3_bucket_public_access_block']
        categories = [CheckCategories.GENERAL_SECURITY]
        super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)

    def get_inspected_key(self):
        return "restrict_public_buckets"

代号 CKV_AWS_56 的策略禁止在 Terraform 代码中声明类型为 aws_s3_bucket_public_access_blockresource,以防止错误地将机密数据存储在拥有公网访问权限的对象存储桶中。

Checkov 的这种策略编写的缺点是你必须使用特定的通用编程语言。如果团队主力语言不是 Python 那么编写自定义策略不会是一件非常轻松的事情。

ConfTest / Opa Terraform / TfSec 的做法

这三个工具

  • ConfTest[16]
  • Opa Terraform[17]
  • TfSec[18]

的共同点是他们都支持采用 Rego 作为策略定义语言。

ConfTest 的例子:

deny[msg] {
 rule := input.resource.aws_security_group_rule[name]
 rule.type == "ingress"
 contains(rule.cidr_blocks[_], "0.0.0.0/0")
 msg = sprintf("ASG `%v` defines a fully open ingress", [name])
}

该策略禁止 aws_security_group_rule 资源的 cidr_blocks 列表包含 0.0.0.0/0

Opa Terraform 的例子:

# Add CIDRS that should be disallowed
invalid_cidrs = [
  "0.0.0.0/0"
]

array_contains(arr, elem) {
  arr[_] = elem
}

# Checks security groups embdedded ingress rules
deny[reason] {
  r := tfplan.resource_changes[_]
  r.type == "aws_security_group"
  in := r.change.after.ingress[_]
  invalid := invalid_cidrs[_]
  array_contains(in.cidr_blocks,invalid)
  reason := sprintf(
              "%-40s :: security group invalid ingress CIDR %s",
              [r.address,invalid]
            )
}

该例子实现的功能与 ConfTest 的差不多。

TfSec 的例子:

package custom.aws.s3.no_insecure_buckets

import data.lib.result

deny[res] {
    bucket := input.aws.s3.buckets[_]
    bucket.name.value == "insecure-bucket"
    msg := "Bucket name should not be 'insecure-bucket'"
    res := result.new(msg, bucket.name)
}

这些使用 Rego 编写的策略也存在着需要学习 Rego 的问题。比 Python 更麻烦的是,Rego 是一门领域特定语言(DSL),其能力是受限的,并且它的语法被设计的与一般的通用编程语言不同,要熟练应用需要在思维范式上进行一定的转变。

SQL to Infra

火线想到的是 SQL。SQL 的有以下两个优点:

  1. 会的人很多,不会的人学习起来也很快
  2. 借助于数据库,使用 SQL 可以快速地在非常庞大的数据集当中进行非常复杂的检索

实际上已经有一些工具提供了用 SQL 检索基础设施的能力,比如 steampipe[19]。火线的设想是利用刚才所叙述的把 Terraform Provider 作为基础设施的方法,把云端资源的状态转化记录到 Postgres 数据库的表里,然后就可以利用 SQL 强大的检索能力来搜寻错误配置。

因为时间的限制,我没有机会请教火线更多的实现细节,所以我猜测该工具的做法大概会是轮询 + 事件驱动的形式,定时调用检索方法获取资源的 ID 列表,然后用 Read 方法获取最新的状态,更新到数据库中,如果做的更进一步则可以通过公有云提供的事件驱动机制,在云端资源发生变更后立即更新相关资源的状态。

火线靶场

火线团队还借鉴了 BridgeCrew(就是开发 Checkov 的公司)的 Terraform Goat[20],推出了火线靶场[21]这个仓库。

火线靶场实际上是一组 Terraform 代码,用以展示用户在使用阿里云、腾讯云、华为云等各种公有云平台服务时常见的配置错误,用户可以使用这些故意编写的不安全代码来测试自己使用的策略工具是否能够成功检测出错误配置,非常有意思。

一些感想

火线是国内很少见的从事 Policy as Code 的团队,他们也是国内很少见的通过将 Terraform Provide 代码作为基础设施来进一步开发其他基础设施的团队,他们开发的 SQL to Infra 的工具不但可以用作右侧安全的基础设施,实际上它也可以用作 FinOps 的基础设施。

因为无论是 Infrastructure as Code 也好,Policy as Code 也好,它们目前在国内都还不是主流技术,这就导致了从事相关产品研发的公司和团队大多是初创公司和小团队,所以各自研发一套 SQL to Infra 这样的基础设施实际上是比较浪费的。其实中国有相关想法的公司和团队完全可以合作,用开源模式共同维护一套高质量的 SQL to Infra 基础设施,然后在它的基础上各自开发各自感兴趣的工具和业务。有这方面兴趣的朋友其实可以联系我,我愿意为大家牵线,看看是不是促成你们的合作。

IaC 在中国仍然不是一项主流技术,这就导致了安全左移在中国比较难实现,另一方面从我刚才介绍的众多工具可以看到,实际上安全左移在国际上已经呈现一种百花齐放的态势,而相关生态在国内因为缺乏市场,很难发展起来。

不但是 IaC,其他的 xxx as Code 在国内都不是主流,所以如果想要围绕着 xxx as Code 开发工具的话,很难选择国内作为主要市场,这就引出了我最近半年以来观察到的一个有趣的现象:工具出海,这一点我将在以后的文章中专门叙述。

国内恶性信息安全事故越来越多,信息安全就像是实数的正负号,安全出问题,信息量越大后果越严重。国内由于数字化转型实践中各种因循守旧抱残守缺的做法,导致这方面与国际生态的差距越来越大,封闭的私有云事实上也没有消除信息安全事故,各种错误配置引发的问题还是在那里,只是更难被发现而已。国内从业者要扭转两个误区:

  1. 认为一切都是人的问题,只要人更有责任心,就可以避免问题
  2. 认为安全可以通过像在 PC 上装杀毒软件一样,通过安装一套防火墙、一套安全管控系统来保障

误区 1 的问题在于,你可以把任意事故都归因为人的问题,因为所有的事情在实现的过程中一定有人的因素,人是第一推动力。既然任意事故都可以归因到人,那么指望通过更强的责任心就能避免事故是不负责任的妄想,因为如果这是可行的我们就不会再看到事故了。误区 2 的错误在于只看到了点而没有看到线和面。我之所以用了很大的篇幅介绍左侧和右侧,就是想向非技术背景的读者说明,安全是一个立体的、全生命周期的工作,需要整个团队的配合。比如国内不用代码来定义和管理基础设施,那么安全的左移就无法实现,我们就无法在最源头的位置把错误配置扼杀掉。

国家这两年愈发重视信息安全和数据安全,陆续出台了相关法律,对因过失而造成重大信息安全事故、数据泄露的事件处以惩罚,这本是好意,但在实践中我们会发现缺乏相关的具体合规细则,这使得无论是左侧还是右侧,我们缺乏对于“合规”的验收标准。

我曾经翻译过 CloudPosse 的 Terraform 最佳实践,在学习 CloudPosse 的 Terraform Module 时,我会注意到他们维护的每一个 Module 的 Readme 上都会有这样一个表格:

这些徽章是由 BridgeCrew 提供的,可以看到其中有不少是合规标准。例如 NIST-800-53,它的全称是“Security and Privacy Controls for Information Systems and Organizations”[22],由美国标准技术局(National Institute of Standards and Technology,NIST)颁发。再比如 PCI-DSS,全称是 Payment Card Industry Data Security Standard[23],由信用卡行业安全标准委员会发布、管理和强制执行。

相关的合规都是相当冗长繁杂的文件,如何确定我们的基础设施符合这些文件的规定?这里 BridgeCrew 就提供了相关的徽章,他们就说,我们有这样一组策略,只要你的基础设施完全满足了这组策略,就是符合相关合规约束的。我们国家也可以借鉴这种做法,由国家牵头制定出行业标准的细则,各家云厂商编写相关的策略,再由各家工具厂商翻译成等价的策略代码,使得法律变成一组可以被集成在持续集成流水线中的测试用例,使法律左移同时右移。

最后还有一个我认为非常重要的趋势。我们重新来看一下 Terraform 工作原理这张图:

我们可以看到,Terraform 的调用顺序是:用户编写的 Terraform 代码 -> Terraform Provider -> Go SDK -> API。目前云厂商实际上是 API First 的,一切云产品对外交互的入口首先是 API,然后才是 Portal 网站和 SDK,再由 SDK 构建出命令行工具 CLI 以及 Terraform Provider 等。在这个过程中,从 API 生成 Go SDK 并不是特别困难,如果 API 采用 OpenAPI 标准的话,有很多工具可以帮助我们直接生成各种语言的 SDK。但是用 SDK 编写 Provider 是一个比较麻烦的过程。

我们可以看到,由 HashiCorp 维护的 aws Provider 有 3400+ 个 issue,415 个 Pull Request 等待审查。

azurerm 的情况也没好太多,有 2000+ 个 issue,103 个 Pull Request 等待审查。

由于 IaC 发展速度太快,加之头部云厂商产品迭代的非常频繁,所以 Provider 的开发逐渐变得有些跟不上节奏了。另外越来越多的团队都像火线、Pulumi、Crossplane 那样将 Terraform Provider 作为基础设施来构建各自的生态,实际上我认为未来云厂商会开始考虑 Provider First,因为越来越多的用户都逐渐减少了对 Portal、CLI 的使用,转向完全使用 IaC 来用云,所以未来云厂商哪怕推出了一个新产品,如果 Provider 不支持,用户还是用不上。所以云厂商必须考虑降低从 SDK 构建 Provider 的难度,在设计 API 时不能随心所欲,而是要先想想如果要在 Provider 里支持这个产品,它对应的 Terraform Resource 是长什么样的?用户写出来的 Terraform 代码是什么样子的?很可能会是从 Provider 里的 Schema 倒推 API 的 Schema 和实现的行为,这样就可以直接从 API 生成 Provider,从而加速产品推向市场的节奏。我们实际上已经可以从 AWS 在 2021 年的 reinvent 大会[24]上推出的 AWS Cloud Control[25]

2021 reinvent 官宣 Cloud Control API

未来的服务开发会逐渐从 API First 转向 Provider First,这是我的一个判断。

参考资料

[1]

趋势科技的调查: https://www.trendmicro.com/en_us/research/21/a/the-top-worry-in-cloud-security-for-2021.html

[2]

BridgeCrew 的 Checkov: https://bridgecrew.io/checkov/

[3]

TfSec: https://github.com/aquasecurity/tfsec

[4]

Wiz: https://www.wiz.io/

[5]

Orca: https://orca.security/

[6]

Snyk: https://snyk.io/

[7]

HashiCorp Sentinel: https://www.hashicorp.com/sentinel

[8]

Opa Terraform: https://www.openpolicyagent.org/docs/latest/terraform/

[9]

ConfTest: https://www.conftest.dev/

[10]

go-plugin: https://github.com/hashicorp/go-plugin

[11]

pulumi-tf-provider-boilerplate: https://github.com/pulumi/pulumi-tf-provider-boilerplate

[12]

crossplane-terrajet: https://github.com/crossplane/terrajet

[13]

Google 的 Terraformer: https://github.com/GoogleCloudPlatform/terraformer

[14]

Azure 的 Aztfy: https://github.com/Azure/aztfy

[15]

Checkov: https://bridgecrew.io/checkov/

[16]

ConfTest: https://www.conftest.dev/

[17]

Opa Terraform: https://www.openpolicyagent.org/docs/latest/terraform/

[18]

TfSec: https://github.com/aquasecurity/tfsec

[19]

steampipe: https://steampipe.io/

[20]

Terraform Goat: https://github.com/bridgecrewio/terragoat

[21]

火线靶场: https://github.com/HXSecurity/TerraformGoat

[22]

“Security and Privacy Controls for Information Systems and Organizations”: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-53r5.pdf

[23]

Payment Card Industry Data Security Standard: https://docs-prv.pcisecuritystandards.org/PCI%20DSS/Standard/PCI-DSS-v4_0.pdf

[24]

2021 年的 reinvent 大会: https://www.youtube.com/watch?v=DxjLbjfTfqA&ab_channel=AWSEvents

[25]

AWS Cloud Control: https://aws.amazon.com/cloudcontrolapi/


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存