Defender 即代码插件

Defender 即代码 (DaC) 是一个 Serverless Framework 插件,用于自动化资源管理和配置即代码。

警告: 该插件正在开发中,行为可能会发生变化。谨慎使用。

前提条件

Serverless Framework: 入门

安装

您可以直接使用我们预配置的模板初始化您的 Serverless 项目:

sls install --url https://github.com/OpenZeppelin/defender-as-code/tree/main/template -n my-service
为了使以上命令能够正确运行,您需要有权访问此 repo。

或者,您可以直接将其安装到现有项目中:

yarn add @openzeppelin/defender-as-code

设置

您可以通过几种方式设置 serverless.yml 配置:

如果您已经在 Defender 中拥有诸如合约、通知、Relayer、Action 等资源,您可以从管理 → 高级页面导出一个包含这些资源的 serverless.yml 配置文件。

Defender 导出 Serverless
如果您之前使用 defender-as-code 部署到同一帐户,并且随后通过 Defender 用户界面创建了新资源,则导出功能将根据您最新的部署堆栈的名称自动为新资源分配一个 stackResourceId。如果您以前未使用 defender-as-code 进行部署,则将使用默认堆栈名称 mystack

此插件允许您从 serverless.yml 中以声明方式定义 Action、Monitor、通知、区块浏览器 API 密钥、Relayer、合约和密钥,并通过 CLI 使用 serverless deploy 来配置它们。以下是一个示例模板,其中定义了一个 Action、一个 Relayer、一个策略和一个 Relayer API 密钥:

service: defender-as-code-template
configValidationMode: error
frameworkVersion: '3'

provider:
  name: defender
  stage: ${opt:stage, 'dev'}
  stackName: 'mystack'
  ssot: false

defender:
  key: '${env:TEAM_API_KEY}'
  secret: '${env:TEAM_API_SECRET}'

resources:
  actions:
    action-example-1:
      name: 'Hello world from serverless'
      path: './actions/hello-world'
      relayer: ${self:resources.relayers.relayer-1}
      trigger:
        type: 'schedule'
        frequency: 1500
      paused: false
      # optional - unencrypted and scoped to the individual action
      environment-variables:
        hello: 'world!'
    action-example-2: 2cbc3f58-d962-4be8-a158-1035be4b661c

  policies:
    policy-1:
      gas-price-cap: 1000
      whitelist-receivers:
        - '0x0f06aB75c7DD497981b75CD82F6566e3a5CAd8f2'
      eip1559-pricing: true

  relayers:
    relayer-1:
      name: 'Test Relayer 1'
      network: 'sepolia'
      min-balance: 1000
      policy: ${self:resources.policies.policy-1}
      api-keys:
        - key1

plugins:
  - '@openzeppelin/defender-as-code'

这需要在 YAML 文件的 defender 属性下设置 keysecret。我们建议使用环境变量或安全的(git 忽略的)配置文件来检索这些值。相应地修改 serverless.yml

确保 Defender Team API 密钥已配置所有相应的 API 功能。

stackName (例如 mystack) 与资源键 (例如 relayer-1) 结合使用,以唯一标识每个资源。此标识符称为 stackResourceId (例如 mystack.relayer-1),允许您在同一租户中管理多个部署。

您还可以通过它们的唯一 ID (例如 2cbc3f58-d962-4be8-a158-1035be4b661c) 直接引用现有的 Defender 资源。这些资源将不受插件管理,并且在部署过程中将被忽略。但是,您可以在其他资源中引用它们,以相应地更新它们的配置。 以下是支持直接引用的属性列表:

  • relayer 可以在 Action 中引用 relayerId

  • action-trigger 可以在 Monitor 中引用 actionid

  • action-condition 可以在 Monitor 中引用 actionId

  • address-from-relayer 可以在 Relayer 中引用 relayerId

  • notify-config.channels 可以在 Monitor 中引用多个 notificationId

  • contracts 可以取代 addresses,并在 Monitor 中引用多个 contractId 以下示例展示了如何在 Monitor 和 Action 中分别使用对 Defender 合约和 Relayer 的直接引用:

...
contracts:
  contract-1: 'sepolia-0xd70d6A0480420b4C788AF91d0E1b0ca6141A9De8' # Defender 中现有资源的 contractId
relayers:
  relayer-2: 'bcb659c6-7e11-4d37-a15b-0fa9f3d3442c' # Defender 中现有 Relayer 的 relayerId
actions:
  action-example-1:
    name: 'Hello world from serverless'
    path: './actions/hello-world'
    relayer: ${self:resources.relayers.relayer-2}
    trigger:
      type: 'schedule'
      frequency: 1500
    paused: false
monitors:
  block-example:
    name: 'Block Example'
    type: 'BLOCK'
    network: 'sepolia'
    risk-category: 'TECHNICAL'
    # optional - either contracts OR addresses should be defined
    contracts:
      - ${self:resources.contracts.contract-1}
    ...
...

SSOT 模式

serverless.yml 文件中的 provider 属性下,您可以选择添加一个 ssot 布尔值。SSOT 或单一数据源,确保您在 Defender 中的堆栈状态与 serverless.yml 模板完全同步。 这意味着,所有未在您当前模板文件中定义的资源,在部署时都会从 Defender 中删除,Relayer 除外。如果 SSOT 未在模板中定义,它将默认为 false

serverless.yml 文件中删除的任何资源都_不会_自动删除,以防止意外删除资源。要预期此行为,必须启用 SSOT 模式。

密钥 (Action)

Action 密钥可以全局定义,也可以按堆栈定义。在 global 下定义的密钥不受 stackName 更改的影响,并在新堆栈下重新部署时保留。当堆栈在新 stackName 下重新部署时,在 stack 下定义的密钥将被删除(前提是启用了 SSOT 模式)。要引用在 stack 下定义的密钥,请使用以下格式:<stackname>_<secretkey>,例如 mystack_test

secrets:
  # optional - 全局密钥不受 stackName 更改的影响
  global:
    foo: ${self:custom.config.secrets.foo}
    hello: ${self:custom.config.secrets.hello}
  # optional - 堆栈密钥(格式为 <stackname>_<secretkey>)
  stack:
    test: ${self:custom.config.secrets.test}

类型和模式验证

我们提供基于 JSON 模式自动生成的文档:

有关类型的更多信息可以在 此处找到。具体而言,是以 Y 开头的类型(例如 YRelayer)。对于模式,您可以查看 docs-schema 文件夹。

此外,还提供了一个 示例项目,该项目提供了可以在 serverless.yml 文件中定义的大多数属性。

命令

部署

您可以使用 sls deploy 将当前堆栈部署到 Defender。

部署采用一个可选的 --stage 标志,该标志在从以上模板安装时默认为 dev

此外,serverless.yml 可能包含一个 ssot 属性。更多信息可以在 SSOT 模式 部分找到。

此命令将在当前工作目录的 .defender 文件夹中附加一个日志条目。此外,如果创建了任何新的 Relayer 密钥,这些密钥将作为 JSON 对象存储在 .defender/relayer-keys 文件夹中。

警告: 从模板安装时,我们确保从任何 git 提交中忽略 .defender 文件夹。但是,当直接安装时,请确保将此文件夹添加到您的 .gitignore 文件中。

信息

您可以使用 sls info 来检索在 serverless.yml 文件中定义的每个资源的信息,包括唯一标识符和每个组件独有的属性。

移除

您可以使用 sls remove 从 Defender 中删除在 serverless.yml 文件中定义的所有资源。

为了避免潜在的资金损失,只能直接从 Defender UI 中删除 Relayer。

日志

您可以使用 sls logs --function <stack_resource_id> 来检索给定操作标识符(例如 mystack.action-example-1)的最新操作日志。此命令将持续运行,并每 2 秒检索一次日志。

调用

您可以使用 sls invoke --function <stack_resource_id> 手动运行一个 Action,给定其标识符(例如 mystack.action-example-1)。

每个命令都有一个到 JSON 对象的标准输出。

注意事项

deploy 过程中抛出的错误不会回滚任何先前的更改。常见的错误是:

  • 未设置 API 密钥和 secret

  • API 密钥的权限不足

  • serverless.yml 文件的验证错误(请参阅 类型和模式验证

通常,修复错误并重试部署应该足够了,因为任何现有资源都将属于部署的 update 子句。但是,如果不确定,您可以随时调用 sls remove 来删除整个堆栈,然后重试。

Action 密钥是加密的键值对,并在运行时注入到 lambda 环境中。密钥自动限定于所有 Action。或者,您可以使用 environment-variables 来定义限定于单个 Action 的键值对,并通过 process.env 在运行时可用。请注意,这些值未加密。