SSD Nodes Learn 🎉 VPS desde $5.50/mês
Guias Matt ConnorPor Matt Connor

Como adicionar uma CA própria ao Ubuntu

Crie uma CA privada com openssl, assine um certificado leaf e instale a raiz em /usr/local/share/ca-certificates para validar seu HTTPS interno.

Adicione a sua própria CA ao repositório de confiança do Ubuntu

Para adicionar a sua própria CA ao repositório de confiança do Ubuntu, copie o certificado raiz para /usr/local/share/ca-certificates/ com um nome que termine em .crt e execute sudo update-ca-certificates. Uma CA (autoridade de certificação) é um par de chaves cujo certificado está autorizado a assinar outros certificados. Depois de a máquina confiar na sua raiz, todos os certificados assinados por essa raiz são aceites. Assim, o HTTPS entre os seus próprios serviços deixa de falhar na verificação.

Este guia cria toda a cadeia offline com openssl. Cria uma chave raiz e um certificado raiz, emite um certificado leaf para um servidor e instala a raiz. Em seguida, acompanha a alteração da resposta do mesmo comando de verificação. Essa ordem é importante: verificar antes e depois da instalação mostra que a instalação alterou o resultado.

O Ubuntu 24.04 inclui o OpenSSL 3 e o pacote ca-certificates numa imagem padrão. Portanto, não é necessário instalar nada primeiro (verificado em agosto de 2026).

Quando deve executar uma CA própria?

Uma CA pública, como a Let's Encrypt, precisa de um nome no DNS público e de um servidor que consiga alcançar. Nomes internos não são válidos. Uma base de dados numa rede privada ou um painel de administração ligado a um túnel não pode obter um certificado público. Também não deve ser exposto à Internet apenas para obter um certificado.

Um certificado autoassinado no Ubuntu resolve o problema de um único host. Todos os clientes têm de confiar nesse certificado, e cada novo host repete o mesmo trabalho. Uma CA privada transfere a decisão para um nível superior. Os clientes confiam na raiz uma vez, e passam a confiar em todos os certificados que a raiz assinar, incluindo certificados para hosts que ainda não existem.

O custo é real. A chave raiz pode assinar tudo o que as restrições permitirem. Por isso, qualquer pessoa que leia ca.key pode emitir certificados que as suas máquinas aceitarão. Proteja-a da mesma forma que protege uma chave privada na gestão de chaves SSH. Se um serviço tiver um nome DNS público, ignore tudo isto e use uma CA pública: Certbot com nginx e Let's Encrypt dá menos trabalho e não requer a instalação de nada no lado do cliente.

Crie a chave da CA e o certificado raiz

Trabalhe num diretório que apenas o seu utilizador consiga abrir. A chave raiz nunca deve sair desse diretório.

install -d -m 700 ~/ca
cd ~/ca
openssl genrsa -aes256 -out ca.key 4096
chmod 600 ca.key

-aes256 cifra a chave com uma frase-passe à sua escolha, e todos os comandos posteriores que assinem com esta chave irão pedi-la. Se deixar -aes256 de fora, a chave fica no disco sem proteção, pelo que uma cópia de segurança ou uma segunda conta de administrador basta para permitir a alguém emitir certificados em que as suas máquinas confiam.

Agora crie o certificado raiz, que a chave da CA assina para si própria.

openssl req -x509 -new -key ca.key -sha256 -days 3650 \
  -subj "/O=Example Internal/CN=Example Internal Root CA" \
  -addext "basicConstraints=critical,CA:TRUE,pathlen:0" \
  -addext "keyUsage=critical,keyCertSign,cRLSign" \
  -addext "subjectKeyIdentifier=hash" \
  -addext "nameConstraints=critical,permitted;DNS:internal.example" \
  -out ca.crt

Substitua internal.example pelo sufixo de nome que realmente utiliza e leia a secção seguinte antes de manter essa última extensão.

Cada extensão tem uma função.

  • basicConstraints com CA:TRUE é o que transforma este num certificado de CA. Sem isto, um cliente rejeita qualquer certificado assinado por esta chave, mesmo quando a assinatura está correta.
  • pathlen:0 indica que a CA pode assinar certificados finais, mas não outras CAs abaixo dela.
  • keyUsage restringe a chave à assinatura de certificados e listas de revogação, impedindo que a mesma chave seja usada por engano como chave de servidor TLS.
  • subjectKeyIdentifier atribui à raiz um identificador para o qual os certificados finais apontam. É assim que um cliente encontra o emissor correto numa store que contenha algumas centenas de certificados.
  • nameConstraints limita os nomes pelos quais esta CA está autorizada a responder.

