Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
781 views
in Technique[技术] by (71.8m points)

macos - Jenkins on OS X: xcodebuild gives Code Sign error

Summary:

Setting up Jenkins on OS X has been made significantly easier with the most recent installer (as of 1.449 - March 9, 2012), however managing the process of code signing is still very difficult with no straightforward answer.

Motivation:

Run a headless CI server that follows common best practices for running services on OS X (Some of which is explained here in plain language).

Background:

Process:

Install Jenkins CI via OS X installer package. For the "Installation Type" step, click the Customize button, and choose "Start at boot as 'jenkins.'"

Discussion:

The naive expectation at this point was that a free-style project with the build script xcodebuild -target MyTarget -sdk iphoneos should work. As indicated by the title of this post, it does not and fails with:

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

It is obvious enough what needs to happen - you need to add a valid code signing certificate and a private key into the default keychain. In researching how to accomplish this, I have not found a solution that doesn't open up the system to some level of vulnerability.

Problem 1: No default keychain for jenkins daemon

sudo -u jenkins security default-keychain ...yields "A default keychain could not be found"

As pointed out below by Ivo Dancet, the UserShell is set to /usr/bin/false for the jenkins daemon by default (I think this is a feature, not a bug); follow his answer to change the UserShell to bash. You can then use sudo su jenkins to get logged in as the jenkins user and get a bash prompt.

  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

Okay, great. We've got a default keychain now; let's move on right? But, first why did we even bother making a default keychain?

Almost all answers, suggestions, or conversation I read throughout researching suggest that one should just chuck their code signing certs and keys into the system keychain. If you run security list-keychains as a free-style project in Jenkins, you see that the only keychain available is the system keychain; I think that's where most people came up with the idea to put their certificate and key in there. But, this just seems like a very bad idea - especially given that you'll need to create a plain text script with the password to open the keychain.

Problem 2: Adding code signing certs and private key

This is where I really start to get squeamish. I have a gut feeling that I should create a new public / private key unique for use with Jenkins. My thought process is if the jenkins daemon is compromised, then I can easily revoke the certificate in Apple's Provisioning Portal and generate another public / private key. If I use the same key and certificate for my user account and Jenkins, then it means more hassle (damage?) if the jenkins service is attacked.

Pointing to Simon Urbanek's answer you'll be unlocking the keychain from a script with a plain text password. It seems irresponsible to keep anything but "disposable" certificates and keys in the jenkins daemon's keychain.

I am very interested in any discussion to the contrary. Am I being overly cautious?

To make a new CSR as the jenkins daemon in Terminal I did the following...

  1. sudo su jenkins
  2. certtool r CertificateSigningRequest.certSigningRequest You'll be prompted for the following (most of these I made educated guesses at the correct answer; do you have better insight? Please share.)...
    • Enter key and certificate label:
    • Select algorithm: r (for RSA)
    • Enter key size in bits: 2048
    • Select signature algorithm: 5 (for MD5)
    • Enter challenge string:
    • Then a bunch of questions for RDN
  3. Submit the generated CSR file (CertificateSigningRequest.certSigningRequest) to Apple's Provisioning Portal under a new Apple ID
  4. Approve the request and download the .cer file
  5. security unlock-keychain
  6. security add-certificate ios_development.cer

This takes us one step closer...

Problem 3: Provisioning profile and Keychain unlocking

I made a special provisioning profile in the Provisioning Portal just for use with CI in hopes that if something bad happens I've made the impact a little smaller. Best practice or overly cautious?

  1. sudo su jenkins
  2. mkdir ~/Library/MobileDevice
  3. mkdir ~/Library/MobileDevice/Provisioning Profiles
  4. Move the provisioning profile that you setup in the Provisioning Portal into this new folder. We're now two short steps away from being able to run xcodebuild from the the command line as jenkins, and so that means we're also close to being able to get the Jenkins CI running builds.
  5. security unlock-keychain -p <keychain password>
  6. xcodebuild -target MyTarget -sdk iphoneos

Now we get a successful build from a command line when logged in as the jenkins daemon, so if we create a free-style project and add those final two steps (#5 and #6 above) we will be able to automate the building of our iOS project!

It might not be necessary, but I felt better setting jenkins UserShell back to /usr/bin/false after I'd successfully gotten all this setup. Am I being paranoid?

Problem 4: Default keychain still not available!

(EDIT: I posted the edits to my question, rebooted to make sure my solution was 100%, and of course, I'd left out a step)

Even after all the steps above, you'll need to modify the Launch Daemon plist at /Library/LaunchDaemons/org.jenkins-ci.plist as stated in this answer. Please note this is also an openrdar bug.

It should look like this:

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

With this setup, I would also recommend the Xcode plugin for Jenkins, which makes setting up the xcodebuild script a little bit easier. At this point, I'd also recommend reading the man pages for xcodebuild - hell you made it this far in Terminal, right?

This setup is not perfect, and any advice or insight is greatly appreciated.

I have had a hard time selecting a "correct" answer since what I've come to use to solve my problem was a collection of just about everyone's input. I've tried to give everyone at least an up vote, but award the answer to Simon because he mostly answered the original question. Furthermore, Sami Tikka deserves a lot of credit for his efforts getting Jenkins to work through AppleScript as a plain ol' OS X app. If you're only interested in getting Jenkins up and going quickly within your user session (i.e. not as a headless server) his solution is much more Mac-like.

I hope that my efforts spark further discussion, and help the next poor


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...