PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/files/gitlab-cookbooks/package/libraries/certificate_helper.rb

https://gitlab.com/oyarzun/omnibus-gitlab
Ruby | 172 lines | 109 code | 28 blank | 35 comment | 15 complexity | 441aa1f330cc326f04638462eae0664a MD5 | raw file
  1. #
  2. # Copyright:: Copyright (c) 2016 GitLab Inc.
  3. # License:: Apache License, Version 2.0
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. require_relative 'helper'
  18. class CertificateHelper
  19. include ShellOutHelper
  20. def initialize(trusted_cert_dir, omnibus_cert_dir, user_dir)
  21. @trusted_certs_dir = trusted_cert_dir
  22. @omnibus_certs_dir = omnibus_cert_dir
  23. @directory_hash_file = File.join(user_dir, "trusted-certs-directory-hash")
  24. end
  25. def whitelisted_files
  26. [
  27. File.join(@omnibus_certs_dir, "README"),
  28. File.join(@omnibus_certs_dir, "cacert.pem")
  29. ]
  30. end
  31. def is_x509_certificate?(file)
  32. return false unless valid?(file)
  33. begin
  34. OpenSSL::X509::Certificate.new(File.read(file)) # DER- or PEM-encoded
  35. true
  36. rescue OpenSSL::X509::CertificateError => e
  37. warn("ERROR: " + file + ": OpenSSL error: " + e.message + "!")
  38. false
  39. rescue StandardError => e
  40. warn(e.message)
  41. false
  42. end
  43. end
  44. # If the number of files between the two directories is different
  45. # something got added so trigger the run
  46. def new_certificate_added?
  47. return true unless File.exist?(@directory_hash_file)
  48. stored_hash = File.read(@directory_hash_file)
  49. trusted_certs_dir_hash != stored_hash
  50. end
  51. def trusted_certs_dir_hash
  52. files = Dir[File.join(@trusted_certs_dir, "*"), File.join(@omnibus_certs_dir, "*")]
  53. files_modification_time = files.map { |name| File.stat(name).mtime if valid?(name) }
  54. Digest::SHA1.hexdigest(files_modification_time.join)
  55. end
  56. # Get all files in /opt/gitlab/embedded/ssl/certs
  57. # - "cacert.pem", "README" -> ignore
  58. # - if valid certificate
  59. # - if symlink
  60. # - remove broken symlinks
  61. # - ignore if pointing to /etc/gitlab/trusted-certs
  62. # - ignore because it might be a symlink user created
  63. # - else
  64. # - copy to trusted-certs dir
  65. # - else (not valid)
  66. # raise and error
  67. def move_existing_certificates
  68. Dir.glob(File.join(@omnibus_certs_dir, "*")) do |file|
  69. next if !valid?(file) || whitelisted?(file)
  70. if is_x509_certificate?(file)
  71. move_certificate(file)
  72. else
  73. raise_msg(file)
  74. end
  75. end
  76. end
  77. def whitelisted?(file)
  78. whitelisted_files.include?(file) || whitelisted_files.include?(File.realpath(file))
  79. end
  80. def valid?(file)
  81. exists = File.exist?(file)
  82. FileUtils.rm_f(file) if File.symlink?(file) && !exists
  83. exists
  84. end
  85. def move_certificate(file)
  86. return if exists_in_trusted?(file)
  87. # Move the certs to the trusted certs directory if it is located within our managed certs directory
  88. # Otherwise copy the cert to the trusted certs directory
  89. realpath = File.realpath(file)
  90. if realpath.start_with?(@omnibus_certs_dir)
  91. FileUtils.mv(realpath, @trusted_certs_dir, force: true)
  92. else
  93. FileUtils.cp(realpath, @trusted_certs_dir)
  94. end
  95. FileUtils.rm_f(file) if File.symlink?(file)
  96. puts "\n Moving #{realpath}"
  97. end
  98. def exists_in_trusted?(file)
  99. trusted_path = File.join(@trusted_certs_dir, File.basename(file))
  100. (File.symlink?(file) && File.readlink(file).start_with?(@trusted_certs_dir)) ||
  101. (File.exist?(trusted_path) && FileUtils.identical?(file, trusted_path))
  102. end
  103. def link_certificates
  104. update_permissions
  105. rehash_status = c_rehash
  106. unless rehash_status.zero?
  107. LoggingHelper.warning("Rehashing of trusted certificates present in `/etc/gitlab/trusted-certs` failed. If on a FIPS-enabled machine, ensure `c_rehash` binary is available in $PATH.")
  108. return
  109. end
  110. link_to_omnibus_ssl_directory
  111. log_directory_hash
  112. end
  113. # c_rehash ran so we now have valid hashed names
  114. # Skip all files that are not symlinks
  115. # If they are symlinks, make sure they are valid certificates
  116. def link_to_omnibus_ssl_directory
  117. Dir.glob(File.join(@trusted_certs_dir, "*")) do |trusted_cert|
  118. if File.symlink?(trusted_cert) && is_x509_certificate?(trusted_cert)
  119. hash_name = File.basename(trusted_cert)
  120. certificate_path = File.realpath(trusted_cert)
  121. symlink_path = File.join(@omnibus_certs_dir, hash_name)
  122. puts "\n Linking #{hash_name} from #{certificate_path}"
  123. FileUtils.ln_s certificate_path, symlink_path unless File.exist?(symlink_path)
  124. end
  125. end
  126. end
  127. def update_permissions
  128. files_directories = Dir.glob(File.join(@trusted_certs_dir, '*'))
  129. # Only operate on files
  130. file_list = files_directories.reject { |f| File.directory?(f) }
  131. FileUtils.chmod(0644, file_list)
  132. end
  133. def c_rehash
  134. cmd = "c_rehash #{@trusted_certs_dir}"
  135. result = do_shell_out_with_embedded_path(cmd)
  136. result.exitstatus
  137. end
  138. def log_directory_hash
  139. File.write(@directory_hash_file, trusted_certs_dir_hash)
  140. end
  141. def raise_msg(file)
  142. raise "ERROR: Not a certificate: #{File.realpath(file)}. Move it from #{File.realpath('..', file)} to a different location and reconfigure again."
  143. end
  144. end