Leia o que criou em vez de presumir que o comando fez o que pretendia.

openssl x509 -noout -subject -issuer -serial -dates -in ca.crt
openssl x509 -noout -text -in ca.crt

O subject e o issuer mostram a mesma string, porque um certificado raiz assina-se a si próprio. O serial e as duas datas vêm do ficheiro que acabou de criar. Por isso, use os valores dessa saída em vez dos valores de qualquer guia.

Limite o que a sua CA pode assinar

Uma root no repositório do sistema é considerada confiável para qualquer nome na Internet, a menos que indique o contrário. É uma autoridade muito ampla para manter num único ficheiro, num único servidor. nameConstraints reduz essa autoridade. Com permitted;DNS:internal.example na root, uma cadeia desta CA para um nome fora de internal.example é rejeitada, mesmo que a assinatura seja válida.

Teste isso em vez de confiar nela.

openssl req -new -newkey rsa:2048 -nodes -keyout /tmp/outside.key -out /tmp/outside.csr \
  -subj "/CN=www.example.com"
openssl x509 -req -in /tmp/outside.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 30 -sha256 \
  -extfile <(printf 'subjectAltName=DNS:www.example.com\n') -out /tmp/outside.crt
openssl verify -CAfile ca.crt /tmp/outside.crt
echo $?

O certificado é emitido, porque a sua CA assina tudo o que lhe pedir. A verificação é que falha: o estado de saída é diferente de zero e o OpenSSL identifica a restrição encontrada. Esse é o objetivo da extensão. Mesmo que a chave da CA seja roubada, ainda não será possível criar um certificado funcional para um nome fora da subárvore. Quando terminar, elimine os ficheiros restantes com rm /tmp/outside.*.

Há quatro aspetos que deve conhecer antes de aplicar uma restrição. A extensão é marcada como critical, por isso um cliente que não a compreenda tem de rejeitar a cadeia em vez de a ignorar. Esta é a opção mais segura, mas pode causar problemas com uma biblioteca TLS antiga. Uma subárvore permitida para nomes DNS não restringe SANs com endereços IP, porque um tipo de nome sem uma subárvore definida permanece sem restrições. Por isso, adicione permitted;IP:10.0.0.0/255.255.0.0 à mesma extensão se os seus certificados incluírem endereços IP. A subárvore tem de abranger todos os nomes que poderá emitir, incluindo nomes de host curtos. Assim, um certificado para o nome simples app falharia com o exemplo acima. A restrição fica incorporada na root. Se mudar de ideias, terá de criar um novo certificado root e instalá-lo novamente em todos os clientes.

Emitir um certificado leaf assinado pela sua CA

Um certificado leaf é o certificado que um servidor apresenta aos clientes. Comece pela respetiva chave e por uma CSR (certificate signing request), que contém a chave pública e o nome solicitado, assinado pela chave leaf para provar que o requerente possui a parte privada.

openssl req -new -newkey rsa:2048 -nodes \
  -keyout app.key -out app.csr \
  -subj "/CN=app.internal.example"
chmod 600 app.key

Os nomes relevantes devem estar num ficheiro de extensões, e não na CSR. Os clientes comparam o nome do host com subjectAltName (SAN) e ignoram completamente o common name. Por isso, um certificado com CN mas sem SAN falha a verificação do nome do host em todos os clientes atuais, independentemente do valor do CN.

basicConstraints = CA:FALSE
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = DNS:app.internal.example, DNS:api.internal.example
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always

Guarde isso como app.ext e assine a solicitação com a CA.

openssl x509 -req -in app.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
  -days 397 -sha256 -extfile app.ext -out app.crt

-CAcreateserial escreve ca.srl junto da CA. Esse ficheiro contém o próximo número de série, para que dois certificados emitidos por esta CA não partilhem o mesmo número. Mantenha o ficheiro no diretório da CA. -days 397 é uma escolha, não um limite da ferramenta. Períodos de validade curtos são mais importantes neste caso do que numa CA pública, porque uma CA privada não tem infraestrutura de revogação. Não existe uma CRL nem um respondedor OCSP, a menos que crie um. Por isso, uma chave leaf exposta continua utilizável até o certificado expirar.

Verifique o resultado antes de alterar o armazenamento de confiança.

openssl x509 -noout -subject -issuer -serial -dates -in app.crt
openssl x509 -noout -ext subjectAltName -in app.crt

