- CI/CD
- DevOps
【免费下载链接】woodpecker
Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.
本指南以 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 公钥
你可以通过两种方式获取公钥:
- HTTP API:直接访问
http://my-woodpecker.tld/api/signature/public-key; - 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):
| 取值 | 含义 |
|---|---|
loopback | 127.0.0.0/8(IPv4)与 ::1/128(IPv6),包含 localhost |
private | RFC 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!_)。结合前文的签名机制,所有扩展(无论是自研还是第三方)都应:
- 在服务端验证每次请求的 ed25519 签名(公钥来自
/api/signature/public-key或仓库设置页面); - 对响应中返回的配置内容保持高度警惕,因为配置会被实际执行;
- 将自身部署在可信任的隔离环境中,限制其出网能力与数据访问范围。
八、仓库级扩展配置的底层模型
在仓库层面,扩展的端点与 netrc 开关作为仓库模型的字段持久化,见 server/model/repo.go:
| 字段 | JSON 字段名 | 说明 |
|---|---|---|
ConfigExtensionEndpoint | config_extension_endpoint | 配置扩展端点(varchar 500) |
ConfigExtensionNetrc | config_extension_netrc | 是否向配置扩展发送 netrc(默认 false) |
RegistryExtensionEndpoint | registry_extension_endpoint | Registry 扩展端点 |
RegistryExtensionNetrc | registry_extension_netrc | 是否发送 netrc(默认 false) |
SecretExtensionEndpoint | secret_extension_endpoint | Secret 扩展端点 |
SecretExtensionNetrc | secret_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 扩展服务的最小清单如下:
- 端点协议:提供 HTTP POST 端点,按扩展类型解析对应的
RequestJSON(Repo + Pipeline [+ Netrc] [+ Configuration]),并按类型返回configs/registries/secretsJSON;不需要变更时返回204 No Content; - 签名验证:从
http://my-woodpecker.tld/api/signature/public-key获取公钥,用httpsign等库校验每个请求的@request-target与content-digest签名; - 幂等与超时:请求可能被重试至多 3 次(5xx/网络错误时),处理逻辑需幂等;响应应在 10 秒超时窗口内完成;
- 主机白名单:若扩展部署在内网/本地,记得通过
WOODPECKER_EXTENSIONS_ALLOWED_HOSTS显式放行(默认仅允许 external); - 权限与信任:牢记 forge 管理员等同于仓库扩展配置管理员;在共享服务器上谨慎启用全局扩展;
- 配置生成安全:配置扩展返回的 YAML 会被实际执行,务必只生成可信内容。
扩展体系的这三大组件,让 Woodpecker 在不修改核心引擎的情况下,即可对接企业自有的配置中心、凭据库与密钥管理基础设施,是 Woodpecker“简单但强大、扩展性出色”定位的关键实现之一。
- CI/CD
- DevOps
【免费下载链接】woodpecker
Woodpecker is a simple, yet powerful CI/CD engine with great extensibility.
相关推荐
Akka Classic Extensions 扩展机制完全指南:从自定义扩展到配置加载与库扩展
Akka Classic Extensions 扩展机制完全指南:从自定义扩展到配置加载与库扩展 Akka Extensions 是 Akka 提供的官方扩展机
后端并发编程异步编程Woodpecker 扩展机制完全指南:用 HTTP 端点替换内部逻辑(配置、注册表与密钥扩展)
Woodpecker 扩展机制完全指南:用 HTTP 端点替换内部逻辑(配置、注册表与密钥扩展) Woodpecker 允许通过预定义的 HTTP 端点将内部逻
CI/CDDevOpsdeck.gl Layer Extensions 扩展机制完全指南:从 @deck.gl/extensions 内置扩展到自定义扩展开发
deck.gl Layer Extensions 扩展机制完全指南:从 @deck.gl/extensions 内置扩展到自定义扩展开发 @deck.gl/ex
前端数据可视化3D渲染图形学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考