Jenkins 在 OSX 上: xcodebuild 出现代码签名错误

摘要:

使用最新的安装程序(截至2012年3月9日) ,在 OS X 上安装 Jenkins 变得非常容易,但是管理代码签名过程仍然非常困难,没有直接的答案。

动机:

运行一个遵循在 OS X (其中一些在这里用简单的语言解释)上运行服务的常见最佳实践的无头 CI 服务器。

背景:

过程:

通过 OS X 安装程序包安装 Jenkins CI。对于“安装类型”步骤,单击“自定义”按钮,然后选择“启动时为‘ jenkins’”。'"

讨论:

在这一点上,一个天真的期望是使用构建脚本 xcodebuild -target MyTarget -sdk iphoneos的自由样式项目应该可以工作。正如本职位的标题所示,它没有,也没有:

Code Sign error: The identity 'iPhone Developer' doesn't match any valid certificate/private key pair in the default keychain

需要做的事情显而易见——您需要在默认密钥链中添加一个有效的代码签名证书和一个私钥。在研究如何实现这一点时,我还没有找到一个解决方案,不开放系统的某种程度的漏洞。

问题1: Jenkins 守护进程没有默认的密钥链

sudo -u jenkins security default-keychain 结果是“找不到默认的钥匙链”

正如下面的 Ivo Dancet所指出的,对于 jenkins 守护进程,UserShell 默认设置为/usr/bin/false (我认为这是一个特性,而不是 bug) ; 按照他的回答将 UserShell 更改为 bash。然后,您可以使用 sudo su jenkins作为 jenkins 用户登录,并获得 bash 提示符。

  1. sudo su jenkins
  2. cd ~/Library
  3. mkdir Keychains
  4. cd Keychains
  5. security create-keychain <keychain-name>.keychain
  6. security default-keychain -s <keychain-name>.keychain

好的,很好。我们现在有了一个默认的钥匙链,让我们继续吧?但是,首先我们为什么要费心制作一个默认的密钥链呢?

几乎所有的答案,建议,或对话,我阅读通过研究建议,一个人应该只是扔他们的代码签名证书和密钥到系统的钥匙链。如果您在 Jenkins 中将 security list-keychains作为一个自由风格的项目来运行,您会看到唯一可用的密钥链是系统密钥链; 我认为这就是大多数人想到将他们的证书和密钥放入其中的想法的地方。但是,这似乎是一个非常糟糕的主意-特别是考虑到 您需要创建一个带密码的纯文本脚本来打开密钥链

问题2: 添加代码签名证书和私钥

这时候我真的开始觉得恶心了。我有一种直觉,我应该创建一个新的公钥/私钥独特的使用与 Jenkins。我的想法是,如果 jenkins 守护进程被破坏,那么我可以很容易地撤销苹果供应门户中的证书,并生成另一个公钥/私钥。如果我为我的用户帐户和 Jenkins 使用相同的密钥和证书,那么这意味着更多的麻烦(损坏?)如果 Jenkins 服务受到攻击。

指向 Simon Urbanek 的回答,您将从一个脚本中解锁密钥链,该脚本使用纯文本密码。在 Jenkins 守护进程的钥匙链里,除了“一次性”的证书和钥匙,其他东西都留着,似乎是不负责任的。

我对任何相反的讨论都很感兴趣。我是不是过于谨慎了?

为了使一个新的 CSR 作为终端中的 jenkins 守护进程,我做了以下..。

  1. sudo su jenkins
  2. 你会被提示以下问题(大多数问题是我根据经验猜出的正确答案,你有更好的洞察力吗?)?请分享。)...
    • 输入密钥和证书标签:
    • 选择算法: r(用于 RSA)
    • 输入密钥大小(以位为单位) : 2048
    • 选择签名算法: 5(对于 MD5)
    • 输入挑战字符串:
    • 还有一堆问题要问 RDN
  3. 使用新的 Apple ID 将生成的 CSR 文件(CericateSigningRequest.certSigningRequest)提交到 Apple 的供应门户
  4. 批准请求并下载. cer 文件
  5. security unlock-keychain
  6. security add-certificate ios_development.cer

这让我们更进一步。

问题3: 提供配置文件和钥匙链解锁

我在 Provisioning Portal 做了一个特殊的供应配置文件,只是为了与 CI 一起使用,希望如果发生了什么不好的事情,我会让影响小一点。最佳实践还是过度谨慎?

  1. sudo su jenkins
  2. mkdir ~/Library/MobileDevice
  3. mkdir ~/Library/MobileDevice/Provisioning\ Profiles
  4. 将您在 Provisioning Portal 设置的配置文件移动到这个新文件夹中
  5. security unlock-keychain -p <keychain password>
  6. xcodebuild -target MyTarget -sdk iphoneos