A linha issuer identifica agora a CA, e não o próprio certificado leaf. A linha SAN lista os nomes para os quais este certificado é válido. O cliente compara o nome apenas com essa lista.

Verifique com um -CAfile explícito antes de instalar qualquer coisa

openssl verify -CAfile ca.crt app.crt
echo $?

Isto faz uma pergunta específica: app.crt forma uma cadeia até ao certificado em ca.crt? Não diz nada sobre aquilo em que esta máquina confia, porque o root foi fornecido ao OpenSSL na linha de comandos. Uma falha neste ponto indica um problema nos próprios certificados. Corrija-o antes de continuar.

Agora consulte a máquina.

openssl verify app.crt
echo $?

Sem -CAfile, o OpenSSL recorre ao diretório de certificados incorporado. openssl version -d mostra o diretório base usado pela sua compilação e, no Ubuntu, o diretório certs dentro dele resolve para /etc/ssl/certs. O seu root ainda não está lá, por isso a verificação falha: a cadeia chega a um emissor que o repositório não contém e não há outro local onde procurar. Registe o código de saída. É esse valor que muda daqui a dois passos.

Um cliente real é um teste melhor do que openssl verify, porque verifica o nome do host e a cadeia. Sirva o certificado e obtenha-o.

openssl s_server -accept 8443 -cert app.crt -key app.key -www &
curl --resolve app.internal.example:8443:127.0.0.1 https://app.internal.example:8443/

--resolve envia a ligação para 127.0.0.1, mas continua a solicitar app.internal.example. Assim, o SAN corresponde e a única questão em aberto é a confiança. curl falha e mostra o motivo pelo qual não conseguiu verificar a cadeia. Adicione -v para obter mais detalhes. Deixe o servidor de teste em execução.

Instale a raiz em /usr/local/share/ca-certificates

sudo cp ca.crt /usr/local/share/ca-certificates/example-internal-root.crt
sudo chmod 644 /usr/local/share/ca-certificates/example-internal-root.crt
sudo update-ca-certificates

Os detalhes que determinam se isto funciona:

  • O nome do ficheiro tem de terminar em .crt. A página de manual update-ca-certificates indica que os certificados com a extensão .crt encontrados abaixo de /usr/local/share/ca-certificates são incluídos e considerados implicitamente confiáveis. Um ficheiro chamado root.pem ou root.cer é ignorado sem qualquer aviso.
  • O conteúdo tem de estar em PEM, ou seja, o bloco base64 delimitado pelas linhas BEGIN CERTIFICATE e END CERTIFICATE. Um ficheiro DER renomeado para .crt continua a ser binário e não é lido. Converta-o com openssl x509 -inform DER -in ca.der -out ca.crt.
  • Apenas a raiz deve ficar aqui. A chave privada da CA e o certificado leaf não pertencem a um repositório de confiança.

update-ca-certificates mostra quantos certificados adicionou e removeu. Se não adicionou nenhum, a causa é a extensão ou o formato do ficheiro.

Confirme a alteração do lado do sistema, e não apenas com base nessa mensagem.

ls -l /etc/ssl/certs/$(openssl x509 -noout -subject_hash -in /usr/local/share/ca-certificates/example-internal-root.crt).0
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt

O primeiro comando cria um nome de ficheiro a partir do hash do subject do próprio certificado e apresenta-o. update-ca-certificates criou esse symlink, que aponta novamente para o ficheiro instalado. O segundo conta os certificados no bundle de um único ficheiro. Execute-o também antes da instalação para acompanhar o aumento de um certificado.

Ao copiar esta raiz para outras máquinas, confirme que a cópia chegou intacta antes de a instalar. Um certificado raiz é um dos ficheiros mais críticos do sistema para não corromper, por isso trate-o como trataria qualquer outro download que iria verificar com um checksum antes de utilizar.

Verifique novamente no armazenamento do sistema

openssl verify app.crt
echo $?
curl --resolve app.internal.example:8443:127.0.0.1 https://app.internal.example:8443/

Os mesmos comandos e os mesmos ficheiros de certificado produzem uma resposta diferente. Nada em app.crt mudou, e o servidor é o que iniciou anteriormente. A única diferença é que a raiz agora está no armazenamento que esses clientes consultam, portanto a cadeia é concluída. Este é o mecanismo que deve memorizar: a verificação procura um emissor em que o cliente já confia, e instalar uma CA é a forma de colocar o emissor no local que o cliente consulta.

Pare o servidor de teste com kill %1.

Por que /etc/ssl/certs não é o local onde deve colocar o seu ficheiro

