Jenkins no OS X: xcodebuild dá erro de sinal de código

107

Resumo:

Configurar o Jenkins no OS X ficou significativamente mais fácil com o instalador mais recente ( de 1.449 - 9 de março de 2012 ), no entanto, gerenciar o processo de assinatura de código ainda é muito difícil sem uma resposta direta.

Motivação:

Execute um servidor CI sem periférico que segue as práticas recomendadas comuns para a execução de serviços no OS X ( algumas das quais são explicadas aqui em linguagem simples ).

Fundo:

Processo:

Instale Jenkins CI via OS X pacote de instalação . Para a etapa "Tipo de instalação", clique no botão Personalizar e escolha "Iniciar na inicialização como 'jenkins'"

Discussão:

A expectativa ingênua neste ponto era que um projeto de estilo livre com o script de construção xcodebuild -target MyTarget -sdk iphoneosdeveria funcionar. Conforme indicado pelo título desta postagem, ele não funciona e falha com:

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

É bastante óbvio o que precisa acontecer - você precisa adicionar um certificado de assinatura de código válido e uma chave privada nas chaves padrão. Ao pesquisar como fazer isso, não encontrei uma solução que não abra o sistema a algum nível de vulnerabilidade.

Problema 1: nenhum keychain padrão para daemon jenkins

sudo -u jenkins security default-keychain ... produz "Não foi possível encontrar um chaveiro padrão"

Conforme apontado abaixo por Ivo Dancet , o UserShell é definido como / usr / bin / false para o daemon jenkins por padrão (acho que isso é um recurso, não um bug); siga sua resposta para alterar o UserShell para bash. Você pode usar sudo su jenkinspara fazer o login como o usuário jenkins e obter um prompt do 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

Certo, ótimo. Agora temos um chaveiro padrão; vamos seguir em frente certo? Mas, primeiro, por que nos incomodamos em fazer um chaveiro padrão?

Quase todas as respostas, sugestões ou conversas que li ao longo da pesquisa sugerem que se deve apenas jogar seus certificados de assinatura de código e chaves nas chaves do sistema. Se você executar security list-keychainscomo um projeto de estilo livre no Jenkins, verá que as únicas chaves disponíveis são as do sistema; Acho que foi aí que a maioria das pessoas teve a ideia de colocar seu certificado e chave lá. Mas, isso parece uma péssima ideia - especialmente considerando que você precisará criar um script de texto simples com a senha para abrir as chaves .

Problema 2: Adicionar certificados de assinatura de código e chave privada

É aqui que eu realmente começo a ficar enjoado. Tenho a sensação de que devo criar uma nova chave pública / privada exclusiva para uso com Jenkins. Meu processo de pensamento é, se o daemon jenkins estiver comprometido, posso facilmente revogar o certificado no Portal de provisionamento da Apple e gerar outra chave pública / privada. Se eu usar a mesma chave e certificado para minha conta de usuário e Jenkins, isso significa mais problemas (danos?) Se o serviço jenkins for atacado.

Apontando para a resposta de Simon Urbanek, você estará desbloqueando o chaveiro de um script com uma senha em texto simples. Parece irresponsável manter qualquer coisa, exceto certificados e chaves "descartáveis" nas chaves do daemon jenkins.

Estou muito interessado em qualquer discussão em contrário. Estou sendo excessivamente cauteloso?

Para fazer um novo CSR como o daemon jenkins no Terminal, fiz o seguinte ...

  1. sudo su jenkins
  2. certtool r CertificateSigningRequest.certSigningRequest Você será solicitado a responder o seguinte (a maioria dessas eu fiz suposições fundamentadas na resposta correta; você tem uma visão melhor? Por favor, compartilhe.) ...
    • Insira a chave e a etiqueta do certificado:
    • Selecione o algoritmo: r (para RSA)
    • Insira o tamanho da chave em bits: 2048
    • Selecione o algoritmo de assinatura: 5 (para MD5)
    • Insira a string de desafio:
    • Em seguida, um monte de perguntas para RDN
  3. Envie o arquivo CSR gerado (CertificateSigningRequest.certSigningRequest) para o Portal de aprovisionamento da Apple com um novo ID Apple
  4. Aprove a solicitação e baixe o arquivo .cer
  5. security unlock-keychain
  6. security add-certificate ios_development.cer