现在,当我们以 jenkins 守护进程的身份登录时,我们从命令行获得了一个成功的构建,所以如果我们创建一个自由风格的项目,并添加最后两个步骤(上面的 # 5和 # 6) ,我们将能够自动构建我们的 iOS 项目!

这可能没有必要,但是在成功地完成所有这些设置之后,我觉得将 jenkins UserShell 设置回/usr/bin/false 会更好。我有妄想症吗?

问题4: 默认的密钥链仍然不可用!

(编辑: 我张贴了我的问题的编辑,重新启动,以确保我的解决方案是100% ,当然,我遗漏了一个步骤)

即使完成了上述所有步骤,您仍然需要修改/Library/LaunchDaemons/org.jenkins-ci 上的 Launch Daemon plist。载于 这个答案的清单。请注意,这也是一个 Openrdar 窃听器

它应该是这样的:

<?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>daemon</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>
<!-- **NEW STUFF** -->
<key>SessionCreate</key>
<true />
</dict>
</plist>

通过这种设置,我还推荐使用 詹金斯的 Xcode 插件,它使得设置 xcodebuild 脚本变得更容易一些。此时,我还建议您阅读 xcodebuild 的手册页——见鬼,您已经在 Terminal 中做到了这一步,对吗?

这种设置并不完美,任何建议或见解都非常值得赞赏。

我一直很难选择一个“正确”的答案,因为我用来解决我的问题是一个集合了几乎每个人的投入。我试着给每个人至少一张赞成票,但是把答案给西蒙,因为他基本上回答了最初的问题。此外,萨米 提卡值得大力赞扬他的努力,让詹金斯工作通过 AppleScript 作为一个普通的 OSX 应用程序。如果您只对让 Jenkins 在您的用户会话(即不是作为无头服务器)中快速运行感兴趣,那么他的解决方案更像 Mac。

我希望我的努力能够引发进一步的讨论,并且帮助下一个可怜的人,因为他们听说了很多关于詹金斯 CI 的美妙的事情,他们认为他们可以在一个周末为他们的 iOS 项目做好准备。


更新: 2013年8月9日

带着这么多的赞和喜好,我想我会在18个月后带着一些简短的经验教训回到这里。

第一课: 不要把 Jenkins 暴露在公共互联网上

在2012年的 WWDC 上,我向 Xcode 和 OSX 服务器的工程师们提出了这个问题。我听到了“不要那样做!”的不和谐声音不管我问谁。他们一致认为自动构建过程非常棒,但是服务器只能在本地网络上访问。OS X Server 工程师建议允许通过 VPN 进行远程访问。

第2课: 现在有了新的安装选项

最近,我给 CocoaHead 做了一个关于我的 Jenkins 经验的演讲,令我惊讶的是,我发现了一些新的安装方法—— Homebrew,甚至是 Bitnami Mac 应用程序商店版本。这些绝对值得一看。Jonathan Wright有一个要点详细说明获得 自制的 Jenkins 在工作

第三课: 不,说真的,不要把你的构建框暴露在互联网上

从最初的帖子可以很清楚地看出,我既不是系统管理员,也不是安全专家。关于私人物品(钥匙链、证书、证书等)的常识让我对把我的詹金斯盒子放到互联网上感到非常不安。在被忽视的潜能 Nick Arnott能够确认我的神经过敏相当容易在 这篇文章

DR

在过去一年半的时间里,我对其他寻求自动化构建过程的人的建议发生了变化。确保你的 Jenkins 机器在防火墙后面。使用安装程序、 Bitnami Mac App Store 版本、 Sami Tikka 的 AppleScript 等,安装并设置 Jenkins 为一个专用的 Jenkins 用户; 这解决了我上面详述的大部分头痛问题。如果需要远程访问,在 OS X Server 中设置 VPN 服务最多需要10分钟。我已经使用这个设置超过一年了,我对它非常满意。祝你好运!

48494 次浏览

Keychains need to be unlocked before they can be used. You can use security unlock-keychain to unlock. You can do that interactively (safer) or by specifying the password on the command line (unsafe), e.g.:

security unlock-keychain -p mySecretPassword...

Obviously, putting this into a script compromises the security of that keychain, so often people setup an individual keychain with only the signing credentials to minimize such damage.

Typically in Terminal the keychain is already unlocked by your session, since the default keychain is unlocked on login, so you don't need to do that. However, any process not run in your session won't have unlocked keychain even if it has you as the user (most commonly this affects ssh, but also any other process).

If you have sudo, you can use passwd to change the Jenkins user's password. Then you can get the Jenkins password.

Also, I'm not sure if this is the issue for you, but the ANT Script I use via Jenkins has this :

<target name="unlock_keychain">
<exec executable="security">
<arg value="-v"/>
<arg value="unlock-keychain"/>
<arg value="-p"/>
<arg value="<My Password>"/>
<arg value="/Users/macbuild/Library/Keychains/login.keychain"/>
</exec>
</target>

I've had the same issue and have been searching around for some time for an answer. Here's one thing that I've learned.

I am running jenkins as the jenkins user, user created by the installer, and as everyone else has mentioned he doesn't have access to the same keychain that your normal user does. Instead of trying to login as the jenkins user, I created a second build project that simply has one build step that is "Execute Shell" in which I run commands I want to test as the jenkins user.

Once I had that set up, I could run the command

security list-keychains

And this revealed to me that the only thing that jenkins could see was the system keychain.

+ security list-keychains
"/Library/Keychains/System.keychain"
"/Library/Keychains/System.keychain"

With that knowledge, I then opened the Keychain Access app and copied my "iPhone Developer: xxxx" certificate into the System keychain (Right-click, copy from the "login" keychain).

This got me passed the certificate/private key pair code sign error but opened up another one with the provisioning profile (seems like a similar, but different, issue).

To change the password you can use sudo passwd jenkins <new-pw>. However I think it would be better to use the dscl command to change the password.

In my install jenkins (official installer) had a user shell /usr/bin/false. Changing it to bash solved the problem of not being able to login:

sudo dscl . -change /Users/jenkins UserShell /usr/bin/false /bin/bash

You should now be able to login with su jenkins.

For people having problems with keychains, I would recommend you try my alternative Jenkins installer at https://github.com/stisti/jenkins-app, downloads at https://github.com/stisti/jenkins-app/downloads

Jenkins.app runs Jenkins in your user session, so keychain access issues are not an issue :)

