# Copyright:: Copyright (c) 2018 GitLab Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require_relative '../../package/libraries/helpers/logging_helper' class LetsEncrypt class << self def parse_variables parse_enable end # Munge the enable parameter # # @return [void] def parse_enable # default for letsencrypt.enable is nil. If a user has specified anything # else leave it alone. return unless Gitlab['letsencrypt']['enable'].nil? if should_auto_enable? && Gitlab['package']['generate_secrets_json_file'] == false LoggingHelper.warning("Writing secrets to `gitlab-secrets.json` file is disabled. Hence, not automatically enabling Let's Encrypt integration.") return end # We get to make a 'guess' as to if we should enable based on other parsed # values Gitlab['letsencrypt']['enable'] = should_auto_enable? # Remember if we auto-enabled, for later runs. Persisted as a secret Gitlab['letsencrypt']['auto_enabled'] = Gitlab['letsencrypt']['enable'] == true end def rails_listen_https? Gitlab['gitlab_rails']['gitlab_https'] end def nginx_enabled? [ Gitlab['nginx']['enable'], Gitlab[:node]['gitlab']['nginx']['enable'], true ].find { |e| !e.nil? } end def nginx_listen_https? Gitlab['nginx']['listen_https'].nil? || Gitlab['nginx']['listen_https'] end def le_auto_enabled? # We store value of this setting to gitlab-secrets.json file. This acts # as a marker whether the certificate present is something we generated # automatically in a previous run, or is something user brought. Gitlab['letsencrypt']['auto_enabled'] end def cert_files_present? File.exist?(Gitlab['nginx']['ssl_certificate_key']) || File.exist?(Gitlab['nginx']['ssl_certificate']) end # Should we enable the recipe even if a user didn't specify it? # # @return [true, false] def should_auto_enable? # We automatically enable LE if all the following conditions are met # 1. Rails component is listening to https # 2. Nginx is enabled and is listening over https # 3. At least one of the following is true # a. LE was automatically enabled in a previous run # b. No certificate/key files are present # c. If certificate is present, and they are from LE, and is ready for renewal # Note: The last condition here is a failsafe to ensure an expired # certificate always gets renewed. # Check https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4244 for # details. The downside is that when expired certificate issued by LE # is encountered, omnibus-gitlab will attempt to renew it, irrespective # of whether it was automatically generated by a previous reconfigure # run or brought by the user. rails_listen_https? && nginx_enabled? && nginx_listen_https? && (le_auto_enabled? || !cert_files_present? || needs_renewal?) end # Save secrets if they do not have letsencrypt.auto_enabled # # @return [void] def save_auto_enabled return unless Gitlab['letsencrypt']['auto_enabled'] secrets = SecretsHelper.load_gitlab_secrets # Avoid writing if the attribute is there and true return if secrets.dig('letsencrypt', 'auto_enabled') SecretsHelper.write_to_gitlab_secrets end private LETSENCRYPT_ISSUER = %r(/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X[1-4]).freeze # Checks wheather the existing Let's Encrypt certificate is expired and needs renewal. # # @return [true, false] def needs_renewal? file_name = Gitlab['nginx']['ssl_certificate'] return false unless File.exist? file_name cert = OpenSSL::X509::Certificate.new File.read(file_name) cert.issuer.to_s =~ LETSENCRYPT_ISSUER && cert.not_after < Time.now end end end