☰
Woodpecker 扩展机制(Extensions)完全指南:配置、Registry 与 Secret 三类 HTTP 扩展的接入与安全实践
2026/9/28 12:41:35 网站建设 项目流程
  • CI/CD
  • DevOps

【免费下载链接】woodpecker

Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.

项目地址:https://gitcode.com/gh_mirrors/wo/woodpecker
点击查看免费下载

本指南以 Woodpecker CI 官方文档(docs/versioned_docs/version-3.16/20-usage/72-extensions/index.md)为骨架,结合当前仓库源码,系统讲解 Woodpecker 的扩展体系:如何通过预定义的 HTTP 端点替换内部逻辑,实现管道配置的运行时生成/改写、容器镜像仓库凭据的外部化获取、以及密钥(Secret)的集中管理。读完本文,你将掌握三类扩展的接入方式、请求/响应协议、签名验签机制与主机访问白名单配置,并能在自己的部署中安全地开发和部署扩展服务。

一、扩展机制概述:用 HTTP 端点替换内部逻辑

Woodpecker 允许你通过预定义的 HTTP 端点(pre-defined http endpoints)将内部逻辑替换为外部扩展(extension)。这是一条典型的“插件化”路径:核心引擎保留调度与执行能力,而配置解析、凭据获取、密钥注入等周边逻辑可以交给独立的、由你掌控的外部服务。

当前版本(v3.16 文档对应版本)提供三类扩展:

扩展类型文档作用
配置扩展(Configuration extension)40-configuration-extension.md在运行前修改或生成管道配置
Registry 扩展(Registry extension)50-registry-extension.md从外部服务获取镜像仓库凭据
密钥扩展(Secret extension)55-secret-extension.md从外部服务获取密钥(Secrets)

三类扩展的接入入口一致:在仓库设置的 Extensions(扩展)标签页中配置一个 HTTP 端点;同时也可以在服务器级配置中设置全局端点,作用于所有仓库。

权限模型提醒(务必阅读)

:::note Woodpecker 的权限处理与 forge(代码托管平台)绑定:在你的 forge 上对仓库拥有管理员权限的用户,在 Woodpecker 中也会拥有该仓库的管理员权限,从而可以修改已配置的扩展。这可能被利用来获取 forge 用户的凭据。请确保你信任所有可以登录 Woodpecker 的仓库管理员。 :::

这一条提醒出自官方文档,属于部署安全基线:扩展端点的可写权限与 forge 管理员权限强耦合,因此在共享服务器上必须谨慎评估仓库管理员的信任边界。

二、安全机制:ed25519 HTTP 签名与公钥获取

为什么必须签名

:::warning 你必须信任扩展服务,因为它们会接收到 secrets、tokens 等私有信息,并可能返回恶意管道配置(例如会被执行的危险命令)。扩展返回的配置是会被 Woodpecker 实际执行的。 :::

签名实现:HTTP Signatures + ed25519

为防止扩展被攻击/篡改,Woodpecker 使用 HTTP signatures 对所有发往扩展的 HTTP 请求进行签名,采用公钥-私钥 ed25519 密钥对。扩展端必须使用公钥验证所有请求的签名,可借助如httpsign之类的库。

从源码看,这一机制实现在 server/services/utils/http.go:

signer, err := httpsign.NewEd25519Signer(ed25519Key, httpsign.NewSignConfig(), httpsign.Headers("@request-target", "content-digest")) // The Content-Digest header will be auto-generated

关键细节:

  • 签名名称(key id)为woodpecker-ci-extensions(见 server/services/utils/http.go);
  • 被签名的头部为@request-target与content-digest(后者由客户端自动生成),即请求目标与请求体摘要都被纳入签名范围,可有效防止请求被重放、改写;
  • 客户端通过httpsign.NewClient包装标准http.Client,整个调用链路封装在Client类型中(server/services/utils/http.go)。

获取 Woodpecker 公钥

