ADF 中的 SSIS Packages 如何通过 Key Pair 连接 Snowflake?
结论
Snowflake 官方 ODBC 3.4.1 支持使用加密私钥和 passphrase,也支持直接传入私钥内容。3.4.0 新增的私钥内容传入方式使用 SQLSetConnectAttr,不能据此认定连接字符串支持 PRIVATE_KEY=<PEM 内容>。ODBC 版本说明
现有 SSIS 包可以考虑两种方案:
- 保留 ODBC 连接管理器,从 Key Vault 获取加密的
.p8内容和 passphrase,在运行节点临时生成私钥文件,通过PRIV_KEY_FILE和PRIV_KEY_FILE_PWD连接。 - 如果要求私钥文件完全不落盘,可以通过原生 ODBC API,将加密私钥内容和 passphrase 分别设置为连接属性。组件必须能够操作实际使用的 ODBC 连接句柄,通常需要自定义组件或封装。
使用 Key Vault 无需先将私钥转换成未加密形式。
先区分连接字符串参数和 ODBC 连接属性
Snowflake 官方文档列出的私钥文件参数是:
PRIV_KEY_FILE
PRIV_KEY_FILE_PWD
前者指定本地私钥文件路径,后者提供解密该文件的口令。PRIV_KEY_FILE_PWD 应与 PRIV_KEY_FILE 配套使用。ODBC 参数说明
直接传入私钥内容时,使用以下 API 属性:
SQL_SF_CONN_ATTR_PRIV_KEY_CONTENT
SQL_SF_CONN_ATTR_PRIV_KEY_PASSWORD
SQL_SF_CONN_ATTR_PRIV_KEY_CONTENT 接受包含头尾标记的完整 PEM 内容。如果私钥已加密,则通过 SQL_SF_CONN_ATTR_PRIV_KEY_PASSWORD 提供 passphrase。官方文档说明,这两个属性适用于 ODBC 3.4.0 及以上版本。ODBC API 说明
这些属性需要通过 SQLSetConnectAttr 设置,不能直接作为同名的连接字符串关键字使用。对于官方 ODBC 3.4.1,文档尚不足以确认 PRIVATE_KEY=<PEM 内容> 这种写法受支持,其他驱动或连接器中的相似参数也不能直接套用。
现有 SSIS 包可采用的方案
如果希望尽量少改动现有包,可以将长期保存在 Blob Storage 中的私钥迁移到 Key Vault secret,并在执行期间临时生成加密私钥文件。
1. 将加密私钥和 passphrase 保存为 secret
例如,分别使用以下 secret 名称:
snowflake-private-key-pem
snowflake-private-key-passphrase
第一个 secret 保存完整的加密 PKCS#8 PEM,并保留真实换行:
-----BEGIN ENCRYPTED PRIVATE KEY-----
...
-----END ENCRYPTED PRIVATE KEY-----
Key Vault 可以保存这类字符串,无需事先解密私钥。即使保存的是未加密 PEM,Key Vault 也会对 secret 进行静态加密。PEM 自身的加密与 Key Vault 的存储加密属于两层不同的保护。Key Vault secret 说明
2. 由 ADF 将 secret 传入 SSIS
在包中创建两个字符串参数,例如:
SnowflakePrivateKeyPem
SnowflakePrivateKeyPassphrase
将这两个参数设为敏感参数,然后在 ADF 的 Execute SSIS Package activity 中,通过 SSIS Parameters 页签为参数选择 Azure Key Vault secret。
此时由 ADF 读取 secret,因此需要为 Data Factory 的 managed identity 授予相应的读取权限,并配置 Key Vault linked service。包内代码无需再自行完成 Key Vault 身份认证。Execute SSIS Package 配置说明
3. 在包内生成临时文件并配置连接
通过前置 Script Task,将收到的加密 PEM 写入执行节点上受限目录中的临时 .p8 文件,再配置 ODBC 连接管理器。
连接字符串可使用以下结构,实际使用时应写成一行:
Driver={SnowflakeDSIIDriver};Server=<account-host>.snowflakecomputing.com;UID=<service-user>;AUTHENTICATOR=SNOWFLAKE_JWT;PRIV_KEY_FILE=C:\ssis-secrets\<execution-id>\rsa_key.p8;PRIV_KEY_FILE_PWD={<passphrase>};WAREHOUSE=<warehouse>;DATABASE=<database>;SCHEMA=<schema>;
驱动名称应替换为运行节点实际注册的名称,passphrase 则在运行时注入。如果通过代码构建连接字符串,可以使用 OdbcConnectionStringBuilder 处理特殊字符,避免手工拼接。
PRIV_KEY_FILE 必须指向 Azure-SSIS IR 执行节点能够读取的文件路径,不能直接填写 Key Vault secret URL。
准备好私钥文件和连接配置后,再执行数据流。如果包加载时会提前验证相关连接,应按需设置 DelayValidation=True。SSIS 运行时验证说明
执行结束后,应关闭连接并删除临时文件,失败时也要执行清理逻辑。取消执行或进程崩溃可能导致清理未完成,还需要处理残留文件。多个并行执行实例应使用不同目录。
完全不落盘时,如何传入加密私钥
如果组件允许在建立连接前调用原生 ODBC API,就可以直接设置私钥内容和 passphrase。
以下调用假设 dbc 是已分配但尚未连接的 ODBC 连接句柄,两个字符串已从安全配置中取得,属性常量来自 Snowflake ODBC 开发头文件:
// encryptedPem 包含完整的加密 PEM 内容及真实换行。
// passphrase 是用于解密该 PEM 的口令。
SQLRETURN rc = SQLSetConnectAttr(
dbc,
SQL_SF_CONN_ATTR_PRIV_KEY_CONTENT,
reinterpret_cast<SQLPOINTER>(
const_cast<char*>(encryptedPem.c_str())),
SQL_NTS);
if (!SQL_SUCCEEDED(rc)) {
throw std::runtime_error("Failed to set private key content");
}
rc = SQLSetConnectAttr(
dbc,
SQL_SF_CONN_ATTR_PRIV_KEY_PASSWORD,
reinterpret_cast<SQLPOINTER>(
const_cast<char*>(passphrase.c_str())),
SQL_NTS);
if (!SQL_SUCCEEDED(rc)) {
throw std::runtime_error("Failed to set private key password");
}
// 随后在同一个 dbc 上调用 SQLDriverConnect,
// 连接字符串中设置 AUTHENTICATOR=SNOWFLAKE_JWT。
驱动可以通过这两个属性处理内存中的加密私钥和 passphrase。上述片段尚未包含句柄管理、诊断信息读取和资源清理。
在 SSIS 中使用这种方式前,需要确认连接管理器是否暴露了这些接口。如果它只接受连接字符串,就无法仅靠修改字符串完成上述设置。在独立 Script Task 中创建连接,也不会自动配置后续 ODBC Source 或 Destination 使用的连接。
部署时需要注意什么
- 确认 Azure-SSIS IR 实际加载的驱动版本和位数与开发机一致。
- 保留 PEM 的头尾标记和真实换行,避免把换行保存成字面量
\n。 - 对传递 secret 的活动启用合适的 Secure input、Secure output 设置。SSIS 日志和异常处理中也不要输出 PEM、passphrase 或完整连接字符串。
- 写入前先限制临时目录的访问权限,文件中仍应保存加密后的私钥内容。
- 分开保存私钥和 passphrase 便于管理,但如果同一身份能够读取两者,就不能将其视为独立的安全边界。
对于依赖现有 ODBC 数据流组件的遗留包,由 Key Vault 保存加密私钥和 passphrase、执行时生成受限临时文件,通常改动更小。如果要求私钥内容完全不落盘,则需要确认组件支持原生连接属性,或开发相应组件。PRIVATE_KEY 连接字符串写法仍不能作为已确认的能力使用。
备注:内容仅供参考。