Isso nos leva um passo mais perto ...

Problema 3: perfil de provisionamento e desbloqueio de chaves

Fiz um perfil de provisionamento especial no Portal de provisionamento apenas para uso com CI, na esperança de que, se algo ruim acontecer, eu diminua um pouco o impacto. Melhor prática ou excessivamente cauteloso?

  1. sudo su jenkins
  2. mkdir ~/Library/MobileDevice
  3. mkdir ~/Library/MobileDevice/Provisioning\ Profiles
  4. Mova o perfil de provisionamento que você configurou no Portal de provisionamento para esta nova pasta. Estamos agora a dois passos de poder executar xcodebuild a partir da linha de comando como jenkins, e isso significa que também estamos perto de conseguir fazer o Jenkins CI rodar compilações.
  5. security unlock-keychain -p <keychain password>
  6. xcodebuild -target MyTarget -sdk iphoneos

Agora obtemos uma construção bem-sucedida de uma linha de comando quando conectado como o daemon jenkins, então se criarmos um projeto de estilo livre e adicionarmos essas duas etapas finais (# 5 e # 6 acima), seremos capazes de automatizar a construção de nosso projeto iOS!

Pode não ser necessário, mas me senti melhor configurando o jenkins UserShell de volta para / usr / bin / false depois de obter toda essa configuração. Estou sendo paranóico?

Problema 4: chaveiro padrão ainda não disponível!

( EDITAR: postei as edições da minha pergunta, reiniciei para ter certeza de que minha solução era 100% e, claro, deixei uma etapa de fora )

Mesmo depois de todas as etapas acima, você precisará modificar o plist Launch Daemon em /Library/LaunchDaemons/org.jenkins-ci.plist conforme declarado nesta resposta . Observe que este também é um bug do openrdar .

Deve ser assim:

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

Com essa configuração, eu também recomendaria o plugin Xcode para Jenkins , que torna a configuração do script xcodebuild um pouco mais fácil. Nesse ponto, eu também recomendo ler as páginas de manual do xcodebuild - diabos, você chegou até aqui no Terminal, certo?

Esta configuração não é perfeita e qualquer conselho ou visão é muito apreciado.

Tive dificuldade em selecionar uma resposta "correta", pois o que passei a usar para resolver meu problema foi uma coleção de opiniões de quase todos. Tentei dar a todos pelo menos um voto favorável, mas conceda a resposta a Simon porque ele respondeu principalmente à pergunta original. Além disso, Sami Tikka merece muito crédito por seus esforços para fazer o Jenkins funcionar por meio do AppleScript como um aplicativo OS X simples e antigo. Se você está interessado apenas em fazer o Jenkins funcionar rapidamente na sua sessão de usuário (ou seja, não como um servidor sem comando), sua solução é muito mais semelhante a um Mac.

Espero que meus esforços gerem mais discussões e ajudem a próxima pobre alma que vier pensando que pode configurar o Jenkins CI para seus projetos iOS em um fim de semana por causa de todas as coisas maravilhosas que ouviram sobre isso.


Atualização: 9 de agosto de 2013

Com tantos votos positivos e favoritos, pensei em voltar a isso 18 meses depois com algumas breves lições aprendidas.

Lição 1: não exponha o Jenkins à Internet pública

No WWDC de 2012, levei essa pergunta aos engenheiros do Xcode e do OS X Server. Recebi uma cacofonia de "não faça isso!" de qualquer pessoa a quem eu perguntei. Todos concordaram que um processo de construção automatizado era ótimo, mas que o servidor só deveria estar acessível na rede local. Os engenheiros do OS X Server sugeriram permitir o acesso remoto via VPN.

Lição 2: Existem novas opções de instalação agora

Recentemente, dei uma palestra no CocoaHeads sobre minha experiência com o Jenkins e, para minha surpresa, encontrei alguns novos métodos de instalação - Homebrew e até mesmo uma versão Bitnami Mac App Store . Definitivamente vale a pena conferir. Jonathan Wright tem uma essência que detalha como o Homebrew Jenkins funciona .

Lição 3: Não, sério, não exponha sua caixa de construção à Internet

Ficou bem claro na postagem original que não sou um administrador de sistema nem um especialista em segurança. O bom senso sobre coisas privadas (chaveiros, credenciais, certificados, etc.) me deixou bastante desconfortável em colocar minha caixa Jenkins na Internet. Nick Arnott, da Neglected Potential, foi capaz de confirmar meus heebie-jeebies com bastante facilidade neste artigo .

TL; DR

Minha recomendação para outras pessoas que buscam automatizar seu processo de criação mudou no último ano e meio. Certifique-se de que sua máquina Jenkins esteja protegida por firewall. Instale e configure o Jenkins como um usuário dedicado do Jenkins usando o instalador, a versão Bitnami Mac App Store, o AppleScript de Sami Tikka, etc; isso resolve a maior parte da dor de cabeça que detalhei acima. Se você precisar de acesso remoto, configurar serviços VPN no OS X Server leva no máximo dez minutos. Eu uso essa configuração há mais de um ano e estou muito feliz com isso. Boa sorte!

edelaney05
fonte
10
Estou triste, só posso dar um voto positivo a esta pergunta com respostas concisa e completa editada em :)
Zsub
Algo interrompeu o lançamento do Jenkins no OS X Yosemite - usei o instalador do Jenkins.
Jonny
Mova seu certificado para o chaveiro do sistema e seja feliz;)
Julian F. Weinert

