404s on LE SSL ACME Check

We are using this package GitHub - smart48/le-ssl-laravel-package: Let's Encrypt Laravel Package to get Let's Encrypt SSL certificates . Issues we are having is that for some reason we hit 404s on the ACME challenge most of the time.

Nginx Access Log

34.217.253.81 - - [13/Apr/2023:04:28:48 +0200] "HEAD /shared/storage/tls/challenges/app.com; HTTP/1.1" 301 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18"

or

49.12.186.96 - - [13/Apr/2023:03:48:57 +0200] "GET /.well-known/acme-challenge/ ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 404 146 "-" "stonemax-acme2/1.0.4"

Laravel logs

Error by package failing to get challenge done:

Illuminate\Queue\MaxAttemptsExceededException: Imagewize\SslManager\Jobs\UpdateCertificate
 has been attempted too many times or run too long. The job may have previously timed out.
 in /home/ploi/app.com/releases/5/vendor/laravel/framework/src/Illuminate/Queue/Worker.php:746

Command used

Currently we have the command

php artisan ssl-controller:update-certificate site.com
Certificate updating requested.

domain DNS will be set to point to our server in advance and propagation is waited for.

Main API Nginx

Main server where all requests wind up at is:

# Ploi Webserver Configuration, do not remove!
include /etc/nginx/ploi/app.com/before/*;

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    server_name .app.com;
    root /home/ploi/app.com/current/public;

    ssl_certificate /etc/nginx/ssl/certificates/app.com.crt;
    ssl_certificate_key /etc/nginx/ssl/certificates/app.com.key;

    client_max_body_size 1024M;

    # include /etc/nginx/ssl/app.com;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/nginx/dhparams.pem;

    index index.php index.html;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    add_header X-Content-Type-Options "application/json";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    charset utf-8;

    # Ploi Configuration, do not remove!
    include /etc/nginx/ploi/app.com/server/*;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    access_log off;
    error_log  /var/log/nginx/app.com-error.log error;

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        try_files $uri /index.php =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_buffers 16 16k;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        include fastcgi_params;
        fastcgi_read_timeout 300;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    #Bugsnag crossorigin
    location ~ \.js {
      add_header Access-Control-Allow-Origin "*";
    }

    #modulesettings remove cache
    location /modulesettings/ {
	expires 0;
    }
}

# Ploi Webserver Configuration, do not remove!
include /etc/nginx/ploi/app.com/after/*;

Custom directory for cons

and in /etc/nginx/nginx.conf we added

include /home/ploi/app.com/shared/storage/tls/sites.d/*.conf;

Example conf to load challenge

so new configs like the one coming up for a site gets loaded:

initially we get this configuration file generated:

server {
    listen 80;
    listen [::]:80;
    server_name newsite.com;

    location /.well-known/acme-challenge {
        default_type "text/plain";
        alias /home/ploi/app.com/shared/storage/tls/challenges/newsite.com;
    }

    # Reset connection
    location / {
        return 444;
    }
}

Conf Post Challenge

then it should load the acme challenge... update the nginx config to be like

server {
    listen 80;
    listen [::]:80;
    server_name newsite.com;

    location /.well-known/acme-challenge {
        default_type "text/plain";
        alias /home/ploi/app.com/shared/storage/tls/challenges/newsite.com;
    }

    # Redirect to HTTPS version
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name newsite2.com;
    root /home/ploi/app.com/current/public;

    ssl_certificate     /home/ploi/app.com/shared/storage/tls/le-storage/b7bd877f/rsa/certificate-fullchained.crt;
    ssl_certificate_key /home/ploi/app.com/shared/storage/tls/le-storage/b7bd877f/rsa/private.pem;

    # Improve HTTPS performance with session resumption
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 5m;

    # Enable server-side protection against BEAST attacks
    ssl_prefer_server_ciphers on;
    ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:EDH+aRSA:HIGH:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!SEED:!DSS:!CAMELLIA;

    # Disable SSLv3
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

    # Diffie-Hellman parameter for DHE ciphersuites
    # $ sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

    # Enable HSTS (https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security)
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    # Enable OCSP stapling (http://blog.mozilla.org/security/2013/07/29/ocsp-stapling-in-firefox)
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /home/ploi/app.com/shared/storage/tls/le-storage/b7bd877f/rsa/certificate-fullchained.crt;

    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    access_log off;
    error_log  /var/log/nginx/newsite.com-error.log error;

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
        include fastcgi_params;
    }
}

But more often than not if fails with a 404 or too many tries to do the challenge.

How to debug Let's Encrypt Challenge failures here?

Welcome back @jasperf

I don't know that package but I offer these ideas anyway

One, check the nginx error / access logs and ensure nothing odd is happening.

Two, after you make the new server block for port 80 and reload nginx how long do you wait before making the cert request?

I ask because as nginx configs get larger they take longer to reload. And, the reload is an async request so the new server block may not be effective yet for an inbound http request that happens right away.

We see similar behavior for larger nginx systems when using the Certbot client's --nginx plug-in, for example.

I don't know this is related but these are the first two things that came to mind. I'm signing off for the night so hopefully this helps or someone else can continue.

4 Likes

Good to be back and get some great help from guys like you @MikeMcQ :slight_smile: . Going to check logs some more. We do have a faster server now so perhaps that causes timeouts on faster checks.

Yesterday we did get

2023/04/12 19:35:52 [crit] 163169#163169: *19023 SSL_do_handshake() failed (SSL: error:0A00006C:SSL routines::bad key share) while SSL handshaking, client: 164.90.205.35, server: 0.0.0.0:443

but not today during the new testing for certificate generation .

main access.log lines are lines like these I think:

49.12.186.96 - - [13/Apr/2023:03:48:45 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 404 146 "-" "stonemax-acme2/1.0.4"
49.12.186.96 - - [13/Apr/2023:03:48:48 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 404 146 "-" "stonemax-acme2/1.0.4"
49.12.186.96 - - [13/Apr/2023:03:48:51 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 404 146 "-" "stonemax-acme2/1.0.4"
49.12.186.96 - - [13/Apr/2023:03:48:54 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 404 146 "-" "stonemax-acme2/1.0.4"
49.12.186.96 - - [13/Apr/2023:03:48:57 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 404 146 "-" "stonemax-acme2/1.0.4"
34.217.253.81 - - [13/Apr/2023:04:28:06 +0200] "HEAD /shared/storage/tls/challenges/imagewize.com; HTTP/1.1" 301 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18"
34.217.253.81 - - [13/Apr/2023:04:28:48 +0200] "HEAD /shared/storage/tls/challenges/imagewize.com; HTTP/1.1" 301 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18"
49.12.186.96 - - [13/Apr/2023:04:34:56 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 200 87 "-" "stonemax-acme2/1.0.4"
2600:1f16:269:da01:352d:2aa3:be3b:2e1d - - [13/Apr/2023:04:34:57 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
2600:3000:1511:200::31 - - [13/Apr/2023:04:34:57 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"
2600:1f14:804:fd02:1f7f:bd40:b8a4:ed4e - - [13/Apr/2023:04:34:57 +0200] "GET /.well-known/acme-challenge/ppnsxM20S4loT7LYIOea_o3dY_k2x61QAidBv3ymvEg HTTP/1.1" 200 87 "-" "Mozilla/5.0 (compatible; Let's Encrypt validation server; +https://www.letsencrypt.org)"

date:

date
Thu Apr 13 06:38:09 CEST 2023

and do see a lot of 404s for challenges and then one 200. Also see HEAD followed by a redirect, but I guess that would be correct as we have a custom location for challenges . Could be a timing thing for sure..

It looks like there are some test requests made resulting in a 404 from your own acme client - notice the user agent. A request every 3 secs. Then once it gets a 200 it sends the cert request to Let's Encrypt which looks to work fine getting the 200's from 3 diff IPv6 addresses.

Other than the 404 results is there an actual problem? It kind of looks OK apart from this initial test timing issue

oops I just realized the time stamps for the later requests were an hour later. but this does look kind of like what I was describing earlier. Those 404 coming from your own acme client and look like a prevalidation that is failing because the server block maybe isn't fully ready

4 Likes

Does seem

cat site.com.conf:

server {
    listen 80;
    listen [::]:80;
    server_name site.com;

    location /.well-known/acme-challenge {
        default_type "text/plain";
        alias /home/ploi/app.com/shared/storage/tls/challenges/site.com;
    }

    # Reset connection
    location / {
        return 444;
    }
}

gets generated right away for challenge to take place post command. But I do not see access logs for new updated SSL right way which is odd.

part of tail -n 100 /var/log/nginx/access.log

69.162.124.231 - - [13/Apr/2023:06:40:13 +0200] "GET / HTTP/1.1" 404 146 "https://imagewize.com" "Mozilla/5.0+(compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)"
114.119.158.97 - - [13/Apr/2023:06:45:52 +0200] "GET /robots.txt HTTP/1.1" 301 162 "-" "Mozilla/5.0 (compatible;PetalBot;+https://webmaster.petalsearch.com/site/petalbot)"
69.162.124.231 - - [13/Apr/2023:06:50:13 +0200] "GET / HTTP/1.1" 404 146 "https://imagewize.com" "Mozilla/5.0+(compatible; UptimeRobot/2.0; http://www.uptimerobot.com/)"
3.101.18.69 - - [13/Apr/2023:06:50:45 +0200] "GET /wp-content/themes/img/9916153089/1432583285194/ HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
3.101.18.69 - - [13/Apr/2023:06:50:45 +0200] "GET /wp-content/themes/img/9916153089/2412583825325/ HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
3.101.18.69 - - [13/Apr/2023:06:50:45 +0200] "GET /wp-content/themes/img/9916153089/1432583285194 HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
3.101.18.69 - - [13/Apr/2023:06:50:45 +0200] "GET /wp-content/themes/img/9916153089/3434192492140 HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
3.101.18.69 - - [13/Apr/2023:06:50:45 +0200] "GET /wp-content/themes/img/9916153089/2412583825325 HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
3.101.18.69 - - [13/Apr/2023:06:50:45 +0200] "GET /wp-content/themes/img/9916153089/3434192492140/ HTTP/1.1" 404 548 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36"
45.11.57.48 - - [13/Apr/2023:06:55:14 +0200] "GET / HTTP/1.1" 404 146 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.71.6212.24) Gecko/25.2.4212.671 Firefox/2.0"

Perhaps I should set up more attempts in the job

Imagewize\SslManager\Jobs\UpdateCertificate
ID
2f7dede5-cab6-4405-bb78-84ef924f452e
Queue
ssl-manager
Attempts
1
Retries
0
Tags
Pushed
2023-04-13 11:53:20
Failed
2023-04-13 11:58:21

and

artisan horizon:work redis --name=default --supervisor=smt-prod-w1-YivK:supervisor-1 
--backoff=0 --max-time=0 --max-jobs=0 --memory=128 --queue=ssl-manager --sleep=3 
--timeout=300 --tries=3 --rest=0

When I check LE Logs I see

cat letsencrypt/letsencrypt.log
2023-04-13 04:30:02,411:DEBUG:urllib3.connectionpool:http://localhost:None "GET /v2/connections?snap=certbot&interface=content HTTP/1.1" 200 97
2023-04-13 04:30:02,741:DEBUG:certbot._internal.main:certbot version: 2.5.0
2023-04-13 04:30:02,742:DEBUG:certbot._internal.main:Location of certbot entry point: /snap/certbot/2913/bin/certbot
2023-04-13 04:30:02,742:DEBUG:certbot._internal.main:Arguments: ['--preconfigured-renewal']
2023-04-13 04:30:02,742:DEBUG:certbot._internal.main:Discovered plugins: PluginsRegistry(PluginEntryPoint#apache,PluginEntryPoint#manual,PluginEntryPoint#nginx,PluginEntryPoint#null,PluginEntryPoint#standalone,PluginEntryPoint#webroot)
2023-04-13 04:30:02,756:DEBUG:certbot._internal.log:Root logging level set at 30
2023-04-13 04:30:02,758:DEBUG:certbot._internal.display.obj:Notifying user:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2023-04-13 04:30:02,758:DEBUG:certbot._internal.display.obj:Notifying user: No renewals were attempted.
2023-04-13 04:30:02,758:DEBUG:certbot._internal.display.obj:Notifying user: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2023-04-13 04:30:02,758:DEBUG:certbot._internal.renewal:no renewal failures

Like it did not do anything for 3 hrs while I did do requests. Wonder if this is related to issue I have. But initial challenge was added. Perhaps only a check is trying to be done and fails..

That's the log for Certbot, but your PHP code in that le-ssl-laravel-package is using a totally different ACME client called Stonemax ACME2, which would not log in the same place. These log entries don't (and wouldn't be expected to) have anything to do with whatever certificates you're attempting to get using the PHP stuff.

4 Likes

@schoen Yeah, thought I was pulling at straws here. Just trying to figure out why the certificates sometimes work and sometimes don't based on all intel I can gather.

Perhaps check is loaded before initial conf to load challenge is added causing a 404 because challenge location is not loaded. But then I would need to see how I can deal with that.
Other issue would be that the earlier pasted nginx configuration files are off somehow.

Still digging to see what change I missed here moving to the new server causing these failures.

Added note we do need passwordless sudo restart Nginx post set up of Nginx config file for custom domain.

sudo visudo to allow for restart of Nginx server without password entry using sudo

# LE SSL Restart Nginx
ploi ALL = NOPASSWD: /etc/init.d/nginx

but not quite there yet still.

php artisan ssl-controller:update-certificate site.com true
Certificate updating now.
+ Starting ...
+ Order expires 2023-04-14T02:28:41Z
+ Adding web server configuration for site.com
+ Starting challenges
+ Getting certificate info (this can take a while)

does request for new LE SSL, but it seems to hang and no access logs on this . Challenges are also not renewed . Which is odd.

Did get this error

2023/04/13 13:28:39 [crit] 390952#390952: *210 SSL_do_handshake() 
failed (SSL: error:0A00006C:SSL routines::bad key share) while SSL handshaking, 
client: 65.49.20.68, server: 0.0.0.0:443

so that is something at least. That was like 20-30 minutes ago

date
Thu Apr 13 13:55:41 CEST 2023

So that is something.

Funnily enough one of the other domains has gotten a decent LE SSL certificate now. An A+ one. See SSL Server Test: thaiconomics.com (Powered by Qualys SSL Labs)

How do you reload nginx after the "Adding web server configuration for site.com"?

Can you add a sleep after that reload before proceeding to "Starting challenges"?

4 Likes

We use 'http_config_reload' => 'sudo /etc/init.d/nginx restart', which is used in our Service Provider

Service providers are the central place of all Laravel application bootstrapping. Your own application, as well as all of Laravel's core services, are bootstrapped via service providers.

in the register() method:

/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(DnsService::class, function (Container $app) {
    return new DnsService(
        config("ssl-manager.target_aname")
    );
});

$this->app->singleton(HttpService::class, function (Container $app) {
    return new HttpService(
        config("ssl-manager.challenge_directory"),
        config("ssl-manager.sites_directory"),
        config("ssl-manager.http_config_reload"),
        $app->make(ViewFactory::class)
    );
});

This service provider is loaded with Imagewize\SslManager\SslManagerProvider::class, in Laravel's config/app.php

The blocks that loads the http_config_reload loads in the Service src/Core/HttpService.php which has a reload method

/**
* @return boolean
*/
    public function reloadConfiguration()
    {
    ob_start();
    system($this->httpReloadCommand, $exitCode);
    ob_end_clean();

    sleep(5);

    return ! $exitCode;
}

