Skip to content

Instantly share code, notes, and snippets.

@Radiergummi
Created February 28, 2020 15:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Radiergummi/bd7a7298a6d07e76912daa06a72707c5 to your computer and use it in GitHub Desktop.
Save Radiergummi/bd7a7298a6d07e76912daa06a72707c5 to your computer and use it in GitHub Desktop.
Updates a custom certificate on Cloudflare from inside a Let's Encrypt renewal hook. Check the blog post at https://www.moritzfriedrich.com/posts/feature-branch-previews for more info..
#!/usr/bin/env php
<?php
// This should be the main domain your certificate is valid for. It's used for reverse
// matching the installed certificate and resolving the file path to your certificate
// files on the local file system.
$domain = 'your.domain.name.tld';
// This should be your Cloudflare zone ID. You can find it in the right sidebar on your
// Cloudflaare account dashboard.
$cloudflareZone = 'your_cloudflare_zone';
// This should be the same settings file used for the Let's Encrypt Cloudflare plugin.
// See https://certbot-dns-cloudflare.readthedocs.io/en/stable/
$settings = parse_ini_file('/etc/letsencrypt/cloudflare.ini');
out('Checking for existing certificates...');
// Create a request to fetch all installed certificates.
// See https://api.cloudflare.com/#custom-ssl-for-a-zone-list-ssl-configurations
$ch = curl_init("https://api.cloudflare.com/client/v4/zones/{$cloudflareZone}/custom_certificates");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// As soon as the Let's Encrypt Cloudflare DNS plugin supports the "new" tokens, you
// should use them instead of auth email and key.
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Auth-Email: ' . $settings['dns_cloudflare_email'],
'X-Auth-Key: ' . $settings['dns_cloudflare_api_key'],
'Content-Type: application/json',
]);
$response = json_decode(curl_exec($ch), true);
$certificates = $response['result'] ?? [];
out('Found %d certificates.', count($certificates));
$certificateId = null;
// Iterate the certificates, try to find ours
foreach ($certificates as $certificate) {
if (in_array($domain, $certificate['hosts'], true)) {
$certificateId = $certificate['id'];
out('Found existing certificate %s for %s', $certificateId, $domain);
break;
}
}
// If we have an existing certificate, we'll want to PATCH the certificate,
// otherwise we POST to create a new one
$requestMethod = $certificateId ? 'PATCH' : 'POST';
// Right-trim the URL to remove the last slash if we don't have an existing certificate ID
$url = rtrim("https://api.cloudflare.com/client/v4/zones/{$cloudflareZone}/custom_certificates/{$certificateId}", '/');
// Read certificate and private key from the default letsencrypt directories. You should obviously
// adjust these paths if you have non-standard locations.
$certificate = file_get_contents("/etc/letsencrypt/live/{$domain}/fullchain.pem");
$privateKey = file_get_contents("/etc/letsencrypt/live/{$domain}/privkey.pem");
// Review the documentation for these parameters here:
// https://api.cloudflare.com/#custom-ssl-for-a-zone-create-ssl-configuration
$payload = json_encode([
'certificate' => $certificate,
'private_key' => $privateKey,
'bundle_method' => 'optimal',
'type' => 'sni_custom'
]);
if ($certificateId) {
out('Updating existing certificate %s on Cloudflare', $certificateId);
} else {
out('Dispatching new certificate to Cloudflare');
}
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $requestMethod);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'X-Auth-Email: ' . $settings['dns_cloudflare_email'],
'X-Auth-Key: ' . $settings['dns_cloudflare_api_key'],
'Content-Type: application/json',
]);
$response = json_decode(curl_exec($ch), true);
if ($response['success'] ?? false) {
out('Successfully set certificate %s', $response['result']['id'] ?? '-');
exit(0);
}
error('Failed to update certificate: %s', json_encode($response, JSON_PRETTY_PRINT));
exit(1);
function out(string $line, ...$replacements): void
{
write(STDOUT, $line, $replacements);
}
function error(string $line, ...$replacements): void
{
write(STDERR, $line, $replacements);
}
function write($stream, string $line, $replacements = []): void
{
$microTime = explode(" ",microtime());
$timestamp = date("d.m.Y H:i:s", $microTime[1]) . substr((string) $microTime[0], 1, 4);
fwrite($stream, $timestamp . "\t " . vsprintf($line, $replacements) . PHP_EOL);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment