Skip to content
Merged
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
2 changes: 2 additions & 0 deletions iocore/net/P_SSLConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ struct SSLConfigParams : public ConfigInfo {
void cleanup();
void reset();
void SSLConfigInit(swoc::IPRangeSet *global);
void SetServerPolicy(const char *);
void SetServerPolicyProperties(const char *);

private:
// c_str() of string passed to in-progess call to updateCTX().
Expand Down
89 changes: 65 additions & 24 deletions iocore/net/SSLConfig.cc
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,67 @@ set_paths_helper(const char *path, const char *filename, char **final_path, char
}
}

int
UpdateServerPolicy(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data, void *cookie)
{
SSLConfigParams *params = SSLConfig::acquire();
char *verify_server = data.rec_string;
if (params != nullptr && verify_server != nullptr) {
Debug("ssl_load", "New Server Policy %s", verify_server);
params->SetServerPolicy(verify_server);
} else {
Debug("ssl_load", "Failed to load new Server Policy %p %p", verify_server, params);
}
return 0;
}

int
UpdateServerPolicyProperties(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data, void *cookie)
{
SSLConfigParams *params = SSLConfig::acquire();
char *verify_server = data.rec_string;
if (params != nullptr && verify_server != nullptr) {
params->SetServerPolicyProperties(verify_server);
}
return 0;
}

void
SSLConfigParams::SetServerPolicyProperties(const char *verify_server)
{
if (strcmp(verify_server, "SIGNATURE") == 0) {
verifyServerProperties = YamlSNIConfig::Property::SIGNATURE_MASK;
} else if (strcmp(verify_server, "NAME") == 0) {
verifyServerProperties = YamlSNIConfig::Property::NAME_MASK;
} else if (strcmp(verify_server, "ALL") == 0) {
verifyServerProperties = YamlSNIConfig::Property::ALL_MASK;
} else if (strcmp(verify_server, "NONE") == 0) {
verifyServerProperties = YamlSNIConfig::Property::NONE;
} else {
Warning("%s is invalid for proxy.config.ssl.client.verify.server.properties. Should be one of ALL, SIGNATURE, NAME, or NONE. "
"Default is ALL",
verify_server);
verifyServerProperties = YamlSNIConfig::Property::NONE;
}
}

void
SSLConfigParams::SetServerPolicy(const char *verify_server)
{
if (strcmp(verify_server, "DISABLED") == 0) {
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
} else if (strcmp(verify_server, "PERMISSIVE") == 0) {
verifyServerPolicy = YamlSNIConfig::Policy::PERMISSIVE;
} else if (strcmp(verify_server, "ENFORCED") == 0) {
verifyServerPolicy = YamlSNIConfig::Policy::ENFORCED;
} else {
Warning("%s is invalid for proxy.config.ssl.client.verify.server.policy. Should be one of DISABLED, PERMISSIVE, or ENFORCED. "
"Default is DISABLED",
verify_server);
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
}
}