which is used in src/Core/SslService.php:

...
echo "+ Adding web server configuration for " . $domain . "\r\n";
$certificateInfo = null;
$this->httpServer->updateSite($domain, $certificateInfo);
$this->httpServer->reloadConfiguration();

echo "+ Starting challenges\r\n";
foreach ($pendingChallenges as $challenge) {
    $challengeType = $challenge->getType();
    $credential = $challenge->getCredential();
...
echo "+ Getting certificate info (this can take a while)\r\n";
$certificateInfo = $order->getCertificateFile();
echo "+ Writing certificate to nginx config\r\n";
$this->httpServer->updateSite($domain, $certificateInfo);
echo "+ Reloading web server configuration\r\n";
$this->httpServer->reloadConfiguration();
...

In post "Getting certificate info.." is where it hangs. At least for complete renewal. A not complete clean slate renewal without removing certificate just sometimes works.

May need to do some debugging in SslService:

<?php

namespace Imagewize\SslManager\Core;

use Exception;
use stonemax\acme2\Client;
use stonemax\acme2\constants\CommonConstant;

class SslService
{
    /**
     * @var string
     */
    private $accountEmail;

    /**
     * @var string
     */
    private $storagePath;

    /**
     * @var string
     */
    private $challengeDirectory;

    /**
     * @var HttpService
     */
    private $httpServer;

    public function __construct(
        $accountEmail,
        $storagePath,
        $challengeDirectory,
        HttpService $httpService
    ) {
        $this->accountEmail = $accountEmail;
        $this->storagePath = $storagePath;
        $this->challengeDirectory = $challengeDirectory;
        $this->httpServer = $httpService;
    }

    public function updateCertificate($domain, $renew = true)
    {

        echo "+ Starting ...\r\n";
        // staging letsencrypt service
        $staging = false;
        $client = new Client([$this->accountEmail], $this->storagePath, $staging);
        $renew = filter_var($renew, FILTER_VALIDATE_BOOLEAN);
        $order = $client->getOrder(
            [
                CommonConstant::CHALLENGE_TYPE_HTTP => [$domain],
            ],
            CommonConstant::KEY_PAIR_TYPE_RSA,
            $renew
        );

        echo "+ Order expires " . $order->expires . "\r\n";

        $pendingChallenges = $order->getPendingChallengeList();


        echo "+ Adding web server configuration for " . $domain . "\r\n";
        $certificateInfo = null;
        $this->httpServer->updateSite($domain, $certificateInfo);
        $this->httpServer->reloadConfiguration();

        echo "+ Starting challenges\r\n";
        foreach ($pendingChallenges as $challenge) {
            $challengeType = $challenge->getType();
            $credential = $challenge->getCredential();

            if ($challengeType == CommonConstant::CHALLENGE_TYPE_HTTP) {
                $domainChallengeDirectory = "{$this->challengeDirectory}/{$domain}";

                if (!file_exists($domainChallengeDirectory)) {
                    mkdir($domainChallengeDirectory, 0755, true);
                }

                echo "+ Saving challenge file for " . $domain . "\r\n";
                file_put_contents(
                    "{$domainChallengeDirectory}/{$credential['fileName']}",
                    $credential['fileContent']
                );
            }
            echo "+ Verifying challenge for " . $domain . "\r\n";
            $challenge->verify();
        }
        
        echo "+ Getting certificate info (this can take a while)\r\n";
        $certificateInfo = $order->getCertificateFile();
        echo "+ Writing certificate to nginx config\r\n";
        $this->httpServer->updateSite($domain, $certificateInfo);
        echo "+ Reloading web server configuration\r\n";
        $this->httpServer->reloadConfiguration();

        echo "Done!\r\n";
    }
}

Also general update might need some more output in command.

Looking into debugging and some pauzes post reloads based on last post. But also just tested with php artisan ssl-controller:update-certificate imagewize.com now and I had an immediate failure notice

[2023-04-14 05:22:02] production.ERROR: Get order info failed, the order url is: https://acme-v02.api.letsencrypt.org/acme/order/1048670227/174665053107, the code is: 404, the header is: HTTP/2 404
server: nginx
date: Fri, 14 Apr 2023 03:22:02 GMT
content-type: application/problem+json
content-length: 113
cache-control: public, max-age=0, no-cache
link: <https://acme-v02.api.letsencrypt.org/directory>;rel="index", the body is: Array
(
    [type] => urn:ietf:params:acme:error:malformed
    [detail] => No order for ID 174665053107
    [status] => 404
)
 {"exception":"[object] (stonemax\\acme2\\exceptions\\OrderException(code: 0): Get order info failed, the order url is: https://acme-v02.api.letsencrypt.org/acme/order/1048670227/174665053107, the code is: 404, the header is: HTTP/2 404

server: nginx

date: Fri, 14 Apr 2023 03:22:02 GMT

content-type: application/problem+json

content-length: 113

cache-control: public, max-age=0, no-cache

link: <https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\", the body is: Array
(
    [type] => urn:ietf:params:acme:error:malformed
    [detail] => No order for ID 174665053107
    [status] => 404

which shows [detail] => No order for ID 174665053107

Perhaps I could add an additional break at

echo "+ Adding web server configuration for " . $domain . "\r\n";
$certificateInfo = null;
$this->httpServer->updateSite($domain, $certificateInfo);
$this->httpServer->reloadConfiguration();
// break here
echo "+ Starting challenges\r\n";

with something like

echo "+ Adding web server configuration for " . $domain . "\r\n";
$certificateInfo = null;
$this->httpServer->updateSite($domain, $certificateInfo);
$this->httpServer->reloadConfiguration();
// sleep for 5 seconds
sleep(5);
echo "+ Starting challenges\r\n";
...

However, the reloadConfiguration already has

public function reloadConfiguration()
    {
        ob_start();
        system($this->httpReloadCommand, $exitCode);
        ob_end_clean();

        sleep(5);

        return ! $exitCode;
    }

On the order missing error I mentioned earlier and that I keep on having. I did some checking

cat ~/site.com/shared/storage/tls/le-storage/95865564/rsa/ORDER
{"orderUrl":"https:\/\/acme-v02.api.letsencrypt.org\/acme\/order\/1048670227\/174665053107","validFromTimestamp":1680836506,"validToTimestamp":1688612505,"validFromTime":"2023-04-07 05:01:46","validToTime":"2023-07-06 05:01:45"}

and

cat ~/site.com/shared/storage/tls/le-storage/b7bd877f/rsa/ORDER
{"orderUrl":"https:\/\/acme-v02.api.letsencrypt.org\/acme\/order\/1048670227\/175839801057","validFromTimestamp":1681349704,"validToTimestamp":1689125703,"validFromTime":"2023-04-13 03:35:04","validToTime":"2023-07-12 03:35:03"}

On updating certificate is says it cannot find order 174665053107 and truth be told I only see 1048670227\/175839801057" and 1048670227\/174665053107" where the latter is the order that was checked at Let's Encrypt.

And error says no order for the ID 174665053107:

...
 {"exception":"[object] (stonemax\\acme2\\exceptions\\OrderException(code: 0): Get order info failed, the order url is: https://acme-v02.api.letsencrypt.org/acme/order/1048670227/174665053107, the code is: 404, the header is: HTTP/2 404

server: nginx
date: Sat, 15 Apr 2023 05:57:12 GMT
....
link: <https://acme-v02.api.letsencrypt.org/directory>;rel=\"index\", the body is: Array
(
    [type] => urn:ietf:params:acme:error:malformed
    [detail] => No order for ID 174665053107
    [status] => 404
)
...

So I need to renew the order or somehow use an existing order of imagewize.com:

cat ~/site.com/shared/storage/tls/le-storage/95865564/DOMAIN
imagewize.com

So how can I renew or get a new order ID? Looking into this some more.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.