使用 Jenkins/Hudson 作为 iOS 和 Mac 开发的持续集成时,钥匙串中缺少证书和密钥
Posted
技术标签:
【中文标题】使用 Jenkins/Hudson 作为 iOS 和 Mac 开发的持续集成时,钥匙串中缺少证书和密钥【英文标题】:Missing certificates and keys in the keychain while using Jenkins/Hudson as Continuous Integration for iOS and Mac development 【发布时间】:2011-10-13 06:16:05 【问题描述】:我正在尝试改进适用于 ios 的 Hudson CI,并在系统启动后立即启动 Hudson。为此,我使用以下启动脚本:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>Hudson CI</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/java</string>
<string>-jar</string>
<string>/Users/user/Hudson/hudson.war</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>user</string>
</dict>
</plist>
这可以正常工作,但是当由 Hudson 启动的 xcodebuild 尝试签署应用程序时,它会失败,因为它无法在钥匙串中找到正确的密钥/证书。但是,如果我从命令行启动 Hudson,密钥/证书对就在那里,因为它工作正常。
你知道为什么会这样吗?
【问题讨论】:
【参考方案1】:我们在 Mac OSX Lion 上作为启动守护程序启动的 hudson slave 遇到了同样的问题。当我们使用 webstart 启动 slave 时,它起作用了。我们发现的唯一区别是环境变量不同。
com.apple.java.jvmTask=WebStart
有效,如果我们在没有 webstart 的情况下启动从站,则变量为
com.apple.java.jvmTask=CommandLine.java
我们发现无法预先影响价值。我建议你在 Hudson 中创建一个新节点,在同一台机器上运行并由 webstart 启动。为了启动从站,我们使用以下启动守护程序配置:
<?xml version"1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>Label</key>
<string>jenkins</string>
<key>UserName</key>
<string>apple</string>
<key>Program</key>
<string>/usr/bin/javaws</string>
<key>ProgramArguments</key>
<array>
<string>-verbose</string>
<string>-wait</string>
<string>http://<hudson-hostname>:8080/computer/<node-name>/slave-agent.jnlp</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>WorkingDirectory</key>
<string>/Users/apple</string>
</dict>
</plist>
【讨论】:
谢谢。我一有时间就试试。 我在这里遇到了完全相同的问题,您找到解决方案了吗?很想来这里。 @DASKAjA 上面的 LaunchDaemon 配置是我们解决问题的方法。通过 webstart 启动从站,它可以访问钥匙串。作为另一种方法,可以将密钥和证书从登录钥匙串移动到系统钥匙串。但我只听说这行得通,但我自己从未尝试过。【参考方案2】:在这个问题上花费了数小时和数天后,我找到了一个相当简单的解决方案。如上所述,您的启动配置中是否有不同的用户名并不重要:
<key>UserName</key>
<string>user</string>
缺少的证书和密钥必须在系统钥匙串 (/Library/Keychains/System.keychain
) 上。我在设置执行多个 security
shell 调用的 jenkins 作业后发现了这一点。有趣的是security list-keychains
:
+ security list-keychains
"/Library/Keychains/System.keychain"
"/Library/Keychains/applepushserviced.keychain"
"/Library/Keychains/System.keychain"
这是 jenkins 将搜索证书和密钥的钥匙串,因此它们应该在那里。在我将证书移到那里后,它就可以工作了。确保您还将»Apple Worldwide Developer Relations Certification Authority« 证书复制到系统钥匙串,否则您将看到来自codesign
的CSSMERR_TP_NOT_TRUSTED
错误。
还可以使用security list-keychains -s [path to additional keychains]
注册更多钥匙串。我还没有尝试过,但是像 security list-keychains -s $HOME/Library/Keychains/login.keychain
这样的东西作为 jenkins 中的预构建 shell 执行可能会起作用。
编辑:我尝试使用-s
将用户钥匙串添加到搜索路径,但我无法让它工作。所以现在,我们必须将我们的证书和密钥复制到系统钥匙串中。
EDIT^2:阅读并使用 joensson' solution 而不是我的,他管理它来访问用户钥匙串,而不仅仅是系统钥匙串。
【讨论】:
但是在无人登录的情况下如何解锁系统钥匙串呢?sudo
命令?
@Zsub 我认为您不需要 System.keychain 的密码。但如果我在这里搞砸了,你可以用密码拨打security unlock -p password /path/to/System.keychain
。
我创建了一个执行“security list-keychains”的简单作业,发现 Jenkins 使用了 $JENKINS_HOME/Library/Keychains/login.keychain,无论守护程序以哪个用户身份运行,或者我添加了哪些选项命令。所以,我放弃并将我想要的钥匙串复制到 $JENKINS_HOME/Library/Keychains/login.keychain 并且它起作用了。
摆弄 System.keychain 会对所有已安装的应用程序产生副作用。 joensson 下面有一个更好的解决方案。【参考方案3】:
我们在 Lion 和 SnowLeopard 上遇到了完全相同的问题。我们必须启动一个带有 xcodebuild 作业的 Tomcat/Hudson 作为服务。从命令行启动时,xcodebuild 可以访问 login.keychain 以使用包含的证书。但重启机器后,xcodebuild 看不到 login.keychain,因此签名失败。
由于我们需要通过钥匙串提供我们的公司证书,因此系统钥匙串不是一个选项。相反,我们通过一个简单的解决方法解决了这个问题。我们删除了用户名,以便启动守护程序在 root 下启动进程。
<plist version="1.0">
<dict>
<key>Label</key>
<string>$LAUNCH_LABEL</string>
<key>Disabled</key>
<false/>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>$INSTALL_DIR/start.sh</string>
</array>
<key>StandardOutPath</key>
<string>$INSTALL_DIR/tomcat-stdout.log</string>
<key>StandardErrorPath</key>
<string>$INSTALL_DIR/tomcat-stderr.log</string>
</dict>
</plist>
启动守护进程调用一个简单的脚本(start.sh),模拟完全登录并运行想要的程序
su -l username -c program
现在,即使在启动之后,xcodebuild 也可以访问 login.keychain。这也适用于 Snow Leopard,但是,如果您在并行会话中关闭用户特定的 login.keychain(如 vnc 登录/注销),则钥匙串会丢失。狮子的行为不同。似乎 Lion 将钥匙串与用户分离并将其分配给登录会话。
【讨论】:
【参考方案4】:为了为 Jenkins/Hudson 保留一个分隔的钥匙串,我将 launchctl 项从
/Library/LaunchDaemons/org.jenkins-ci.plist
到
/Users/Shared/Jenkins/Home/Library/LaunchAgents/org.jenkins-ci.plist
这让我可以访问为 Jenkins 创建的私钥链。
【讨论】:
我相信您的回答只会在 Jenkins 用户登录后立即启动? 没错。如果不登录用户,我找不到方法。 因为我们的解决方案并没有太大的不同:“我的”Jenkins 在启动时启动,但我仍然需要登录用户:P 经过一段时间的处理,LaunchAgents 和 LaunchDaemons 具有不同的功能,不仅仅是它们何时以及如何启动,这些会影响构建过程的行为(尤其是在构建 mac 应用程序时)。跨度> 【参考方案5】:我找到了一个解决方案,让我可以访问我的 Jenkins 用户的常规钥匙串。
除了按照公认的答案在 plist 中指定 UserName 元素之外,为您在 UserName 中指定的用户获取普通钥匙串的访问权限的技巧是还向 plist 文件添加一个值为 true 的 SessionCreate 元素 - /Library/LaunchDaemons/org.jenkins-ci.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>EnvironmentVariables</key>
<dict>
<key>JENKINS_HOME</key>
<string>/Users/Shared/Jenkins/Home</string>
</dict>
<key>GroupName</key>
<string>wheel</string>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>org.jenkins-ci</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Library/Application Support/Jenkins/jenkins-runner.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>jenkins</string>
<key>SessionCreate</key>
<true />
</dict>
然后重新启动守护程序并尝试在 Jenkins 中运行调用安全列表钥匙串的作业 - 您不应再将 System.keychain 作为唯一条目,而是常规登录和您可能已添加到列表中的任何自定义钥匙链“jenkins”用户的钥匙串。
我现在在我的 Jenkins 构建服务器上使用自定义钥匙串中的代码签名证书 - 我没有在我的系统钥匙串中安装任何证书或密钥。
【讨论】:
很好的解决方案!用这个,而不是我的! 谢谢!这正是我所需要的。太糟糕了,它没有记录在案。我为它创建了一个错误报告rdar://11708751 当我执行命令 security list-keychains 时,我的 login.keychain 在列表中。问题是Jenkins仍然输出:security: SecKeychainUnlock /Users/Shared/Jenkins/Library/Keychains/login.keychain: The specified keychain could not be found. 仅供参考,Jenkins 1.477 的更改日志,“更改了 Mac 安装程序的默认设置,使 iOS 代码设计更容易。”,似乎默认解决了这个问题。 这是充满陷阱的旅程中最后丢失的部分。非常感谢!【参考方案6】:您可以尝试我的 Jenkins.app,https://github.com/stisti/jenkins-app,这是运行 Jenkins 的另一种方式。它在用户会话中运行 Jenkins,因此 Keychain 访问不是问题。
【讨论】:
【参考方案7】:我遇到了同样的问题,并尝试按照其他帖子中的说明更改 /Library/LaunchDaemons/org.jenkins-ci.plist 中的用户名。但是,它仍然没有工作,一些不起眼的 NullPointerException 并没有帮助我识别问题。因此,我只想分享我的解决方案:我还必须更改 JENKINS_HOME 目录的所有者(也在 org.jenkins-ci.plist 中定义):
chown -R myBuildUser /Users/Shared/Jenkins
myBuildUser 是安装了证书的用户,这是我在 plist 文件中指定的用户。
当我终于意识到时,这个解决方案非常明显 - 但我花了几个小时才知道这一点,所以希望这篇文章可以为其他人节省时间:-)
【讨论】:
【参考方案8】:添加 会话创建 并在钥匙串管理器中设置大量证书以“始终信任” 使用从 plist 开始的 buildbot 为我工作......但在某些时候,代码设计开始失败 与 CSSMERR_TP_NOT_TRUSTED。我通过在钥匙串管理器中将 iPhone 分发证书设置为“使用系统默认值”来恢复。即使在重新启动后,没有登录, 然后 buildbot 从站就可以签署代码了,哇哦。
【讨论】:
【参考方案9】:添加这个是因为我遇到了同样的问题,但这些解决方案都不适合我。
我的问题是我的签名证书已过期。更新后,手动运行 xcode 和运行 xcodebuild 工作正常,但 Jenkins 无法签署应用程序。
这是我修复它的方法:
查看钥匙串并搜索钥匙。出于某种我不明白的原因,我有多个结果。
确保私钥处于系统级别(如果不是,则将其拖放到左侧的系统图标中。
【讨论】:
【参考方案10】:对于手动签名将您的证书从登录名移至钥匙串中的系统。在存档和生成 iPA 期间无法访问登录。
【讨论】:
以上是关于使用 Jenkins/Hudson 作为 iOS 和 Mac 开发的持续集成时,钥匙串中缺少证书和密钥的主要内容,如果未能解决你的问题,请参考以下文章
Jenkins:hudson.plugins.git.GitException:命令“git fetch --tags --progress origin返回状态码143: