do-know / Crypt-LE

Crypt::LE - Let's Encrypt / Buypass / ZeroSSL and other ACME-servers client and library in Perl for obtaining free SSL certificates (inc. generating RSA/ECC keys and CSRs). HTTP/DNS verification is supported out of the box, EAB (External Account Binding) supported, easily extended with plugins, easily dockerized.
https://Do-Know.com
Artistic License 2.0
353 stars 60 forks source link

Custom directory handler support #52

Closed oregano87 closed 4 years ago

oregano87 commented 4 years ago

I am using a self-hosted ACME Server by Nexus and I am testing Crypt-LE (ZeroSSL Crypt::LE client v0.35) under windows server 2016 with strawberry perl (5.30.1.1).

I suspect that if the parameter "server" is given, the path of the URL is ignored. Unfortunately I can't create a positive case, because I can't change the path. My assumption is based on the fact that the same behaviour happens if I call the URL via curl incorrectly.

C:\tmp>le.bat --server "https://pgwy.company.test/acme/directory" --key account.key --csr domain.csr --csr-key domain.key --domains "win.company.test" --
log-config logfile.conf --crt domain.crt --generate-missing
2020/03/13 18:26:03 [INFO] [ ZeroSSL Crypt::LE client v0.35 started. ]
2020/03/13 18:26:03 [INFO] Custom server URL 'https://pgwy.company.test/acme/directory' is used.
2020/03/13 18:26:03 [INFO] Loading an account key from account.key
2020/03/13 18:26:03 [INFO] Loading a CSR from domain.csr
2020/03/13 18:26:03 [ERROR] Could not load the resource directory: <!doctype html><html lang="en"><head><title>HTTP Status 501 – Not Implemented</title><style type="text/css">H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}</style></head><body><h1>HTTP Status 501 – Not Implemented</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The server does not support the functionality required to fulfill the request.</p><hr class="line" /><h3>Apache Tomcat/7.0.96</h3></body></html>

Here an example in the same environment but called with curl.

-------------------------
positive case with curl
-------------------------
acme@wwwlinux:~$ curl https://pgwy.company.test/acme/directory
{"newNonce":"https://pgwy.company.test/acme/new-nonce","newAccount":"https://pgwy.company.test/acme/new-account","newOrder":"https://pgwy.company.test/acme/new-order","revokeCert":"https://pgwy.company.test/acme/revoke-cert","keyChange":"https://pgwy.company.test/acme/key-change","meta":{"externalAccountRequired":false}}

-------------------------
negative case with curl
-------------------------
acme@wwwlinux:~$ curl https://pgwy.company.test/directory
<!doctype html><html lang="en"><head><title>HTTP Status 501 – Not Implemented</title><style type="text/css">H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}A {color : black;}A.name {color : black;}HR {color : #525D76;}</style></head><body><h1>HTTP Status 501 – Not Implemented</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The server does not support the functionality required to fulfill the request.</p><hr class="line" /><h3>Apache Tomcat/7.0.96</h3></body></html>
do-know commented 4 years ago

The server parameter is supposed to be pointing to the "root" of the API rather than specific "method" (such as 'directory'). In your example you are setting it to

--server "https://pgwy.company.test/acme/directory"

This means that the actual call for a directory of resources would be sent to

https://pgwy.company.test/acme/directory/directory

I believe if you set the parameter as follows, it should work as expected:

--server "https://pgwy.company.test/acme"

oregano87 commented 4 years ago

It works, thx.

oregano87 commented 4 years ago

In the meantime, I've come to believe that this is a bug after all. Or better said, that the RFC might have been misinterpreted.

According to RFC it is not defined how the directory has to be called. In section 7.1.1 it is not defined that it has to be /directory.

There is no constraint on the URL of the directory except that it should be different from the other ACME server resources' URLs, and that it should not clash with other services.

In my case, the ACME server can map multiple CAs, so it uses this scheme: http://<acme-host>[:<port>]/acme/<handler> Where <handler> represents the directory of the respective CA.

Therefore, I cannot use this client if I configure multiple handlers in my ACME environment.

do-know commented 4 years ago

That makes sense. I believe I'll make some changes to support a custom-named directory handler (likely through a different option key, to ensure that it is not breaking for those already successfully using --server option).

do-know commented 4 years ago

Support for --directory parameter containing full path to the directory handler is added in beta: https://github.com/do-know/Crypt-LE/tree/v36_beta

Could you check if it works for you? You just need to put le.pl and LE.pm on top of your currently installed ones.

oregano87 commented 4 years ago

not working, sorry. The custom directory URL is written to be used but then acme-staging v1 is used in real.

2020/03/23 16:49:36 [INFO] [ ZeroSSL Crypt::LE client v0.36 started. ]
2020/03/23 16:49:36 [INFO] Custom directory URL 'https://pgwy.company.test/acme/directory' is used.
2020/03/23 16:49:36 [INFO] Loading an account key from C:\tmp\account.key
2020/03/23 16:49:36 [DEBUG] Account key loaded.
2020/03/23 16:49:36 [INFO] Loading a CSR from C:\tmp\domain.csr
2020/03/23 16:49:36 [DEBUG] Loaded domain names from CSR: crypt-le-acme.company.test
2020/03/23 16:49:36 [DEBUG] CSR loaded.
2020/03/23 16:49:36 [DEBUG] CSR key loaded
2020/03/23 16:49:36 [DEBUG] Account email has been set to 'cryptle@company.test'
2020/03/23 16:49:37 [DEBUG] $VAR1 = {
          'url' => 'https://acme-staging.api.letsencrypt.org/directory',
          'headers' => {
                         'server' => 'nginx',
                         'cache-control' => 'public, max-age=0, no-cache',
                         'date' => 'Mon, 23 Mar 2020 15:49:37 GMT',
                         'strict-transport-security' => 'max-age=604800',
                         'content-length' => '704',
                         'x-frame-options' => 'DENY',
                         'replay-nonce' => '0002v9rugfLzuXT3IHJfSJ7395iE4Jq3rjL5uYKcDmJgrQk',
                         'connection' => 'keep-alive',
                         'content-type' => 'application/json'
                       },
          'content' => '{
  "JU3cRjHJ7MQ": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
  "key-change": "https://acme-staging.api.letsencrypt.org/acme/key-change",
  "meta": {
    "caaIdentities": [
      "letsencrypt.org"
    ],
    "terms-of-service": "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf",
    "website": "https://letsencrypt.org/docs/staging-environment/"
  },
  "new-authz": "https://acme-staging.api.letsencrypt.org/acme/new-authz",
  "new-cert": "https://acme-staging.api.letsencrypt.org/acme/new-cert",
  "new-reg": "https://acme-staging.api.letsencrypt.org/acme/new-reg",
  "revoke-cert": "https://acme-staging.api.letsencrypt.org/acme/revoke-cert"
}',
          'status' => '200',
          'reason' => 'OK',
          'success' => 1,
          'protocol' => 'HTTP/1.1'
        };
