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

https://gitlab.com/gaveen/omnibus-gitlab · Ruby · 159 lines · 102 code · 23 blank · 34 comment · 11 complexity · e32e958291b717f9ce88ae40a98592f7 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 Exception => 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.exists?(@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, "*")]
  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. case
  70. when !valid?(file),whitelisted?(file)
  71. next
  72. when is_x509_certificate?(file)
  73. move_certificate(file)
  74. else
  75. raise_msg(file)
  76. end
  77. end
  78. end
  79. def whitelisted?(file)
  80. whitelisted_files.include?(file) || whitelisted_files.include?(File.realpath(file))
  81. end
  82. def valid?(file)
  83. exists = File.exists?(file)
  84. FileUtils.rm_f(file) if File.symlink?(file) && !exists
  85. exists
  86. end
  87. def move_certificate(file)
  88. return if File.symlink?(file) && File.readlink(file).start_with?(@trusted_certs_dir)
  89. # Move the certs to the trusted certs directory if it is located within our managed certs directory
  90. # Otherwise copy the cert to the trusted certs directory
  91. realpath = File.realpath(file)
  92. if realpath.start_with?(@omnibus_certs_dir)
  93. FileUtils.mv(realpath, @trusted_certs_dir, force: true)
  94. else
  95. FileUtils.cp(realpath, @trusted_certs_dir)
  96. end
  97. FileUtils.rm_f(file) if File.symlink?(file)
  98. puts "\n Moving #{realpath}"
  99. end
  100. def link_certificates
  101. update_permissions
  102. c_rehash
  103. link_to_omnibus_ssl_directory
  104. log_directory_hash
  105. end
  106. # c_rehash ran so we now have valid hashed names
  107. # Skip all files that are not symlinks
  108. # If they are symlinks, make sure they are valid certificates
  109. def link_to_omnibus_ssl_directory
  110. Dir.glob(File.join(@trusted_certs_dir, "*")) do |trusted_cert|
  111. if File.symlink?(trusted_cert) && is_x509_certificate?(trusted_cert)
  112. hash_name = File.basename(trusted_cert)
  113. certificate_path = File.realpath(trusted_cert)
  114. symlink_path = File.join(@omnibus_certs_dir, hash_name)
  115. FileUtils.ln_s certificate_path, symlink_path unless File.exist?(symlink_path)
  116. else
  117. puts "\n Skipping #{trusted_cert}."
  118. end
  119. end
  120. end
  121. def update_permissions
  122. file_list = Dir.glob(File.join(@trusted_certs_dir, '*'))
  123. FileUtils.chmod(0755, file_list)
  124. end
  125. def c_rehash
  126. cmd = "/opt/gitlab/embedded/bin/c_rehash #{@trusted_certs_dir}"
  127. result = do_shell_out(cmd)
  128. result.exitstatus
  129. end
  130. def log_directory_hash
  131. File.write(@directory_hash_file, trusted_certs_dir_hash)
  132. end
  133. def raise_msg(file)
  134. raise "ERROR: Not a certificate: #{file} -> #{File.realpath(file)}"
  135. end
  136. end