在使用Jenkins/Hudson作为iOS和Mac开发的持续集成工具时,钥匙串中缺少证书和密钥

48

我正在尝试改进Hudson CI以适用于iOS,并在系统启动时启动Hudson。为此,我正在使用以下launchd脚本:

<?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,则可以正常工作。

你有什么想法为什么会出现这种情况吗?

10个回答

80

我找到了一个解决方案,让我可以访问Jenkins用户的常规密钥链。

找到这个plist:/Library/LaunchDaemons/org.jenkins-ci.plist 然后:

  • 添加UserName元素,值为jenkins
  • 在plist文件中添加一个SessionCreate元素,其值为true。这将允许您访问指定在UserName中的用户的正常密钥链。

示例:

<?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>
</plist>

然后重新启动守护进程并尝试在Jenkins中运行调用security list-keychains的作业。 您将不再看到System.keychain作为唯一条目,而是登录和任何自定义密钥链(如果您已将它们添加到“jenkins”用户的密钥链列表中)。

通过上述设置,我能够在我的Jenkins构建服务器上使用来自自定义密钥链的代码签名证书。 我不必在我的System密钥链中安装任何证书或密钥。


4
好的方案!使用它,而不是我的! - Jens Kohl
除此之外,我还需要在Jenkins作业中添加“security unlock-keychain -p“密码”path/to/keychain”的命令。构建作业后,可以将该行从Jenkins作业中删除。 - asgoth
能否使用这种方法来处理org.apache.httpd.plist?我有一个运行安全命令的php脚本,遇到了这个确切的问题。 - atreat
我希望我能说这对我起作用了,因为我非常需要这个。但是在添加之后,list-keychains仅显示System.keychain和default-keychain相同。就像根本没有任何影响一样。【使用Yosemite】 - Br.Bill
1
那是充满陷阱的旅程中最后缺失的一块。非常感谢! - marcusficner
显示剩余7条评论

23

在花费了数小时和数天来解决这个问题后,我找到了一个相当简单的解决方法。无论你是否在你的launchd配置中拥有一个独特的用户名,如上所述都没有关系:

<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全球开发者关系认证机构”证书复制到系统密钥链中,否则您将从codesign看到一个CSSMERR_TP_NOT_TRUSTED错误。
您也可以使用security list-keychains -s [path to additional keychains]注册更多的关键链。我没有尝试过,但像security list-keychains -s $HOME/Library/Keychains/login.keychain这样的预构建Shell执行命令可能有效。
编辑:我尝试使用-s将用户关键链添加到搜索路径,但我无法使其正常工作。因此,现在我们必须将证书和密钥复制到系统密钥链中。
编辑^2:请阅读并使用joensson的解决方案,而不是我的解决方案,他成功访问了用户密钥链而不仅仅是系统密钥链。

但是当没有任何人登录时,你如何解锁系统钥匙链呢?使用sudo命令吗? - Zsub
@Zsub 我认为你不需要为 System.keychain 设置密码。但是以防万一我搞错了,你可以使用密码调用 security unlock -p password /path/to/System.keychain - Jens Kohl
我创建了一个简单的任务,执行了“security list-keychains”,发现Jenkins无论守护程序运行为哪个用户或添加了哪些选项到命令中,都使用$JENKINS_HOME/Library/Keychains/login.keychain。所以,我不得不妥协,将我想要的钥匙串复制到$JENKINS_HOME/Library/Keychains/login.keychain,然后它就可以工作了。 - Petra Kahn
调整System.keychain会对所有已安装的应用程序产生副作用。joensson在下面有更好的解决方案。 - user111823

6

我们在 Mac OSX Lion 上启动的 Hudson 从机上遇到了同样的问题。当我们使用 webstart 启动从机时,它能正常工作。唯一发现的区别是不同的环境变量。

com.apple.java.jvmTask=WebStart

如果我们在没有使用webstart的情况下启动从节点,那么变量就会起作用。

com.apple.java.jvmTask=CommandLine.java

我们发现没有办法提前影响这个值。我建议您在同一台计算机上创建一个新的Hudson节点,并通过webstart启动它。启动从节点时,我们使用以下launchdaemon配置:

<?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>

谢谢。我会尽快尝试它。 - Dmytro
我在这里遇到了完全相同的问题,你找到解决方案了吗?很想听听你的想法。 - Jens Kohl
1
@DASKAjA 上面的LaunchDaemon配置是我们解决问题的方案。通过Webstart启动从机,它能够访问钥匙串。另一种方法是将密钥和证书从登录钥匙串移动到系统钥匙串中,但我只听说过这种方法可行,从未尝试过。 - cemonds

3

由于我遇到了相同的问题,但是这些解决方案都不适用于我,所以我添加了一些内容。

我的问题是我的签名证书已过期。 更新后,xcode和手动运行xcodebuild都没有问题,但Jenkins无法签署应用程序。

以下是我如何解决它的方法:

  1. 查看Keychain并搜索该密钥。 由于某种我不理解的原因,我有多个结果。

  2. 确保私钥在系统级别(如果没有,则将其拖放到左侧的系统图标中)。

enter image description here


3

我遇到了同样的问题,并尝试根据其他文章中的描述在 /Library/LaunchDaemons/org.jenkins-ci.plist 中更改用户名。但是,这仍然没有起作用,一些晦涩的 NullPointerException 也没有帮助我识别问题。因此,我想分享我的解决方案:我还必须更改 JENKINS_HOME 目录的所有者(也在 org.jenkins-ci.plist 中定义):

chown -R myBuildUser /Users/Shared/Jenkins

myBuildUser是已安装证书的用户,这也是我在plist文件中指定的用户。

当我最终意识到这个解决方案时,它显然很明显 - 但我花了几个小时才发现这一点,因此希望这篇文章可以为其他人节省时间 :-)


2
你可以尝试使用我的Jenkins.app,链接为https://github.com/stisti/jenkins-app,这是一种运行Jenkins的替代方式。它在用户会话中运行Jenkins,因此Keychain访问不是问题。

2
我们在Lion和SnowLeopard上遇到了完全相同的问题。我们需要启动一个Tomcat/Hudson服务来运行xcodebuild任务。在命令行启动时,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的行为不同。似乎Lion将密钥链与用户分离,并将其分配给登录会话。

0

添加SessionCreate并在钥匙串管理器中将许多证书设置为“始终信任”对我有用,但是在某个时候,codesign开始失败,并显示CSSMERR_TP_NOT_TRUSTED。我通过在钥匙串管理器中将iPhone分发证书设置为“使用系统默认值”来恢复。即使重新启动且未登录,构建机器人从属者随后也能够签署代码,呼。


0

对于手动签名,请将您的证书从登录钥匙串移动到系统中。在归档和生成iPA期间,无法访问登录。


0
为了为Jenkins/Hudson保留一个分隔的钥匙串,我将launchctl项目从原来的位置移动了。
/Library/LaunchDaemons/org.jenkins-ci.plist

to

/Users/Shared/Jenkins/Home/Library/LaunchAgents/org.jenkins-ci.plist

这使我能够访问为Jenkins创建的私钥链。


我认为你的回答只是让它在Jenkins用户登录时立即启动? - Zsub
没错。我找不到不需要用户登录的方法。 - igorsales
因为那样我们的解决方案并没有太大区别:'我的' Jenkins 在启动时开始工作,但我仍然需要登录用户:P - Zsub
在处理这个问题上花费了一些时间后,我们发现LaunchAgents和LaunchDaemons具有不同的功能,除了它们启动的时间和方式之外,它们还会影响构建过程的行为(特别是在构建Mac应用程序时)。 - Andrew Theken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接