Respostas:

30

As chaves precisam ser desbloqueadas antes de serem usadas. Você pode usar security unlock-keychainpara desbloquear. Você pode fazer isso de forma interativa (mais seguro) ou especificando a senha na linha de comando (não seguro), por exemplo:

security unlock-keychain -p mySecretPassword...

Obviamente, colocar isso em um script compromete a segurança desse keychain, então muitas vezes as pessoas configuram um keychain individual com apenas as credenciais de assinatura para minimizar tais danos.

Normalmente, Terminalas chaves já estão desbloqueadas pela sua sessão, uma vez que as chaves padrão são desbloqueadas no login, então você não precisa fazer isso. No entanto, qualquer processo não executado em sua sessão não terá as chaves desbloqueadas, mesmo se tiver você como o usuário (mais comumente isso afeta ssh, mas também qualquer outro processo).

Simon Urbanek
fonte
Mas, também estou tendo dificuldades para carregar qualquer chaveiro além do chaveiro do sistema. security unlock-keychain -p password -k /path/codesign.keychainnão funciona.
edelaney05
Você usou o chaveiro padrão como no meu exemplo acima? Observe que, para as chaves personalizadas, você precisa configurá-las primeiro para estar no caminho de pesquisa, portanto, tente primeiro as chaves padrão. Observe também que não há -kargumento para, unlock-keychainportanto, tudo o que você está tentando fazer não parece certo (consulte Recursos security help unlock-keychain).
Simon Urbanek
Tentei algo um pouco diferente, mas acabei voltando ao mesmo lugar. Eu editei minha pergunta, espero que esteja um pouco mais clara?
edelaney05
É totalmente diferente da pergunta original ... Para começar, você deve fazer o login como jenkins (por exemplo, via sudo -u jenkins bash) e verificar se você tem as permissões corretas em todo o caminho. Você fez muitas coisas que não disse (como usar dsclpara criar o usuário), então está realmente por conta própria. Você também vai querer verificar a configuração inicial (dependendo se você configurou o shell ou não, você pode usar sudo -u jenkins -ipara obter as configurações de login correspondentes).
Simon Urbanek
12

Suponha que você também queira fazer distribuição ad hoc por meio do Jenkins, isso exige que o Jenkins tenha acesso a um certificado de distribuição e à identidade do administrador da equipe, além dos perfis de provisionamento.

Usando uma identidade exportada em um arquivo .cer, você pode importá-la programaticamente dessa forma, a opção -A é para permitir que todos os programas acessem esta entrada. Como alternativa, você pode usar várias -T /path/to/programopções para permitir codesigne xcodebuildacessar:

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

Claro, também devemos ter o certificado Apple WWDCRA, importado praticamente da mesma maneira:

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

No entanto, também precisamos da chave privada para o devcertificate.cer. Para fazer isso, você precisa exportar a chave privada correspondente como uma chave .p12 e definir uma senha. Coloque-o em algum lugar onde possa acessá-lo do shell do Jenkins, desbloquear o chaveiro e importá-lo:

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

A importação do certificado de distribuição funciona da mesma maneira. Não sei por que você precisa desbloquear o chaveiro para importar um .p12 e não para um .cer, mas bem.

Você também precisará acessar os perfis de provisionamento. Em breve, editarei essas instruções nesta postagem.

Zsub
fonte
1
Você poderia atualizar com as instruções de acesso ao perfil de aprovisionamento?
Lucas
Consulte modelset.com/what-we-know/2013/03/11/jenkins_keychain_timeouts para uma abordagem relacionada.
Gili
5

Eu tive o mesmo problema e estou procurando por uma resposta há algum tempo. Aqui está uma coisa que aprendi.

Estou executando jenkins como o usuário jenkins, usuário criado pelo instalador e, como todos os outros mencionaram, ele não tem acesso às mesmas chaves que seu usuário normal. Em vez de tentar fazer login como o usuário jenkins, criei um segundo projeto de compilação que simplesmente tem uma etapa de compilação que é "Executar Shell", na qual executo comandos que desejo testar como o usuário jenkins.

Depois de configurar, eu poderia executar o comando

security list-keychains

E isso me revelou que a única coisa que Jenkins conseguia ver era o chaveiro do sistema.

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

Com esse conhecimento, abri o aplicativo Keychain Access e copiei meu certificado "iPhone Developer: xxxx" no keychain System (clique com o botão direito, copie do keychain "login").

Isso me fez passar o erro de sinal de código do par de certificado / chave privada, mas abriu outro com o perfil de provisionamento (parece um problema semelhante, mas diferente).

brianestey
fonte
Estou tendo o mesmo problema de perfil de provisão, alguma ideia de como resolver isso?
Santthosh de
2
Descobri que os perfis de provisionamento para o usuário Jenkins estão armazenados em '/ Users / Shared / Jenkins / Library / MobileDevice / Provisioning Profiles' e, portanto, iniciei minha construção para copiar um perfil de provisionamento de dentro do meu repositório git para esse local. Isso me permite atualizar o perfil de provisionamento e colocá-lo no SCM, e o Jenkins detecta automaticamente essa alteração.
brianestey,
se você copiar o login.keychain para sua biblioteca de chaves jenkins, você precisará fazer o chown para que o usuário jenkins possa desbloqueá-lo com segurança
ganoro
1
Você é o herói, eu estava arrancando os cabelos da minha cabeça por 2 dias, copiar certificados para o sistema ajudou
Roman Bobelyuk
5

Para alterar a senha, você pode usar sudo passwd jenkins <new-pw>. No entanto, acho que seria melhor usar o comando dscl para alterar a senha.

Na minha instalação, jenkins (instalador oficial) tinha um shell de usuário / usr / bin / false. Alterá-lo para bash resolveu o problema de não conseguir fazer o login:

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

Agora você deve conseguir fazer o login com su jenkins.

Ivo Dancet
fonte
Isso foi muito útil para mim - tive um problema semelhante com o create-keychaincomando que não funcionava com o usuário jenkins. A execução deste comando pareceu resolver o problema.
lxt
Quando executo o comando para alterar a senha do usuário jenkins, é solicitada a senha atual. Não tenho ideia do que é isso e não consigo apertar enter. Alguma sugestão?
CMVR
4

Eu usei o plugin Xcode para construir o aplicativo iOS. Na configuração de um projeto.

escolha Adicionar etapa de compilação> Xcode> assinatura de código e opções de chaveiro do OS X.

marque a caixa Desbloquear chaveiro e adicione o seguinte (para exemplos) insira a descrição da imagem aqui

às vezes, se eu obtiver o erro

Erro de sinal de código: ...

Vou reabrir o Jenkins e inserir a senha novamente para desbloquear

biolinh
fonte
3

Para pessoas com problemas com chaveiros, eu recomendo que você experimente meu instalador alternativo do Jenkins em https://github.com/stisti/jenkins-app , downloads em https://github.com/stisti/jenkins-app/downloads

