Created
February 28, 2020 15:45
-
-
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..
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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