Conan2 包签名指南

发布时间: 更新时间: 总字数:1190 阅读时间:3m 作者:IP:上海 网址

在 Conan 2.2x.x 中,包签名(Package Signing)不再绑定于某种特定的工具,而是通过**插件化(Extension API)**实现的。因此可以使用 OpenSSL、GPG 甚至 Sigstore (Cosign) 来进行签名。

介绍

官方推荐通过在配置目录中编写 sign.py 插件来实现。当你在终端执行 conan cache sign 时,Conan 会生成一个包含该包所有文件校验和的清单文件 (pkgsign-manifest.json),然后调用你编写的 sign.py 对该清单文件进行签名。

注意:Package Signing 目前是 Conan 2 的**实验性(experimental)**特性,插件 API 与命令行为在后续版本中可能发生变更,生产使用前请以官方文档为准。

以下是一个可以直接在 Bash/终端 中运行的完整示例代码。它使用 OpenSSL 作为底层签名工具,自动生成密钥对、配置好 Conan 的签名插件,并最终对一个测试包进行签名验证。

一键执行脚本示例 (Bash)

请确保你的系统已经安装了 conan 和 openssl。

bash
#!/bin/bash

# 1. 确保 Conan 插件存放目录存在
PLUGIN_DIR="$HOME/.conan2/extensions/plugins/sign"
ORG_DIR="$PLUGIN_DIR/my-organization"
mkdir -p "$ORG_DIR"

# 2. 生成 RSA 私钥和公钥 (仅做演示,存放在插件同级目录)
echo "=> 生成 OpenSSL 密钥对..."
openssl genpkey -algorithm RSA -out "$ORG_DIR/private_key.pem" -pkeyopt rsa_keygen_bits:2048
openssl pkey -in "$ORG_DIR/private_key.pem" -pubout -out "$ORG_DIR/public_key.pem"

# 3. 创建 Conan 的签名插件 sign.py
echo "=> 正在写入 Conan 签名插件..."
cat << 'EOF' > "$PLUGIN_DIR/sign.py"
import os
import json
import subprocess
from conan.errors import ConanException
from conan.api.output import ConanOutput

def sign(ref, artifacts_folder, signature_folder, **kwargs):
    """该函数用于为生成的包进行签名"""
    manifest_filepath = os.path.join(signature_folder, "pkgsign-manifest.json")
    signature_filepath = os.path.join(signature_folder, "pkgsign-manifest.json.sig")

    # 获取存放在缓存目录里的私钥(生产环境中请替换为从环境变量/KMS服务读取)
    privkey_filepath = os.path.join(os.path.dirname(__file__), "my-organization", "private_key.pem")

    openssl_sign_cmd = [
        "openssl", "dgst", "-sha256", "-sign", privkey_filepath,
        "-out", signature_filepath, manifest_filepath
    ]

    try:
        subprocess.run(openssl_sign_cmd, check=True, capture_output=True)
        ConanOutput().success(f"Package successfully signed: {ref}")
    except subprocess.CalledProcessError as exc:
        raise ConanException(f"Error signing artifact: {exc.stderr.decode()}")

    # 将签名的元数据信息返回给 Conan 保存
    return [{
        "method": "openssl-dgst",
        "provider": "my-organization",
        "sign_artifacts": {
            "manifest": "pkgsign-manifest.json",
            "signature": "pkgsign-manifest.json.sig"
        }
    }]

def verify(ref, artifacts_folder, signature_folder, files, **kwargs):
    """该函数用于在下载/使用包前验证签名"""
    signatures_filepath = os.path.join(signature_folder, "pkgsign-signatures.json")
    if not os.path.exists(signatures_filepath):
        raise ConanException(f"No signatures found for {ref}")

    with open(signatures_filepath, "r") as f:
        signatures = json.loads(f.read()).get("signatures", [])

    pubkey_filepath = os.path.join(os.path.dirname(__file__), "my-organization", "public_key.pem")

    for signature in signatures:
        signature_filename = signature.get("sign_artifacts").get("signature")
        manifest_filename = signature.get("sign_artifacts").get("manifest")

        signature_filepath = os.path.join(signature_folder, signature_filename)
        manifest_filepath = os.path.join(signature_folder, manifest_filename)

        openssl_verify_cmd = [
            "openssl", "dgst", "-sha256", "-verify", pubkey_filepath,
            "-signature", signature_filepath, manifest_filepath
        ]

        try:
            subprocess.run(openssl_verify_cmd, check=True, capture_output=True)
            ConanOutput().success(f"Signature correctly verified for: {ref}")
        except subprocess.CalledProcessError as exc:
            raise ConanException(f"Signature validation failed for {ref}: {exc.stderr.decode()}")
EOF

# 4. 创建一个用于测试用的虚拟包 (hello/1.0)
echo "=> 创建测试包 hello/1.0..."
mkdir -p /tmp/conan_hello_test && cd /tmp/conan_hello_test
conan new cmake_lib -d name=hello -d version=1.0
conan create .

# 5. 执行包签名(对 hello/1.0 及其所有 Revision 进行签名)
echo "=> 对缓存中的包执行签名命令..."
conan cache sign "hello/1.0:*"

# 6. 验证包签名
echo "=> 验证该包的签名..."
conan cache verify "hello/1.0:*"

echo "=> 演示完成!清理测试数据..."
rm -rf /tmp/conan_hello_test

执行说明:

  1. 这个脚本会自动注入实现 sign() 和 verify() 接口的 python 插件到 ~/.conan2/extensions/plugins/sign/sign.py 中。
  2. 随后使用 conan new 产生一个极简的演示库 hello/1.0 存储于你的 Local Cache 中。
  3. conan cache sign "hello/1.0:*" 是 Conan 2 新引入的缓存包签名标准命令,它将遍历本地缓存,为该包生成数字证书并将校验元数据打包写入缓存内部的 metadata/sign 目录中。
  4. conan cache verify "hello/1.0:*" 则是用来校验防篡改的命令。

⚠️ 生产环境重要提示: 上述示例为了简化流程,将私钥直接储存在了 ~/.conan2 目录下。在真实的生产环境中,绝不要把私钥文件放在 Conan Cache 或代码仓库里。请通过调用环境变量读取私钥路径、对接 AWS KMS/HashiCorp Vault、或替换为类似 Sigstore(Cosign)这种 Keyless 等签名认证体系。

参考

  1. https://docs.conan.io/2/reference/extensions/package_signing.html
  2. https://docs.conan.io/2/reference/commands/cache.html