Jenkins en OS X: xcodebuild da error de signo de código

107

Resumen:

La configuración de Jenkins en OS X se ha simplificado significativamente con el instalador más reciente ( desde 1.449 hasta el 9 de marzo de 2012 ), sin embargo, administrar el proceso de firma de código sigue siendo muy difícil sin una respuesta sencilla.

Motivación:

Ejecute un servidor CI sin cabeza que siga las mejores prácticas comunes para ejecutar servicios en OS X ( algunas de las cuales se explican aquí en lenguaje sencillo ).

Antecedentes:

Proceso:

Instalar Jenkins CI a través de OS X paquete de instalación . Para el paso "Tipo de instalación", haga clic en el botón Personalizar y seleccione "Iniciar al arrancar como 'jenkins'".

Discusión:

La expectativa ingenua en este punto era que un proyecto de estilo libre con el script de compilación xcodebuild -target MyTarget -sdk iphoneosdebería funcionar. Como indica el título de esta publicación, no lo hace y falla con:

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

Es bastante obvio lo que debe suceder: debe agregar un certificado de firma de código válido y una clave privada en el llavero predeterminado. Al investigar cómo lograr esto, no he encontrado una solución que no abra el sistema a algún nivel de vulnerabilidad.

Problema 1: No hay llavero predeterminado para el demonio jenkins

sudo -u jenkins security default-keychain ... produce "No se pudo encontrar un llavero predeterminado"

Como señala Ivo Dancet a continuación , UserShell está configurado en / usr / bin / false para el demonio jenkins de forma predeterminada (creo que esta es una característica, no un error); siga su respuesta para cambiar UserShell a bash. Luego puede usar sudo su jenkinspara iniciar sesión como usuario de jenkins y obtener un indicador de 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

Ok genial. Ahora tenemos un llavero predeterminado; sigamos adelante, ¿verdad? Pero, primero, ¿por qué nos molestamos en hacer un llavero predeterminado?

Casi todas las respuestas, sugerencias o conversaciones que leí a lo largo de la investigación sugieren que uno debería simplemente arrojar sus certificados y claves de firma de código en el llavero del sistema. Si ejecuta security list-keychainscomo un proyecto de estilo libre en Jenkins, verá que el único llavero disponible es el llavero del sistema; Creo que ahí es donde a la mayoría de la gente se le ocurrió la idea de poner su certificado y clave allí. Pero, esto parece una muy mala idea, especialmente dado que necesitará crear un script de texto sin formato con la contraseña para abrir el llavero .

Problema 2: agregar certificados de firma de código y clave privada

Aquí es donde realmente empiezo a sentirme aprensivo. Tengo el presentimiento de que debería crear una nueva clave pública / privada única para usar con Jenkins. Mi proceso de pensamiento es que si el demonio jenkins está comprometido, entonces puedo revocar fácilmente el certificado en el portal de aprovisionamiento de Apple y generar otra clave pública / privada. Si utilizo la misma clave y certificado para mi cuenta de usuario y Jenkins, significa más molestias (¿daños?) Si el servicio de jenkins es atacado.

Al señalar la respuesta de Simon Urbanek, desbloqueará el llavero de un script con una contraseña de texto sin formato. Parece irresponsable mantener cualquier cosa que no sean certificados y claves "desechables" en el llavero del demonio jenkins.

Estoy muy interesado en cualquier discusión en sentido contrario. ¿Estoy siendo demasiado cauteloso?

Para hacer una nueva CSR como el demonio jenkins en Terminal, hice lo siguiente ...

  1. sudo su jenkins
  2. certtool r CertificateSigningRequest.certSigningRequest Se le pedirá lo siguiente (la mayoría de ellos hice conjeturas fundamentadas sobre la respuesta correcta; ¿tiene una mejor idea? Por favor, comparta) ...
    • Ingrese la clave y la etiqueta del certificado:
    • Seleccionar algoritmo: r(para RSA)
    • Ingrese el tamaño de la clave en bits: 2048
    • Seleccionar algoritmo de firma: 5(para MD5)
    • Ingrese la cadena de desafío:
    • Luego, un montón de preguntas para RDN.
  3. Envíe el archivo CSR generado (CertificateSigningRequest.certSigningRequest) al portal de aprovisionamiento de Apple con un nuevo ID de Apple
  4. Apruebe la solicitud y descargue el archivo .cer
  5. security unlock-keychain
  6. security add-certificate ios_development.cer

Esto nos acerca un paso más ...

Problema 3: perfil de aprovisionamiento y desbloqueo del llavero

Hice un perfil de aprovisionamiento especial en el Portal de aprovisionamiento solo para usar con CI con la esperanza de que, si ocurre algo malo, haya reducido un poco el impacto. ¿Mejores prácticas o demasiado cauteloso?

  1. sudo su jenkins
  2. mkdir ~/Library/MobileDevice
  3. mkdir ~/Library/MobileDevice/Provisioning\ Profiles
  4. Mueva el perfil de aprovisionamiento que configuró en el portal de aprovisionamiento a esta nueva carpeta. Ahora estamos a dos pasos de poder ejecutar xcodebuild desde la línea de comandos como jenkins, y eso significa que también estamos cerca de poder ejecutar las compilaciones de Jenkins CI.
  5. security unlock-keychain -p <keychain password>
  6. xcodebuild -target MyTarget -sdk iphoneos

Ahora obtenemos una compilación exitosa desde una línea de comando cuando iniciamos sesión como el demonio jenkins, por lo que si creamos un proyecto de estilo libre y agregamos esos dos pasos finales (# 5 y # 6 arriba) podremos automatizar la compilación de nuestro proyecto iOS!

Puede que no sea necesario, pero me sentí mejor configurando jenkins UserShell de nuevo en / usr / bin / false después de haber logrado toda esta configuración. ¿Me estoy volviendo paranoico?

Problema 4: ¡El llavero predeterminado aún no está disponible!

( EDITAR: publiqué las ediciones de mi pregunta, reinicié para asegurarme de que mi solución estaba al 100% y, por supuesto, había omitido un paso )

Incluso después de todos los pasos anteriores, deberá modificar la lista Launch Daemon en /Library/LaunchDaemons/org.jenkins-ci.plist como se indica en esta respuesta . Tenga en cuenta que esto también es un error de openrdar .

Debe tener un aspecto como este:

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

Con esta configuración, también recomendaría el complemento Xcode para Jenkins , que facilita un poco la configuración del script xcodebuild. En este punto, también recomendaría leer las páginas de manual de xcodebuild; diablos, llegaste tan lejos en Terminal, ¿verdad?

Esta configuración no es perfecta y cualquier consejo o conocimiento es muy apreciado.

Me ha costado mucho seleccionar una respuesta "correcta", ya que lo que he llegado a utilizar para resolver mi problema fue una recopilación de las opiniones de todos. Intenté darles a todos al menos un voto positivo, pero le doy la respuesta a Simon porque él respondió principalmente a la pregunta original. Además, Sami Tikka merece mucho crédito por sus esfuerzos para que Jenkins funcione a través de AppleScript como una simple aplicación OS X. Si solo está interesado en poner a Jenkins en funcionamiento e ir rápidamente dentro de su sesión de usuario (es decir, no como un servidor sin cabeza), su solución es mucho más parecida a Mac.

Espero que mis esfuerzos susciten más discusiones y ayuden al próximo pobre que venga pensando que pueden configurar Jenkins CI para sus proyectos de iOS en un fin de semana debido a todas las cosas maravillosas que han escuchado sobre él.


Actualización: 9 de agosto de 2013

Con tantos votos positivos y favoritos, pensé que volvería a esto 18 meses después con algunas breves lecciones aprendidas.

Lección 1: No exponga a Jenkins a la Internet pública

En la WWDC de 2012, llevé esta pregunta a los ingenieros de Xcode y OS X Server. Recibí una cacofonía de "¡no hagas eso!" de cualquiera a quien le pregunté. Todos estuvieron de acuerdo en que un proceso de construcción automatizado era excelente, pero que el servidor solo debería ser accesible en la red local. Los ingenieros de OS X Server sugirieron permitir el acceso remoto a través de VPN.

Lección 2: ahora hay nuevas opciones de instalación

Recientemente di una charla de CocoaHeads sobre mi experiencia con Jenkins y, para mi sorpresa, encontré algunos métodos de instalación nuevos: Homebrew e incluso una versión de Bitnami Mac App Store . Definitivamente vale la pena echarle un vistazo. Jonathan Wright tiene una esencia que detalla cómo funciona Homebrew Jenkins .

Lección 3: No, en serio, no exponga su caja de compilación a Internet

De la publicación original queda bastante claro que no soy administrador de sistemas ni experto en seguridad. El sentido común acerca de las cosas privadas (llaveros, credenciales, certificados, etc.) me hizo sentir bastante incómodo acerca de poner mi caja Jenkins en Internet. Nick Arnott de Neglected Potential fue capaz de confirmar mis heebie-jeebies con bastante facilidad en este artículo .

TL; DR

Mi recomendación para otros que buscan automatizar su proceso de construcción ha cambiado durante el último año y medio. Asegúrese de que su máquina Jenkins esté detrás de su firewall. Instale y configure Jenkins como un usuario dedicado de Jenkins, ya sea utilizando el instalador, la versión de Bitnami Mac App Store, el AppleScript de Sami Tikka, etc. esto resuelve la mayor parte del dolor de cabeza que detallo anteriormente. Si necesita acceso remoto, configurar los servicios VPN en OS X Server toma diez minutos como máximo. He estado usando esta configuración durante más de un año y estoy muy contento con ella. ¡Buena suerte!

edelaney05
fuente
10
Estoy triste, solo puedo dar un voto a favor a esta pregunta concisa y completa con respuestas editadas en :)
Zsub
Algo rompió el lanzamiento de Jenkins en OS X Yosemite: usó el instalador de Jenkins.
Jonny
Mueva su certificado al llavero del sistema y sea feliz;)
Julian F. Weinert

