From 87465036e0b2998a43e8e7bf96f6512fe5edd945 Mon Sep 17 00:00:00 2001 From: spacewander Date: Thu, 8 Dec 2022 15:28:42 +0800 Subject: [PATCH] feat(gRPC): support user/password Signed-off-by: spacewander --- apisix/cli/snippet.lua | 4 - apisix/core/etcd.lua | 3 +- ci/linux_apisix_current_luarocks_runner.sh | 2 + ci/linux_openresty_common_runner.sh | 2 + t/cli/test_etcd_grpc.sh | 70 +++++++++++++ t/core/etcd-grpc-auth-fail.t | 106 ++++++++++++++++++++ t/core/etcd-grpc-auth.t | 108 +++++++++++++++++++++ 7 files changed, 289 insertions(+), 6 deletions(-) create mode 100755 t/cli/test_etcd_grpc.sh create mode 100644 t/core/etcd-grpc-auth-fail.t create mode 100644 t/core/etcd-grpc-auth.t diff --git a/apisix/cli/snippet.lua b/apisix/cli/snippet.lua index 6225c213fea54..9569ca97b699e 100644 --- a/apisix/cli/snippet.lua +++ b/apisix/cli/snippet.lua @@ -104,10 +104,6 @@ server { local function is_grpc_used(env, etcd) local is_grpc_available = env.use_apisix_base - if etcd.user then - -- TODO: support user/password - is_grpc_available = false - end return is_grpc_available and etcd.use_grpc end diff --git a/apisix/core/etcd.lua b/apisix/core/etcd.lua index 9e6786c86b027..1b66249730c57 100644 --- a/apisix/core/etcd.lua +++ b/apisix/core/etcd.lua @@ -73,8 +73,7 @@ local function _new(etcd_conf) end if etcd_conf.use_grpc then - -- TODO: let lua-resty-etcd support more use cases - if etcd.user or ngx_get_phase() == "init" then + if ngx_get_phase() == "init" then etcd_conf.use_grpc = false else local ok = pcall(require, "resty.grpc") diff --git a/ci/linux_apisix_current_luarocks_runner.sh b/ci/linux_apisix_current_luarocks_runner.sh index 7fa165fb75021..f605b6d897c57 100755 --- a/ci/linux_apisix_current_luarocks_runner.sh +++ b/ci/linux_apisix_current_luarocks_runner.sh @@ -70,6 +70,8 @@ script() { ulimit -n -S ulimit -n -H + git clone https://github.com/spacewander/lua-resty-etcd.git -b f1 --depth 1 + cp lua-resty-etcd/lib/resty/etcd/*.lua deps/share/lua/5.1/resty/etcd/ for f in ./t/cli/test_*.sh; do PATH="$PATH" "$f" done diff --git a/ci/linux_openresty_common_runner.sh b/ci/linux_openresty_common_runner.sh index cd44725213785..69edeb733a24b 100755 --- a/ci/linux_openresty_common_runner.sh +++ b/ci/linux_openresty_common_runner.sh @@ -85,6 +85,8 @@ script() { sleep 1 done + git clone https://github.com/spacewander/lua-resty-etcd.git -b f1 --depth 1 + cp lua-resty-etcd/lib/resty/etcd/*.lua deps/share/lua/5.1/resty/etcd/ # APISIX_ENABLE_LUACOV=1 PERL5LIB=.:$PERL5LIB prove -Itest-nginx/lib -r t FLUSH_ETCD=1 prove --timer -Itest-nginx/lib -I./ -r $TEST_FILE_SUB_DIR | tee /tmp/test.result rerun_flaky_tests /tmp/test.result diff --git a/t/cli/test_etcd_grpc.sh b/t/cli/test_etcd_grpc.sh new file mode 100755 index 0000000000000..a103049051a1e --- /dev/null +++ b/t/cli/test_etcd_grpc.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# 'make init' operates scripts and related configuration files in the current directory +# The 'apisix' command is a command in the /usr/local/apisix, +# and the configuration file for the operation is in the /usr/local/apisix/conf + +. ./t/cli/common.sh + +exit_if_not_customed_nginx + +# check etcd while enable auth +git checkout conf/config.yaml + +export ETCDCTL_API=3 +etcdctl version +etcdctl --endpoints=127.0.0.1:2379 user add "root:apache-api6" +etcdctl --endpoints=127.0.0.1:2379 role add root +etcdctl --endpoints=127.0.0.1:2379 user grant-role root root +etcdctl --endpoints=127.0.0.1:2379 user get root +etcdctl --endpoints=127.0.0.1:2379 auth enable +etcdctl --endpoints=127.0.0.1:2379 --user=root:apache-api6 del /apisix --prefix + +echo ' +deployment: + role: traditional + role_traditional: + config_provider: etcd + etcd: + host: + - http://127.0.0.1:2379 + prefix: /apisix + timeout: 30 + use_grpc: true + user: root + password: apache-api6 +' > conf/config.yaml + +make run +sleep 1 + +code=$(curl -o /dev/null -s -w %{http_code} http://127.0.0.1:9180/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1') +make stop + +if [ ! $code -eq 200 ]; then + echo "failed: could not work with etcd" + exit 1 +fi + +echo "passed: work well with etcd auth enabled" + +etcdctl --endpoints=127.0.0.1:2379 --user=root:apache-api6 auth disable +etcdctl --endpoints=127.0.0.1:2379 role delete root +etcdctl --endpoints=127.0.0.1:2379 user delete root diff --git a/t/core/etcd-grpc-auth-fail.t b/t/core/etcd-grpc-auth-fail.t new file mode 100644 index 0000000000000..b11f51ae24bfa --- /dev/null +++ b/t/core/etcd-grpc-auth-fail.t @@ -0,0 +1,106 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +BEGIN { + $ENV{"ETCD_ENABLE_AUTH"} = "false"; + delete $ENV{"FLUSH_ETCD"}; +} + +use t::APISIX; + +my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $version = eval { `$nginx_binary -V 2>&1` }; + +if ($version !~ m/\/apisix-nginx-module/) { + plan(skip_all => "apisix-nginx-module not installed"); +} else { + plan('no_plan'); +} + + +repeat_each(1); +no_long_string(); +no_root_location(); +log_level("info"); + +# Authentication is enabled at etcd and credentials are set +system('etcdctl --endpoints="http://127.0.0.1:2379" user add root:5tHkHhYkjr6cQY'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role add root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user grant-role root root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role list'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user user list'); +# Grant the user access to the specified directory +system('etcdctl --endpoints="http://127.0.0.1:2379" user add apisix:abc123'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role add apisix'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user grant-role apisix apisix'); +system('etcdctl --endpoints=http://127.0.0.1:2379 role grant-permission apisix --prefix=true readwrite /apisix/'); +system('etcdctl --endpoints="http://127.0.0.1:2379" auth enable'); + +run_tests; + +# Authentication is disabled at etcd +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY auth disable'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user delete root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role delete root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user delete apisix'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role delete apisix'); +__DATA__ + +=== TEST 1: Set and Get a value pass +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local key = "/test_key" + local val = "test_value" + local res, err = core.etcd.set(key, val) + ngx.say(err) + } + } +--- request +GET /t +--- error_log eval +qr /(insufficient credentials code: 401|etcdserver: user name is empty)/ + + + +=== TEST 2: etcd grants permissions with a different prefix than the one used by apisix, etcd will forbidden +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local key = "/test_key" + local val = "test_value" + local res, err = core.etcd.set(key, val) + ngx.say(err) + } + } +--- yaml_config +deployment: + role: traditional + role_traditional: + config_provider: etcd + etcd: + host: + - "http://127.0.0.1:2379" + use_grpc: false + prefix: "/apisix" + user: apisix + password: abc123 +--- request +GET /t +--- error_log eval +qr /etcd forbidden code: 403/ diff --git a/t/core/etcd-grpc-auth.t b/t/core/etcd-grpc-auth.t new file mode 100644 index 0000000000000..12e2ce28079af --- /dev/null +++ b/t/core/etcd-grpc-auth.t @@ -0,0 +1,108 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +BEGIN { + $ENV{"ETCD_ENABLE_AUTH"} = "true"; + delete $ENV{"FLUSH_ETCD"}; +} + +use t::APISIX; + +my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $version = eval { `$nginx_binary -V 2>&1` }; + +if ($version !~ m/\/apisix-nginx-module/) { + plan(skip_all => "apisix-nginx-module not installed"); +} else { + plan('no_plan'); +} + + +repeat_each(1); +no_long_string(); +no_root_location(); +log_level("info"); + +# Authentication is enabled at etcd and credentials are set +system('etcdctl --endpoints="http://127.0.0.1:2379" user add root:5tHkHhYkjr6cQY'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role add root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user grant-role root root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role list'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user user list'); +# Grant the user access to the specified directory +system('etcdctl --endpoints="http://127.0.0.1:2379" user add apisix:abc123'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role add apisix'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user grant-role apisix apisix'); +system('etcdctl --endpoints=http://127.0.0.1:2379 role grant-permission apisix --prefix=true readwrite /apisix'); +system('etcdctl --endpoints="http://127.0.0.1:2379" auth enable'); + +run_tests; + +# Authentication is disabled at etcd +system('etcdctl --endpoints="http://127.0.0.1:2379" --user root:5tHkHhYkjr6cQY auth disable'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user delete root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role delete root'); +system('etcdctl --endpoints="http://127.0.0.1:2379" user delete apisix'); +system('etcdctl --endpoints="http://127.0.0.1:2379" role delete apisix'); + + +__DATA__ + +=== TEST 1: Set and Get a value pass with authentication +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local key = "/test_key" + local val = "test_value" + core.etcd.set(key, val) + local res, err = core.etcd.get(key) + ngx.say(res.body.node.value) + core.etcd.delete(val) + } + } +--- request +GET /t +--- response_body +test_value + + + +=== TEST 2: etcd grants permissions with the same prefix as apisix uses, etcd is normal +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local key = "/test_key" + local val = "test_value" + local res, err = core.etcd.set(key, val) + ngx.say(err) + } + } +--- yaml_config +deployment: + role: traditional + role_traditional: + config_provider: etcd + etcd: + use_grpc: true + host: + - "http://127.0.0.1:2379" + prefix: "/apisix" + user: apisix + password: abc123 +--- request +GET /t