diff --git a/files/gitlab-config-template/gitlab.rb.template b/files/gitlab-config-template/gitlab.rb.template index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvbmZpZy10ZW1wbGF0ZS9naXRsYWIucmIudGVtcGxhdGU=..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvbmZpZy10ZW1wbGF0ZS9naXRsYWIucmIudGVtcGxhdGU= 100644 --- a/files/gitlab-config-template/gitlab.rb.template +++ b/files/gitlab-config-template/gitlab.rb.template @@ -2811,6 +2811,9 @@ ##! Uncomment to change default port # sentinel['port'] = 26379 +##! Uncomment to require a Sentinel password. This may be different from the Redis master password. +# sentinel['password'] = 'sentinel-password-goes-here' + #### Support to run sentinels in a Docker or NAT environment #####! Docs: https://redis.io/topics/sentinel#sentinel-docker-nat-and-possible-issues # In an standard case, Sentinel will run in the same network service as Redis, so the same IP will be announce for Redis and Sentinel diff --git a/files/gitlab-cookbooks/gitlab-ee/attributes/default.rb b/files/gitlab-cookbooks/gitlab-ee/attributes/default.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvYXR0cmlidXRlcy9kZWZhdWx0LnJi..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvYXR0cmlidXRlcy9kZWZhdWx0LnJi 100644 --- a/files/gitlab-cookbooks/gitlab-ee/attributes/default.rb +++ b/files/gitlab-cookbooks/gitlab-ee/attributes/default.rb @@ -21,6 +21,7 @@ default['gitlab']['sentinel']['log_directory'] = '/var/log/gitlab/sentinel' default['gitlab']['sentinel']['ha'] = false default['gitlab']['sentinel']['port'] = 26379 +default['gitlab']['sentinel']['password'] = nil default['gitlab']['sentinel']['quorum'] = 1 default['gitlab']['sentinel']['announce_ip'] = nil default['gitlab']['sentinel']['announce_port'] = nil diff --git a/files/gitlab-cookbooks/gitlab-ee/libraries/sentinel_helper.rb b/files/gitlab-cookbooks/gitlab-ee/libraries/sentinel_helper.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvbGlicmFyaWVzL3NlbnRpbmVsX2hlbHBlci5yYg==..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvbGlicmFyaWVzL3NlbnRpbmVsX2hlbHBlci5yYg== 100644 --- a/files/gitlab-cookbooks/gitlab-ee/libraries/sentinel_helper.rb +++ b/files/gitlab-cookbooks/gitlab-ee/libraries/sentinel_helper.rb @@ -30,4 +30,10 @@ return unless OmnibusHelper.new(@node).service_up?('sentinel') command = "/opt/gitlab/embedded/bin/redis-cli -h #{sentinel['bind']} -p #{sentinel['port']} INFO" + env = + if sentinel['password'] + { 'REDISCLI_AUTH' => sentinel['password'] } + else + {} + end @@ -33,5 +39,6 @@ - command_output = VersionHelper.version(command) + command_output = VersionHelper.version(command, env: env) + raise "Execution of the command `#{command}` failed" unless command_output version_match = command_output.match(/redis_version:(?<redis_version>\d*\.\d*\.\d*)/) diff --git a/files/gitlab-cookbooks/gitlab-ee/resources/sentinel_service.rb b/files/gitlab-cookbooks/gitlab-ee/resources/sentinel_service.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvcmVzb3VyY2VzL3NlbnRpbmVsX3NlcnZpY2UucmI=..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvcmVzb3VyY2VzL3NlbnRpbmVsX3NlcnZpY2UucmI= 100644 --- a/files/gitlab-cookbooks/gitlab-ee/resources/sentinel_service.rb +++ b/files/gitlab-cookbooks/gitlab-ee/resources/sentinel_service.rb @@ -76,6 +76,7 @@ ) notifies :restart, 'runit_service[sentinel]', :immediately if omnibus_helper.should_notify?('redis') only_if { new_resource.config_path } + sensitive true end ruby_block 'warn pending sentinel restart' do diff --git a/files/gitlab-cookbooks/gitlab-ee/templates/default/sentinel.conf.erb b/files/gitlab-cookbooks/gitlab-ee/templates/default/sentinel.conf.erb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvdGVtcGxhdGVzL2RlZmF1bHQvc2VudGluZWwuY29uZi5lcmI=..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWItZWUvdGVtcGxhdGVzL2RlZmF1bHQvc2VudGluZWwuY29uZi5lcmI= 100644 --- a/files/gitlab-cookbooks/gitlab-ee/templates/default/sentinel.conf.erb +++ b/files/gitlab-cookbooks/gitlab-ee/templates/default/sentinel.conf.erb @@ -88,6 +88,21 @@ # Default is 30 seconds. sentinel down-after-milliseconds <%= @redis['master_name'] %> <%= @sentinel['down_after_milliseconds'] %> +# requirepass <password> +# +# You can configure Sentinel itself to require a password, however when doing +# so Sentinel will try to authenticate with the same password to all the +# other Sentinels. So you need to configure all your Sentinels in a given +# group with the same "requirepass" password. Check the following documentation +# for more info: https://redis.io/topics/sentinel +# +# IMPORTANT NOTE: starting with Redis 6.2 "requirepass" is a compatibility +# layer on top of the ACL system. The option effect will be just setting +# the password for the default user. Clients will still authenticate using +# AUTH <password> as usually, or more explicitly with AUTH default <password> +# if they follow the new protocol: both will work. +<%= %Q(requirepass "#{@sentinel['password']}") if @sentinel['password'] %> + # sentinel parallel-syncs <master-name> <numreplicas> # # How many replicas we can reconfigure to point to the new replica simultaneously diff --git a/files/gitlab-cookbooks/gitlab/libraries/redis.rb b/files/gitlab-cookbooks/gitlab/libraries/redis.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzLnJi..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzLnJi 100644 --- a/files/gitlab-cookbooks/gitlab/libraries/redis.rb +++ b/files/gitlab-cookbooks/gitlab/libraries/redis.rb @@ -21,6 +21,7 @@ class << self def parse_variables parse_redis_settings + parse_redis_sentinel_settings parse_rename_commands end @@ -49,6 +50,16 @@ raise "redis 'master_password' is not defined" unless Gitlab['redis']['master_password'] end + def parse_redis_sentinel_settings + return unless RedisHelper::Checks.sentinel_daemon_enabled? + + Gitlab['gitlab_rails']['redis_sentinels_password'] ||= Gitlab['sentinel']['password'] + + RedisHelper::REDIS_INSTANCES.each do |instance| + Gitlab['gitlab_rails']["redis_#{instance}_sentinels_password"] ||= Gitlab['sentinel']['password'] + end + end + def parse_rename_commands return unless Gitlab['redis']['rename_commands'].nil? diff --git a/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb b/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX2hlbHBlci5yYg==..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX2hlbHBlci5yYg== 100644 --- a/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb +++ b/files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb @@ -2,6 +2,7 @@ require 'cgi' class RedisHelper + REDIS_INSTANCES = %w[cache queues shared_state trace_chunks rate_limiting sessions repository_cache cluster_rate_limiting].freeze ALLOWED_REDIS_CLUSTER_INSTANCE = %w[rate_limiting cluster_rate_limiting].freeze def initialize(node) diff --git a/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb b/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMucmI=..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_ZmlsZXMvZ2l0bGFiLWNvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHMucmI= 100644 --- a/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb +++ b/files/gitlab-cookbooks/gitlab/recipes/gitlab-rails.rb @@ -263,7 +263,7 @@ sensitive true end -%w(cache queues shared_state trace_chunks rate_limiting sessions repository_cache cluster_rate_limiting).each do |instance| +RedisHelper::REDIS_INSTANCES.each do |instance| filename = "redis.#{instance}.yml" url = node['gitlab']['gitlab_rails']["redis_#{instance}_instance"] sentinels = node['gitlab']['gitlab_rails']["redis_#{instance}_sentinels"] diff --git a/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_helper_spec.rb b/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_helper_spec.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvbGlicmFyaWVzL3NlbnRpbmVsX2hlbHBlcl9zcGVjLnJi..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvbGlicmFyaWVzL3NlbnRpbmVsX2hlbHBlcl9zcGVjLnJi 100644 --- a/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_helper_spec.rb +++ b/spec/chef/cookbooks/gitlab-ee/libraries/sentinel_helper_spec.rb @@ -5,6 +5,48 @@ subject { described_class.new(chef_run.node) } before { allow(Gitlab).to receive(:[]).and_call_original } + context '#running_version' do + let(:cmd) { '/opt/gitlab/embedded/bin/redis-cli -h 0.0.0.0 -p 26379 INFO' } + let(:status_success) { double("status", stdout: 'redis_version:1.2.3', exitstatus: 0) } + let(:gitlab_rb) do + { + redis: { + master_ip: '127.0.0.1', + master_password: 'masterpass' + }, + sentinel: { + enable: true, + password: sentinel_password + } + } + end + + before do + allow_any_instance_of(OmnibusHelper).to receive(:service_up?).with('sentinel').and_return(true) + stub_gitlab_rb(gitlab_rb) + end + + context 'with a Sentinel password defined' do + let(:sentinel_password) { 'some password' } + + it 'sets REDISCLI_AUTH' do + expect(VersionHelper).to receive(:do_shell_out).with(cmd, env: { 'REDISCLI_AUTH' => sentinel_password }).and_return(status_success) + + expect(subject.running_version).to eq('1.2.3') + end + end + + context 'without a Sentinel password defined' do + let(:sentinel_password) { nil } + + it 'does not set REDISCLI_AUTH' do + expect(VersionHelper).to receive(:do_shell_out).with(cmd, env: {}).and_return(status_success) + + expect(subject.running_version).to eq('1.2.3') + end + end + end + context '#myid' do context 'when retrieving from config' do it 'fails when myid is not 40 hex-characters long' do diff --git a/spec/chef/cookbooks/gitlab-ee/recipes/sentinel_spec.rb b/spec/chef/cookbooks/gitlab-ee/recipes/sentinel_spec.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9zZW50aW5lbF9zcGVjLnJi..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWItZWUvcmVjaXBlcy9zZW50aW5lbF9zcGVjLnJi 100644 --- a/spec/chef/cookbooks/gitlab-ee/recipes/sentinel_spec.rb +++ b/spec/chef/cookbooks/gitlab-ee/recipes/sentinel_spec.rb @@ -60,6 +60,7 @@ expect(content).to match(%r{sentinel failover-timeout gitlab-redis 60000}) expect(content).to match(%r{sentinel auth-pass gitlab-redis blahblahblah}) expect(content).not_to match(%r{^tls}) + expect(content).not_to match(%r{^requirepass}) expect(content).to match(%r{SENTINEL resolve-hostnames no}) expect(content).to match(%r{SENTINEL announce-hostnames no}) } @@ -164,6 +165,22 @@ } end end + + context 'user sets sentinel password' do + before do + stub_gitlab_rb( + sentinel: { + password: 'some pass' + } + ) + end + + it 'enables requirepass' do + expect(chef_run).to render_file(sentinel_conf).with_content { |content| + expect(content).to match(%r{requirepass "some pass"}) + } + end + end end context 'with tls settings specified' do diff --git a/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb b/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3NwZWMucmI=..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvbGlicmFyaWVzL3JlZGlzX3NwZWMucmI= 100644 --- a/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb +++ b/spec/chef/cookbooks/gitlab/libraries/redis_spec.rb @@ -161,6 +161,33 @@ expect(node['redis']['master_password']).to eq redis_password end + it 'instance Sentinel passwords are nil' do + RedisHelper::REDIS_INSTANCES.each do |instance| + expect(node['gitlab']['gitlab_rails']["redis_#{instance}_sentinels_password"]).to be_nil + end + end + + context 'when sentinel password is defined' do + before do + stub_gitlab_rb( + sentinel: { + enable: true, + password: 'sentinel pass!' + }, + redis: { + password: redis_password, + master_ip: '10.0.0.0' + } + ) + end + + it 'instance Sentinel passwords are autofilled based on Sentinel password' do + RedisHelper::REDIS_INSTANCES.each do |instance| + expect(node['gitlab']['gitlab_rails']["redis_#{instance}_sentinels_password"]).to eq('sentinel pass!') + end + end + end + context 'announce_ip is defined' do let(:redis_port) { 6379 } let(:redis_announce_ip) { '10.10.10.10' } diff --git a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb index 57790f1f1cf8e5715ece550a5b3ba159465a8c8d_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHNfc3BlYy5yYg==..e43c9979b55057c16e64bf29a2d1d60bbe28af7e_c3BlYy9jaGVmL2Nvb2tib29rcy9naXRsYWIvcmVjaXBlcy9naXRsYWItcmFpbHNfc3BlYy5yYg== 100644 --- a/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb +++ b/spec/chef/cookbooks/gitlab/recipes/gitlab-rails_spec.rb @@ -4,7 +4,7 @@ using RSpec::Parameterized::TableSyntax let(:chef_run) { ChefSpec::SoloRunner.new(step_into: %w(templatesymlink runit_service)).converge('gitlab::default') } - let(:redis_instances) { %w(cache queues shared_state trace_chunks rate_limiting sessions repository_cache) } + let(:redis_instances) { RedisHelper::REDIS_INSTANCES } let(:redis_cluster_instances) { %w(cluster_rate_limiting) } let(:config_dir) { '/var/opt/gitlab/gitlab-rails/etc/' } let(:default_vars) do