Respuestas:

30

Los llaveros deben desbloquearse antes de poder usarse. Puede usar security unlock-keychainpara desbloquear. Puede hacerlo de forma interactiva (más seguro) o especificando la contraseña en la línea de comando (inseguro), por ejemplo:

security unlock-keychain -p mySecretPassword...

Obviamente, poner esto en un script compromete la seguridad de ese llavero, por lo que a menudo las personas configuran un llavero individual con solo las credenciales de firma para minimizar dicho daño.

Normalmente, Terminalel llavero ya está desbloqueado por su sesión, ya que el llavero predeterminado se desbloquea al iniciar sesión, por lo que no es necesario que lo haga. Sin embargo, cualquier proceso que no se ejecute en su sesión no tendrá el llavero desbloqueado incluso si lo tiene a usted como usuario (lo más común es que esto afecte ssh, pero también a cualquier otro proceso).

Simon Urbanek
fuente
Pero también estoy teniendo dificultades para cargar cualquier llavero más allá del llavero del sistema. security unlock-keychain -p password -k /path/codesign.keychainno funciona.
edelaney05
¿Ha utilizado el llavero predeterminado como en mi ejemplo anterior? Tenga en cuenta que para los llaveros personalizados, primero debe configurarlos para que estén en la ruta de búsqueda, así que pruebe primero con el llavero predeterminado. También tenga en cuenta que no hay ningún -kargumento para unlock-keychainque lo que esté tratando de hacer no parezca correcto (ver security help unlock-keychain).
Simon Urbanek
Probé algo un poco diferente, pero finalmente volví al mismo lugar. Edité mi pregunta, espero que sea un poco más clara.
edelaney05
Es completamente diferente a la pregunta original ... Para empezar, debe iniciar sesión como jenkins (por ejemplo, via sudo -u jenkins bash) y verificar que tiene los permisos en toda la ruta correcta. Hiciste muchas cosas que no dijiste (como usar dsclpara crear el usuario), así que estás realmente solo. También querrá verificar la configuración de inicio (dependiendo de si configura el shell o no, puede usarlo sudo -u jenkins -ipara obtener la configuración de inicio de sesión correspondiente).
Simon Urbanek
12

Suponga que también desea realizar una distribución ad hoc a través de Jenkins, esto requiere que Jenkins tenga acceso a un certificado de distribución y la identidad del administrador del equipo, además de los perfiles de aprovisionamiento.

Al usar una identidad exportada en un archivo .cer, puede importarlo mediante programación de esta manera, el interruptor -A permite que todos los programas accedan a esta entrada. Alternativamente, puede usar varios -T /path/to/programinterruptores para permitir codesigny xcodebuildacceder:

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

Por supuesto, también deberíamos tener el certificado WWDCRA de Apple, importado prácticamente de la misma manera:

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

