Skip to content

Commit 869e801

Browse files
authored
feat(deployment): control plane connects to conf server (#7392)
Signed-off-by: spacewander <[email protected]>
1 parent dbd78df commit 869e801

9 files changed

+328
-112
lines changed

apisix/cli/schema.lua

+14-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,20 @@ local deployment_schema = {
305305
},
306306
},
307307
required = {"config_provider", "conf_server"}
308-
}
308+
},
309+
certs = {
310+
properties = {
311+
cert = { type = "string" },
312+
cert_key = { type = "string" },
313+
trusted_ca_cert = { type = "string" },
314+
},
315+
dependencies = {
316+
cert = {
317+
required = {"cert_key"},
318+
},
319+
},
320+
default = {},
321+
},
309322
},
310323
required = {"etcd", "role_control_plane"}
311324
}

apisix/cli/snippet.lua

+13
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ function _M.generate_conf_server(env, conf)
6363
end
6464
end
6565

66+
local trusted_ca_cert
67+
if conf.deployment.certs then
68+
if conf.deployment.certs.trusted_ca_cert then
69+
trusted_ca_cert = pl_path.abspath(conf.deployment.certs.trusted_ca_cert)
70+
end
71+
end
72+
6673
local conf_render = template.compile([[
6774
upstream apisix_conf_backend {
6875
server 0.0.0.0:80;
@@ -71,6 +78,11 @@ function _M.generate_conf_server(env, conf)
7178
conf_server.balancer()
7279
}
7380
}
81+
82+
{% if trusted_ca_cert then %}
83+
lua_ssl_trusted_certificate {* trusted_ca_cert *};
84+
{% end %}
85+
7486
server {
7587
{% if control_plane then %}
7688
listen {* control_plane.listen *} ssl;
@@ -144,6 +156,7 @@ function _M.generate_conf_server(env, conf)
144156
enable_https = enable_https,
145157
client_cert = client_cert,
146158
client_cert_key = client_cert_key,
159+
trusted_ca_cert = trusted_ca_cert,
147160
})
148161
end
149162

apisix/core/config_etcd.lua

+2-7
Original file line numberDiff line numberDiff line change
@@ -812,18 +812,13 @@ function _M.init()
812812
return true
813813
end
814814

815-
local etcd_cli, err = get_etcd()
815+
-- don't go through proxy during start because the proxy is not available
816+
local etcd_cli, prefix, err = etcd_apisix.new_without_proxy()
816817
if not etcd_cli then
817818
return nil, "failed to start a etcd instance: " .. err
818819
end
819820

820-
-- don't go through proxy during start because the proxy is not available
821-
local proxy = etcd_cli.unix_socket_proxy
822-
etcd_cli.unix_socket_proxy = nil
823-
local etcd_conf = local_conf.etcd
824-
local prefix = etcd_conf.prefix
825821
local res, err = readdir(etcd_cli, prefix, create_formatter(prefix))
826-
etcd_cli.unix_socket_proxy = proxy
827822
if not res then
828823
return nil, err
829824
end

apisix/core/etcd.lua

+71-30
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,53 @@ local setmetatable = setmetatable
2929
local string = string
3030
local tonumber = tonumber
3131
local ngx_config_prefix = ngx.config.prefix()
32+
local ngx_socket_tcp = ngx.socket.tcp
3233

3334

3435
local is_http = ngx.config.subsystem == "http"
3536
local _M = {}
3637

3738

38-
-- this function create the etcd client instance used in the Admin API
39+
local function has_mtls_support()
40+
local s = ngx_socket_tcp()
41+
return s.tlshandshake ~= nil
42+
end
43+
44+
45+
local function _new(etcd_conf)
46+
local prefix = etcd_conf.prefix
47+
etcd_conf.http_host = etcd_conf.host
48+
etcd_conf.host = nil
49+
etcd_conf.prefix = nil
50+
etcd_conf.protocol = "v3"
51+
etcd_conf.api_prefix = "/v3"
52+
53+
-- default to verify etcd cluster certificate
54+
etcd_conf.ssl_verify = true
55+
if etcd_conf.tls then
56+
if etcd_conf.tls.verify == false then
57+
etcd_conf.ssl_verify = false
58+
end
59+
60+
if etcd_conf.tls.cert then
61+
etcd_conf.ssl_cert_path = etcd_conf.tls.cert
62+
etcd_conf.ssl_key_path = etcd_conf.tls.key
63+
end
64+
65+
if etcd_conf.tls.sni then
66+
etcd_conf.sni = etcd_conf.tls.sni
67+
end
68+
end
69+
70+
local etcd_cli, err = etcd.new(etcd_conf)
71+
if not etcd_cli then
72+
return nil, nil, err
73+
end
74+
75+
return etcd_cli, prefix
76+
end
77+
78+
3979
local function new()
4080
local local_conf, err = fetch_local_conf()
4181
if not local_conf then
@@ -60,32 +100,20 @@ local function new()
60100
proxy_by_conf_server = true
61101

62102
elseif local_conf.deployment.role == "control_plane" then
63-
-- TODO: add the proxy conf in control_plane
64-
proxy_by_conf_server = true
65-
end
66-
end
67-
68-
local prefix = etcd_conf.prefix
69-
etcd_conf.http_host = etcd_conf.host
70-
etcd_conf.host = nil
71-
etcd_conf.prefix = nil
72-
etcd_conf.protocol = "v3"
73-
etcd_conf.api_prefix = "/v3"
74-
75-
-- default to verify etcd cluster certificate
76-
etcd_conf.ssl_verify = true
77-
if etcd_conf.tls then
78-
if etcd_conf.tls.verify == false then
79-
etcd_conf.ssl_verify = false
80-
end
103+
local addr = local_conf.deployment.role_control_plane.conf_server.listen
104+
etcd_conf.host = {"https://" .. addr}
105+
etcd_conf.tls = {
106+
verify = false,
107+
}
81108

82-
if etcd_conf.tls.cert then
83-
etcd_conf.ssl_cert_path = etcd_conf.tls.cert
84-
etcd_conf.ssl_key_path = etcd_conf.tls.key
85-
end
109+
if has_mtls_support() and local_conf.deployment.certs.cert then
110+
local cert = local_conf.deployment.certs.cert
111+
local cert_key = local_conf.deployment.certs.cert_key
112+
etcd_conf.tls.cert = cert
113+
etcd_conf.tls.key = cert_key
114+
end
86115

87-
if etcd_conf.tls.sni then
88-
etcd_conf.sni = etcd_conf.tls.sni
116+
proxy_by_conf_server = true
89117
end
90118
end
91119

@@ -102,15 +130,28 @@ local function new()
102130
})
103131
end
104132

