diff --git a/apisix/cli/ops.lua b/apisix/cli/ops.lua index d514de4a8c45..2cc893f6a7bb 100644 --- a/apisix/cli/ops.lua +++ b/apisix/cli/ops.lua @@ -281,22 +281,14 @@ local function init(env) end -- check the Admin API token - local checked_admin_key = false - local allow_admin = yaml_conf.deployment.admin and - yaml_conf.deployment.admin.allow_admin - if yaml_conf.apisix.enable_admin and allow_admin - and #allow_admin == 1 and allow_admin[1] == "127.0.0.0/24" then - checked_admin_key = true - end - -- check if admin_key is required - if yaml_conf.deployment.admin.admin_key_required == false then - checked_admin_key = true + local admin_key_required = yaml_conf.deployment.admin.admin_key_required ~= false + if not admin_key_required then print("Warning! Admin key is bypassed! " .. "If you are deploying APISIX in a production environment, " .. "please enable `admin_key_required` and set a secure admin key!") end - if yaml_conf.apisix.enable_admin and not checked_admin_key then + if yaml_conf.apisix.enable_admin and admin_key_required then local help = [[ %s @@ -308,26 +300,30 @@ Please modify "admin_key" in conf/config.yaml . admin_key = admin_key.admin_key end - if type(admin_key) ~= "table" or #admin_key == 0 - then - util.die(help:format("ERROR: missing valid Admin API token.")) - end - - for _, admin in ipairs(admin_key) do - if type(admin.key) == "table" then + -- A declared but empty admin key is not a usable credential. APISIX used to + -- generate one on the fly and write it back to conf/config.yaml, which silently + -- rewrote the user's config file. Fail fast instead. + for _, admin in ipairs(admin_key or {}) do + if type(admin.key) == "table" or admin.key == nil then admin.key = "" else admin.key = tostring(admin.key) end if admin.key == "" then - stderr:write( - help:format([[WARNING: using empty Admin API. - This will trigger APISIX to automatically generate a random Admin API token.]]), - "\n" - ) + util.die(help:format([[ERROR: empty Admin API token. +APISIX no longer generates a random token automatically. Set a key explicitly, or set +"admin_key_required: false" to run the Admin API without authentication.]])) end end + + local allow_admin = yaml_conf.deployment.admin.allow_admin + local admin_on_localhost_only = allow_admin and #allow_admin == 1 + and allow_admin[1] == "127.0.0.0/24" + if (type(admin_key) ~= "table" or #admin_key == 0) + and not admin_on_localhost_only then + util.die(help:format("ERROR: missing valid Admin API token.")) + end end if yaml_conf.deployment.admin then diff --git a/apisix/core/id.lua b/apisix/core/id.lua index ef8f72783320..5c8dc791f003 100644 --- a/apisix/core/id.lua +++ b/apisix/core/id.lua @@ -21,19 +21,11 @@ local fetch_local_conf = require("apisix.core.config_local").local_conf local try_read_attr = require("apisix.core.table").try_read_attr -local profile = require("apisix.core.profile") local log = require("apisix.core.log") local uuid = require("resty.jit-uuid") -local lyaml = require("lyaml") local smatch = string.match local open = io.open -local type = type -local ipairs = ipairs -local string = string -local math = math local prefix = ngx.config.prefix() -local pairs = pairs -local ngx_exit = ngx.exit local apisix_uid local _M = {version = 0.1} @@ -68,70 +60,12 @@ local function write_file(path, data) end -local function generate_yaml(table) - -- By default lyaml will parse null values as [] - -- The following logic is a workaround so that null values are parsed as null - local function replace_null(tbl) - for k, v in pairs(tbl) do - if type(v) == "table" then - replace_null(v) - elseif v == nil then - tbl[k] = "" - end - end - end - - -- Replace null values with "" - replace_null(table) - local yaml = lyaml.dump({ table }) - yaml = yaml:gsub("", "null"):gsub("%[%s*%]", "null") - return yaml -end - - _M.gen_uuid_v4 = uuid.generate_v4 ---- This will autogenerate the admin key if it's passed as an empty string in the configuration. -local function autogenerate_admin_key(default_conf) - local changed = false - -- Check if deployment.role is either traditional or control_plane - local deployment_role = default_conf.deployment and default_conf.deployment.role - if deployment_role and (deployment_role == "traditional" or - deployment_role == "control_plane") then - -- Check if deployment.admin.admin_key is not nil and it's an empty string - local admin_keys = try_read_attr(default_conf, "deployment", "admin", "admin_key") - if admin_keys and type(admin_keys) == "table" then - for i, admin_key in ipairs(admin_keys) do - if admin_key.role == "admin" and admin_key.key == "" then - changed = true - admin_keys[i].key = "" - for _ = 1, 32 do - admin_keys[i].key = admin_keys[i].key .. - string.char(math.random(65, 90) + math.random(0, 1) * 32) - end - end - end - end - end - return default_conf,changed -end - - function _M.init() local local_conf = fetch_local_conf() - local local_conf, changed = autogenerate_admin_key(local_conf) - if changed then - local yaml_conf = generate_yaml(local_conf) - local local_conf_path = profile:yaml_path("config") - local ok, err = write_file(local_conf_path, yaml_conf) - if not ok then - log.error("failed to write updated local configuration: ", err) - ngx_exit(-1) - end - end - --allow user to specify a meaningful id as apisix instance id local uid_file_path = prefix .. "/conf/apisix.uid" apisix_uid = read_file(uid_file_path) diff --git a/conf/config.yaml b/conf/config.yaml index 6a3c43068f4a..d224594bc52e 100644 --- a/conf/config.yaml +++ b/conf/config.yaml @@ -59,5 +59,5 @@ deployment: admin: admin_key: - name: admin - key: '' # using fixed API token has security risk, please update it when you deploy to production environment. If passed empty then will be autogenerated by APISIX and will be written back here. Recommended is to use external mechanism to generate and store the token. + key: '' # Admin API token. APISIX refuses to start while this is empty: set a key here (e.g. generated with `openssl rand -hex 32`), or set `admin_key_required: false` to run the Admin API without authentication. Recommended is to use an external mechanism to generate and store the token. role: admin diff --git a/docs/en/latest/FAQ.md b/docs/en/latest/FAQ.md index 96c3f3a0c542..718bedf4b211 100644 --- a/docs/en/latest/FAQ.md +++ b/docs/en/latest/FAQ.md @@ -449,7 +449,7 @@ HTTP/1.1 200 OK `X-API-KEY` of the Admin API refers to `deployment.admin.admin_key[0].key` in your `conf/config.yaml` file. It is the access token for the Admin API. -In the default configuration, this field is empty. APISIX generates a random Admin API key during initialization and writes it back to `conf/config.yaml`. You can also set the key explicitly by changing the parameter in your `conf/config.yaml` file: +In the default configuration, this field is empty and APISIX refuses to start. Set the key explicitly in your `conf/config.yaml` file: ```yaml deployment: diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md index 31496f1aadfd..86dbb221767d 100644 --- a/docs/en/latest/admin-api.md +++ b/docs/en/latest/admin-api.md @@ -54,7 +54,7 @@ deployment: admin: admin_key: - name: admin - key: your-admin-key # set a secure Admin API key; if left empty, APISIX will generate one during initialization and write it back to this file + key: your-admin-key # set a secure Admin API key; APISIX refuses to start if it is left empty role: admin allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow - 127.0.0.0/24 diff --git a/docs/en/latest/dashboard.md b/docs/en/latest/dashboard.md index f12a4bf79038..8df64f17739a 100644 --- a/docs/en/latest/dashboard.md +++ b/docs/en/latest/dashboard.md @@ -73,7 +73,7 @@ deployment: - name: admin role: admin - # Set a secure Admin API Key. If left empty, APISIX will generate one during initialization and write it back to this file. + # Set a secure Admin API Key. APISIX refuses to start if it is left empty. key: your-admin-key ``` diff --git a/docs/zh/latest/FAQ.md b/docs/zh/latest/FAQ.md index 2d58dec59a6e..4d503e8d97ea 100644 --- a/docs/zh/latest/FAQ.md +++ b/docs/zh/latest/FAQ.md @@ -452,7 +452,7 @@ curl http://127.0.0.1:9080/ip -i Admin API 的 `X-API-KEY` 指的是 `./conf/config.yaml` 文件中的 `deployment.admin.admin_key[0].key`。它是 Admin API 的访问 token。 -在默认配置中,该字段为空。APISIX 会在初始化时自动生成一个随机的 Admin API Key,并将其写回 `./conf/config.yaml`。你也可以通过修改 `./conf/config.yaml` 中的参数来显式设置该 Key,如下示例: +在默认配置中,该字段为空,此时 APISIX 会拒绝启动。你需要在 `./conf/config.yaml` 中显式设置该 Key,如下示例: ```yaml deployment: diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md index a57d30188176..a6496f02c92b 100644 --- a/docs/zh/latest/admin-api.md +++ b/docs/zh/latest/admin-api.md @@ -56,7 +56,7 @@ deployment: admin: admin_key: - name: admin - key: your-admin-key # 设置安全的 Admin API Key;如果留空,APISIX 会在初始化时自动生成并写回此文件 + key: your-admin-key # 设置安全的 Admin API Key;如果留空,APISIX 会拒绝启动 role: admin allow_admin: # http://nginx.org/en/docs/http/ngx_http_access_module.html#allow - 127.0.0.0/24 diff --git a/docs/zh/latest/dashboard.md b/docs/zh/latest/dashboard.md index 25b97cf8ab8d..03ba3b328c33 100644 --- a/docs/zh/latest/dashboard.md +++ b/docs/zh/latest/dashboard.md @@ -73,7 +73,7 @@ deployment: - name: admin role: admin - # 设置安全的 Admin API Key。如果留空,APISIX 会在初始化时自动生成并写回此文件。 + # 设置安全的 Admin API Key。如果留空,APISIX 会拒绝启动。 key: your-admin-key ``` diff --git a/t/cli/test_admin.sh b/t/cli/test_admin.sh index 1298cc1dd406..d6df4f647997 100755 --- a/t/cli/test_admin.sh +++ b/t/cli/test_admin.sh @@ -26,6 +26,10 @@ git checkout conf/config.yaml echo " deployment: admin: + admin_key: + - name: admin + key: edd1c9f034335f136f87ad84b625c8f1 + role: admin admin_listen: port: 9180 https_admin: true @@ -61,6 +65,10 @@ apisix: enable_admin: true deployment: admin: + admin_key: + - name: admin + key: edd1c9f034335f136f87ad84b625c8f1 + role: admin admin_listen: ip: 127.0.0.2 port: 9181 @@ -248,7 +256,7 @@ fi echo "pass: allow empty admin_key, when admin_key_required=false" -# admin api, allow any IP but use default key +# an explicitly configured but empty admin key is rejected echo ' deployment: @@ -262,13 +270,18 @@ deployment: make init > output.log 2>&1 | true -grep -E "WARNING: using empty Admin API." output.log > /dev/null -if [ ! $? -eq 0 ]; then - echo "failed: need to show `WARNING: using fixed Admin API token has security risk`" +if ! grep -E "ERROR: empty Admin API token." output.log > /dev/null; then + echo "failed: should show 'ERROR: empty Admin API token.'" + exit 1 +fi + +# the config file must not be rewritten when the admin key is empty +if ! grep -E "key: ''" conf/config.yaml > /dev/null; then + echo "failed: conf/config.yaml was rewritten" exit 1 fi -echo "pass: show WARNING message if the user uses empty key" +echo "pass: reject empty admin_key without rewriting conf/config.yaml" # admin_listen set echo '