Suppose you also want to do ad hoc distribution through Jenkins, this necessitates that Jenkins has access to a Distribution certificate, and the team admin identity, in addition to the provisioning profiles.

Using an exported identity in a .cer file, you can programmatically import it like so, the -A switch is to allow all programs access to this entry. Alternatively, you could use several -T /path/to/program switches to allow codesign and xcodebuild access.:

$ security import devcertificate.cer -k jenkins.keychain -A

Of course, we should also have the Apple WWDCRA certificate, imported in pretty much the same way:

$ security import AppleWWDRCA.cer -k jenkins.keychain -A

However, we also need the private key for the devcertificate.cer. To do this, you need to export the corresponding private key as a .p12 key and set a password. Put it somewhere you can access it from your Jenkins shell, unlock the keychain, and import it:

$ security unlock-keychain -p YourKeychainPass jenkins.keychain
$ security import devprivatekey.p12 -k login.keychain -P ThePasswordYouSetWhenExporting -A

Importing the distribution certificate works the same way. I don't know why you need to unlock the keychain for importing a .p12 and not for a .cer, but well.

You will also need access to the provisioning profiles, I will edit those instructions into this post shortly.

For some reason, "security" utility wasn't working for me on Lion with fresh Jenkins install.

After "sudo su jenkins" it was able to create new keychain, but silently ignored all "default-keychain -s ..." or "unlock" commands returning zero exit status and printing nothing to the console. Listing default or login keychains gave nothing, keychains search list was containing only system keychain, and I couldn't change this whatever I type.

After I logged in to that user's desktop and launched Keychain Utility, it did show my created keychain and after that everything worked as described in upper posts.

I am wondering whether some of initial keychains behavior changed in Lion, or am I missing something?

I added the private and public key for the company to the keychain. I added the provision profiles for the production I will be building.

Since this user did not have an account I logged into devcenter with my account. Downloaded the provisioning certs and loaded them into Xcode.

I did not add a cert specifically for the build role account, ex. jenkins.

I did add this to the build script: security unlock-keychain -p mySecretPassword as above, but...

I created a file ~/.ssh/mypass and add the password to the file.

Then the command becomes: security unlock-keychain -p cat ~/.ssh/mypass

Builds are working like a champ. I get the ipa file, it loads on app central and works on the device.

Could also install & launch JenkinsCI as a OS X user instead of a daemon:

  1. install jenkins using the official installer (https://jenkins-ci.org/)
    • Click next
    • Click "Customize"
    • Unselect "Start at boot as 'jenkins'" - * IMPORTANT * this option normally allows a headless jenkins which doesn't work well with keychain access
  2. Launch http://127.0.0.1:8080
    • verify it DOESN'T launch
    • may need to stop jenkins sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist
  3. Double click /Applications/Jenkins/jenkins.war
    • of course this should be automated to start @ start up
  4. Open http://127.0.0.1:8080
    • verify it's now running

I have used Xcode plugin to build iOS app. In configuration of a project.

choose Add build step > Xcode > code signing & OS X keychain options.

tick Unlock keychain box and add as follow (for examples) enter image description here

somtimes, if I get the error

Code Sign error: ...

I will reopen Jenkins and enter password again to unlock

To solve this issue try to log in to http://appleid.apple.com and update your security questions.

It helped me.