你可以通过两种方式获取公钥:

  1. HTTP API:直接访问http://my-woodpecker.tld/api/signature/public-key;
  2. UI 界面:打开仓库设置 → Extensions(扩展)页面查看。

该 API 由 server/api/signature_public_key.go 实现,返回 PKIX 格式的 ed25519 公钥(x509.MarshalPKIXPublicKey),路由注册于 server/router/api.go(GET /signature/public-key)。

扩展端拿到公钥后,需在每次收到请求时校验签名头。参考实现可参见 woodpecker-ci 官方的example-extensions示例仓库(在扩展服务开发时可作为起点;注意其中的地址指向外部 GitHub,本文不展开)。

客户端调用行为(源码级补充)

从 server/services/utils/http.go 的Send方法可以确认扩展调用的网络行为,这对扩展服务端设计很重要:

  • 请求超时:10 * time.Second(http.go);
  • 重试机制:最多重试 3 次,使用指数退避(backoff.NewExponentialBackOff());网络超时、连接拒绝、连接重置、主机不存在、TLS 握手超时等视为可重试错误;5xx 状态码会重试,4xx 客户端错误不重试(http.go);
  • User-Agent:server-extensions(http.go);
  • TLS 校验:默认开启证书校验(InsecureSkipVerify: false)。

这意味着扩展服务端应当对请求做幂等设计,因为同一请求可能被重试多次。

三、主机访问白名单:WOODPECKER_EXTENSIONS_ALLOWED_HOSTS

出于安全考虑,默认只允许扩展调用外部主机/IP,防止扩展端点被用来访问本地内部服务。要放宽限制,设置环境变量WOODPECKER_EXTENSIONS_ALLOWED_HOSTS,支持逗号分隔的多种取值:

1. 内置网络(Built-in networks):

取值含义
loopback127.0.0.0/8(IPv4)与 ::1/128(IPv6),包含 localhost
privateRFC 1918(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)与 RFC 4193(FC00::/7),即 LAN/内网
external合法的非私有单播 IP,可访问公网上的所有主机
*允许所有主机

2. CIDR 列表:例如 IPv4 的1.2.3.0/8、IPv6 的2001:db8::/32。

3. (通配)主机名:例如example.com、*.example.com、192.168.100.*。

源码佐证:默认值即hostmatcher.MatchBuiltinExternal(当环境变量为空时),并通过hostmatcher.ParseHostMatchList("WOODPECKER_EXTENSIONS_ALLOWED_HOSTS", allowedHostListValue)解析,最终由hostmatcher.NewDialContext注入到http.Transport.DialContext,在 TCP 拨号层直接拦截非法目标(server/services/utils/http.go)。

典型场景示例:

# 允许访问内网仓库服务和本地开发机 WOODPECKER_EXTENSIONS_ALLOWED_HOSTS=private,192.168.100.*,extensions.internal.example.com

四、配置扩展(Configuration Extension):运行时生成与改写管道配置

适用场景

配置扩展用于修改或生成 Woodpecker 管道配置,在仓库设置 → Extensions 标签页配置 HTTP 端点即可启用。典型用途包括:

  • 用 Go templating 等方式预处理原始配置文件;
  • 将自定义属性转换为 Woodpecker 属性;
  • 为配置添加默认值(如默认步骤);
  • 将完全不同格式的配置文件(如 GitLab CI 配置、Starlark、Jsonnet 等)转换为 Woodpecker 格式;
  • 集中管理多个仓库的配置(在单一位置统一维护)。

安全警告

:::warning Woodpecker 会向扩展传递 tokens 等私有信息,并会执行扩展返回的配置,因此保护外部扩展极其重要。Woodpecker 会对每个请求签名,详见前文 安全机制。 :::

全局配置

除了按仓库配置,还可在服务器配置中设置全局端点,让所有仓库共用。注意:如果与别人共享 Woodpecker 服务器,他们也会使用你的配置扩展。

WOODPECKER_CONFIG_EXTENSION_ENDPOINT=https://example.com/ciconfig

