Estamos escrevendo um plug-in para receber certificados curinga Let's Encrypt

Olá novamente, querido leitor. O segundo capítulo sobre aventuras Let's Encrypt no painel ISPmanager é declarado aberto. Em um artigo anterior, discutimos o plug-in do ACME v01. Nele, falaremos sobre sua evolução do ponto de vista da lógica de trabalhar com o usuário e, é claro, sobre o protocolo ACME v02 com suporte para certificados curinga.



Cuidado excessivo


Tentando cercar o usuário com cuidado, você pode ir longe. Até o momento, ele não poderá trabalhar com a funcionalidade. E a primeira parte da nossa história é exatamente sobre isso.

Ao desenvolver o módulo, queríamos salvar o cliente de uma longa preparação para a emissão do certificado. Duas restrições foram introduzidas para isso: elas permitiram solicitar SSL apenas para domínios da web registrados no painel e apenas para os aliases de domínio que o painel conhece.

Ambas as restrições pareciam lógicas. O primeiro não permitia que você solicitasse certificados para domínios inexistentes e produzisse entidades "inativas" - certificados que não seriam emitidos, porque não há onde colocar os tokens de verificação. O segundo também eliminou entidades desnecessárias e ainda não permitia encomendar certificados para "*." - aliases - enquanto a LE simplesmente não suportava esses certificados.

Tudo estava bem até que um dia um recurso para verificar um domínio por meio de registros DNS e a capacidade de solicitar um certificado para um domínio de email apareceram no LE . Em seguida, ao solicitar um domínio de email, decidimos adicionar o seguinte aos aliases: "mail", "pop", "smtp" - afinal, na maioria das vezes os certificados são conectados a eles. No final, tudo acabou mal: havia usuários que inicialmente configuraram seus servidores de correio para aliases completamente diferentes. Devido às nossas limitações no pedido, eles não puderam adicionar os nomes necessários.

Felizmente, rapidamente percebemos e corrigimos o erro, permitindo que os usuários especifiquem os dados necessários ao solicitar um certificado. Ainda assim, às vezes há muitas preocupações :).

Curinga


Agora vamos falar sobre a mudança para o ACME v02 , porque somente nesta versão do protocolo LE o suporte para certificados curinga apareceu. Vamos começar com um novo diretório, ou melhor, um diretório modificado:

curl -o- 'https://acme-v02.api.letsencrypt.org/directory' { "keyChange": "https://acme-v02.api.letsencrypt.org/acme/key-change", "mIU2Y2m2FsA": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417", "meta": { "caaIdentities": [ "letsencrypt.org" ], "termsOfService": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf", "website": "https://letsencrypt.org" }, "newAccount": "https://acme-v02.api.letsencrypt.org/acme/new-acct", "newNonce": "https://acme-v02.api.letsencrypt.org/acme/new-nonce", "newOrder": "https://acme-v02.api.letsencrypt.org/acme/new-order", "revokeCert": "https://acme-v02.api.letsencrypt.org/acme/revoke-cert" } 

A primeira e mais óbvia diferença é que as teclas são diferentes :). Na minha opinião, eles se tornaram muito mais intuitivos. A segunda diferença é um URL separado para obter o Replay-Nonce. Agora isso é feito assim:

 curl -LD - 'https://acme-v02.api.letsencrypt.org/acme/new-nonce' HTTP/1.1 204 No Content Server: nginx Replay-Nonce: QQgdAERh1MLQ6LHC0SVmB9OJXBcEWnwGB53CP0V4JlQ X-Frame-Options: DENY Strict-Transport-Security: max-age=604800 Expires: Sat, 02 Jun 2018 09:49:47 GMT Cache-Control: max-age=0, no-cache, no-store Pragma: no-cache Date: Sat, 02 Jun 2018 09:49:47 GMT Connection: keep-alive 

Nonce, é claro, será útil para nós mais de uma vez.

Agora, vamos falar sobre as mudanças não óbvias que a transição para o ACME v02 acarreta.