/etc/ssl/certs é uma saída gerada. update-ca-certificates preenche-o com links simbólicos para os ficheiros de certificados reais e escreve o bundle concatenado /etc/ssl/certs/ca-certificates.crt no mesmo diretório.

Um certificado que copie manualmente para esse diretório não é encontrado por nenhum componente. A pesquisa de diretórios do OpenSSL só abre ficheiros com nomes baseados no hash do subject do certificado, portanto um ficheiro chamado myca.crt fica invisível para o OpenSSL. No Ubuntu, o curl lê o ficheiro bundle. Como o bundle é reconstruído a partir das fontes registadas, a sua cópia também não fica incluída nesse caminho. Execute update-ca-certificates --fresh para remover e reconstruir os links simbólicos do diretório. Qualquer link criado manualmente será removido nesse processo.

A outra parte da separação é /usr/share/ca-certificates, que pertence ao pacote ca-certificates e está listado em /etc/ca-certificates.conf. As atualizações do pacote reescrevem esse ficheiro. /usr/local/share/ca-certificates é o diretório reservado ao administrador local. Assim, a sua CA permanece após cada atualização do pacote que gere os restantes certificados.

Quais programas ignoram o armazenamento de confiança do sistema

A instalação do certificado raiz corrige todos os programas que consultam o OpenSSL ou leem /etc/ssl/certs. Isso inclui curl, wget, git, o módulo padrão ssl do Python e programas Go, que leem os ficheiros do sistema no Linux. Os runtimes que incluem a sua própria lista de certificados não são afetados. É daí que surge a maior parte da confusão depois de uma instalação bem-sucedida.

  • O Node.js usa uma lista compilada no programa. Aponte-o para o seu certificado raiz com NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/example-internal-root.crt, definido no ambiente antes de o processo arrancar, porque o Node lê a variável uma vez, no arranque. As versões atuais do Node também têm uma opção para ler o armazenamento do sistema; execute node --help | grep -i system-ca para verificar se a sua versão a possui.
  • A biblioteca requests do Python usa o bundle certifi. Defina REQUESTS_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt para esse processo ou passe verify="/etc/ssl/certs/ca-certificates.crt" à chamada. pip aceita --cert pelo mesmo motivo.
  • O Java lê um keystore. No Ubuntu, o pacote ca-certificates-java instala um hook em /etc/ca-certificates/update.d/. Por isso, update-ca-certificates também atualiza o keystore do Java quando esse pacote está presente. Sem esse pacote, importe o certificado raiz com keytool -importcert.
  • O Firefox mantém o seu próprio armazenamento e nunca consulta /etc/ssl/certs. Importe o certificado através das definições de certificados. No Linux, o Chromium lê uma base de dados NSS por utilizador, que pode editar com certutil do pacote libnss3-tools.
  • Os contentores têm o seu próprio sistema de ficheiros. Por isso, o armazenamento do host não tem efeito dentro deles. Copie o certificado raiz para a imagem e execute update-ca-certificates durante o build. Tenha isto em conta se os seus serviços forem executados com Docker Compose numa VPS.

Quando um programa continua a rejeitar o certificado depois de uma instalação limpa, descubra que ficheiros ele abre antes de alterar qualquer outra coisa. strace -f -e trace=openat <command> 2>&1 | grep -i cert é uma verificação pouco específica, mas responde à pergunta numa única execução.

Manter a CA utilizável ao longo do tempo

A reemissão de um certificado leaf consiste em repetir a etapa de CSR e a etapa de assinatura, usando o mesmo ficheiro app.ext. Os clientes não precisam de nenhuma ação, porque a root em que confiam não mudou. Mantenha ca.srl e todos os ficheiros .ext no diretório da CA para que a próxima emissão seja a repetição de um comando que funcionou, e não uma reconstrução baseada na memória.

Faça uma cópia de segurança de ca.key e ca.crt num local fora da máquina, mantendo a encriptação. Se perder a chave, não poderá emitir nada novo: terá de criar uma segunda CA e instalar a respetiva root em todos os locais onde a primeira foi instalada. Mantenha uma lista escrita de todas as máquinas e de todos os repositórios de aplicações que receberam a root, porque essa lista é indispensável para permitir a rotação e a remoção.

Quando a própria root se aproximar do fim da validade, gere a substituta antecipadamente e instale ambas as roots lado a lado. É aceitável ter duas roots no repositório, e um cliente aceita qualquer uma delas. Reemita os certificados leaf usando a nova root e remova a antiga quando já nada depender dela.

Remover uma CA do repositório de confiança