105-
local etcd_cli
106-
etcd_cli, err = etcd.new(etcd_conf)
107-
if not etcd_cli then
133+
return _new(etcd_conf)
134+
end
135+
_M.new = new
136+
137+
138+
---
139+
-- Create an etcd client which will connect to etcd without being proxyed by conf server.
140+
-- This method is used in init_worker phase when the conf server is not ready.
141+
--
142+
-- @function core.etcd.new_without_proxy
143+
-- @treturn table|nil the etcd client, or nil if failed.
144+
-- @treturn string|nil the configured prefix of etcd keys, or nil if failed.
145+
-- @treturn nil|string the error message.
146+
function _M.new_without_proxy()
147+
local local_conf, err = fetch_local_conf()
148+
if not local_conf then
108149
return nil, nil, err
109150
end
110151

111-
return etcd_cli, prefix
152+
local etcd_conf = clone_tab(local_conf.etcd)
153+
return _new(etcd_conf)
112154
end
113-
_M.new = new
114155

115156

116157
-- convert ETCD v3 entry to v2 one

docs/en/latest/architecture-design/deployment-role.md

+23
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,29 @@ deployment:
123123
trusted_ca_cert: /path/to/ca-cert
124124
```
125125

126+
As OpenResty <= 1.21.4 doesn't support sending mTLS request, if you need to accept the connections from APISIX running on these OpenResty versions,
127+
you need to disable client certificate verification in the CP instance.
128+
129+
Here is the example of configuration:
130+
131+
```yaml title="conf/config.yaml"
132+
deployment:
133+
role: control_plane
134+
role_control_plan:
135+
config_provider: etcd
136+
conf_server:
137+
listen: 0.0.0.0:9280
138+
cert: /path/to/ca-cert
139+
cert_key: /path/to/ca-cert
140+
etcd:
141+
host:
142+
- https://xxxx
143+
prefix: /apisix
144+
timeout: 30
145+
certs:
146+
trusted_ca_cert: /path/to/ca-cert
147+
```
148+
126149
### Standalone
127150

128151
In this mode, APISIX is deployed as DP and reads configurations from yaml file in the local file system.

t/cli/test_deployment_control_plane.sh

+11-4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ fi
4040

4141
echo "passed: should check deployment schema during init"
4242

43+
# The 'admin.apisix.dev' is injected by ci/common.sh@set_coredns
4344
echo '
4445
apisix:
4546
enable_admin: false
@@ -48,14 +49,15 @@ deployment:
4849
role_control_plane:
4950
config_provider: etcd
5051
conf_server:
51-
listen: 0.0.0.0:12345
52+
listen: admin.apisix.dev:12345
5253
cert: t/certs/mtls_server.crt
5354
cert_key: t/certs/mtls_server.key
54-
client_ca_cert: t/certs/mtls_ca.crt
5555
etcd:
5656
prefix: "/apisix"
5757
host:
5858
- http://127.0.0.1:2379
59+
certs:
60+
trusted_ca_cert: t/certs/mtls_ca.crt
5961
' > conf/config.yaml
6062

6163
make run
@@ -71,20 +73,25 @@ fi
7173

7274
echo "passed: control_plane should enable Admin API"
7375

76+
# use https
77+
# The 'admin.apisix.dev' is injected by ci/common.sh@set_coredns
7478
echo '
7579
deployment:
7680
role: control_plane
7781
role_control_plane:
7882
config_provider: etcd
7983
conf_server:
80-
listen: 0.0.0.0:12345
84+
listen: admin.apisix.dev:12345
8185
cert: t/certs/mtls_server.crt
8286
cert_key: t/certs/mtls_server.key
83-
client_ca_cert: t/certs/mtls_ca.crt
8487
etcd:
8588
prefix: "/apisix"
8689
host:
8790
- http://127.0.0.1:2379
91+
certs:
92+
cert: t/certs/mtls_client.crt
93+
cert_key: t/certs/mtls_client.key
94+
trusted_ca_cert: t/certs/mtls_ca.crt
8895
' > conf/config.yaml
8996

9097
make run
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#!/usr/bin/env bash
2+
3+
#
4+
# Licensed to the Apache Software Foundation (ASF) under one or more
5+
# contributor license agreements. See the NOTICE file distributed with
6+
# this work for additional information regarding copyright ownership.
7+
# The ASF licenses this file to You under the Apache License, Version 2.0
8+
# (the "License"); you may not use this file except in compliance with
9+
# the License. You may obtain a copy of the License at
10+
#
11+
# http://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing, software
14+
# distributed under the License is distributed on an "AS IS" BASIS,
15+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
# See the License for the specific language governing permissions and
17+
# limitations under the License.
18+
#
19+
20+
. ./t/cli/common.sh
21+
22+
exit_if_not_customed_nginx
23+
24+
# use mTLS
25+
# The 'admin.apisix.dev' is injected by ci/common.sh@set_coredns
26+
echo '
27+
deployment:
28+
role: control_plane
29+
role_control_plane:
30+
config_provider: etcd
31+
conf_server:
32+
listen: admin.apisix.dev:12345
33+
cert: t/certs/mtls_server.crt
34+
cert_key: t/certs/mtls_server.key
35+
client_ca_cert: t/certs/mtls_ca.crt
36+
etcd:
37+
prefix: "/apisix"
38+
host:
39+
- http://127.0.0.1:2379
40+
certs:
41+
cert: t/certs/mtls_client.crt
42+
cert_key: t/certs/mtls_client.key
43+
trusted_ca_cert: t/certs/mtls_ca.crt
44+
' > conf/config.yaml
45+
46+
make run
47+
sleep 1
48+
49+
code=$(curl -o /dev/null -s -w %{http_code} http://127.0.0.1:9080/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1')
50+
make stop
51+
52+
if [ ! $code -eq 200 ]; then
53+
echo "failed: could not work with etcd"
54+
exit 1
55+
fi
56+
57+
echo "passed: work well with etcd in control plane"

0 commit comments

Comments
 (0)