void
SSLConfigParams::initialize()
{
Expand Down Expand Up @@ -389,34 +450,14 @@ SSLConfigParams::initialize()

char *verify_server = nullptr;
REC_ReadConfigStringAlloc(verify_server, "proxy.config.ssl.client.verify.server.policy");
if (strcmp(verify_server, "DISABLED") == 0) {
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
} else if (strcmp(verify_server, "PERMISSIVE") == 0) {
verifyServerPolicy = YamlSNIConfig::Policy::PERMISSIVE;
} else if (strcmp(verify_server, "ENFORCED") == 0) {
verifyServerPolicy = YamlSNIConfig::Policy::ENFORCED;
} else {
Warning("%s is invalid for proxy.config.ssl.client.verify.server.policy. Should be one of DISABLED, PERMISSIVE, or ENFORCED",
verify_server);
verifyServerPolicy = YamlSNIConfig::Policy::DISABLED;
}
this->SetServerPolicy(verify_server);
ats_free(verify_server);
REC_RegisterConfigUpdateFunc("proxy.config.ssl.client.verify.server.policy", UpdateServerPolicy, nullptr);

REC_ReadConfigStringAlloc(verify_server, "proxy.config.ssl.client.verify.server.properties");
if (strcmp(verify_server, "SIGNATURE") == 0) {
verifyServerProperties = YamlSNIConfig::Property::SIGNATURE_MASK;
} else if (strcmp(verify_server, "NAME") == 0) {
verifyServerProperties = YamlSNIConfig::Property::NAME_MASK;
} else if (strcmp(verify_server, "ALL") == 0) {
verifyServerProperties = YamlSNIConfig::Property::ALL_MASK;
} else if (strcmp(verify_server, "NONE") == 0) {
verifyServerProperties = YamlSNIConfig::Property::NONE;
} else {
Warning("%s is invalid for proxy.config.ssl.client.verify.server.properties. Should be one of SIGNATURE, NAME, or ALL",
verify_server);
verifyServerProperties = YamlSNIConfig::Property::NONE;
}
this->SetServerPolicyProperties(verify_server);
ats_free(verify_server);
REC_RegisterConfigUpdateFunc("proxy.config.ssl.client.verify.server.properties", UpdateServerPolicyProperties, nullptr);

ssl_client_cert_filename = nullptr;
ssl_client_cert_path = nullptr;
Expand Down
190 changes: 190 additions & 0 deletions tests/gold_tests/tls/tls_verify4.test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
'''
'''
# 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.

Test.Summary = '''
Test tls server certificate verification options
Specifically update via traffic_ctl (reloadable)
'''

# Define default ATS
ts = Test.MakeATSProcess("ts", select_ports=True, enable_tls=True)
server_foo = Test.MakeOriginServer("server_foo",
ssl=True,
options={"--key": "{0}/signed-foo.key".format(Test.RunDirectory),
"--cert": "{0}/signed-foo.pem".format(Test.RunDirectory)})
server_bar = Test.MakeOriginServer("server_bar",
ssl=True,
options={"--key": "{0}/signed-bar.key".format(Test.RunDirectory),
"--cert": "{0}/signed-bar.pem".format(Test.RunDirectory)})
server = Test.MakeOriginServer("server", ssl=True)

request_foo_header = {"headers": "GET / HTTP/1.1\r\nHost: foo.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
request_bad_foo_header = {"headers": "GET / HTTP/1.1\r\nHost: bad_foo.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
request_bar_header = {"headers": "GET / HTTP/1.1\r\nHost: bar.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
request_bad_bar_header = {"headers": "GET / HTTP/1.1\r\nHost: bad_bar.com\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
response_header = {"headers": "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n", "timestamp": "1469733493.993", "body": ""}
server_foo.addResponse("sessionlog.json", request_foo_header, response_header)
server_foo.addResponse("sessionlog.json", request_bad_foo_header, response_header)
server_bar.addResponse("sessionlog.json", request_bar_header, response_header)
server_bar.addResponse("sessionlog.json", request_bad_bar_header, response_header)

# add ssl materials like key, certificates for the server
ts.addSSLfile("ssl/signed-foo.pem")
ts.addSSLfile("ssl/signed-foo.key")
ts.addSSLfile("ssl/signed-bar.pem")
ts.addSSLfile("ssl/signed-bar.key")
ts.addSSLfile("ssl/server.pem")
ts.addSSLfile("ssl/server.key")
ts.addSSLfile("ssl/signer.pem")
ts.addSSLfile("ssl/signer.key")

ts.Disk.remap_config.AddLine(
'map https://foo.com/ https://127.0.0.1:{0}'.format(server_foo.Variables.SSL_Port))
ts.Disk.remap_config.AddLine(
'map https://bad_foo.com/ https://127.0.0.1:{0}'.format(server_foo.Variables.SSL_Port))
ts.Disk.remap_config.AddLine(
'map https://bar.com/ https://127.0.0.1:{0}'.format(server_bar.Variables.SSL_Port))
ts.Disk.remap_config.AddLine(
'map https://bad_bar.com/ https://127.0.0.1:{0}'.format(server_bar.Variables.SSL_Port))
ts.Disk.remap_config.AddLine(
'map / https://127.0.0.1:{0}'.format(server.Variables.SSL_Port))

ts.Disk.ssl_multicert_config.AddLine(
'dest_ip=* ssl_cert_name=server.pem ssl_key_name=server.key'
)

# Case 1, global config policy=permissive properties=signature
# override for foo.com policy=enforced properties=all
ts.Disk.records_config.update({
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
'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',
# set global policy
'proxy.config.ssl.client.verify.server.policy': 'ENFORCED',
'proxy.config.ssl.client.verify.server.properties': 'ALL',
'proxy.config.ssl.client.CA.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.client.CA.cert.filename': 'signer.pem',
'proxy.config.exec_thread.autoconfig.scale': 1.0,
'proxy.config.url_remap.pristine_host_hdr': 1,
'proxy.config.diags.debug.enabled': 0,
'proxy.config.diags.debug.tags': 'ssl'
})

tr = Test.AddTestRun("default-enforce-bad-sig")
tr.Setup.Copy("ssl/signed-foo.key")
tr.Setup.Copy("ssl/signed-foo.pem")
tr.Setup.Copy("ssl/signed-bar.key")
tr.Setup.Copy("ssl/signed-bar.pem")
tr.Processes.Default.Command = "curl -k -H \"host: random2.com\" https://127.0.0.1:{0}".format(ts.Variables.ssl_port)
tr.ReturnCode = 0
tr.Processes.Default.StartBefore(server_foo)
tr.Processes.Default.StartBefore(server_bar)
tr.Processes.Default.StartBefore(server)
tr.Processes.Default.StartBefore(Test.Processes.ts)
tr.StillRunningAfter = server
tr.StillRunningAfter = ts
tr.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Curl attempt should have failed")

tr2 = Test.AddTestRun("Update config files")
recordspath = ts.Disk.records_config.AbsPath
# recreate the records.config with the cert filename changed
tr2.Disk.File(recordspath, id="records_config", typename="ats:config:records"),
tr2.Disk.records_config.update({
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
'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',
# set global policy
'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE',
'proxy.config.ssl.client.verify.server.properties': 'ALL',
'proxy.config.ssl.client.CA.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.client.CA.cert.filename': 'signer.pem',
'proxy.config.exec_thread.autoconfig.scale': 1.0,
'proxy.config.url_remap.pristine_host_hdr': 1,
'proxy.config.diags.debug.enabled': 0,
'proxy.config.diags.debug.tags': 'ssl'
})
tr2.StillRunningAfter = ts
tr2.StillRunningAfter = server
tr2.Processes.Default.Command = 'echo Updated configs'
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
tr2.Processes.Default.Env = ts.Env
tr2.Processes.Default.ReturnCode = 0

# Change the config to PERMISSIVE. Same command should work now
trreload = Test.AddTestRun("Reload config")
trreload.StillRunningAfter = ts
trreload.StillRunningAfter = server
# takes a few seconds for the reload to be ready for the next connection
trreload.Processes.Default.Command = 'traffic_ctl config reload; sleep 5'
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
trreload.Processes.Default.Env = ts.Env
trreload.Processes.Default.ReturnCode = 0

tragain = Test.AddTestRun("permissive-after-update")
tragain.Processes.Default.Command = "curl -k -H \"host: random3.com\" https://127.0.0.1:{0}".format(ts.Variables.ssl_port)
tragain.ReturnCode = 0
tragain.StillRunningAfter = server
tragain.StillRunningAfter = ts
tragain.Processes.Default.Streams.stdout = Testers.ExcludesExpression("Could Not Connect", "Curl attempt should have succeeded")

tr2 = Test.AddTestRun("Update config files to enforced")
recordspath = ts.Disk.records_config.AbsPath
# recreate the records.config with the cert filename changed
tr2.Disk.File(recordspath, id="records_config", typename="ats:config:records"),
tr2.Disk.records_config.update({
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path': '{0}'.format(ts.Variables.SSLDir),
'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',
# set global policy
'proxy.config.ssl.client.verify.server.policy': 'ENFORCED',
'proxy.config.ssl.client.verify.server.properties': 'ALL',
'proxy.config.ssl.client.CA.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.client.CA.cert.filename': 'signer.pem',
'proxy.config.exec_thread.autoconfig.scale': 1.0,
'proxy.config.url_remap.pristine_host_hdr': 1,
'proxy.config.diags.debug.enabled': 0,
'proxy.config.diags.debug.tags': 'ssl'
})
tr2.StillRunningAfter = ts
tr2.StillRunningAfter = server
tr2.Processes.Default.Command = 'echo Updated configs to ENFORCED'
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
tr2.Processes.Default.Env = ts.Env
tr2.Processes.Default.ReturnCode = 0

trreload = Test.AddTestRun("Reload config again")
trreload.StillRunningAfter = ts
trreload.StillRunningAfter = server
# takes a few seconds for the reload to be ready for the next connection
trreload.Processes.Default.Command = 'traffic_ctl config reload; sleep 5'
# Need to copy over the environment so traffic_ctl knows where to find the unix domain socket
trreload.Processes.Default.Env = ts.Env
trreload.Processes.Default.ReturnCode = 0

tragain = Test.AddTestRun("enforced-after-update")
tragain.Processes.Default.Command = "curl -k -H \"host: random4.com\" https://127.0.0.1:{0}".format(ts.Variables.ssl_port)
tragain.ReturnCode = 0
tragain.StillRunningAfter = server
tragain.StillRunningAfter = ts
tragain.Processes.Default.Streams.stdout = Testers.ContainsExpression("Could Not Connect", "Curl attempt should have succeeded")

# No name checking for the sig-only permissive override for bad_bar
ts.Disk.diags_log.Content += Testers.ContainsExpression(
"Core server certificate verification failed for \(random3.com\). Action=Continue", "Permissive can connect")
ts.Disk.diags_log.Content += Testers.ContainsExpression(
"Core server certificate verification failed for \(random2.com\). Action=Terminate", "Enforced cannot connect")