Skip to content

elct9620/rails-letsencrypt

Repository files navigation

LetsEncrypt Gem Version Code Climate

Provide manageable Let's Encrypt Certificate for Rails.

Requirement

  • Rails 6.1+
  • Ruby 2.7+

Installation

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'

Configuration

Add a file to config/initializers/letsencrypt.rb and put below config you need.

LetsEncrypt.config do |config|
  # 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'
end

Usage

The SSL certificate setup depends on the web server, this gem can work with ngx_mruby or kong.

Certificate Model

Create

Add a new domain into the database.

cert = LetsEncrypt::Certificate.create(domain: 'example.com')
cert.get # alias  `verify && issue`

Verify

Makes a request to Let's Encrypt and verify domain

cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.verify

Issue

Ask Let's Encrypt to issue a new certificate.

cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.issue

Renew

cert = LetsEncrypt::Certificate.find_by(domain: 'example.com')
cert.renew

Status

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

Tasks

To renew a certificate, you can run renew task to renew coming expires certificates.

rake letsencrypt:renew

Jobs

If you are using Sidekiq or others, you can enqueue renew task daily.

LetsEncrypt::RenewCertificatesJob.perform_later

Subscribe

When the certificate is trying to issue a new one, you can subscribe it for logging or error handling.

ActiveSupport::Notifications.subscribe('letsencrypt.issue') do |name, start, finish, id, payload|
  Rails.logger.info("Certificate for #{payload[:domain]} is issued")
end

ngx_mruby

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
  ';
}

Kong

Coming soon.

License

The gem is available as open source under the terms of the MIT License.