From 5d458ed45bfbb08d1b1a37c7daa127dd4f5a5b4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20J=C3=B8rgensen?= Date: Wed, 15 May 2024 13:48:17 +0200 Subject: [PATCH] (#527) Add masteruser parameter Enable setting the masteruser parameter which was introduced in Redis 6+ to be able to connect using the new ACL rules. --- README.md | 10 ++++++++++ REFERENCE.md | 31 +++++++++++++++++++++++++++-- manifests/init.pp | 5 ++++- manifests/instance.pp | 6 +++++- manifests/sentinel.pp | 9 +++++++++ spec/classes/redis_sentinel_spec.rb | 4 ++++ spec/classes/redis_spec.rb | 14 +++++++++++++ templates/redis-sentinel.conf.erb | 3 +++ templates/redis.conf.epp | 13 ++++++++++++ 9 files changed, 91 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 32e6fa48..818643d5 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,16 @@ class { 'redis': } ``` +With ACL authentication + +```puppet +class { 'redis': + bind => '10.0.1.1', + masterauth => 'secret', + masteruser => 'username', +} +``` + ### Slave node ```puppet diff --git a/REFERENCE.md b/REFERENCE.md index c2b5a708..f793b818 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -122,6 +122,7 @@ The following parameters are available in the `redis` class: * [`manage_package`](#-redis--manage_package) * [`managed_by_cluster_manager`](#-redis--managed_by_cluster_manager) * [`masterauth`](#-redis--masterauth) +* [`masteruser`](#-redis--masteruser) * [`maxclients`](#-redis--maxclients) * [`maxmemory`](#-redis--maxmemory) * [`maxmemory_policy`](#-redis--maxmemory_policy) @@ -532,7 +533,15 @@ Default value: `false` Data type: `Optional[Variant[String[1], Sensitive[String[1]], Deferred]]` -If the master is password protected (using the "requirepass" configuration +If the master is password protected (using the "requirepass" configuration) + +Default value: `undef` + +##### `masteruser` + +Data type: `Optional[Variant[String[1], Sensitive[String[1]], Deferred]]` + +If the master is password protected and a user is defined (using the "user" configuration) Default value: `undef` @@ -1514,6 +1523,7 @@ class {'redis::sentinel': The following parameters are available in the `redis::sentinel` class: * [`auth_pass`](#-redis--sentinel--auth_pass) +* [`auth_user`](#-redis--sentinel--auth_user) * [`config_file`](#-redis--sentinel--config_file) * [`config_file_orig`](#-redis--sentinel--config_file_orig) * [`config_file_mode`](#-redis--sentinel--config_file_mode) @@ -1563,6 +1573,14 @@ The password to use to authenticate with the master and slaves. Default value: `undef` +##### `auth_user` + +Data type: `Optional[Variant[String[1], Sensitive[String[1]]]]` + +The username to use to authenticate with the master and slaves. + +Default value: `undef` + ##### `config_file` Data type: `Stdlib::Absolutepath` @@ -1953,6 +1971,7 @@ The following parameters are available in the `redis::instance` defined type: * [`managed_by_cluster_manager`](#-redis--instance--managed_by_cluster_manager) * [`manage_service_file`](#-redis--instance--manage_service_file) * [`masterauth`](#-redis--instance--masterauth) +* [`masteruser`](#-redis--instance--masteruser) * [`maxclients`](#-redis--instance--maxclients) * [`maxmemory`](#-redis--instance--maxmemory) * [`maxmemory_policy`](#-redis--instance--maxmemory_policy) @@ -2305,10 +2324,18 @@ Default value: `true` Data type: `Optional[Variant[String[1], Sensitive[String[1]], Deferred]]` -If the master is password protected (using the "requirepass" configuration +If the master is password protected (using the "requirepass" configuration) Default value: `$redis::masterauth` +##### `masteruser` + +Data type: `Optional[Variant[String[1], Sensitive[String[1]], Deferred]]` + +If the master is password protected and a user is defined (using the "user" configuration) + +Default value: `$redis::masteruser` + ##### `maxclients` Data type: `Integer[1]` diff --git a/manifests/init.pp b/manifests/init.pp index e48ed67c..f19f8d79 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -95,7 +95,9 @@ # @param managed_by_cluster_manager # Choose if redis will be managed by a cluster manager such as pacemaker or rgmanager # @param masterauth -# If the master is password protected (using the "requirepass" configuration +# If the master is password protected (using the "requirepass" configuration) +# @param masteruser +# If the master is password protected and a user is defined (using the "user" configuration) # @param maxclients # Set the max number of connected clients at the same time. # @param maxmemory @@ -392,6 +394,7 @@ Boolean $manage_package = true, Boolean $manage_repo = false, Optional[Variant[String[1], Sensitive[String[1]], Deferred]] $masterauth = undef, + Optional[Variant[String[1], Sensitive[String[1]], Deferred]] $masteruser = undef, Integer[1] $maxclients = 10000, $maxmemory = undef, Optional[Redis::MemoryPolicy] $maxmemory_policy = undef, diff --git a/manifests/instance.pp b/manifests/instance.pp index 3b18cfca..028eb0c8 100644 --- a/manifests/instance.pp +++ b/manifests/instance.pp @@ -74,7 +74,9 @@ # @param manage_service_file # Determine if the systemd service file should be managed # @param masterauth -# If the master is password protected (using the "requirepass" configuration +# If the master is password protected (using the "requirepass" configuration) +# @param masteruser +# If the master is password protected and a user is defined (using the "user" configuration) # @param maxclients # Set the max number of connected clients at the same time. # @param maxmemory @@ -325,6 +327,7 @@ Stdlib::Filemode $log_dir_mode = $redis::log_dir_mode, Redis::LogLevel $log_level = $redis::log_level, Optional[Variant[String[1], Sensitive[String[1]], Deferred]] $masterauth = $redis::masterauth, + Optional[Variant[String[1], Sensitive[String[1]], Deferred]] $masteruser = $redis::masteruser, Integer[1] $maxclients = $redis::maxclients, Optional[Variant[Integer, String]] $maxmemory = $redis::maxmemory, Optional[Redis::MemoryPolicy] $maxmemory_policy = $redis::maxmemory_policy, @@ -526,6 +529,7 @@ slaveof => $slaveof, replicaof => $replicaof, masterauth => $masterauth, + masteruser => $masteruser, slave_serve_stale_data => $slave_serve_stale_data, slave_read_only => $slave_read_only, repl_announce_ip => $repl_announce_ip, diff --git a/manifests/sentinel.pp b/manifests/sentinel.pp index 2813802b..ea956e3b 100644 --- a/manifests/sentinel.pp +++ b/manifests/sentinel.pp @@ -3,6 +3,9 @@ # @param auth_pass # The password to use to authenticate with the master and slaves. # +# @param auth_user +# The username to use to authenticate with the master and slaves. +# # @param config_file # The location and name of the sentinel config file. # @@ -147,6 +150,7 @@ # class redis::sentinel ( Optional[Variant[String[1], Sensitive[String[1]]]] $auth_pass = undef, + Optional[Variant[String[1], Sensitive[String[1]]]] $auth_user = undef, Stdlib::Absolutepath $config_file = $redis::params::sentinel_config_file, Stdlib::Absolutepath $config_file_orig = $redis::params::sentinel_config_file_orig, Stdlib::Filemode $config_file_mode = '0644', @@ -193,6 +197,11 @@ } else { $auth_pass } + $auth_user_unsensitive = if $auth_user =~ Sensitive { + $auth_user.unwrap + } else { + $auth_user + } contain 'redis' diff --git a/spec/classes/redis_sentinel_spec.rb b/spec/classes/redis_sentinel_spec.rb index 945d8561..9b450024 100644 --- a/spec/classes/redis_sentinel_spec.rb +++ b/spec/classes/redis_sentinel_spec.rb @@ -110,6 +110,7 @@ class { 'redis': { sentinel_tls_port: 26_380, auth_pass: 'password', + auth_user: 'username', sentinel_bind: '192.0.2.10', protected_mode: false, master_name: 'cow', @@ -151,6 +152,7 @@ class { 'redis': sentinel parallel-syncs cow 1 sentinel failover-timeout cow 28000 sentinel auth-pass cow password + sentinel auth-user cow username sentinel notification-script cow /path/to/bar.sh sentinel client-reconfig-script cow /path/to/foo.sh @@ -177,6 +179,7 @@ class { 'redis': let(:params) do { auth_pass: 'password', + auth_user: 'username', sentinel_bind: ['192.0.2.10', '192.168.1.1'], master_name: 'cow', down_after: 6000, @@ -203,6 +206,7 @@ class { 'redis': sentinel parallel-syncs cow 1 sentinel failover-timeout cow 28000 sentinel auth-pass cow password + sentinel auth-user cow username sentinel notification-script cow /path/to/bar.sh sentinel client-reconfig-script cow /path/to/foo.sh diff --git a/spec/classes/redis_spec.rb b/spec/classes/redis_spec.rb index b0cae74a..fed3e39f 100644 --- a/spec/classes/redis_spec.rb +++ b/spec/classes/redis_spec.rb @@ -523,6 +523,20 @@ class { 'redis': } end + describe 'with parameter masteruser' do + let(:params) do + { + masteruser: '_VALUE_' + } + end + + it { + is_expected.to contain_file(config_file_orig).with( + 'content' => %r{masteruser.*_VALUE_} + ) + } + end + describe 'with parameter maxclients' do let(:params) do { diff --git a/templates/redis-sentinel.conf.erb b/templates/redis-sentinel.conf.erb index 283743c1..ed6c9a20 100644 --- a/templates/redis-sentinel.conf.erb +++ b/templates/redis-sentinel.conf.erb @@ -27,6 +27,9 @@ sentinel failover-timeout <%= @master_name %> <%= @failover_timeout %> <% if @auth_pass_unsensitive -%> sentinel auth-pass <%= @master_name %> <%= @auth_pass_unsensitive %> <% end -%> +<% if @auth_user_unsensitive -%> +sentinel auth-user <%= @master_name %> <%= @auth_user_unsensitive %> +<% end -%> <% if @notification_script -%> sentinel notification-script <%= @master_name %> <%= @notification_script %> <% end -%> diff --git a/templates/redis.conf.epp b/templates/redis.conf.epp index a14f7060..ece21e8e 100644 --- a/templates/redis.conf.epp +++ b/templates/redis.conf.epp @@ -23,6 +23,7 @@ Optional[String[1]] $slaveof, Optional[String[1]] $replicaof, Optional[Variant[String[1], Sensitive[String[1]]]] $masterauth, + Optional[Variant[String[1], Sensitive[String[1]]]] $masteruser, Boolean $slave_serve_stale_data, Boolean $slave_read_only, Optional[Stdlib::Host] $repl_announce_ip, @@ -411,6 +412,18 @@ dir <%= $workdir %> # masterauth <% if $masterauth { -%>masterauth <%= $masterauth %><% } -%> +# However this is not enough if you are using Redis ACLs (for Redis version +# 6 or greater), and the default user is not capable of running the PSYNC +# command and/or other commands needed for replication. In this case it's +# better to configure a special user to use with replication, and specify the +# username configuration as such: +# +# masteruser +<% if $masteruser { -%>masteruser <%= $masteruser %><% } -%> + +# When username is specified, the replica will authenticate against its +# master using the new AUTH form: AUTH . + # When a slave loses the connection with the master, or when the replication # is still in progress, the slave can act in two different ways: #