feat(redis): add support for `username` on AUTH command

Fixes https://emqx.atlassian.net/browse/EMQX-9911
This commit is contained in:
Paulo Zulato 2023-08-07 11:27:30 -03:00
parent fc3720627b
commit 2c458b62f5
23 changed files with 106 additions and 24 deletions

View File

@ -4,12 +4,11 @@ services:
redis_server:
container_name: redis
image: redis:${REDIS_TAG}
volumes:
- ./redis/single-tcp:/usr/local/etc/redis/
ports:
- "6379:6379"
command:
- redis-server
- "--bind 0.0.0.0 ::"
- --requirepass public
command: redis-server /usr/local/etc/redis/redis.conf
restart: always
networks:
- emqx_bridge

View File

@ -8,18 +8,10 @@ services:
- ./certs/server.crt:/etc/certs/redis.crt
- ./certs/server.key:/etc/certs/redis.key
- ./certs/ca.crt:/etc/certs/ca.crt
- ./redis/single-tls:/usr/local/etc/redis
ports:
- "6380:6380"
command:
- redis-server
- "--bind 0.0.0.0 ::"
- --requirepass public
- --tls-port 6380
- --tls-cert-file /etc/certs/redis.crt
- --tls-key-file /etc/certs/redis.key
- --tls-ca-cert-file /etc/certs/ca.crt
- --tls-protocols "TLSv1.3"
- --tls-ciphersuites "TLS_CHACHA20_POLY1305_SHA256"
command: redis-server /usr/local/etc/redis/redis.conf
restart: always
networks:
emqx_bridge:

View File

@ -1,10 +1,11 @@
bind :: 0.0.0.0
port 6379
requirepass public
cluster-enabled yes
masteruser default
masterauth public
aclfile /usr/local/etc/redis/users.acl
protected-mode no
daemonize no

View File

@ -0,0 +1,2 @@
user default on >public ~* &* +@all
user test_user on >test_passwd ~* &* +@all

View File

@ -1,10 +1,11 @@
bind :: 0.0.0.0
port 6379
requirepass public
cluster-enabled yes
masteruser default
masterauth public
aclfile /usr/local/etc/redis/users.acl
tls-port 6389
tls-cert-file /etc/certs/cert.pem

View File

@ -0,0 +1,2 @@
user default on >public ~* &* +@all
user test_user on >test_passwd ~* &* +@all

View File

@ -1,6 +1,6 @@
bind :: 0.0.0.0
port 6379
requirepass public
aclfile /usr/local/etc/redis/users.acl
protected-mode no
daemonize no

View File

@ -1,9 +1,10 @@
bind :: 0.0.0.0
port 6379
requirepass public
replicaof redis-sentinel-master 6379
masteruser default
masterauth public
aclfile /usr/local/etc/redis/users.acl
protected-mode no
daemonize no

View File

@ -0,0 +1,2 @@
user default on >public ~* &* +@all
user test_user on >test_passwd ~* &* +@all

View File

@ -1,6 +1,6 @@
bind :: 0.0.0.0
port 6379
requirepass public
aclfile /usr/local/etc/redis/users.acl
tls-port 6389
tls-cert-file /etc/certs/cert.pem

View File

@ -1,9 +1,10 @@
bind :: 0.0.0.0
port 6379
requirepass public
replicaof redis-sentinel-tls-master 6389
masteruser default
masterauth public
aclfile /usr/local/etc/redis/users.acl
tls-port 6389
tls-replication yes

View File

@ -0,0 +1,2 @@
user default on >public ~* &* +@all
user test_user on >test_passwd ~* &* +@all

View File

@ -0,0 +1,3 @@
bind :: 0.0.0.0
port 6379
aclfile /usr/local/etc/redis/users.acl

View File

@ -0,0 +1,2 @@
user default on >public ~* &* +@all
user test_user on >test_passwd ~* &* +@all

View File

@ -0,0 +1,9 @@
bind :: 0.0.0.0
aclfile /usr/local/etc/redis/users.acl
tls-port 6380
tls-cert-file /etc/certs/redis.crt
tls-key-file /etc/certs/redis.key
tls-ca-cert-file /etc/certs/ca.crt
tls-protocols "TLSv1.3"
tls-ciphersuites "TLS_CHACHA20_POLY1305_SHA256"

View File

@ -0,0 +1,2 @@
user default on >public ~* &* +@all
user test_user on >test_passwd ~* &* +@all

View File