sudo rm /usr/local/share/ca-certificates/example-internal-root.crt
sudo update-ca-certificates --fresh

--fresh remove os links simbólicos em /etc/ssl/certs e recria-os a partir das fontes que ainda estão presentes. Assim, a raiz removida deixa de estar presente tanto no diretório como no bundle. Comprove a remoção da mesma forma que comprovou a instalação.

openssl verify app.crt
echo $?
grep -c 'BEGIN CERTIFICATE' /etc/ssl/certs/ca-certificates.crt
ls -l /etc/ssl/certs/$(openssl x509 -noout -subject_hash -in ~/ca/ca.crt).0

A verificação falha novamente, a contagem de certificados volta ao valor inicial e o link simbólico baseado no hash desaparece.

Esse comando altera apenas o repositório do sistema. Reverta manualmente a instalação em cada um dos outros locais: limpe NODE_EXTRA_CA_CERTS, elimine o alias de qualquer keystore Java, remova a raiz de cada perfil do navegador e recrie qualquer imagem de container que a tenha incorporado. Remover a raiz também não invalida os certificados que ela assinou. Eles continuam válidos em todas as máquinas que ainda confiam nela. Esse é o motivo prático para uma CA privada manter uma lista escrita dos locais onde a raiz foi instalada. Uma CA que não pode ser retirada completamente representa uma falha permanente. Por isso, teste a remoção numa máquina no dia em que configurar a CA, enquanto a lista ainda for curta.

FAQ

Onde coloco um certificado de CA no Ubuntu?

Em /usr/local/share/ca-certificates/, com um nome de ficheiro terminado em .crt e conteúdo PEM; depois execute sudo update-ca-certificates. Esse diretório é reservado ao administrador local, por isso as atualizações de pacotes não o alteram. /usr/share/ca-certificates pertence ao pacote ca-certificates, e /etc/ssl/certs é gerado a partir de ambos. Por isso, um ficheiro colocado num desses diretórios é substituído ou ignorado.

Por que motivo o curl continua a rejeitar o certificado depois de executar update-ca-certificates?

Analise as causas pela ordem. O ficheiro pode não terminar em .crt ou pode estar em formato DER em vez de PEM. Nesse caso, update-ca-certificates ignorou-o e não adicionou nada. O certificado pode não ter nenhum subjectAltName correspondente ao nome do host. Isso é uma falha de validação do nome do host, não uma falha de confiança; verifique com openssl x509 -noout -ext subjectAltName -in app.crt. O servidor pode estar a enviar apenas o certificado final, quando também é necessário um certificado intermédio. O curl pode estar a usar um bundle diferente, indicado por CURL_CA_BUNDLE ou --cacert. Um serviço de longa duração também precisa de ser reiniciado, porque a maioria dos programas lê o trust store apenas quando arranca.

O trust store do sistema é usado pelo Firefox, Chrome, Node e Java?

Não. O curl, o wget, o git, o módulo ssl padrão do Python e os programas Go leem os ficheiros do sistema. Por isso, funcionam assim que update-ca-certificates é executado. O Firefox mantém o seu próprio trust store. O Chromium no Linux usa uma base de dados NSS por utilizador, editada com certutil do pacote libnss3-tools. O Node.js precisa de NODE_EXTRA_CA_CERTS apontar para o seu ficheiro raiz. O Java lê um keystore, que update-ca-certificates só atualiza quando o pacote ca-certificates-java está instalado. O requests do Python usa certifi e precisa de REQUESTS_CA_BUNDLE.

Como removo uma CA do trust store do Ubuntu?

Elimine o ficheiro de /usr/local/share/ca-certificates/ e execute sudo update-ca-certificates --fresh. A opção --fresh limpa os links simbólicos em /etc/ssl/certs e volta a criá-los. Assim, o certificado é removido dos links simbólicos baseados em hash e do bundle ca-certificates.crt ao mesmo tempo. Confirme executando openssl verify contra um certificado assinado por essa CA e verificando o código de saída. Depois, repita a remoção em todos os outros stores onde o adicionou, porque esse comando não altera nenhum deles.

Posso usar uma CA privada em vez da Let's Encrypt num site público?

Não. O browser do visitante nunca viu a sua raiz, por isso apresenta um aviso em página inteira. Além disso, não pode instalar a sua raiz em máquinas que não controla. Uma CA privada destina-se a nomes que apenas as suas próprias máquinas resolvem e a clientes que administra. Para qualquer site visitado por desconhecidos, obtenha o certificado de uma CA pública.

#tls#certificates#openssl#ubuntu#security#pki