Sin embargo, también necesitamos la clave privada para devcertificate.cer. Para hacer esto, necesita exportar la clave privada correspondiente como clave .p12 y establecer una contraseña. Colóquelo en un lugar donde pueda acceder a él desde su caparazón de Jenkins, desbloquee el llavero e impórtelo:

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

La importación del certificado de distribución funciona de la misma manera. No sé por qué necesitas desbloquear el llavero para importar un .p12 y no para un .cer, pero bueno.

También necesitará acceso a los perfiles de aprovisionamiento, editaré esas instrucciones en esta publicación en breve.

Zsub
fuente
1
¿Podría actualizar con las instrucciones de acceso al perfil de aprovisionamiento?
Lucas
Consulte modeset.com/what-we-know/2013/03/11/jenkins_keychain_timeouts para obtener un enfoque relacionado.
Gili
5

Tuve el mismo problema y he estado buscando una respuesta durante algún tiempo. Aquí hay una cosa que he aprendido.

Estoy ejecutando jenkins como usuario de jenkins, usuario creado por el instalador y, como todos los demás han mencionado, no tiene acceso al mismo llavero que su usuario normal. En lugar de intentar iniciar sesión como usuario de jenkins, creé un segundo proyecto de compilación que simplemente tiene un paso de compilación que es "Ejecutar Shell" en el que ejecuto los comandos que quiero probar como usuario de jenkins.

Una vez que lo tuve configurado, pude ejecutar el comando

security list-keychains

Y esto me reveló que lo único que Jenkins podía ver era el llavero del sistema.

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

Con ese conocimiento, abrí la aplicación Keychain Access y copié mi certificado "iPhone Developer: xxxx" en el llavero del sistema (clic derecho, copia del llavero de "inicio de sesión").

Esto hizo que pasara el error de firma del código del par certificado / clave privada, pero abrió otro con el perfil de aprovisionamiento (parece un problema similar, pero diferente).

Brianestey
fuente
Me encuentro con el mismo problema de perfil de provisión, ¿alguna idea sobre cómo resolver esto?
Santthosh
2
Descubrí que los perfiles de aprovisionamiento para el usuario de Jenkins se almacenan en '/ Users / Shared / Jenkins / Library / MobileDevice / Provisioning Profiles' y, por lo tanto, puse un paso en mi compilación para copiar un perfil de aprovisionamiento desde dentro de mi repositorio git a esa ubicación. Esto me permite actualizar el perfil de aprovisionamiento y enviarlo a SCM y Jenkins detecta automáticamente ese cambio.
brianestey
si copia el login.keychain a su biblioteca de llaveros de jenkins, deberá cambiarlo para que el usuario de jenkins pueda desbloquearlo de forma
segura
1
Tú eres el héroe, estuve arrancándome el pelo de la cabeza durante 2 días, copiar certificados al sistema me ayudó
Roman Bobelyuk
5

Para cambiar la contraseña puede utilizar sudo passwd jenkins <new-pw>. Sin embargo, creo que sería mejor usar el comando dscl para cambiar la contraseña.

En mi instalación, jenkins (instalador oficial) tenía un usuario shell / usr / bin / false. Cambiarlo a bash resolvió el problema de no poder iniciar sesión:

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

Ahora debería poder iniciar sesión con su jenkins.

Ivo Dancet
fuente
Esto fue muy útil para mí: tuve un problema similar con el create-keychaincomando que no funcionaba con el usuario de jenkins. La ejecución de este comando pareció solucionar el problema.
lxt
Cuando ejecuto el comando para cambiar la contraseña de usuario de jenkins, se me solicita la contraseña actual. No tengo idea de qué es esto y no puedo presionar Enter. ¿Alguna sugerencia?
CMVR
4

He usado el complemento Xcode para crear una aplicación iOS. En configuración de un proyecto.

elija Agregar paso de compilación> Xcode> firma de código y opciones de llavero de OS X.

marque la casilla Desbloquear llavero y agregue lo siguiente (para ejemplos) ingrese la descripción de la imagen aquí

a veces, si me sale el error

Error de signo de código: ...

Volveré a abrir Jenkins e ingresaré la contraseña nuevamente para desbloquear

biolinh
fuente
3