Apenas para o caso, deixe-me lembrá-lo de como nossa solicitação antiga do POST procurou se comunicar com a primeira versão do ACME :

 { "header": jws, // JSON Web Signature "protected": Base64Url(jws + Replay-Nonce), // Nonce —    "payload": Base64Url(payload), //  "signature": Base64Url(sign(protected.payload, private.pem)) //  } 

Agora a estrutura geral dos dados será diferente:

 { "protected": Base64Url(protected), "payload": Base64Url(payload), // "signature": Base64Url(sign(protected.payload, private.pem)) } 

Como você pode ver, o campo do cabeçalho foi cancelado. O estágio preparatório, para a grande alegria de "amantes de criptografia" como eu, não mudou nada: precisaremos das mesmas chaves rsa, JWK e JWS (mais sobre isso na primeira parte ).

Registo


Para registrar um usuário, você só precisa aceitar o contrato do usuário e enviar uma solicitação para "newAccount" no diretório

 payload = {"termsOfServiceAgreed": true} 

E compile o correto protegido:

 { "alg" : "RS256", "jwk" : jwk, \\ JSON Web Key “url” : url, \\       “nonce” : Replay-Nonce \\    } 

Formamos o corpo do pedido, enviamos e ... não se apresse! Processe com cuidado e cuidado os cabeçalhos de resposta da ACME . Encontramos um cabeçalho chamado Localização e salvamos seu conteúdo. Este é o chamado KID - a chave de identificação do usuário recém-registrado. Todas as solicitações subsequentes precisarão conter esse valor em protected em vez de JWK . Cuidado : se você continuar enviando solicitações de acordo com o esquema antigo, apenas as mensagens de erro serão a resposta.

Aqui está nosso subseqüente protegido:

 { "alg" : "RS256", "kid" : kid, \\    “url” : url, \\        “nonce” : Replay-Nonce \\    } 

Pedido de certificado


Estamos nos preparando para enviar uma solicitação ao diretório ["newOrder"]. Adicionamos à carga todos os aliases de nosso domínio da web para os quais vamos emitir um certificado:

 payload ={ "identifiers":[ { "type":"dns", "value":"name1" }, ... { "type":"dns", "value":"nameN" } ] } 

Lembre-se de que, se você deseja emitir um certificado curinga, os nomes devem conter apenas o nome principal e “*.” - um alias. A presença de outros nomes resultará em um erro de liberação.

Na resposta, obtemos um JSON contendo métodos para confirmar a propriedade do domínio e a URL que será usada para concluir a emissão do certificado.

 { "status":"pending", "expires":"2018-06-08T08:05:49.437251947Z", "identifiers":[ { "type":"dns", "value":"name1" }, { "type":"dns", "value":"www.name1" } ], "authorizations":[ //    "https://acme-v02.api.letsencrypt.org/acme/authz/Xp0a_...", "https://acme-v02.api.letsencrypt.org/acme/authz/o3Bvy..." ], "finalize":"https://acme-v02.api.letsencrypt.org/acme/finalize/..." //   } 

Além disso, recebemos instruções detalhadas sobre cheques:

 curl -o- 'https://acme-v02.api.letsencrypt.org/acme/authz/Xp0a_...' { "identifier":{ "type":"dns", "value":"name1" }, "status":"pending", "expires":"2018-06-08T08:05:49Z", "challenges":[ { "type":"http-01", "status":"pending", "url":"https://acme-v02.api.letsencrypt.org/acme/challenge/Xp0a_.../4906756205", "token":"Me_cKM2Stu3iyCJQWEssho8Kj2nvRKuSJvIPF5tRyko" }, { "type":"dns-01", "status":"pending", "url":"https://acme-v02.api.letsencrypt.org/acme/challenge/Xp0a_.../4906756206", "token":"p-0xyySPQClTXVlgTxwJUvVOQtdHmNPpFht95bWrq8s" } ] } 

O processo de confirmação não é diferente do que foi implementado para o ACME v01 . Observe: para um certificado curinga, você deve selecionar a confirmação "dns-01".

Obtendo um certificado


Após o procedimento de confirmação, resta chamar o URL Finalize . São possíveis pequenos atrasos; portanto, uma solicitação GET para esse endereço deve ser feita até obtermos o seguinte na resposta:

 { "status": "valid", ///<    "expires": "2018-06-11T10:39:24Z", "identifiers": [ { "type": "dns", "value": "name1" }, { "type": "dns", "value": "name2" } ], "authorizations": [ "https://acme-v02.api.letsencrypt.org/acme/authz/Xp0a_...", "https://acme-v02.api.letsencrypt.org/acme/authz/o3Bvy..." ], "finalize": "https://acme-v02.api.letsencrypt.org/acme/finalize/...", "certificate": "https://acme-v02.api.letsencrypt.org/acme/cert/..." ///<  } 

O certificado já conterá uma cadeia, por isso está completamente pronto para o trabalho.

Comparada à primeira, a segunda versão do ACME se tornou muito mais conveniente e compreensível. A integração de escrita tornou-se ainda mais fácil, dado que a "criptografia" em si não mudou. Estarei assistindo com interesse o desenvolvimento desta ferramenta incrível e certamente voltarei aqui com novas informações, caso ocorram mudanças importantes e úteis.

Source: https://habr.com/ru/post/pt413429/


All Articles