diff --git a/.ci/docker-compose-file/docker-compose-redis-single-tcp.yaml b/.ci/docker-compose-file/docker-compose-redis-single-tcp.yaml index 6706fe84f..ec7283219 100644 --- a/.ci/docker-compose-file/docker-compose-redis-single-tcp.yaml +++ b/.ci/docker-compose-file/docker-compose-redis-single-tcp.yaml @@ -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 diff --git a/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml b/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml index 8f59e7a9e..2ea36affd 100644 --- a/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml +++ b/.ci/docker-compose-file/docker-compose-redis-single-tls.yaml @@ -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: diff --git a/.ci/docker-compose-file/redis/cluster-tcp/redis.conf b/.ci/docker-compose-file/redis/cluster-tcp/redis.conf index 79a0d8a73..6930bde1c 100644 --- a/.ci/docker-compose-file/redis/cluster-tcp/redis.conf +++ b/.ci/docker-compose-file/redis/cluster-tcp/redis.conf @@ -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 diff --git a/.ci/docker-compose-file/redis/cluster-tcp/users.acl b/.ci/docker-compose-file/redis/cluster-tcp/users.acl new file mode 100644 index 000000000..5bafe9f6d --- /dev/null +++ b/.ci/docker-compose-file/redis/cluster-tcp/users.acl @@ -0,0 +1,2 @@ +user default on >public ~* &* +@all +user test_user on >test_passwd ~* &* +@all diff --git a/.ci/docker-compose-file/redis/cluster-tls/redis.conf b/.ci/docker-compose-file/redis/cluster-tls/redis.conf index 3020f46a7..5d203de80 100644 --- a/.ci/docker-compose-file/redis/cluster-tls/redis.conf +++ b/.ci/docker-compose-file/redis/cluster-tls/redis.conf @@ -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 diff --git a/.ci/docker-compose-file/redis/cluster-tls/users.acl b/.ci/docker-compose-file/redis/cluster-tls/users.acl new file mode 100644 index 000000000..5bafe9f6d --- /dev/null +++ b/.ci/docker-compose-file/redis/cluster-tls/users.acl @@ -0,0 +1,2 @@ +user default on >public ~* &* +@all +user test_user on >test_passwd ~* &* +@all diff --git a/.ci/docker-compose-file/redis/sentinel-tcp/master.conf b/.ci/docker-compose-file/redis/sentinel-tcp/master.conf index 25940c887..a531d1b40 100644 --- a/.ci/docker-compose-file/redis/sentinel-tcp/master.conf +++ b/.ci/docker-compose-file/redis/sentinel-tcp/master.conf @@ -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 diff --git a/.ci/docker-compose-file/redis/sentinel-tcp/slave.conf b/.ci/docker-compose-file/redis/sentinel-tcp/slave.conf index 2c61aeb6c..4a7e240fc 100644 --- a/.ci/docker-compose-file/redis/sentinel-tcp/slave.conf +++ b/.ci/docker-compose-file/redis/sentinel-tcp/slave.conf @@ -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 diff --git a/.ci/docker-compose-file/redis/sentinel-tcp/users.acl b/.ci/docker-compose-file/redis/sentinel-tcp/users.acl new file mode 100644 index 000000000..5bafe9f6d --- /dev/null +++ b/.ci/docker-compose-file/redis/sentinel-tcp/users.acl @@ -0,0 +1,2 @@ +user default on >public ~* &* +@all +user test_user on >test_passwd ~* &* +@all diff --git a/.ci/docker-compose-file/redis/sentinel-tls/master.conf b/.ci/docker-compose-file/redis/sentinel-tls/master.conf index f55433f79..68e01f323 100644 --- a/.ci/docker-compose-file/redis/sentinel-tls/master.conf +++ b/.ci/docker-compose-file/redis/sentinel-tls/master.conf @@ -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 diff --git a/.ci/docker-compose-file/redis/sentinel-tls/slave.conf b/.ci/docker-compose-file/redis/sentinel-tls/slave.conf index d8758da51..25102d5ed 100644 --- a/.ci/docker-compose-file/redis/sentinel-tls/slave.conf +++ b/.ci/docker-compose-file/redis/sentinel-tls/slave.conf @@ -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 diff --git a/.ci/docker-compose-file/redis/sentinel-tls/users.acl b/.ci/docker-compose-file/redis/sentinel-tls/users.acl new file mode 100644 index 000000000..5bafe9f6d --- /dev/null +++ b/.ci/docker-compose-file/redis/sentinel-tls/users.acl @@ -0,0 +1,2 @@ +user default on >public ~* &* +@all +user test_user on >test_passwd ~* &* +@all diff --git a/.ci/docker-compose-file/redis/single-tcp/redis.conf b/.ci/docker-compose-file/redis/single-tcp/redis.conf new file mode 100644 index 000000000..1e5f629d2 --- /dev/null +++ b/.ci/docker-compose-file/redis/single-tcp/redis.conf @@ -0,0 +1,3 @@ +bind :: 0.0.0.0 +port 6379 +aclfile /usr/local/etc/redis/users.acl diff --git a/.ci/docker-compose-file/redis/single-tcp/users.acl b/.ci/docker-compose-file/redis/single-tcp/users.acl new file mode 100644 index 000000000..5bafe9f6d --- /dev/null +++ b/.ci/docker-compose-file/redis/single-tcp/users.acl @@ -0,0 +1,2 @@ +user default on >public ~* &* +@all +user test_user on >test_passwd ~* &* +@all diff --git a/.ci/docker-compose-file/redis/single-tls/redis.conf b/.ci/docker-compose-file/redis/single-tls/redis.conf new file mode 100644 index 000000000..e2d40bca6 --- /dev/null +++ b/.ci/docker-compose-file/redis/single-tls/redis.conf @@ -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" diff --git a/.ci/docker-compose-file/redis/single-tls/users.acl b/.ci/docker-compose-file/redis/single-tls/users.acl new file mode 100644 index 000000000..5bafe9f6d --- /dev/null +++ b/.ci/docker-compose-file/redis/single-tls/users.acl @@ -0,0 +1,2 @@ +user default on >public ~* &* +@all +user test_user on >test_passwd ~* &* +@all diff --git a/apps/emqx_bridge_redis/src/emqx_bridge_redis.app.src b/apps/emqx_bridge_redis/src/emqx_bridge_redis.app.src index b380bc86d..5b6163969 100644 --- a/apps/emqx_bridge_redis/src/emqx_bridge_redis.app.src +++ b/apps/emqx_bridge_redis/src/emqx_bridge_redis.app.src @@ -1,6 +1,6 @@ {application, emqx_bridge_redis, [ {description, "EMQX Enterprise Redis Bridge"}, - {vsn, "0.1.2"}, + {vsn, "0.1.3"}, {registered, []}, {applications, [ kernel, diff --git a/apps/emqx_bridge_redis/test/emqx_bridge_redis_SUITE.erl b/apps/emqx_bridge_redis/test/emqx_bridge_redis_SUITE.erl index 6a0248b67..0c17ab775 100644 --- a/apps/emqx_bridge_redis/test/emqx_bridge_redis_SUITE.erl +++ b/apps/emqx_bridge_redis/test/emqx_bridge_redis_SUITE.erl @@ -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), diff --git a/apps/emqx_redis/rebar.config b/apps/emqx_redis/rebar.config index c14536384..4e67e0986 100644 --- a/apps/emqx_redis/rebar.config +++ b/apps/emqx_redis/rebar.config @@ -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"}} ]}. diff --git a/apps/emqx_redis/src/emqx_redis.app.src b/apps/emqx_redis/src/emqx_redis.app.src index 294a642f5..23e13bb72 100644 --- a/apps/emqx_redis/src/emqx_redis.app.src +++ b/apps/emqx_redis/src/emqx_redis.app.src @@ -1,6 +1,6 @@ {application, emqx_redis, [ {description, "EMQX Redis Database Connector"}, - {vsn, "0.1.0"}, + {vsn, "0.1.1"}, {registered, []}, {applications, [ kernel, diff --git a/apps/emqx_redis/src/emqx_redis.erl b/apps/emqx_redis/src/emqx_redis.erl index ef89c3931..c76cbdfb4 100644 --- a/apps/emqx_redis/src/emqx_redis.erl +++ b/apps/emqx_redis/src/emqx_redis.erl @@ -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(), diff --git a/apps/emqx_redis/test/emqx_redis_SUITE.erl b/apps/emqx_redis/test/emqx_redis_SUITE.erl index c425f19d9..e03b05921 100644 --- a/apps/emqx_redis/test/emqx_redis_SUITE.erl +++ b/apps/emqx_redis/test/emqx_redis_SUITE.erl @@ -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" ++ " " ++ "" diff --git a/changes/ce/feat-11469.en.md b/changes/ce/feat-11469.en.md new file mode 100644 index 000000000..827fe3a87 --- /dev/null +++ b/changes/ce/feat-11469.en.md @@ -0,0 +1 @@ +Added support for specifying username in Redis authentication.