调用顺序:若同时配置了全局端点与仓库级端点,且仓库未启用 exclusive(独占)设置,则先调用全局扩展,再调用仓库级扩展。

工作原理

管道触发后,Woodpecker 从仓库拉取管道配置文件,然后向配置的扩展发送 HTTPPOST请求,JSON 载荷中包含仓库信息、管道信息及从仓库取回的配置文件;扩展可以返回修改后的、甚至是全新的管道配置(遵循 Woodpecker 官方 YAML 格式)。

如果启用exclusive(独占)设置(全局或仓库级均可),Woodpecker只调用你的扩展而不做其他事,从而可以完全跳过 forge;此时发送给扩展的请求中不会附带配置文件。

请求格式(Request)

扩展收到一个 HTTP POST 请求,JSON 载荷结构如下:

class Request { repo: Repo; pipeline: Pipeline; netrc?: Netrc; // 仅当启用 netrc 发送时包含(见下) configuration?: { // 配置文件列表;若仓库没有配置文件则不发送 name: string; // 配置文件名 data: string; // 配置文件内容 }[]; }

各模型对应源码位置:

  • repo 模型:server/model/repo.go
  • pipeline 模型:server/model/pipeline.go
  • netrc 模型:server/model/netrc.go

:::infonetrc字段仅在全局WOODPECKER_CONFIG_EXTENSION_NETRC设为true(默认false)或仓库勾选了 “Send netrc credentials” 时才会包含在请求中。 :::

:::tipnetrc数据非常强大,它包含访问仓库所需的凭据。你可以用它克隆仓库,甚至调用 forge(GitHub、GitLab 等)API 获取更多仓库信息。 :::

示例请求:

{ "repo": { "id": 100, "uid": "", "user_id": 0, "namespace": "", "name": "woodpecker-test-pipeline", "slug": "", "scm": "git", "git_http_url": "", "git_ssh_url": "", "link": "", "default_branch": "", "private": true, "visibility": "private", "active": true, "config": "", "trusted": false, "protected": false, "ignore_forks": false, "ignore_pulls": false, "cancel_pulls": false, "timeout": 60, "counter": 0, "synced": 0, "created": 0, "updated": 0, "version": 0 }, "pipeline": { "author": "myUser", "author_avatar": "https://myforge.com/avatars/d6b3f7787a685fcdf2a44e2c685c7e03", "author_email": "my@email.com", "branch": "main", "changed_files": ["some-filename.txt"], "commit": "2fff90f8d288a4640e90f05049fe30e61a14fd50", "created_at": 0, "deploy_to": "", "enqueued_at": 0, "error": "", "event": "push", "finished_at": 0, "id": 0, "link_url": "https://myforge.com/myUser/woodpecker-testpipe/commit/2fff90f8d288a4640e90f05049fe30e61a14fd50", "message": "test old config\n", "number": 0, "parent": 0, "ref": "refs/heads/main", "refspec": "", "clone_url": "", "reviewed_at": 0, "reviewed_by": "", "sender": "myUser", "signed": false, "started_at": 0, "status": "", "timestamp": 1645962783, "title": "", "updated_at": 0, "verified": false }, "configuration": [ { "name": ".woodpecker.yaml", "data": "steps:\n - name: backend\n image: alpine\n commands:\n - echo \"Hello there from Repo (.woodpecker.yaml)\"\n" } ], "netrc": { "machine": "myforge.com", "login": "myUser", "password": "forge-access-token" } }

响应格式(Response)

扩展应返回 JSON 载荷,包含遵循 Woodpecker 官方 YAML 格式的新配置文件。若希望保留现有配置文件,可返回 HTTP 状态码204 No Content。

class Response { configs: { name: string; // 配置文件名 data: string; // 配置文件内容 }[]; }

示例响应:

{ "configs": [ { "name": "central-override", "data": "steps:\n - name: backend\n image: alpine\n commands:\n - echo \"Hello there from ConfigAPI\"\n" } ] }

五、Registry 扩展:外部化镜像仓库凭据

适用场景

Woodpecker 使用 Registry 扩展获取镜像仓库凭据,在仓库设置 → Extensions 标签页配置 HTTP 端点。典型用途:

  • 集中管理仓库凭据;
  • 使用外部存储存放凭据;
  • 动态决定 Woodpecker 应使用哪组凭据。

安全警告

:::warning 同上,Woodpecker 会传递 tokens 等私有信息并执行返回的配置,必须保护好外部扩展并验证每个请求的签名。 :::

全局配置

WOODPECKER_REGISTRY_EXTENSION_ENDPOINT=https://example.com/ciconfig

优先级规则:若全局扩展与仓库级扩展都返回了同一仓库的凭据,则使用仓库级扩展的凭据。

工作原理

管道触发时,Woodpecker 先从你的扩展服务获取凭据;作为回退(fallback),使用直接配置在 Woodpecker 中的凭据。

请求/响应结构(Repo、Pipeline、Netrc 模型同上文):

class Request { repo: Repo; pipeline: Pipeline; netrc?: Netrc; // 仅当启用 netrc 发送时包含 }
class Response { registries: { address: string; // Docker 仓库地址 username: string; // 仓库用户名 password: string; // 仓库密码 }[]; }

示例响应:

{ "registries": [ { "address": "docker.io", "username": "woodpecker-bot", "password": "your-pass-word-123" } ] }

源码佐证:在 server/services/manager.go 中,当仓库配置了RegistryExtensionEndpoint时,会构造registry.NewWithExtension(m.registry, registry.NewHTTP(...)),即本地配置凭据作为底层的回退来源,扩展凭据优先叠加其上——这与文档中“先查扩展、回退到本地配置”的描述一致。同时WOODPECKER_REGISTRY_EXTENSION_NETRC控制是否发送 netrc 凭据(默认false)。

六、密钥扩展(Secret Extension):集中管理与动态生成 Secrets

适用场景

Woodpecker 使用 Secret 扩展从外部服务获取密钥,在仓库设置 → Extensions 标签页配置 HTTP 端点。典型用途:

  • 集中管理密钥(例如 HashiCorp Vault、AWS Secrets Manager 等外部系统);
  • 按管道动态生成密钥。

全局配置

WOODPECKER_SECRET_EXTENSION_ENDPOINT=https://example.com/secrets WOODPECKER_SECRET_EXTENSION_NETRC=false

优先级规则:若全局扩展与仓库级扩展返回同名密钥,则使用仓库级扩展的密钥。

工作原理

管道触发时,Woodpecker 从你的服务获取密钥,与直接配置在 Woodpecker 中的密钥合并,扩展密钥按名称优先;若扩展不可用,则回退到本地配置的密钥。

请求格式

class Request { repo: Repo; pipeline: Pipeline; netrc?: Netrc; // 仅当启用 netrc 发送时包含 }

(请求示例中的字段结构可参考上文配置扩展的示例,netrc字段在未启用发送时会被省略;文档同时提示:请以 server/model/repo.go、server/model/pipeline.go、server/model/netrc.go 中的最新模型为准,示例可能过时。)

响应格式

扩展应返回包含secrets数组的 JSON 对象;若不想添加任何密钥、仅保留现有密钥,可返回204 No Content。

class Response { secrets: { name: string; // 密钥名,与管道配置中的 from_secret 对应 value: string; // 密钥值 images?: string[]; // 可选:限制仅用于特定插件 events?: string[]; // 可选:限制仅用于特定管道事件 }[]; }

示例响应:

{ "secrets": [ { "name": "docker_password", "value": "your-secret-password-123" }, { "name": "deploy_token", "value": "super-secret-token", "events": ["push", "tag"] } ] }

其中events可选值对应 Woodpecker 的管道事件类型(如push、tag等),配合images可实现“某个密钥只对某插件、某事件可见”的细粒度控制。管道配置中通过from_secret: 密钥名引用扩展返回的密钥。

源码佐证:在 server/services/manager.go 中,仓库配置了SecretExtensionEndpoint时构造secret.NewCombined(m.secret, secret.NewHTTP(...)),同样以本地 secret 服务为底层、扩展为叠加层,印证了“扩展密钥按名称优先、本地密钥兜底”的行为。

七、第三方扩展与生态注意事项

:::danger 以下列出的第三方扩展既不是 Woodpecker CI 开发,也未经过其验证。使用前请确保你信任它们。 :::

Secret 扩展文档的 “3rd Party Extensions” 一节明确提示:任何第三方扩展均未被 Woodpecker CI 开发或验证,使用前必须自行审计其安全性。官方同时欢迎社区将自研扩展补充进该列表(_Add your extension here!_)。结合前文的签名机制,所有扩展(无论是自研还是第三方)都应:

