Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 19 additions & 23 deletions apisix/cli/ops.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
66 changes: 0 additions & 66 deletions apisix/core/id.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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] = "<PLACEHOLDER>"
end
end
end

-- Replace null values with "<PLACEHOLDER>"
replace_null(table)
local yaml = lyaml.dump({ table })
yaml = yaml:gsub("<PLACEHOLDER>", "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)
Expand Down
2 changes: 1 addition & 1 deletion conf/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion docs/en/latest/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion docs/en/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/en/latest/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
2 changes: 1 addition & 1 deletion docs/zh/latest/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/latest/admin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/zh/latest/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ deployment:
-
name: admin
role: admin
# 设置安全的 Admin API Key。如果留空,APISIX 会在初始化时自动生成并写回此文件
# 设置安全的 Admin API Key。如果留空,APISIX 会拒绝启动
key: your-admin-key
```

Expand Down
23 changes: 18 additions & 5 deletions t/cli/test_admin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand All @@ -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 '
Expand Down
Loading