Skip to content

Commit 7d05578

Browse files
shinrichzwoop
authored andcommitted
Add logic to make the server.policy and server.properties settings reloadable (#9572)
(cherry picked from commit 6d1382d) Conflicts: iocore/net/P_SSLConfig.h
1 parent bfa0dee commit 7d05578

File tree

3 files changed

+257
-24
lines changed

3 files changed

+257
-24
lines changed

iocore/net/P_SSLConfig.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ struct SSLConfigParams : public ConfigInfo {
167167
void cleanup();
168168
void reset();
169169
void SSLConfigInit(IpMap *global);
170+
void SetServerPolicy(const char *);
171+
void SetServerPolicyProperties(const char *);
170172

171173
private:
172174
// c_str() of string passed to in-progess call to updateCTX().

iocore/net/SSLConfig.cc

Lines changed: 65 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,67 @@ set_paths_helper(const char *path, const char *filename, char **final_path, char
190190
}
191191
}
192192

193+
int
194+
UpdateServerPolicy(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data, void *cookie)
195+
{
196+
SSLConfigParams *params = SSLConfig::acquire();
197+
char *verify_server = data.rec_string;
198+
if (params != nullptr && verify_server != nullptr) {
199+
Debug("ssl_load", "New Server Policy %s", verify_server);
200+
params->SetServerPolicy(verify_server);
201+
} else {
202+
Debug("ssl_load", "Failed to load new Server Policy %p %p", verify_server, params);
203+
}
204+
return 0;
205+
}
206+
207+
int
208+
UpdateServerPolicyProperties(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data, void *cookie)
209+
{
210+
SSLConfigParams *params = SSLConfig::acquire();
211+
char *verify_server = data.rec_string;
212+
if (params != nullptr && verify_server != nullptr) {
213+
params->SetServerPolicyProperties(verify_server);
214+
}
215+
return 0;
216+
}
217+
218+
void
219+
SSLConfigParams::SetServerPolicyProperties(const char *verify_server)
220+
{
221+
if (strcmp(verify_server, "SIGNATURE") == 0) {
222+
verifyServerProperties = YamlSNIConfig::Property::SIGNATURE_MASK;
223+
} else if (strcmp(verify_server, "NAME") == 0) {
224+
verifyServerProperties = YamlSNIConfig::Property::NAME_MASK;
225+
} else if (strcmp(verify_server, "ALL") == 0) {
226+
verifyServerProperties = YamlSNIConfig::Property::ALL_MASK;
227+
} else if (strcmp(verify_server, "NONE") == 0) {
228+
verifyServerProperties = YamlSNIConfig::Property::NONE;
229+
} else {
230+
Warning("%s is invalid for proxy.config.ssl.client.verify.server.properties. Should be one of ALL, SIGNATURE, NAME, or NONE. "
231+
"Default is ALL",
232+
verify_server);
233+
verifyServerProperties = YamlSNIConfig::Property::NONE;
234+
}
235+
}
236+
237+
void
238+
SSLConfigParams::SetServerPolicy(const char *verify_server)
239+
{
240+
if (strcmp(verify_server, "DISABLED") == 0) {
241+
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
242+
} else if (strcmp(verify_server, "PERMISSIVE") == 0) {
243+
verifyServerPolicy = YamlSNIConfig::Policy::PERMISSIVE;
244+
} else if (strcmp(verify_server, "ENFORCED") == 0) {
245+
verifyServerPolicy = YamlSNIConfig::Policy::ENFORCED;
246+
} else {
247+
Warning("%s is invalid for proxy.config.ssl.client.verify.server.policy. Should be one of DISABLED, PERMISSIVE, or ENFORCED. "
248+
"Default is DISABLED",
249+
verify_server);
250+
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
251+
}
252+
}
253+
193254
void
194255
SSLConfigParams::initialize()
195256
{
@@ -380,34 +441,14 @@ SSLConfigParams::initialize()
380441

381442
char *verify_server = nullptr;
382443
REC_ReadConfigStringAlloc(verify_server, "proxy.config.ssl.client.verify.server.policy");
383-
if (strcmp(verify_server, "DISABLED") == 0) {
384-
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
385-
} else if (strcmp(verify_server, "PERMISSIVE") == 0) {
386-
verifyServerPolicy = YamlSNIConfig::Policy::PERMISSIVE;
387-
} else if (strcmp(verify_server, "ENFORCED") == 0) {
388-
verifyServerPolicy = YamlSNIConfig::Policy::ENFORCED;
389-
} else {
390-
Warning("%s is invalid for proxy.config.ssl.client.verify.server.policy. Should be one of DISABLED, PERMISSIVE, or ENFORCED",
391-
verify_server);
392-
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
393-
}
444+
this->SetServerPolicy(verify_server);
394445
ats_free(verify_server);
446+
REC_RegisterConfigUpdateFunc("proxy.config.ssl.client.verify.server.policy", UpdateServerPolicy, nullptr);
395447

396448
REC_ReadConfigStringAlloc(verify_server, "proxy.config.ssl.client.verify.server.properties");
397-
if (strcmp(verify_server, "SIGNATURE") == 0) {
398-
verifyServerProperties = YamlSNIConfig::Property::SIGNATURE_MASK;
399-
} else if (strcmp(verify_server, "NAME") == 0) {
400-
verifyServerProperties = YamlSNIConfig::Property::NAME_MASK;
401-
} else if (strcmp(verify_server, "ALL") == 0) {
402-
verifyServerProperties = YamlSNIConfig::Property::ALL_MASK;
403-
} else if (strcmp(verify_server, "NONE") == 0) {
404-
verifyServerProperties = YamlSNIConfig::Property::NONE;
405-
} else {
406-
Warning("%s is invalid for proxy.config.ssl.client.verify.server.properties. Should be one of SIGNATURE, NAME, or ALL",
407-
verify_server);
408-
verifyServerProperties = YamlSNIConfig::Property::NONE;
409-
}
449+
this->SetServerPolicyProperties(verify_server);
410450
ats_free(verify_server);
451+
REC_RegisterConfigUpdateFunc("proxy.config.ssl.client.verify.server.properties", UpdateServerPolicyProperties, nullptr);
411452

412453
ssl_client_cert_filename = nullptr;
413454
ssl_client_cert_path = nullptr;
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
'''
2+
'''
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with 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+
Test.Summary = '''
20+
Test tls server certificate verification options
21+
Specifically update via traffic_ctl (reloadable)
22+
'''
23+
24+
# Define default ATS
25+
ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
26+
server_foo = Test.MakeOriginServer("server_foo",
27+
ssl=True,
28+
options={"--key": "{0}/signed-foo.key".format(Test.RunDirectory),
29+
"--cert": "{0}/signed-foo.pem".format(Test.RunDirectory)})
30+
server_bar = Test.MakeOriginServer("server_bar",
31+
ssl=True,
32+
options={"--key": "{0}/signed-bar.key".format(Test.RunDirectory),
33+
"--cert": "{0}/signed-bar.pem".format(Test.RunDirectory)})
34+
server = Test.MakeOriginServer("server", ssl=True)
35+
36+
request_foo_header = {"headers": "GET / HTTP/1.1\r\nHost: foo.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
37+
request_bad_foo_header = {"headers": "GET / HTTP/1.1\r\nHost: bad_foo.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
38+
request_bar_header = {"headers": "GET / HTTP/1.1\r\nHost: bar.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
39+
request_bad_bar_header = {"headers": "GET / HTTP/1.1\r\nHost: bad_bar.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
40+
response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
41+
server_foo.addResponse("sessionlog.json", request_foo_header, response_header)
42+
server_foo.addResponse("sessionlog.json", request_bad_foo_header, response_header)
43+
server_bar.addResponse("sessionlog.json", request_bar_header, response_header)
44+
server_bar.addResponse("sessionlog.json", request_bad_bar_header, response_header)
45+
46+
# add ssl materials like key, certificates for the server
47+
ts.addSSLfile("ssl/signed-foo.pem")
48+
ts.addSSLfile("ssl/signed-foo.key")
49+
ts.addSSLfile("ssl/signed-bar.pem")
50+
ts.addSSLfile("ssl/signed-bar.key")
51+
ts.addSSLfile("ssl/server.pem")
52+
ts.addSSLfile("ssl/server.key")
53+
ts.addSSLfile("ssl/signer.pem")
54+
ts.addSSLfile("ssl/signer.key")
55+
56+
ts.Disk.remap_config.AddLine(
57+
'map https://foo.com/ https://127.0.0.1:{0}'.format(server_foo.Variables.SSL_Port))
58+
ts.Disk.remap_config.AddLine(
59+
'map https://bad_foo.com/ https://127.0.0.1:{0}'.format(server_foo.Variables.SSL_Port))
60+
ts.Disk.remap_config.AddLine(
61+
'map https://bar.com/ https://127.0.0.1:{0}'.format(server_bar.Variables.SSL_Port))
62+
ts.Disk.remap_config.AddLine(
63+
'map https://bad_bar.com/ https://127.0.0.1:{0}'.format(server_bar.Variables.SSL_Port))
64+
ts.Disk.remap_config.AddLine(
65+
'map / https://127.0.0.1:{0}'.format(server.Variables.SSL_Port))
66+
67+
ts.Disk.ssl_multicert_config.AddLine(
68+
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
69+
)
70+
71+
# Case 1, global config policy=permissive properties=signature
72+
# override for foo.com policy=enforced properties=all
73+
ts.Disk.records_config.update({
74+
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
75+
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
76+
'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
77+
# set global policy
78+
'proxy.config.ssl.client.verify.server.policy': 'ENFORCED',
79+
'proxy.config.ssl.client.verify.server.properties': 'ALL',
80+
'proxy.config.ssl.client.CA.cert.path': '{0}'.format(ts.Variables.SSLDir),
81+
'proxy.config.ssl.client.CA.cert.filename': 'signer.pem',
82+
'proxy.config.exec_thread.autoconfig.scale': 1.0,
83+
'proxy.config.url_remap.pristine_host_hdr': 1,
84+
'proxy.config.diags.debug.enabled': 0,
85+
'proxy.config.diags.debug.tags': 'ssl'
86+
})
87+
88+
tr = Test.AddTestRun("default-enforce-bad-sig")
89+
tr.Setup.Copy("ssl/signed-foo.key")
90+
tr.Setup.Copy("ssl/signed-foo.pem")
91+
tr.Setup.Copy("ssl/signed-bar.key")
92+
tr.Setup.Copy("ssl/signed-bar.pem")
93+
tr.Processes.Default.Command = "curl -k -H \"host: random2.com\" https://127.0.0.1:{0}".format(ts.Variables.ssl_port)
94+
tr.ReturnCode = 0
95+
tr.Processes.Default.StartBefore(server_foo)
96+
tr.Processes.Default.StartBefore(server_bar)
97+
tr.Processes.Default.StartBefore(server)
98+
tr.Processes.Default.StartBefore(Test.Processes.ts)
99+
tr.StillRunningAfter = server
100+
tr.StillRunningAfter = ts
101+
tr.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Curl attempt should have failed")
102+
103+
tr2 = Test.AddTestRun("Update config files")
104+
recordspath = ts.Disk.records_config.AbsPath
105+
# recreate the records.config with the cert filename changed
106+
tr2.Disk.File(recordspath, id="records_config", typename="ats:config:records"),
107+
tr2.Disk.records_config.update({
108+
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
109+
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
110+
'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
111+
# set global policy
112+
'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE',
113+
'proxy.config.ssl.client.verify.server.properties': 'ALL',
114+
'proxy.config.ssl.client.CA.cert.path': '{0}'.format(ts.Variables.SSLDir),
115+
'proxy.config.ssl.client.CA.cert.filename': 'signer.pem',
116+
'proxy.config.exec_thread.autoconfig.scale': 1.0,
117+
'proxy.config.url_remap.pristine_host_hdr': 1,
118+
'proxy.config.diags.debug.enabled': 0,
119+
'proxy.config.diags.debug.tags': 'ssl'
120+
})
121+
tr2.StillRunningAfter = ts
122+
tr2.StillRunningAfter = server
123+
tr2.Processes.Default.Command = 'echo Updated configs'
124+
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
125+
tr2.Processes.Default.Env = ts.Env
126+
tr2.Processes.Default.ReturnCode = 0
127+
128+
# Change the config to PERMISSIVE. Same command should work now
129+
trreload = Test.AddTestRun("Reload config")
130+
trreload.StillRunningAfter = ts
131+
trreload.StillRunningAfter = server
132+
# takes a few seconds for the reload to be ready for the next connection
133+
trreload.Processes.Default.Command = 'traffic_ctl config reload; sleep 5'
134+
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
135+
trreload.Processes.Default.Env = ts.Env
136+
trreload.Processes.Default.ReturnCode = 0
137+
138+
tragain = Test.AddTestRun("permissive-after-update")
139+
tragain.Processes.Default.Command = "curl -k -H \"host: random3.com\" https://127.0.0.1:{0}".format(ts.Variables.ssl_port)
140+
tragain.ReturnCode = 0
141+
tragain.StillRunningAfter = server
142+
tragain.StillRunningAfter = ts
143+
tragain.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded")
144+
145+
tr2 = Test.AddTestRun("Update config files to enforced")
146+
recordspath = ts.Disk.records_config.AbsPath
147+
# recreate the records.config with the cert filename changed
148+
tr2.Disk.File(recordspath, id="records_config", typename="ats:config:records"),
149+
tr2.Disk.records_config.update({
150+
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
151+
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
152+
'proxy.config.ssl.server.cipher_suite': 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:RC4-SHA:RC4-MD5:AES128-SHA:AES256-SHA:DES-CBC3-SHA!SRP:!DSS:!PSK:!aNULL:!eNULL:!SSLv2',
153+
# set global policy
154+
'proxy.config.ssl.client.verify.server.policy': 'ENFORCED',
155+
'proxy.config.ssl.client.verify.server.properties': 'ALL',
156+
'proxy.config.ssl.client.CA.cert.path': '{0}'.format(ts.Variables.SSLDir),
157+
'proxy.config.ssl.client.CA.cert.filename': 'signer.pem',
158+
'proxy.config.exec_thread.autoconfig.scale': 1.0,
159+
'proxy.config.url_remap.pristine_host_hdr': 1,
160+
'proxy.config.diags.debug.enabled': 0,
161+
'proxy.config.diags.debug.tags': 'ssl'
162+
})
163+
tr2.StillRunningAfter = ts
164+
tr2.StillRunningAfter = server
165+
tr2.Processes.Default.Command = 'echo Updated configs to ENFORCED'
166+
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
167+
tr2.Processes.Default.Env = ts.Env
168+
tr2.Processes.Default.ReturnCode = 0
169+
170+
trreload = Test.AddTestRun("Reload config again")
171+
trreload.StillRunningAfter = ts
172+
trreload.StillRunningAfter = server
173+
# takes a few seconds for the reload to be ready for the next connection
174+
trreload.Processes.Default.Command = 'traffic_ctl config reload; sleep 5'
175+
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
176+
trreload.Processes.Default.Env = ts.Env
177+
trreload.Processes.Default.ReturnCode = 0
178+
179+
tragain = Test.AddTestRun("enforced-after-update")
180+
tragain.Processes.Default.Command = "curl -k -H \"host: random4.com\" https://127.0.0.1:{0}".format(ts.Variables.ssl_port)
181+
tragain.ReturnCode = 0
182+
tragain.StillRunningAfter = server
183+
tragain.StillRunningAfter = ts
184+
tragain.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Curl attempt should have succeeded")
185+
186+
# No name checking for the sig-only permissive override for bad_bar
187+
ts.Disk.diags_log.Content += Testers.ContainsExpression(
188+
"Core server certificate verification failed for \(random3.com\). Action=Continue", "Permissive can connect")
189+
ts.Disk.diags_log.Content += Testers.ContainsExpression(
190+
"Core server certificate verification failed for \(random2.com\). Action=Terminate", "Enforced cannot connect")

0 commit comments

Comments
 (0)