2020/03/23 16:49:37 [DEBUG] API version is set to 1.
2020/03/23 16:49:37 [DEBUG] Directory loaded successfully.
2020/03/23 16:49:37 [INFO] Registering the account key
2020/03/23 16:49:37 [DEBUG] $VAR1 = {
          'url' => 'https://acme-staging.api.letsencrypt.org/acme/new-reg',
          'reason' => 'Forbidden',
          'protocol' => 'HTTP/1.1',
          'success' => '',
          'headers' => {
                         'date' => 'Mon, 23 Mar 2020 15:49:37 GMT',
                         'server' => 'nginx',
                         'cache-control' => 'public, max-age=0, no-cache',
                         'replay-nonce' => '0001ctFeDNNVSkq9KjlYhsnNZ11XJ6jrqLbDzI5bLUyuhaM',
                         'connection' => 'keep-alive',
                         'content-type' => 'application/problem+json',
                         'content-length' => '280'
                       },
          'status' => '403',
          'content' => '{
  "type": "urn:acme:error:unauthorized",
  "detail": "Account creation on ACMEv1 is disabled. Please upgrade your ACME client to a version that supports ACMEv2 / RFC 8555. See https://community.letsencrypt.org/t/end-of-life-plan-for-acmev1/88430 for details.",
  "status": 403
}'
        };
2020/03/23 16:49:37 [DEBUG] $VAR1 = {
          'type' => 'urn:acme:error:unauthorized',
          'status' => 403,
          'detail' => 'Account creation on ACMEv1 is disabled. Please upgrade your ACME client to a version that supports ACMEv2 / RFC 8555. See https://community.letsencrypt.org/t/end-of-life-plan-for-acmev1/88430 for details.'
        };
2020/03/23 16:49:37 [ERROR] Account creation on ACMEv1 is disabled. Please upgrade your ACME client to a version that supports ACMEv2 / RFC 8555. See https://community.letsencrypt.org/t/end-of-life-plan-for-acmev1/88430 for details.
do-know commented 4 years ago

This is actually quite strange, since that worked for me and connected to the server specified with -directory and fetched the correct URL. However, such log could have been produced for example if le.pl from v36b was run with the 'stable' v0.35 LE.pm library. If both le.pl and LE.pm were taken from https://github.com/do-know/Crypt-LE/tree/v36_beta, thish should have worked as intended.

If it does not work for you, could you ensure that both le.pl and LE.pm are taken from v36b and specify the command line you used? Thanks.

do-know commented 4 years ago

Presumably this has been solved (and it works in my tests), so closing.