Para las personas que tienen problemas con los llaveros, les recomendaría que prueben mi instalador alternativo de Jenkins en https://github.com/stisti/jenkins-app , descargas en https://github.com/stisti/jenkins-app/downloads

Jenkins.app ejecuta Jenkins en su sesión de usuario, por lo que los problemas de acceso al llavero no son un problema :)

sti
fuente
La preocupación es que este usuario de Jenkins está en el espacio de usuario, no en el espacio del demonio ... por lo que si un atacante pudiera comprometer a su usuario de Jenkins, tendría acceso completo a su computadora.
edelaney05
Esto podría resolver los problemas que he tenido. El problema es que tengo una instalación de Jenkins existente (hecha al revés) y no quiero perder todas mis compilaciones, etc. ¿Qué pasará en mi caso?
Mike S
2

Si tiene sudo, puede usar passwd para cambiar la contraseña del usuario de Jenkins. Entonces puede obtener la contraseña de Jenkins.

Además, no estoy seguro de si este es su problema, pero el script ANT que uso a través de Jenkins tiene esto:

<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>
Feasoron
fuente
1
Parece que tiene Jenkins configurado con un usuario "macbuild"; Supongo que este usuario es un usuario y no un demonio. Definitivamente entiendo (ahora) que el llavero deberá desbloquearse a través de argumentos de línea de comando (vea los comentarios de Simon Urbanek), pero todavía no estoy seguro de cómo crear un llavero predeterminado para el demonio jenkins.
edelaney05
1

Por alguna razón, la utilidad de "seguridad" no me funcionaba en Lion con una instalación nueva de Jenkins.

Después de "sudo su jenkins" fue capaz de crear un nuevo llavero, pero silenciosamente ignoró todos los comandos "default-keychain -s ..." o "desbloquear" que devolvieron el estado de salida cero y no imprimieron nada en la consola. La lista de llaveros predeterminados o de inicio de sesión no dio nada, la lista de búsqueda de llaveros solo contenía el llavero del sistema, y ​​no pude cambiar esto sin importar lo que escriba.

Después de iniciar sesión en el escritorio de ese usuario e iniciar Keychain Utility, mostró mi llavero creado y luego todo funcionó como se describe en las publicaciones superiores.

Me pregunto si algunos de los comportamientos iniciales de llaveros cambiaron en Lion, o me estoy perdiendo algo.

Jazzcat
fuente
Realicé los pasos anteriores en una instalación "limpia" de Lion, por lo que tal vez hubo un problema con la seguridad antes de que usted entrara. Otra posibilidad es que haya habido una actualización de seguridad / OS X desde que publiqué inicialmente.
edelaney05
0

Agregué la clave pública y privada de la empresa al llavero. Agregué los perfiles de provisión para la producción que construiré.

Dado que este usuario no tenía una cuenta, inicié sesión en devcenter con mi cuenta. Descargó los certificados de aprovisionamiento y los cargó en Xcode.

No agregué un certificado específicamente para la cuenta de rol de compilación, ej. jenkins.

Agregué esto al script de construcción: desbloqueo de seguridad-llavero -p mySecretPassword como arriba, pero ...

Creé un archivo ~ / .ssh / mypass y agregué la contraseña al archivo.

Entonces el comando se convierte en: desbloqueo de seguridad-llavero -p cat ~/.ssh/mypass

Las construcciones funcionan como un campeón. Obtengo el archivo ipa, se carga en la central de aplicaciones y funciona en el dispositivo.

mpechner
fuente
0

También podría instalar y ejecutar JenkinsCI como un usuario de OS X en lugar de un demonio:

  1. instale jenkins usando el instalador oficial ( https://jenkins-ci.org/ )
    • Haga clic en Siguiente
    • Haga clic en "Personalizar"
    • Anule la selección de "Empezar al arrancar como 'jenkins'" - * IMPORTANTE * esta opción normalmente permite un jenkins sin cabeza que no funciona bien con el acceso a llavero
  2. Lanzamiento http://127.0.0.1:8080
    • verificar que NO se inicia
    • puede que necesite detener a Jenkins sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist
  3. Haga doble clic /Applications/Jenkins/jenkins.war
    • por supuesto, esto debería estar automatizado para iniciar @ iniciar
  4. Abierto http://127.0.0.1:8080
    • verificar que ahora se esté ejecutando
escotópico
fuente