From 3c33724e2e182436a2d8b44ef71d0bdac37c585b Mon Sep 17 00:00:00 2001 From: Vladimir Shushlin Date: Thu, 16 May 2019 09:32:25 +0000 Subject: Add Let's Encrypt client Part of adding Let's Encrypt certificates for pages domains Add acme-client gem Client is being initialized by private key stored in secrets.yml Let's Encrypt account is being created lazily. If it's already created, Acme::Client just gets account_kid by calling new_account method Make Let's Encrypt client an instance Wrap order and challenge classes --- lib/gitlab/lets_encrypt/challenge.rb | 17 +++++++++ lib/gitlab/lets_encrypt/client.rb | 74 ++++++++++++++++++++++++++++++++++++ lib/gitlab/lets_encrypt/order.rb | 23 +++++++++++ 3 files changed, 114 insertions(+) create mode 100644 lib/gitlab/lets_encrypt/challenge.rb create mode 100644 lib/gitlab/lets_encrypt/client.rb create mode 100644 lib/gitlab/lets_encrypt/order.rb (limited to 'lib') diff --git a/lib/gitlab/lets_encrypt/challenge.rb b/lib/gitlab/lets_encrypt/challenge.rb new file mode 100644 index 00000000000..6a7f5e965c5 --- /dev/null +++ b/lib/gitlab/lets_encrypt/challenge.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Gitlab + module LetsEncrypt + class Challenge + def initialize(acme_challenge) + @acme_challenge = acme_challenge + end + + delegate :url, :token, :file_content, :status, :request_validation, to: :acme_challenge + + private + + attr_reader :acme_challenge + end + end +end diff --git a/lib/gitlab/lets_encrypt/client.rb b/lib/gitlab/lets_encrypt/client.rb new file mode 100644 index 00000000000..d7468b06767 --- /dev/null +++ b/lib/gitlab/lets_encrypt/client.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Gitlab + module LetsEncrypt + class Client + PRODUCTION_DIRECTORY_URL = 'https://acme-v02.api.letsencrypt.org/directory' + STAGING_DIRECTORY_URL = 'https://acme-staging-v02.api.letsencrypt.org/directory' + + def new_order(domain_name) + ensure_account + + acme_order = acme_client.new_order(identifiers: [domain_name]) + + ::Gitlab::LetsEncrypt::Order.new(acme_order) + end + + def load_order(url) + ensure_account + + # rubocop: disable CodeReuse/ActiveRecord + ::Gitlab::LetsEncrypt::Order.new(acme_client.order(url: url)) + # rubocop: enable CodeReuse/ActiveRecord + end + + def load_challenge(url) + ensure_account + + ::Gitlab::LetsEncrypt::Challenge.new(acme_client.challenge(url: url)) + end + + def terms_of_service_url + acme_client.terms_of_service + end + + def enabled? + return false unless Feature.enabled?(:pages_auto_ssl) + + Gitlab::CurrentSettings.lets_encrypt_terms_of_service_accepted + end + + private + + def acme_client + @acme_client ||= ::Acme::Client.new(private_key: private_key, directory: acme_api_directory_url) + end + + def private_key + @private_key ||= OpenSSL::PKey.read(Gitlab::Application.secrets.lets_encrypt_private_key) + end + + def admin_email + Gitlab::CurrentSettings.lets_encrypt_notification_email + end + + def contact + "mailto:#{admin_email}" + end + + def ensure_account + raise 'Acme integration is disabled' unless enabled? + + @acme_account ||= acme_client.new_account(contact: contact, terms_of_service_agreed: true) + end + + def acme_api_directory_url + if Rails.env.production? + PRODUCTION_DIRECTORY_URL + else + STAGING_DIRECTORY_URL + end + end + end + end +end diff --git a/lib/gitlab/lets_encrypt/order.rb b/lib/gitlab/lets_encrypt/order.rb new file mode 100644 index 00000000000..5109b5e9843 --- /dev/null +++ b/lib/gitlab/lets_encrypt/order.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + module LetsEncrypt + class Order + def initialize(acme_order) + @acme_order = acme_order + end + + def new_challenge + authorization = @acme_order.authorizations.first + challenge = authorization.http + ::Gitlab::LetsEncrypt::Challenge.new(challenge) + end + + delegate :url, :status, to: :acme_order + + private + + attr_reader :acme_order + end + end +end -- cgit v1.2.3