Provide manageable Let's Encrypt Certificate for Rails.
- Rails 7.2+
- Ruby 3.2+
Puts this in your Gemfile:
gem 'rails-letsencrypt'
Run install migrations
rails generate lets_encrypt:install
rake db:migrate
Setup private key for Let's Encrypt API, and create an account at letsencrypt.org associated with that key
rails generate lets_encrypt:register
Add acme-challenge
mounts in config/routes.rb
mount LetsEncrypt::Engine => '/.well-known'
Add a file to config/initializers/letsencrypt.rb
and put below config you need.
LetsEncrypt.config do |config|
# Configure the ACME server
# Default is Let's Encrypt production server
# config.acme_server = 'https://acme-v02.api.letsencrypt.org/directory'
# Using Let's Encrypt staging server or not
# Default only `Rails.env.production? == true` will use Let's Encrypt production server.
# config.use_staging = true
# Set the private key path
# Default is locate at config/letsencrypt.key
config.private_key_path = Rails.root.join('config', 'letsencrypt.key')
# Use environment variable to set private key
# If enable, the API Client will use `LETSENCRYPT_PRIVATE_KEY` as private key
# Default is false
config.use_env_key = false
# Should sync certificate into redis
# When using ngx_mruby to dynamic load certificate, this will be helpful
# Default is false
config.save_to_redis = false
# The redis server url
# Default is nil
config.redis_url = 'redis://localhost:6379/1'
# Enable it if you want to customize the model
# Default is LetsEncrypt::Certificate
# config.certificate_model = 'MyCertificate'
# Configure the maximum attempts to re-check status when verifying or issuing
# config.max_attempts = 30
# Configure the interval between attempts
# config.retry_interval = 1
end
Warning
Depcrecation Notice
The use_staging
will be removed in the future, and the acme_server
will be used to determine the server.
The SSL certificate setup depends on the web server, this gem can work with ngx_mruby
or kong
.
certificate = LetsEncrypt::Certificate.find_by(domain: 'example.com')
service = LetsEncrypt::RenewService.new
service.execute(certificate)
certificate = LetsEncrypt::Certificate.find_by(domain: 'example.com')
order = LetsEncrypt.client.new_order(identifiers: [certificate.domain])
service = LetsEncrypt::VerifyService.new
service.execute(certificate, order)
certificate = LetsEncrypt::Certificate.find_by(domain: 'example.com')
order = LetsEncrypt.client.new_order(identifiers: [certificate.domain])
service = LetsEncrypt::IssueService.new
service.execute(certificate, order)
Add a new domain into the database.
cert = LetsEncrypt::Certificate.create(domain: 'example.com')
cert.get # alias `verify && issue`
Warning
Depcrecation Notice
The get
will be replaced by RenewService
in the future.
Makes a request to Let's Encrypt and verify domain
cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.verify
Warning
Depcrecation Notice
The verify
will be replaced by VerifyService
in the future.
Ask Let's Encrypt to issue a new certificate.
cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.issue
Warning
Depcrecation Notice
The issue
will be replaced by IssueService
in the future.
cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.renew
Warning
Depcrecation Notice
The renew
will be replaced by RenewService
in the future.
Check a certificate is verified and issued.
cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.active? # => true
Check a certificate is expired.
cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.expired? # => false
To renew a certificate, you can run renew
task to renew coming expires certificates.
rake letsencrypt:renew
If you are using Sidekiq or others, you can enqueue renew task daily.
LetsEncrypt::RenewCertificatesJob.perform_later
When the certificate is trying to issue a new one, you can subscribe it for logging or error handling.
ActiveSupport::Notifications.subscribe('letsencrypt.renew') do |name, start, finish, id, payload|
Rails.logger.info("Certificate for #{payload[:domain]} is renewed")
end
The available events are:
letsencrypt.renew
letsencrypt.verify
letsencrypt.issue
The setup is following this Article
Add config/initializers/letsencrypt.rb
to add config to sync certificate.
LetsEncrypt.config do |config|
config.redis_url = 'redis://localhost:6379/1'
config.save_to_redis = true
end
Connect Redis
when Nginx worker start
http {
# ...
mruby_init_worker_code '
userdata = Userdata.new
userdata.redis = Redis.new "127.0.0.1", 6379
# If your redis database is not 0, please select a correct one
userdata.redis.select 1
';
}
Setup SSL using mruby
server {
listen 443 ssl;
server_name _;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_certificate certs/dummy.crt;
ssl_certificate_key certs/dummy.key;
mruby_ssl_handshake_handler_code '
ssl = Nginx::SSL.new
domain = ssl.servername
redis = Userdata.new.redis
unless redis["#{domain}.crt"].nil? and redis["#{domain}.key"].nil?
ssl.certificate_data = redis["#{domain}.crt"]
ssl.certificate_key_data = redis["#{domain}.key"]
end
';
}
The gem is available as open source under the terms of the MIT License.