Though really, you shouldn't be using --renew-by-default. Once you've successfully obtained the cert, a simple daily certbot renew should be all you need.
I am using HAProxy, so I need the .pems to be moved, hence using this script:
class CertChecker
require "openssl"
def initialize(report_all=true)
# When 'report_all' above is set to false will
# only report when there are any failures.
@report = report_all
@msgs = []
@days_window = 30
@domains_to_check = %w[
domain.org
domain.com
etc.com
]
end
def do_checks
check_domains
prepare_results
# results
end
def check_domains
@total_count = 0
@updated_count = 0
@not_updated_count = 0
@fail_count = 0
@failed_domains = []
@domains_to_check.each do |domain|
@msgs << "Starting check for #{domain} \n"
www_domain = "www.#{domain}"
renew_command = "certbot certonly --agree-tos --standalone --text -vvvvvv --preferred-challenges http --http-01-port 54321 -d #{domain} -d #{www_domain}"
raw = File.read "/etc/letsencrypt/live/#{domain}/fullchain.pem"
cert = OpenSSL::X509::Certificate.new raw
expires = cert.not_after
days_remaining = (expires - Time.now).to_i / (60 * 60 * 24)
if days_remaining > @days_window
@msgs << "The certificate for #{domain} is up to date (#{days_remaining} days remaining). \n"
else
create_cert(domain, command=renew_command)
end
@msgs << "--------------------------------\n"
@total_count += 1
sleep 1
end
end
def create_cert(domain, command)
@msgs << "The certificate for #{domain} is about to expire, starting Let's Encrypt renewal script... \n"
if system(command)
@updated_count += 1
@msgs << "Certificate creation successful for #{domain}"
create_combined_file(domain)
reloading_haproxy(domain)
else
@fail_count += 1
@msgs << "Certificate creation failed for #{domain}"
@failed_domains << domain
end
end
def create_combined_file(domain)
@msgs << "Creating combined file at /etc/haproxy/certs/#{domain}.pem with latest certs... \n"
fullchain = File.read("/etc/letsencrypt/live/#{domain}/fullchain.pem")
privkey = File.read("/etc/letsencrypt/live/#{domain}/privkey.pem")
File.open("/etc/haproxy/certs/#{domain}.pem", "w") do |f|
f.write(fullchain)
f.write(privkey)
end
@msgs << "Finished creating combined_file... \n"
end
def reloading_haproxy(domain)
@msgs << "Reloading HAProxy \n"
system("/usr/sbin/service haproxy reload")
@msgs << "Renewal process finished for #{domain} \n"
end
# def get_ip(domain)
# IPSocket::getaddress(domain)
# rescue
# nil
# end
def prepare_results
if @fail_count > 0
@msgs << ""
@msgs << "WARNING #{@fail_count} FAILED DOMAINS!"
@msgs << ""
@msgs << @failed_domains unless @failed_domains.empty?
@msgs << ""
@msgs << "WARNING #{@fail_count} FAILED DOMAINS!"
@msgs << "Total checked: #{@total_count}"
elsif @report
@msgs << "Success! All domains checked and passed"
end
end
def results
if @fail_count > 0 || @report
@msgs
end
end
end
checker = CertChecker.new
checker.do_checks
results = checker.results
if results
results.each do |r|
puts r
end
end
Is there a better/simpler way to get the same thing done?
The reason we use post 54321 is because port 80 is used by HAProxy and so running certbot renew alone would report Attempting to renew cert (domain.com) from /etc/letsencrypt/renewal/domain.com.conf produced an unexpected error: Problem binding to port 80: Could not bind to IPv4 or IPv6.. Skipping.
Yes, very much so. Almost everything in that script is something certbot (and letsencrypt before it) already does. As to creating the combined file and putting it in the right place, check out the certbot docs on the --deploy-hook flag. It could be as simple as --deploy-hook "cat fullchain.pem privkey.pem > domain.pem && service haproxy reload", or you could write a small script to accomplish only those two things and use --deploy-hook /path/to/script. But whichever way you do it, you'd only need to enter that option the first time you obtain the cert. For that, you'd do (adding whatever other options are needed)
I think the script below should be ok now - posting here as it might help someone in future.
The only other thing I did was manually check each configuration file in /etc/letsencrypt/renewal/ (some of them needed the port updating).
class CertChecker
require "openssl"
def initialize(report_all=true)
# When 'report_all' above is set to false will
# only report when there are any failures.
@report = report_all
@msgs = []
@days_window = 30
@domains_to_check = %w[
domain.org
domain.com
]
@list_of_domains_requiring_renewal = []
@renew_command = "/usr/bin/certbot renew -q"
end
def do_checks
check_domains
if @list_of_domains_requiring_renewal.size > 0
create_certs
create_combined_files
reload_haproxy
end
prepare_results
# results
end
def check_domains
@total_count = 0
@domains_to_check.each do |domain|
@msgs << "Starting check for #{domain} \n"
raw = File.read "/etc/letsencrypt/live/#{domain}/fullchain.pem"
cert = OpenSSL::X509::Certificate.new raw
expires = cert.not_after
days_remaining = (expires - Time.now).to_i / (60 * 60 * 24)
if days_remaining > @days_window
@msgs << "The certificate for #{domain} is up to date (#{days_remaining} days remaining). \n"
else
@list_of_domains_requiring_renewal << domain
end
@msgs << "--------------------------------\n"
@total_count += 1
end
end
def create_certs
@msgs << "Starting Let's Encrypt renewal script... \n"
if system(@renew_command)
@msgs << "Certificate creation output..."
else
@msgs << "Certificate creation failed output..."
end
end
def create_combined_files
@msgs << "Creating combined files... \n"
@list_of_domains_requiring_renewal.each do |domain|
fullchain = File.read("/etc/letsencrypt/live/#{domain}/fullchain.pem")
privkey = File.read("/etc/letsencrypt/live/#{domain}/privkey.pem")
File.open("/etc/haproxy/certs/#{domain}.pem", "w") do |f|
f.write(fullchain)
f.write(privkey)
end
@msgs << "Finished creating combined_file for #{domain}... \n"
end
end
def reload_haproxy
@msgs << "Reloading HAProxy \n"
system("/bin/systemctl reload haproxy.service")
@msgs << "Renewal process finished. \n"
end
def prepare_results
if @list_of_domains_requiring_renewal.size > 0
@msgs << "Total number of domains: #{@domains_to_check.size}"
@msgs << ""
@msgs << "Total number of domains that required renewal: #{@list_of_domains_requiring_renewal.size}"
@msgs << ""
@msgs << "Domains that required renewal: #{@list_of_domains_requiring_renewal}"
else
@msgs << "Success! No domains required renewing"
end
end
def results
@msgs
end
end
checker = CertChecker.new
checker.do_checks
results = checker.results
if results
results.each do |r|
puts r
end
end
(If you or anyone else notices any mistakes please let me know)
Just a quick update, I'm getting emails like the below, though when I go to the domain and check the cert it is valid until January (Expires: Monday, 14 January 2019 at 23:44:18 Greenwich Mean Time) - any idea why this may be happening?
Hello,
Your certificate (or certificates) for the names listed below will expire in 19 days (on 13 Nov 18 21:40 +0000). Please make sure to renew your certificate before then, or visitors to your website will encounter errors.
We recommend renewing certificates automatically when they have a third of their
total lifetime left. For Let's Encrypt's current 90-day certificates, that means
renewing 30 days before expiration. See https://letsencrypt.org/docs/integration-guide/ for details.