Jenkins.app executa Jenkins em sua sessão de usuário, portanto, problemas de acesso de keychain não são um problema :)

sti
fonte
A preocupação é que esse usuário Jenkins esteja no espaço do usuário, não no espaço daemon ... então, se um invasor conseguisse comprometer o usuário Jenkins, ele teria acesso total ao seu computador.
edelaney05
Isso pode resolver os problemas que tenho recebido. O problema é que tenho uma instalação existente do Jenkins (feita de outra forma) e não quero perder todas as minhas compilações - etc. O que acontecerá no meu caso?
Mike S
2

Se você tiver sudo, poderá usar passwd para alterar a senha do usuário Jenkins. Então você pode obter a senha do Jenkins.

Além disso, não tenho certeza se esse é o problema para você, mas o ANT Script que uso via Jenkins tem o seguinte:

<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
fonte
1
Parece que você configurou o Jenkins com um usuário "macbuild"; Estou assumindo que este usuário é um usuário e não um daemon. Eu definitivamente entendo (agora) que o keychain precisará ser desbloqueado via args de linha de comando (veja os comentários de Simon Urbanek), mas ainda não tenho certeza de como criar um keychain padrão para o daemon jenkins.
edelaney05
1

Por alguma razão, o utilitário de "segurança" não estava funcionando para mim no Lion com a nova instalação do Jenkins.

Depois de "sudo su jenkins", ele foi capaz de criar novas chaves, mas ignorou silenciosamente todos os comandos "default-keychain -s ..." ou "unlock", retornando o status de saída zero e não imprimindo nada no console. A lista de keychains padrão ou de login não deu nada, a lista de pesquisa de keychains continha apenas keychain do sistema e eu não pude alterar isso seja lá o que for que digito.

Depois que eu loguei na área de trabalho do usuário e iniciei o Keychain Utility, ele mostrou meu keychain criado e depois disso tudo funcionou conforme descrito nos posts superiores.

Estou me perguntando se algum comportamento inicial do chaveiro mudou no Lion ou estou faltando alguma coisa?

jazzcat
fonte
Eu executei as etapas acima em uma instalação "limpa" do Lion, então talvez tenha ocorrido um problema de segurança antes de você começar? Outra possibilidade é que tenha havido uma atualização de segurança / OS X desde que eu postei inicialmente?
edelaney05
0

Eu adicionei a chave privada e pública da empresa ao chaveiro. Eu adicionei os perfis de provisão para a produção que irei construir.

Como esse usuário não tinha uma conta, eu entrei no devcenter com minha conta. Baixou os certificados de provisionamento e os carregou no Xcode.

Não adicionei um certificado especificamente para a conta de função de construção, por exemplo, Jenkins.

Eu adicionei isso ao script de construção: security unlock-keychain -p mySecretPassword como acima, mas ...

Eu criei um arquivo ~ / .ssh / mypass e adicionei a senha ao arquivo.

Em seguida, o comando se torna: security unlock-keychain -p cat ~/.ssh/mypass

Builds estão funcionando como um campeão. Pego o arquivo ipa, ele carrega na central de aplicativos e funciona no dispositivo.

mpechner
fonte
0

Também pode instalar e iniciar o JenkinsCI como um usuário OS X em vez de um daemon:

  1. instale jenkins usando o instalador oficial ( https://jenkins-ci.org/ )
    • Clique próximo
    • Clique em "Personalizar"
    • Desmarque "Iniciar na inicialização como 'jenkins'" - * IMPORTANTE * esta opção normalmente permite um jenkins sem cabeça que não funciona bem com acesso por chaveiro
  2. Lançamento http://127.0.0.1:8080
    • verifique se NÃO inicia
    • pode precisar parar Jenkins sudo launchctl unload /Library/LaunchDaemons/org.jenkins-ci.plist
  3. Duplo click /Applications/Jenkins/jenkins.war
    • claro que isso deve ser automatizado para iniciar @ start up
  4. Abrir http://127.0.0.1:8080
    • verifique se agora está em execução
escotópico
fonte
0

Para resolver esse problema, tente fazer login em http://appleid.apple.com e atualize suas perguntas de segurança.

Isso me ajudou.

Vlad
fonte