Pular para o conteúdo
ecdsa.com
curl (OpenSSL backend)curl

Mensagem de erro

curl: (60) SSL certificate problem: self-signed certificate in certificate chain

Também aparece como

  • curl: (60) SSL certificate problem: self signed certificate in certificate chain
  • curl: (60) SSL certificate OpenSSL verify result: self-signed certificate in certificate chain (19)

O OpenSSL 3.0 e posteriores imprimem “self-signed”; o OpenSSL 1.1.1 imprime “self signed”, sem hífen, por isso as duas grafias aparecem em logs e resultados de busca. Versões mais novas do curl (8.22.0 no nosso teste) formulam assim: “SSL certificate OpenSSL verify result: self-signed certificate in certificate chain (19)”. Builds do curl com Schannel no Windows relatam o caso como “SEC_E_UNTRUSTED_ROOT”.

curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN (19)

O que significa

O curl percorreu a cadeia enviada pelo servidor até um certificado raiz autoassinado, e essa raiz não está no bundle de CAs do curl. Ao contrário de “unable to get local issuer certificate”, aqui a cadeia está completa: o curl enxerga a raiz, só não tem motivo para confiar nela. Na prática, a raiz quase sempre pertence a uma CA da empresa ou a um proxy que intercepta o TLS.

Por que acontece

Como corrigir

  1. 1.

    Descobrir de quem é a raiz

    Examine o último certificado da cadeia. Se o titular for um proxy ou um fornecedor de segurança, ou o nome da sua empresa, a correção é confiar nessa raiz nesta máquina. Se for um nome inesperado em uma rede que você não controla, pare: alguém está interceptando a conexão.

    bash
    # Print the subject and issuer of every certificate the server (or the proxy) sends
    openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
      | grep -E '^ *[0-9]+ s:|^ *i:'
    # The last entry has s: == i: — that self-signed root is what curl does not trust.
  2. 2.

    Adicionar a raiz da empresa ou do proxy ao repositório do sistema

    Obtenha o certificado raiz com o departamento de TI (ou exporte-o do trust store de uma máquina gerenciada) e instale-o no bundle do sistema operacional que o curl usa. Contêineres precisam dele durante o build; o WSL precisa dele dentro da distribuição Linux, e não só no Windows.

    bash
    # Debian, Ubuntu, Alpine
    sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt
    sudo update-ca-certificates
    
    # RHEL, Fedora, Amazon Linux
    sudo cp corp-root-ca.pem /etc/pki/ca-trust/source/anchors/
    sudo update-ca-trust
  3. 3.

    Confiar na raiz para um comando ou um ambiente

    --cacert e CURL_CA_BUNDLE substituem o bundle padrão do curl, então aponte-os para um arquivo que contenha tanto as raízes públicas quanto a privada quando o mesmo processo também se comunica com sites públicos.

    bash
    curl --cacert corp-root-ca.pem https://api.example.com/
    
    # Keep public roots too: build one bundle with both
    cat /etc/ssl/certs/ca-certificates.crt corp-root-ca.pem > /opt/certs/bundle.pem
    export CURL_CA_BUNDLE=/opt/certs/bundle.pem
  4. 4.

    Donos do servidor: não depender do envio da raiz

    Sirva o certificado folha e os intermediários e distribua a raiz privada aos clientes pelo gerenciamento de configuração, em vez de enviá-la. Incluir a raiz desperdiça bytes e nunca torna confiável uma raiz não confiável.

    bash
    # Serve leaf + intermediates only; clients must already have the root
    cat api.example.com.crt intermediate.crt > fullchain.pem
  5. 5.

    Diagnóstico temporário: ver o emissor com a verificação desligada

    A opção -k desativa as verificações de certificado para que a saída detalhada mostre o titular e o emissor que o servidor apresenta. Use-a só para identificar a raiz. Esquecida em scripts, a opção -k permite que qualquer um no caminho leia e altere o tráfego, inclusive tokens e instaladores baixados.

    bash
    # Diagnostic only: shows who issued the certificate even though curl distrusts it
    curl -sv -k -o /dev/null https://api.example.com/ 2>&1 | grep -E 'subject:|issuer:'

Erros relacionados

Ver toda a base de erros de assinatura