@ -1,6 +1,6 @@
{application, emqx_bridge_redis, [
{description, "EMQX Enterprise Redis Bridge"},
{vsn, "0.1.2"},
{vsn, "0.1.3"},
{registered, []},
{applications, [
kernel,

View File

@ -30,6 +30,11 @@
<<"local_topic">> => <<"local_topic/#">>
}).
-define(USERNAME_PASSWORD_AUTH_OPTS, #{
<<"username">> => <<"test_user">>,
<<"password">> => <<"test_passwd">>
}).
-define(BATCH_SIZE, 5).
-define(PROXY_HOST, "toxiproxy").
@ -319,6 +324,22 @@ t_permanent_error(_Config) ->
),
{ok, _} = emqx_bridge:remove(Type, Name).
t_auth_username_password(_Config) ->
Name = <<"mybridge">>,
Type = <<"redis_single">>,
ResourceId = emqx_bridge_resource:resource_id(Type, Name),
BridgeConfig = username_password_redis_bridge_config(),
?assertMatch(
{ok, _},
emqx_bridge:create(Type, Name, BridgeConfig)
),
?WAIT(
{ok, connected},
emqx_resource:health_check(ResourceId),
5
),
{ok, _} = emqx_bridge:remove(Type, Name).
t_create_disconnected(Config) ->
Name = <<"toxic_bridge">>,
Type = <<"redis_single">>,
@ -528,6 +549,19 @@ toxiproxy_redis_bridge_config() ->
},
maps:merge(Conf0, ?COMMON_REDIS_OPTS).
username_password_redis_bridge_config() ->
Conf0 = ?REDIS_TOXYPROXY_CONNECT_CONFIG#{
<<"resource_opts">> => #{
<<"query_mode">> => <<"sync">>,
<<"worker_pool_size">> => <<"1">>,
<<"batch_size">> => integer_to_binary(?BATCH_SIZE),
<<"health_check_interval">> => <<"1s">>,
<<"start_timeout">> => <<"15s">>
}
},
Conf1 = maps:merge(Conf0, ?COMMON_REDIS_OPTS),
maps:merge(Conf1, ?USERNAME_PASSWORD_AUTH_OPTS).
invalid_command_bridge_config() ->
#{redis_single := #{tcp := Conf0}} = redis_connect_configs(),
Conf1 = maps:merge(Conf0, ?COMMON_REDIS_OPTS),

View File

@ -3,7 +3,7 @@
{erl_opts, [debug_info]}.
{deps, [
%% NOTE: mind ecpool version when updating eredis_cluster version
{eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.8.1"}}},
{eredis_cluster, {git, "https://github.com/emqx/eredis_cluster", {tag, "0.8.2"}}},
{emqx_connector, {path, "../../apps/emqx_connector"}},
{emqx_resource, {path, "../../apps/emqx_resource"}}
]}.

View File

@ -1,6 +1,6 @@
{application, emqx_redis, [
{description, "EMQX Redis Database Connector"},
{vsn, "0.1.0"},
{vsn, "0.1.1"},
{registered, []},
{applications, [
kernel,

View File

@ -146,6 +146,7 @@ on_start(
Opts =
[
{pool_size, PoolSize},
{username, maps:get(username, Config, undefined)},
{password, maps:get(password, Config, "")},
{auto_reconnect, ?AUTO_RECONNECT_INTERVAL}
] ++ Database ++ Servers,
@ -292,6 +293,7 @@ connect(Opts) ->
redis_fields() ->
[
{pool_size, fun emqx_connector_schema_lib:pool_size/1},
{username, fun emqx_connector_schema_lib:username/1},
{password, fun emqx_connector_schema_lib:password/1},
{database, #{
type => non_neg_integer(),

View File

@ -137,6 +137,31 @@ perform_lifecycle_check(ResourceId, InitialConfig, RedisCommand) ->
#{timeout => 500}
)
),
% check authentication methods
?assertEqual(
{ok, <<"OK">>},
emqx_resource:query(ResourceId, {cmd, ["AUTH", "public"]})
),
?assertEqual(
{error, <<"WRONGPASS invalid username-password pair or user is disabled.">>},
emqx_resource:query(ResourceId, {cmd, ["AUTH", "test_passwd"]})
),
?assertEqual(
{ok, <<"OK">>},
emqx_resource:query(ResourceId, {cmd, ["AUTH", "test_user", "test_passwd"]})
),
?assertEqual(
{error, <<"WRONGPASS invalid username-password pair or user is disabled.">>},
emqx_resource:query(ResourceId, {cmd, ["AUTH", "test_user", "public"]})
),
?assertEqual(
{error, <<"WRONGPASS invalid username-password pair or user is disabled.">>},
emqx_resource:query(ResourceId, {cmd, ["AUTH", "wrong_user", "test_passwd"]})
),
?assertEqual(
{error, <<"WRONGPASS invalid username-password pair or user is disabled.">>},
emqx_resource:query(ResourceId, {cmd, ["AUTH", "wrong_user", "public"]})
),
?assertEqual(ok, emqx_resource:stop(ResourceId)),
% Resource will be listed still, but state will be changed and healthcheck will fail
% as the worker no longer exists.
@ -186,7 +211,8 @@ redis_config_sentinel() ->
" redis_type = ~s\n" ++
MaybeSentinel ++
MaybeDatabase ++
" password = public\n" ++
" username = test_user\n" ++
" password = test_passwd\n" ++
" ~s = \"~s:~b\"\n" ++
" " ++
""

View File

@ -0,0 +1 @@
Added support for specifying username in Redis authentication.