Skip to content

Commit

Permalink
✨ only use wildcard Certificates, as that will reduce the certificate…
Browse files Browse the repository at this point in the history
… requests by 5 times
  • Loading branch information
Kanti committed Apr 24, 2023
1 parent 3f2ce67 commit 42dd24e
Show file tree
Hide file tree
Showing 25 changed files with 426 additions and 456 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
${{ runner.os }}-composer-
- run: composer install --no-interaction --no-progress
- run: ./vendor/bin/grumphp run --ansi
- run: composer test
docker:
runs-on: ubuntu-latest
needs: [ grumphp ]
Expand Down Expand Up @@ -56,8 +57,8 @@ jobs:
id: docker_build
uses: docker/build-push-action@v4
with:
push: ${{ github.ref == 'refs/heads/update-and-auto-build' }}
tags: ghcr.io/kanti/local-https:alpha,kanti/local-https:alpha
push: ${{ github.ref == 'refs/heads/wildcard-certificates' }}
tags: ghcr.io/kanti/local-https:wild,kanti/local-https:wild
build-args: |
STAGE=prod
RELEASE_TAG=${{ github.sha }}
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,17 @@ FROM ${BUILD_STAGE} as finish
COPY src /app/src
COPY templates /app/templates
COPY config /app/config
COPY script.php composer.json composer.lock /app/
COPY s composer.json composer.lock /app/

WORKDIR /app

RUN composer install --no-dev -n
RUN composer install --no-dev -n && chmod +x /app/s

ARG RELEASE_TAG
ENV RELEASE_TAG=${RELEASE_TAG}

ENTRYPOINT ["php", "/app/script.php", "entrypoint" , "--ansi"]
ENTRYPOINT ["/app/s", "entrypoint"]

CMD ["docker-gen --watch --interval 60 --wait 2s --notify-output --notify 'php /app/script.php notify --ansi' templates/data.tmpl var/data.json"]
CMD ["docker-gen --watch --interval 60 --notify-output --notify './s' templates/data.tmpl var/data.json"]


File renamed without changes.
4 changes: 3 additions & 1 deletion src/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Kanti\LetsencryptClient;

use Throwable;
use Kanti\LetsencryptClient\Certificate\LetsEncryptCertificateFactory;
use Kanti\LetsencryptClient\Certificate\CertbotHelper;
use Kanti\LetsencryptClient\Command\EntrypointCommand;
use Kanti\LetsencryptClient\Command\ListDomainsCommand;
use Kanti\LetsencryptClient\Command\NotifyCommand;
Expand Down Expand Up @@ -49,6 +49,7 @@ public function __construct()

$input = new ArgvInput();
$output = new ConsoleOutput();
$output->setDecorated(true);

$container->register(InputInterface::class, ArgvInput::class)->setSynthetic(true);
$container->register(OutputInterface::class, ConsoleOutput::class)->setSynthetic(true);
Expand All @@ -62,6 +63,7 @@ public function __construct()

$this->add($container->get(EntrypointCommand::class));
$this->add($container->get(NotifyCommand::class));
$this->setDefaultCommand('notify');

$this->setCatchExceptions(false);
try {
Expand Down
58 changes: 0 additions & 58 deletions src/CertChecker.php

This file was deleted.

99 changes: 99 additions & 0 deletions src/Certificate/CertbotHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

declare(strict_types=1);

namespace Kanti\LetsencryptClient\Certificate;

use InvalidArgumentException;
use Kanti\LetsencryptClient\Dto\Domain;
use Kanti\LetsencryptClient\Dto\WildCardCert;
use Kanti\LetsencryptClient\Exception\CertbotException;
use Kanti\LetsencryptClient\Helper\SlackNotification;
use Kanti\LetsencryptClient\Utility\ProcessUtility;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;

use function Sentry\captureMessage;
use function sprintf;

final class CertbotHelper
{
public function __construct(private OutputInterface $output, private SlackNotification $slackNotification)
{
}

public function create(Domain $domain): WildCardCert
{
$cmd = sprintf(
'certbot certonly -n --dns-cloudflare --dns-cloudflare-credentials var/cloudflare.ini -d %s --register-unsafely-without-email --agree-tos --dns-cloudflare-propagation-seconds=20',
$domain . ',*.' . $domain
);
try {
$result = ProcessUtility::runProcess($cmd)->getOutput();
} catch (ProcessFailedException $processFailedException) {
throw new CertbotException(
sprintf('This should never happen, cert got not created? (process error) domains: %s', $domain),
0,
$processFailedException
);
}

$this->notifyUser($result, $domain);

return new WildCardCert(
'/etc/letsencrypt/live/' . $domain . '/fullchain.pem',
'/etc/letsencrypt/live/' . $domain . '/privkey.pem',
$domain
);
}

public function getCurrentCertificateInformation(): mixed
{
try {
$result = ProcessUtility::runProcess('certbot certificates')->getOutput();
} catch (ProcessFailedException $processFailedException) {
throw new CertbotException('cloud not list all certificates', 0, $processFailedException);
}

$resultList = explode('- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -', $result);
$yaml = trim($resultList[1]);
$yaml = preg_replace('#Certificate Name: ([\S.]+)#', '$1:', $yaml);
assert(is_string($yaml));
$yaml = preg_replace('#\(VALID(:)#', '(VALID', $yaml);
assert(is_string($yaml));
$yaml = preg_replace('#\(INVALID(:)#', '(INVALID', $yaml);
assert(is_string($yaml));
return yaml_parse($yaml);
}

public function removeCertificate(string $certificateName): void
{
if (!$certificateName) {
throw new InvalidArgumentException('$certificateName must not be empty');
}

ProcessUtility::runProcess(sprintf('certbot delete -n --cert-name %s', $certificateName));
}

private function notifyUser(string $result, Domain $domain): void
{
if (str_contains($result, 'Certificate not yet due for renewal')) {
$this->output->writeln(sprintf('Certificate for *.%s is still valid.', $domain));
$this->slackNotification->sendNotification(sprintf(':white_circle:CERTIFICATE is still valid for <https://%s|*.%s>', $domain, $domain));
return;
}

if (str_contains($result, 'Requesting a certificate for')) {
$this->output->writeln(sprintf('Certificate for *.%s created.', $domain));
$this->slackNotification->sendNotification(sprintf(':new:CERTIFICATE created for <https://%s|*.%s>', $domain, $domain));
return;
}

if (str_contains($result, 'Renewing an existing certificate for')) {
$this->output->writeln(sprintf('Certificate for *.%s renewed.', $domain));
$this->slackNotification->sendNotification(sprintf(':recycle:CERTIFICATE renewed for <https://%s|*.%s>', $domain, $domain));
}

captureMessage('notifyUser without specific message' . PHP_EOL . $result);
}
}
6 changes: 6 additions & 0 deletions src/Certificate/CertificateWithDomainCheck.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function __construct(private OutputInterface $output)

public function checkCertificate(string $certPathAndName, Domain $domain): bool
{
$certPathAndName = rtrim($certPathAndName, '.crt');
$this->output->writeln(sprintf('Testing Certificate for <fg=magenta>%s</>', $domain));

if (!file_exists($certPathAndName . '.crt')) {
Expand Down Expand Up @@ -61,6 +62,11 @@ private function isValidForAllDomains(string $certPathAndName, Domain $domain):
return false;
}

if (!in_array('*.' . $domain, $acceptedDomains, true)) {
$this->output->writeln(sprintf('Domain *.%s is not in the list of Acceptable Domains: %s', (string)$domain, implode(',', $acceptedDomains)));
return false;
}

$result = ProcessUtility::runProcess(sprintf('openssl x509 -noout -enddate -in "%s" | cut -d "=" -f 2', $certPathAndName . '.crt'))->getOutput();
assert(is_string($result));
$certDate = new DateTimeImmutable($result);
Expand Down
151 changes: 0 additions & 151 deletions src/Certificate/LetsEncryptCertificateFactory.php

This file was deleted.

Loading

0 comments on commit 42dd24e

Please sign in to comment.