From 207b9ce1a38de9b34edba347adcfc62e770ad606 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Thu, 4 Jun 2020 15:38:25 -0700 Subject: [PATCH 1/9] POC for providing encrypted ldap passwords Extend Rails encrypted credentials to provide encryption for yaml files Introduce two new rake commands to allow editing the credentials - One generic edit command where you pass the filename - One ldap specific one where the filename is pre-set --- config/initializers/1_settings.rb | 1 + config/settings.rb | 14 ++++++++ lib/gitlab/auth/ldap/config.rb | 14 ++++++-- lib/gitlab/encrypted_command.rb | 36 ++++++++++++++++++++ lib/gitlab/encrypted_configuration.rb | 23 +++++++++++++ lib/gitlab/encrypted_ldap_command.rb | 47 +++++++++++++++++++++++++++ lib/tasks/gitlab/encrypted.rake | 17 ++++++++++ lib/tasks/gitlab/ldap.rake | 12 +++++++ 8 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 lib/gitlab/encrypted_command.rb create mode 100644 lib/gitlab/encrypted_configuration.rb create mode 100644 lib/gitlab/encrypted_ldap_command.rb create mode 100644 lib/tasks/gitlab/encrypted.rake diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 166649fc24a00c..f5eab56c8ce87f 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -6,6 +6,7 @@ Settings['ldap'] ||= Settingslogic.new({}) Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil? Settings.ldap['prevent_ldap_sign_in'] = false if Settings.ldap['prevent_ldap_sign_in'].blank? +Settings.ldap['secret_file'] = Settings.absolute(Settings.ldap['secret_file'] || 'ldap_secret.yaml.enc') Gitlab.ee do Settings.ldap['sync_time'] = 3600 if Settings.ldap['sync_time'].nil? diff --git a/config/settings.rb b/config/settings.rb index 99f1b85202e39d..bed49f8b56e9c9 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -152,6 +152,20 @@ def attr_encrypted_db_key_base Gitlab::Application.secrets.db_key_base end + def encrypted(path, key: nil, key_path: nil, env_key: nil, allow_in_safe_mode: false) + return Gitlab::EncryptedConfiguration.new if ENV['GITLAB_ENCRYPTED_SAFE_MODE'] && !allow_in_safe_mode + + # If no key defaults are provided, use our db_key_base + key = Settings.attr_encrypted_db_key_base_truncated unless key || key_path || env_key + + Gitlab::EncryptedConfiguration.new( + config_path: Rails.root.join(path), + key: key, + key_path: key_path ? Rails.root.join(key_path) : nil, + env_key: env_key + ) + end + def load_dynamic_cron_schedules! cron_jobs['gitlab_usage_ping_worker']['cron'] ||= cron_for_usage_ping end diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb index 7677189eb9f5e3..0499f1b5bbf3be 100644 --- a/lib/gitlab/auth/ldap/config.rb +++ b/lib/gitlab/auth/ldap/config.rb @@ -90,7 +90,7 @@ def omniauth_options if has_auth? opts.merge!( bind_dn: options['bind_dn'], - password: options['password'] + password: auth_password ) end @@ -155,7 +155,7 @@ def external_groups end def has_auth? - options['password'] || options['bind_dn'] + auth_password || options['bind_dn'] end def allow_username_or_email_login @@ -268,11 +268,19 @@ def auth_options auth: { method: :simple, username: options['bind_dn'], - password: options['password'] + password: auth_password } } end + def auth_password + return options['password'] if options['password'] + + if File.exist?(Gitlab.config.ldap.secret_file) + Settings.encrypted(Gitlab.config.ldap.secret_file)[@provider.delete_prefix('ldap').to_sym]&.fetch(:password)&.chomp + end + end + def omniauth_user_filter uid_filter = Net::LDAP::Filter.eq(uid, '%{username}') diff --git a/lib/gitlab/encrypted_command.rb b/lib/gitlab/encrypted_command.rb new file mode 100644 index 00000000000000..ba4ec0517f4992 --- /dev/null +++ b/lib/gitlab/encrypted_command.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "rails/command/helpers/editor" + +# rubocop:disable Rails/Output +module Gitlab + class EncryptedCommand + class << self + include Rails::Command::Helpers::Editor + + alias_method :say, :puts + + def edit(file_path) + editor = ENV['editor'] || 'editor' + + catch_editing_exceptions do + Settings.encrypted(file_path, allow_in_safe_mode: true).write("") unless File.exist?(Rails.root.join(file_path)) + Settings.encrypted(file_path, allow_in_safe_mode: true).change do |tmp_path| + system("#{editor} #{tmp_path}") + end + end + + puts "File encrypted and saved." + rescue ActiveSupport::MessageEncryptor::InvalidMessage + puts "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?" + end + + def show(file_path) + encrypted = Settings.encrypted(file_path, allow_in_safe_mode: true) + + puts encrypted.read.presence || "File '#{file_path}' does not exist. Use `rake gitlab:encrypted:edit #{file_path}` to change that." + end + end + end +end +# rubocop:enable Rails/Output diff --git a/lib/gitlab/encrypted_configuration.rb b/lib/gitlab/encrypted_configuration.rb new file mode 100644 index 00000000000000..ac370c5055f0d1 --- /dev/null +++ b/lib/gitlab/encrypted_configuration.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Gitlab + class EncryptedConfiguration < ActiveSupport::EncryptedConfiguration + def initialize(config_path: nil, key: nil, key_path: nil, env_key: nil, raise_if_missing_key: false) + @content_path = Pathname.new(config_path).yield_self { |path| path.symlink? ? path.realpath : path } if config_path + @key_path = Pathname.new(key_path) if key_path + @key, @env_key, @raise_if_missing_key = key, env_key, raise_if_missing_key + end + + def key + @key || super + end + + def read_env_key + super if env_key + end + + def read_key_file + super if key_path + end + end +end diff --git a/lib/gitlab/encrypted_ldap_command.rb b/lib/gitlab/encrypted_ldap_command.rb new file mode 100644 index 00000000000000..e7782f6ac37247 --- /dev/null +++ b/lib/gitlab/encrypted_ldap_command.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require "rails/command/helpers/editor" + +# rubocop:disable Rails/Output +module Gitlab + class EncryptedLdapCommand + class << self + include Rails::Command::Helpers::Editor + + alias_method :say, :puts + + def edit + file_path = Gitlab.config.ldap.secret_file + + editor = ENV['EDITOR'] || 'editor' + + catch_editing_exceptions do + Settings.encrypted(file_path, allow_in_safe_mode: true).write(encrypted_file_template) unless File.exist?(file_path) + Settings.encrypted(file_path, allow_in_safe_mode: true).change do |tmp_path| + system("#{editor} #{tmp_path}") + end + end + + puts "File encrypted and saved." + rescue ActiveSupport::MessageEncryptor::InvalidMessage + puts "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?" + end + + def show + encrypted = Settings.encrypted(Gitlab.config.ldap.secret_file, allow_in_safe_mode: true) + + puts encrypted.read.presence || "File '#{Gitlab.config.ldap.secret_file}' does not exist. Use `rake gitlab:ldap:secret:edit` to change that." + end + + private + + def encrypted_file_template + <<~YAML + # main: + # password: '123' + YAML + end + end + end +end +# rubocop:enable Rails/Output diff --git a/lib/tasks/gitlab/encrypted.rake b/lib/tasks/gitlab/encrypted.rake new file mode 100644 index 00000000000000..f3354ebc57afbe --- /dev/null +++ b/lib/tasks/gitlab/encrypted.rake @@ -0,0 +1,17 @@ +namespace :gitlab do + namespace :encrypted do + desc 'GitLab | Encrypted | edit encrypted config files' + task :edit, [:path] => ['gitlab:encrypted:safe_mode', :environment] do |t, args| + Gitlab::EncryptedCommand.edit(args[:path]) + end + + desc 'GitLab | Encrypted | show encrypted config files' + task :show, [:path] => ['gitlab:encrypted:safe_mode', :environment] do |t, args| + Gitlab::EncryptedCommand.show(args[:path]) + end + + task :safe_mode do + ENV['GITLAB_ENCRYPTED_SAFE_MODE'] = 'true' + end + end +end diff --git a/lib/tasks/gitlab/ldap.rake b/lib/tasks/gitlab/ldap.rake index 0459de27c965ca..b686362a4e3218 100644 --- a/lib/tasks/gitlab/ldap.rake +++ b/lib/tasks/gitlab/ldap.rake @@ -36,5 +36,17 @@ namespace :gitlab do puts "Successfully updated #{plural_updated_count} out of #{plural_id_count} total" end end + + namespace :secret do + desc 'GitLab | LDAP | Secret | Edit ldap secrets' + task edit: ['gitlab:encrypted:safe_mode', :environment] do + Gitlab::EncryptedLdapCommand.edit + end + + desc 'GitLab | LDAP | Secret | Show ldap secrets' + task show: ['gitlab:encrypted:safe_mode', :environment] do + Gitlab::EncryptedLdapCommand.show + end + end end end -- GitLab From 9b5f37ad8dc3a56abce96253d400e17cc45cea03 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 17 Jun 2020 13:42:18 -0700 Subject: [PATCH 2/9] Added tests for the encrypted ldap secrets poc Added tests for the encrypted config, the new rake commands, and cleaned up some minor errors found while testing. --- lib/gitlab/encrypted_command.rb | 4 +- spec/config/settings_spec.rb | 17 ++++++ .../gitlab/encrypted_configuration_spec.rb | 54 +++++++++++++++++++ spec/tasks/gitlab/encrypted_rake_spec.rb | 41 ++++++++++++++ spec/tasks/gitlab/ldap_rake_spec.rb | 38 +++++++++++++ 5 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 spec/lib/gitlab/encrypted_configuration_spec.rb create mode 100644 spec/tasks/gitlab/encrypted_rake_spec.rb diff --git a/lib/gitlab/encrypted_command.rb b/lib/gitlab/encrypted_command.rb index ba4ec0517f4992..d48a8edcdd38d6 100644 --- a/lib/gitlab/encrypted_command.rb +++ b/lib/gitlab/encrypted_command.rb @@ -11,7 +11,7 @@ class << self alias_method :say, :puts def edit(file_path) - editor = ENV['editor'] || 'editor' + editor = ENV['EDITOR'] || 'editor' catch_editing_exceptions do Settings.encrypted(file_path, allow_in_safe_mode: true).write("") unless File.exist?(Rails.root.join(file_path)) @@ -28,7 +28,7 @@ def edit(file_path) def show(file_path) encrypted = Settings.encrypted(file_path, allow_in_safe_mode: true) - puts encrypted.read.presence || "File '#{file_path}' does not exist. Use `rake gitlab:encrypted:edit #{file_path}` to change that." + puts encrypted.read.presence || "File '#{file_path}' does not exist. Use `rake gitlab:encrypted:edit[#{file_path}]` to change that." end end end diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb index 9db3d35cbe5672..0157c018bd4570 100644 --- a/spec/config/settings_spec.rb +++ b/spec/config/settings_spec.rb @@ -112,4 +112,21 @@ end end end + + describe '.encrypted' do + it 'defaults to using the db_key_base for the key' do + expect(Gitlab::EncryptedConfiguration).to receive(:new).with(hash_including(key: Settings.attr_encrypted_db_key_base_truncated)) + Settings.encrypted('tmp/tests/test.enc') + end + + it 'defaults the configpath within the rails root' do + expect(Settings.encrypted('tmp/tests/test.enc').content_path.fnmatch?(File.join(Rails.root,'**'))).to be true + end + + it 'returns empty encrypted config when in safe mode' do + stub_env('GITLAB_ENCRYPTED_SAFE_MODE', 'true') + expect(Gitlab::EncryptedConfiguration).to receive(:new).with(no_args) + Settings.encrypted('tmp/tests/test.enc') + end + end end diff --git a/spec/lib/gitlab/encrypted_configuration_spec.rb b/spec/lib/gitlab/encrypted_configuration_spec.rb new file mode 100644 index 00000000000000..e08aa8209b7f1b --- /dev/null +++ b/spec/lib/gitlab/encrypted_configuration_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe Gitlab::EncryptedConfiguration do + subject(:configuration) { described_class.new } + + describe '#initialize' do + it 'accepts all args as optional fields' do + expect { configuration }.not_to raise_exception + + expect(configuration.key).to be_nil + expect(configuration.read_env_key).to be_nil + expect(configuration.read_key_file).to be_nil + end + end + + context 'when provided key and config file' do + let!(:config_tmp_dir) { Dir.mktmpdir('config-') } + let(:credentials_config_path) { File.join(config_tmp_dir, 'credentials.yml.enc') } + let(:credentials_key_path) { File.join(config_tmp_dir, 'somekey.key') } + + before do + File.write(credentials_key_path, ActiveSupport::EncryptedConfiguration.generate_key) + end + + after do + FileUtils.rm_f(config_tmp_dir) + end + + describe '#read' do + it 'reads yaml configuration' do + config = described_class.new(config_path: credentials_config_path, key_path: credentials_key_path) + + config.write({ foo: { bar: true } }.to_yaml) + expect(config.foo[:bar]).to be true + end + end + + describe '#change' do + it 'changes yaml configuration' do + config = described_class.new(config_path: credentials_config_path, key_path: credentials_key_path) + + config.write({ foo: { bar: true } }.to_yaml) + config.change do |unencrypted_file| + contents = YAML.load(unencrypted_file.read) + unencrypted_file.write contents.merge(beef: "stew").to_yaml + end + expect(config.foo[:bar]).to be true + expect(config.beef).to eq('stew') + end + end + end +end diff --git a/spec/tasks/gitlab/encrypted_rake_spec.rb b/spec/tasks/gitlab/encrypted_rake_spec.rb new file mode 100644 index 00000000000000..f0895ccf3f43bc --- /dev/null +++ b/spec/tasks/gitlab/encrypted_rake_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'rake_helper' + +describe 'gitlab:encrypted rake tasks' do + let(:encoded_file) { 'tmp/tests/enc/encryptedtest.enc' } + + before do + Rake.application.rake_require 'tasks/gitlab/encrypted' + stub_env('EDITOR', 'cat') + stub_warn_user_is_not_gitlab + FileUtils.mkdir_p('tmp/tests/enc/') + end + + after do + FileUtils.rm_rf('tmp/tests/enc/') + end + + describe ':show' do + it 'displays error when file does not exist' do + expect { run_rake_task('gitlab:encrypted:show', [encoded_file]) }.to output(/File '#{encoded_file}' does not exist. Use `rake gitlab:encrypted:edit\[#{encoded_file}\]` to change that./).to_stdout + end + + it 'outputs the unencrypted content when present' do + encrypted = Settings.encrypted(encoded_file, allow_in_safe_mode: true) + encrypted.write('somevalue') + expect { run_rake_task('gitlab:encrypted:show', [encoded_file]) }.to output(/somevalue/).to_stdout + end + end + + describe ':edit' do + it 'creates encrypted file' do + stub_env('EDITOR', 'echo "foo: bar" >') + expect { run_rake_task('gitlab:encrypted:edit', [encoded_file]) }.to output(/File encrypted and saved./).to_stdout + expect(File.exist?(encoded_file)).to be true + value = Settings.encrypted(encoded_file, allow_in_safe_mode: true) + expect(value.read.present?).to be true + expect(value.foo).to eq('bar') + end + end +end diff --git a/spec/tasks/gitlab/ldap_rake_spec.rb b/spec/tasks/gitlab/ldap_rake_spec.rb index bbc3f625088661..c415766901f273 100644 --- a/spec/tasks/gitlab/ldap_rake_spec.rb +++ b/spec/tasks/gitlab/ldap_rake_spec.rb @@ -13,3 +13,41 @@ run_rake_task('gitlab:ldap:rename_provider', 'ldapmain', 'ldapfoo') end end + +describe 'gitlab:ldap:secret rake tasks' do + let(:ldap_secret_file) { 'tmp/tests/ldapenc/ldap_secret.yaml.enc' } + + before do + Rake.application.rake_require 'tasks/gitlab/encrypted' + Rake.application.rake_require 'tasks/gitlab/ldap' + stub_env('EDITOR', 'cat') + stub_warn_user_is_not_gitlab + FileUtils.mkdir_p('tmp/tests/ldapenc/') + allow(Gitlab.config.ldap).to receive(:secret_file).and_return(ldap_secret_file) + end + + after do + FileUtils.rm_rf('tmp/tests/ldapenc/') + end + + describe ':show' do + it 'displays error when file does not exist' do + expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/File '#{ldap_secret_file}' does not exist. Use `rake gitlab:ldap:secret:edit` to change that./).to_stdout + end + + it 'outputs the unencrypted content when present' do + encrypted = Settings.encrypted(ldap_secret_file, allow_in_safe_mode: true) + encrypted.write('somevalue') + expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/somevalue/).to_stdout + end + end + + describe 'edit' do + it 'creates encrypted file' do + expect { run_rake_task('gitlab:ldap:secret:edit') }.to output(/File encrypted and saved./).to_stdout + expect(File.exist?(ldap_secret_file)).to be true + value = Settings.encrypted(ldap_secret_file, allow_in_safe_mode: true) + expect(value.read).to match(/password: '123'/) + end + end +end -- GitLab From c708d491e2f285be8c26f0a0b965fad976a27489 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 17 Jun 2020 13:51:17 -0700 Subject: [PATCH 3/9] Fix rubocop failures in tests --- spec/config/settings_spec.rb | 2 +- spec/lib/gitlab/encrypted_configuration_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/config/settings_spec.rb b/spec/config/settings_spec.rb index 0157c018bd4570..1f1f7957fe75df 100644 --- a/spec/config/settings_spec.rb +++ b/spec/config/settings_spec.rb @@ -120,7 +120,7 @@ end it 'defaults the configpath within the rails root' do - expect(Settings.encrypted('tmp/tests/test.enc').content_path.fnmatch?(File.join(Rails.root,'**'))).to be true + expect(Settings.encrypted('tmp/tests/test.enc').content_path.fnmatch?(File.join(Rails.root, '**'))).to be true end it 'returns empty encrypted config when in safe mode' do diff --git a/spec/lib/gitlab/encrypted_configuration_spec.rb b/spec/lib/gitlab/encrypted_configuration_spec.rb index e08aa8209b7f1b..51eb099ea03a1e 100644 --- a/spec/lib/gitlab/encrypted_configuration_spec.rb +++ b/spec/lib/gitlab/encrypted_configuration_spec.rb @@ -43,7 +43,7 @@ config.write({ foo: { bar: true } }.to_yaml) config.change do |unencrypted_file| - contents = YAML.load(unencrypted_file.read) + contents = YAML.safe_load(unencrypted_file.read) unencrypted_file.write contents.merge(beef: "stew").to_yaml end expect(config.foo[:bar]).to be true -- GitLab From c1a723490f7f2e3408731eabb16e2184a5c07a64 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Wed, 17 Jun 2020 15:13:21 -0700 Subject: [PATCH 4/9] Fxi yaml load failure --- spec/lib/gitlab/encrypted_configuration_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/gitlab/encrypted_configuration_spec.rb b/spec/lib/gitlab/encrypted_configuration_spec.rb index 51eb099ea03a1e..710d3eea03f245 100644 --- a/spec/lib/gitlab/encrypted_configuration_spec.rb +++ b/spec/lib/gitlab/encrypted_configuration_spec.rb @@ -43,7 +43,7 @@ config.write({ foo: { bar: true } }.to_yaml) config.change do |unencrypted_file| - contents = YAML.safe_load(unencrypted_file.read) + contents = YAML.safe_load(unencrypted_file.read, [Symbol]) unencrypted_file.write contents.merge(beef: "stew").to_yaml end expect(config.foo[:bar]).to be true -- GitLab From a8fbe10f719470eaacfeba0ff4459a803bdbf299 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Tue, 23 Jun 2020 09:14:35 -0700 Subject: [PATCH 5/9] Reduce encrypted method signature down to just what is used --- config/settings.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/config/settings.rb b/config/settings.rb index bed49f8b56e9c9..81693f0df1c658 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -152,17 +152,12 @@ def attr_encrypted_db_key_base Gitlab::Application.secrets.db_key_base end - def encrypted(path, key: nil, key_path: nil, env_key: nil, allow_in_safe_mode: false) + def encrypted(path, allow_in_safe_mode: false) return Gitlab::EncryptedConfiguration.new if ENV['GITLAB_ENCRYPTED_SAFE_MODE'] && !allow_in_safe_mode - # If no key defaults are provided, use our db_key_base - key = Settings.attr_encrypted_db_key_base_truncated unless key || key_path || env_key - Gitlab::EncryptedConfiguration.new( config_path: Rails.root.join(path), - key: key, - key_path: key_path ? Rails.root.join(key_path) : nil, - env_key: env_key + key: Settings.attr_encrypted_db_key_base_truncated, ) end -- GitLab From bb0fb3d203e93513ee3364f66d9b646695cc13f1 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Tue, 23 Jun 2020 09:21:48 -0700 Subject: [PATCH 6/9] Change filepath to be configurable from anywhere Default to Rails root/config/ldap_secrets.yml.enc --- config/initializers/1_settings.rb | 2 +- config/settings.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index f5eab56c8ce87f..6ba3731899d29f 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -6,7 +6,7 @@ Settings['ldap'] ||= Settingslogic.new({}) Settings.ldap['enabled'] = false if Settings.ldap['enabled'].nil? Settings.ldap['prevent_ldap_sign_in'] = false if Settings.ldap['prevent_ldap_sign_in'].blank? -Settings.ldap['secret_file'] = Settings.absolute(Settings.ldap['secret_file'] || 'ldap_secret.yaml.enc') +Settings.ldap['secret_file'] = Settings.absolute(Settings.ldap['secret_file'] || Rails.root.join("config/ldap_secret.yaml.enc")) Gitlab.ee do Settings.ldap['sync_time'] = 3600 if Settings.ldap['sync_time'].nil? diff --git a/config/settings.rb b/config/settings.rb index 81693f0df1c658..b3ee36028bfb41 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -156,7 +156,7 @@ def encrypted(path, allow_in_safe_mode: false) return Gitlab::EncryptedConfiguration.new if ENV['GITLAB_ENCRYPTED_SAFE_MODE'] && !allow_in_safe_mode Gitlab::EncryptedConfiguration.new( - config_path: Rails.root.join(path), + config_path: Settings.absolute(path), key: Settings.attr_encrypted_db_key_base_truncated, ) end -- GitLab From 7ebcc70d7ba4809736a75f357b82581c42141746 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Tue, 23 Jun 2020 09:38:24 -0700 Subject: [PATCH 7/9] Cleanup encrypted config initializer to just use what we need Drop the more generic encrypted command as we aren't using it currently --- lib/gitlab/encrypted_command.rb | 36 --------------------- lib/gitlab/encrypted_configuration.rb | 11 +++---- lib/tasks/gitlab/encrypted.rake | 10 ------ spec/tasks/gitlab/encrypted_rake_spec.rb | 41 ------------------------ 4 files changed, 5 insertions(+), 93 deletions(-) delete mode 100644 lib/gitlab/encrypted_command.rb delete mode 100644 spec/tasks/gitlab/encrypted_rake_spec.rb diff --git a/lib/gitlab/encrypted_command.rb b/lib/gitlab/encrypted_command.rb deleted file mode 100644 index d48a8edcdd38d6..00000000000000 --- a/lib/gitlab/encrypted_command.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require "rails/command/helpers/editor" - -# rubocop:disable Rails/Output -module Gitlab - class EncryptedCommand - class << self - include Rails::Command::Helpers::Editor - - alias_method :say, :puts - - def edit(file_path) - editor = ENV['EDITOR'] || 'editor' - - catch_editing_exceptions do - Settings.encrypted(file_path, allow_in_safe_mode: true).write("") unless File.exist?(Rails.root.join(file_path)) - Settings.encrypted(file_path, allow_in_safe_mode: true).change do |tmp_path| - system("#{editor} #{tmp_path}") - end - end - - puts "File encrypted and saved." - rescue ActiveSupport::MessageEncryptor::InvalidMessage - puts "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?" - end - - def show(file_path) - encrypted = Settings.encrypted(file_path, allow_in_safe_mode: true) - - puts encrypted.read.presence || "File '#{file_path}' does not exist. Use `rake gitlab:encrypted:edit[#{file_path}]` to change that." - end - end - end -end -# rubocop:enable Rails/Output diff --git a/lib/gitlab/encrypted_configuration.rb b/lib/gitlab/encrypted_configuration.rb index ac370c5055f0d1..2eae02ef215365 100644 --- a/lib/gitlab/encrypted_configuration.rb +++ b/lib/gitlab/encrypted_configuration.rb @@ -2,22 +2,21 @@ module Gitlab class EncryptedConfiguration < ActiveSupport::EncryptedConfiguration - def initialize(config_path: nil, key: nil, key_path: nil, env_key: nil, raise_if_missing_key: false) + def initialize(config_path: nil, key: nil) @content_path = Pathname.new(config_path).yield_self { |path| path.symlink? ? path.realpath : path } if config_path - @key_path = Pathname.new(key_path) if key_path - @key, @env_key, @raise_if_missing_key = key, env_key, raise_if_missing_key + @key = key end def key - @key || super + @key end def read_env_key - super if env_key + nil end def read_key_file - super if key_path + nil end end end diff --git a/lib/tasks/gitlab/encrypted.rake b/lib/tasks/gitlab/encrypted.rake index f3354ebc57afbe..35b7ba56c41020 100644 --- a/lib/tasks/gitlab/encrypted.rake +++ b/lib/tasks/gitlab/encrypted.rake @@ -1,15 +1,5 @@ namespace :gitlab do namespace :encrypted do - desc 'GitLab | Encrypted | edit encrypted config files' - task :edit, [:path] => ['gitlab:encrypted:safe_mode', :environment] do |t, args| - Gitlab::EncryptedCommand.edit(args[:path]) - end - - desc 'GitLab | Encrypted | show encrypted config files' - task :show, [:path] => ['gitlab:encrypted:safe_mode', :environment] do |t, args| - Gitlab::EncryptedCommand.show(args[:path]) - end - task :safe_mode do ENV['GITLAB_ENCRYPTED_SAFE_MODE'] = 'true' end diff --git a/spec/tasks/gitlab/encrypted_rake_spec.rb b/spec/tasks/gitlab/encrypted_rake_spec.rb deleted file mode 100644 index f0895ccf3f43bc..00000000000000 --- a/spec/tasks/gitlab/encrypted_rake_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -# frozen_string_literal: true - -require 'rake_helper' - -describe 'gitlab:encrypted rake tasks' do - let(:encoded_file) { 'tmp/tests/enc/encryptedtest.enc' } - - before do - Rake.application.rake_require 'tasks/gitlab/encrypted' - stub_env('EDITOR', 'cat') - stub_warn_user_is_not_gitlab - FileUtils.mkdir_p('tmp/tests/enc/') - end - - after do - FileUtils.rm_rf('tmp/tests/enc/') - end - - describe ':show' do - it 'displays error when file does not exist' do - expect { run_rake_task('gitlab:encrypted:show', [encoded_file]) }.to output(/File '#{encoded_file}' does not exist. Use `rake gitlab:encrypted:edit\[#{encoded_file}\]` to change that./).to_stdout - end - - it 'outputs the unencrypted content when present' do - encrypted = Settings.encrypted(encoded_file, allow_in_safe_mode: true) - encrypted.write('somevalue') - expect { run_rake_task('gitlab:encrypted:show', [encoded_file]) }.to output(/somevalue/).to_stdout - end - end - - describe ':edit' do - it 'creates encrypted file' do - stub_env('EDITOR', 'echo "foo: bar" >') - expect { run_rake_task('gitlab:encrypted:edit', [encoded_file]) }.to output(/File encrypted and saved./).to_stdout - expect(File.exist?(encoded_file)).to be true - value = Settings.encrypted(encoded_file, allow_in_safe_mode: true) - expect(value.read.present?).to be true - expect(value.foo).to eq('bar') - end - end -end -- GitLab From c22f99602c7b52da0d7d0d1f5682b12af80e62f5 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Tue, 23 Jun 2020 10:51:05 -0700 Subject: [PATCH 8/9] Provide a helper function for the ldap secrets And update config spec to reflect latest changes --- config/settings.rb | 2 +- lib/gitlab/auth/ldap/config.rb | 12 +++++++++--- lib/gitlab/encrypted_configuration.rb | 6 ++---- lib/gitlab/encrypted_ldap_command.rb | 12 ++++++------ spec/lib/gitlab/encrypted_configuration_spec.rb | 10 +++------- spec/tasks/gitlab/ldap_rake_spec.rb | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/config/settings.rb b/config/settings.rb index b3ee36028bfb41..4c070317b46bc5 100644 --- a/config/settings.rb +++ b/config/settings.rb @@ -157,7 +157,7 @@ def encrypted(path, allow_in_safe_mode: false) Gitlab::EncryptedConfiguration.new( config_path: Settings.absolute(path), - key: Settings.attr_encrypted_db_key_base_truncated, + key: Settings.attr_encrypted_db_key_base_truncated ) end diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb index 0499f1b5bbf3be..d51774ff321132 100644 --- a/lib/gitlab/auth/ldap/config.rb +++ b/lib/gitlab/auth/ldap/config.rb @@ -53,6 +53,10 @@ def self.invalid_provider(provider) raise InvalidProvider.new("Unknown provider (#{provider}). Available providers: #{providers}") end + def self.encrypted_secrets(allow_in_safe_mode: false) + Settings.encrypted(Gitlab.config.ldap.secret_file, allow_in_safe_mode: allow_in_safe_mode) + end + def initialize(provider) if self.class.valid_provider?(provider) @provider = provider @@ -273,12 +277,14 @@ def auth_options } end + def secrets + self.class.encrypted_secrets[@provider.delete_prefix('ldap').to_sym] + end + def auth_password return options['password'] if options['password'] - if File.exist?(Gitlab.config.ldap.secret_file) - Settings.encrypted(Gitlab.config.ldap.secret_file)[@provider.delete_prefix('ldap').to_sym]&.fetch(:password)&.chomp - end + secrets&.fetch(:password)&.chomp end def omniauth_user_filter diff --git a/lib/gitlab/encrypted_configuration.rb b/lib/gitlab/encrypted_configuration.rb index 2eae02ef215365..8ff4491aeac69c 100644 --- a/lib/gitlab/encrypted_configuration.rb +++ b/lib/gitlab/encrypted_configuration.rb @@ -2,15 +2,13 @@ module Gitlab class EncryptedConfiguration < ActiveSupport::EncryptedConfiguration + attr_reader :key + def initialize(config_path: nil, key: nil) @content_path = Pathname.new(config_path).yield_self { |path| path.symlink? ? path.realpath : path } if config_path @key = key end - def key - @key - end - def read_env_key nil end diff --git a/lib/gitlab/encrypted_ldap_command.rb b/lib/gitlab/encrypted_ldap_command.rb index e7782f6ac37247..44d7140d4e8721 100644 --- a/lib/gitlab/encrypted_ldap_command.rb +++ b/lib/gitlab/encrypted_ldap_command.rb @@ -11,26 +11,26 @@ class << self alias_method :say, :puts def edit - file_path = Gitlab.config.ldap.secret_file + encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets(allow_in_safe_mode: true) editor = ENV['EDITOR'] || 'editor' catch_editing_exceptions do - Settings.encrypted(file_path, allow_in_safe_mode: true).write(encrypted_file_template) unless File.exist?(file_path) - Settings.encrypted(file_path, allow_in_safe_mode: true).change do |tmp_path| + encrypted.write(encrypted_file_template) unless File.exist?(encrypted.content_path) + encrypted.change do |tmp_path| system("#{editor} #{tmp_path}") end end puts "File encrypted and saved." rescue ActiveSupport::MessageEncryptor::InvalidMessage - puts "Couldn't decrypt #{file_path}. Perhaps you passed the wrong key?" + puts "Couldn't decrypt #{encrypted.content_path}. Perhaps you passed the wrong key?" end def show - encrypted = Settings.encrypted(Gitlab.config.ldap.secret_file, allow_in_safe_mode: true) + encrypted = Gitlab::Auth::Ldap::Config.encrypted_secrets(allow_in_safe_mode: true) - puts encrypted.read.presence || "File '#{Gitlab.config.ldap.secret_file}' does not exist. Use `rake gitlab:ldap:secret:edit` to change that." + puts encrypted.read.presence || "File '#{encrypted.content_path}' does not exist. Use `rake gitlab:ldap:secret:edit` to change that." end private diff --git a/spec/lib/gitlab/encrypted_configuration_spec.rb b/spec/lib/gitlab/encrypted_configuration_spec.rb index 710d3eea03f245..c1d8d96bfe39c1 100644 --- a/spec/lib/gitlab/encrypted_configuration_spec.rb +++ b/spec/lib/gitlab/encrypted_configuration_spec.rb @@ -18,11 +18,7 @@ context 'when provided key and config file' do let!(:config_tmp_dir) { Dir.mktmpdir('config-') } let(:credentials_config_path) { File.join(config_tmp_dir, 'credentials.yml.enc') } - let(:credentials_key_path) { File.join(config_tmp_dir, 'somekey.key') } - - before do - File.write(credentials_key_path, ActiveSupport::EncryptedConfiguration.generate_key) - end + let(:credentials_key) { ActiveSupport::EncryptedConfiguration.generate_key } after do FileUtils.rm_f(config_tmp_dir) @@ -30,7 +26,7 @@ describe '#read' do it 'reads yaml configuration' do - config = described_class.new(config_path: credentials_config_path, key_path: credentials_key_path) + config = described_class.new(config_path: credentials_config_path, key: credentials_key) config.write({ foo: { bar: true } }.to_yaml) expect(config.foo[:bar]).to be true @@ -39,7 +35,7 @@ describe '#change' do it 'changes yaml configuration' do - config = described_class.new(config_path: credentials_config_path, key_path: credentials_key_path) + config = described_class.new(config_path: credentials_config_path, key: credentials_key) config.write({ foo: { bar: true } }.to_yaml) config.change do |unencrypted_file| diff --git a/spec/tasks/gitlab/ldap_rake_spec.rb b/spec/tasks/gitlab/ldap_rake_spec.rb index c415766901f273..23ee54658389a5 100644 --- a/spec/tasks/gitlab/ldap_rake_spec.rb +++ b/spec/tasks/gitlab/ldap_rake_spec.rb @@ -32,7 +32,7 @@ describe ':show' do it 'displays error when file does not exist' do - expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/File '#{ldap_secret_file}' does not exist. Use `rake gitlab:ldap:secret:edit` to change that./).to_stdout + expect { run_rake_task('gitlab:ldap:secret:show') }.to output(/File .* does not exist. Use `rake gitlab:ldap:secret:edit` to change that./).to_stdout end it 'outputs the unencrypted content when present' do -- GitLab From 80a12cc076ceb56ae22014160b53b61ae761c7a3 Mon Sep 17 00:00:00 2001 From: DJ Mountney Date: Thu, 2 Jul 2020 16:28:39 -0700 Subject: [PATCH 9/9] Add test to confirm provided key is being used --- spec/lib/gitlab/encrypted_configuration_spec.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/spec/lib/gitlab/encrypted_configuration_spec.rb b/spec/lib/gitlab/encrypted_configuration_spec.rb index c1d8d96bfe39c1..a14d696486f028 100644 --- a/spec/lib/gitlab/encrypted_configuration_spec.rb +++ b/spec/lib/gitlab/encrypted_configuration_spec.rb @@ -24,6 +24,16 @@ FileUtils.rm_f(config_tmp_dir) end + describe '#write' do + it 'encrypts the file using the provided key' do + encryptor = ActiveSupport::MessageEncryptor.new([credentials_key].pack('H*'), cipher: 'aes-128-gcm') + config = described_class.new(config_path: credentials_config_path, key: credentials_key) + + config.write('sample-content') + expect(encryptor.decrypt_and_verify(File.read(credentials_config_path))).to eq('sample-content') + end + end + describe '#read' do it 'reads yaml configuration' do config = described_class.new(config_path: credentials_config_path, key: credentials_key) -- GitLab