  1. 在服务端验证每次请求的 ed25519 签名(公钥来自/api/signature/public-key或仓库设置页面);
  2. 对响应中返回的配置内容保持高度警惕,因为配置会被实际执行;
  3. 将自身部署在可信任的隔离环境中,限制其出网能力与数据访问范围。

八、仓库级扩展配置的底层模型

在仓库层面,扩展的端点与 netrc 开关作为仓库模型的字段持久化,见 server/model/repo.go:

字段JSON 字段名说明
ConfigExtensionEndpointconfig_extension_endpoint配置扩展端点(varchar 500)
ConfigExtensionNetrcconfig_extension_netrc是否向配置扩展发送 netrc(默认 false)
RegistryExtensionEndpointregistry_extension_endpointRegistry 扩展端点
RegistryExtensionNetrcregistry_extension_netrc是否发送 netrc(默认 false)
SecretExtensionEndpointsecret_extension_endpointSecret 扩展端点
SecretExtensionNetrcsecret_extension_netrc是否发送 netrc(默认 false)

仓库 API 更新接口 server/api/repo.go 支持通过PATCH修改这些字段,即你在 UI 的 Extensions 标签页上的操作最终会写入这些字段,并由 server/services/manager.go 在服务装配阶段据此构造对应的扩展客户端(config.NewCombined/registry.NewWithExtension/secret.NewCombined,端点均经过strings.TrimRight(endpoint, "/")规范化)。

九、开发扩展的检查清单

综合以上内容,开发一个 Woodpecker 扩展服务的最小清单如下:

  1. 端点协议:提供 HTTP POST 端点,按扩展类型解析对应的RequestJSON(Repo + Pipeline [+ Netrc] [+ Configuration]),并按类型返回configs/registries/secretsJSON;不需要变更时返回204 No Content;
  2. 签名验证:从http://my-woodpecker.tld/api/signature/public-key获取公钥,用httpsign等库校验每个请求的@request-target与content-digest签名;
  3. 幂等与超时:请求可能被重试至多 3 次(5xx/网络错误时),处理逻辑需幂等;响应应在 10 秒超时窗口内完成;
  4. 主机白名单:若扩展部署在内网/本地,记得通过WOODPECKER_EXTENSIONS_ALLOWED_HOSTS显式放行(默认仅允许 external);
  5. 权限与信任:牢记 forge 管理员等同于仓库扩展配置管理员;在共享服务器上谨慎启用全局扩展;
  6. 配置生成安全:配置扩展返回的 YAML 会被实际执行,务必只生成可信内容。

扩展体系的这三大组件,让 Woodpecker 在不修改核心引擎的情况下,即可对接企业自有的配置中心、凭据库与密钥管理基础设施,是 Woodpecker“简单但强大、扩展性出色”定位的关键实现之一。

  • CI/CD
  • DevOps

【免费下载链接】woodpecker

Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.

项目地址:https://gitcode.com/gh_mirrors/wo/woodpecker
点击查看免费下载

相关推荐

上一篇:Qt Go蓝牙通信:bluetooth模块设备发现与数据传输
下一篇:effect-smol HttpApi 中间件:认证、授权与安全完全指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询