nghttpx: Add --client-psk-secret option to enable PSK in backend
This commit is contained in:
parent
83c759572c
commit
79a24f5dd9
|
@ -154,6 +154,7 @@ OPTIONS = [
|
||||||
"dns-max-try",
|
"dns-max-try",
|
||||||
"frontend-keep-alive-timeout",
|
"frontend-keep-alive-timeout",
|
||||||
"psk-secrets",
|
"psk-secrets",
|
||||||
|
"client-psk-secrets",
|
||||||
]
|
]
|
||||||
|
|
||||||
LOGVARS = [
|
LOGVARS = [
|
||||||
|
|
12
src/shrpx.cc
12
src/shrpx.cc
|
@ -2112,6 +2112,13 @@ SSL/TLS:
|
||||||
are skipped. The default enabled cipher list might not
|
are skipped. The default enabled cipher list might not
|
||||||
contain any PSK cipher suite. In that case, desired PSK
|
contain any PSK cipher suite. In that case, desired PSK
|
||||||
cipher suites must be enabled using --ciphers option.
|
cipher suites must be enabled using --ciphers option.
|
||||||
|
--client-psk-secrets=<PATH>
|
||||||
|
Read PSK identity and secrets from <PATH>. This is used
|
||||||
|
for backend connection. The each line of input file is
|
||||||
|
formatted as <identity>:<hex-secret>, where <identity>
|
||||||
|
is PSK identity, and <hex-secret> is secret in hex. An
|
||||||
|
empty line, and line which starts with '#' are skipped.
|
||||||
|
The first identity and secret pair encountered is used.
|
||||||
|
|
||||||
HTTP/2 and SPDY:
|
HTTP/2 and SPDY:
|
||||||
-c, --frontend-http2-max-concurrent-streams=<N>
|
-c, --frontend-http2-max-concurrent-streams=<N>
|
||||||
|
@ -3088,6 +3095,7 @@ int main(int argc, char **argv) {
|
||||||
{SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
|
{SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT.c_str(), required_argument,
|
||||||
&flag, 146},
|
&flag, 146},
|
||||||
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
|
{SHRPX_OPT_PSK_SECRETS.c_str(), required_argument, &flag, 147},
|
||||||
|
{SHRPX_OPT_CLIENT_PSK_SECRETS.c_str(), required_argument, &flag, 148},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
|
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
|
@ -3782,6 +3790,10 @@ int main(int argc, char **argv) {
|
||||||
// --psk-secrets
|
// --psk-secrets
|
||||||
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
|
cmdcfgs.emplace_back(SHRPX_OPT_PSK_SECRETS, StringRef{optarg});
|
||||||
break;
|
break;
|
||||||
|
case 148:
|
||||||
|
// --client-psk-secrets
|
||||||
|
cmdcfgs.emplace_back(SHRPX_OPT_CLIENT_PSK_SECRETS, StringRef{optarg});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1263,6 +1263,66 @@ int parse_psk_secrets(Config *config, const StringRef &path) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Reads PSK secrets from path, and parses each line. The result is
|
||||||
|
// directly stored into config->tls.client_psk. This function returns
|
||||||
|
// 0 if it succeeds, or -1.
|
||||||
|
int parse_client_psk_secrets(Config *config, const StringRef &path) {
|
||||||
|
auto &tlsconf = config->tls;
|
||||||
|
|
||||||
|
std::ifstream f(path.c_str(), std::ios::binary);
|
||||||
|
if (!f) {
|
||||||
|
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": could not open file "
|
||||||
|
<< path;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t lineno = 0;
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(f, line)) {
|
||||||
|
++lineno;
|
||||||
|
if (line.empty() || line[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sep_it = std::find(std::begin(line), std::end(line), ':');
|
||||||
|
if (sep_it == std::end(line)) {
|
||||||
|
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
|
||||||
|
<< ": could not fine separator at line " << lineno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sep_it == std::begin(line)) {
|
||||||
|
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty identity at line "
|
||||||
|
<< lineno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sep_it + 1 == std::end(line)) {
|
||||||
|
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS << ": empty secret at line "
|
||||||
|
<< lineno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!util::is_hex_string(StringRef{sep_it + 1, std::end(line)})) {
|
||||||
|
LOG(ERROR) << SHRPX_OPT_CLIENT_PSK_SECRETS
|
||||||
|
<< ": secret must be hex string at line " << lineno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsconf.client_psk.identity =
|
||||||
|
make_string_ref(config->balloc, StringRef{std::begin(line), sep_it});
|
||||||
|
|
||||||
|
tlsconf.client_psk.secret =
|
||||||
|
util::decode_hex(config->balloc, StringRef{sep_it + 1, std::end(line)});
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// generated by gennghttpxfun.py
|
// generated by gennghttpxfun.py
|
||||||
int option_lookup_token(const char *name, size_t namelen) {
|
int option_lookup_token(const char *name, size_t namelen) {
|
||||||
switch (namelen) {
|
switch (namelen) {
|
||||||
|
@ -1620,6 +1680,11 @@ int option_lookup_token(const char *name, size_t namelen) {
|
||||||
return SHRPX_OPTID_ADD_REQUEST_HEADER;
|
return SHRPX_OPTID_ADD_REQUEST_HEADER;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
if (util::strieq_l("client-psk-secret", name, 17)) {
|
||||||
|
return SHRPX_OPTID_CLIENT_PSK_SECRETS;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
if (util::strieq_l("dns-lookup-timeou", name, 17)) {
|
if (util::strieq_l("dns-lookup-timeou", name, 17)) {
|
||||||
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
|
return SHRPX_OPTID_DNS_LOOKUP_TIMEOUT;
|
||||||
|
@ -3207,6 +3272,8 @@ int parse_config(Config *config, int optid, const StringRef &opt,
|
||||||
optarg);
|
optarg);
|
||||||
case SHRPX_OPTID_PSK_SECRETS:
|
case SHRPX_OPTID_PSK_SECRETS:
|
||||||
return parse_psk_secrets(config, optarg);
|
return parse_psk_secrets(config, optarg);
|
||||||
|
case SHRPX_OPTID_CLIENT_PSK_SECRETS:
|
||||||
|
return parse_client_psk_secrets(config, optarg);
|
||||||
case SHRPX_OPTID_CONF:
|
case SHRPX_OPTID_CONF:
|
||||||
LOG(WARN) << "conf: ignored";
|
LOG(WARN) << "conf: ignored";
|
||||||
|
|
||||||
|
|
|
@ -320,6 +320,8 @@ constexpr auto SHRPX_OPT_DNS_MAX_TRY = StringRef::from_lit("dns-max-try");
|
||||||
constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
|
constexpr auto SHRPX_OPT_FRONTEND_KEEP_ALIVE_TIMEOUT =
|
||||||
StringRef::from_lit("frontend-keep-alive-timeout");
|
StringRef::from_lit("frontend-keep-alive-timeout");
|
||||||
constexpr auto SHRPX_OPT_PSK_SECRETS = StringRef::from_lit("psk-secrets");
|
constexpr auto SHRPX_OPT_PSK_SECRETS = StringRef::from_lit("psk-secrets");
|
||||||
|
constexpr auto SHRPX_OPT_CLIENT_PSK_SECRETS =
|
||||||
|
StringRef::from_lit("client-psk-secrets");
|
||||||
|
|
||||||
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
constexpr size_t SHRPX_OBFUSCATED_NODE_LENGTH = 8;
|
||||||
|
|
||||||
|
@ -550,6 +552,13 @@ struct TLSConfig {
|
||||||
StringRef cert_file;
|
StringRef cert_file;
|
||||||
} client;
|
} client;
|
||||||
|
|
||||||
|
// Client PSK configuration
|
||||||
|
struct {
|
||||||
|
// identity must be NULL terminated string.
|
||||||
|
StringRef identity;
|
||||||
|
StringRef secret;
|
||||||
|
} client_psk;
|
||||||
|
|
||||||
// PSK secrets. The key is identity, and the associated value is
|
// PSK secrets. The key is identity, and the associated value is
|
||||||
// its secret.
|
// its secret.
|
||||||
std::map<StringRef, StringRef> psk_secrets;
|
std::map<StringRef, StringRef> psk_secrets;
|
||||||
|
@ -918,6 +927,7 @@ enum {
|
||||||
SHRPX_OPTID_CLIENT_CERT_FILE,
|
SHRPX_OPTID_CLIENT_CERT_FILE,
|
||||||
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
|
SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE,
|
||||||
SHRPX_OPTID_CLIENT_PROXY,
|
SHRPX_OPTID_CLIENT_PROXY,
|
||||||
|
SHRPX_OPTID_CLIENT_PSK_SECRETS,
|
||||||
SHRPX_OPTID_CONF,
|
SHRPX_OPTID_CONF,
|
||||||
SHRPX_OPTID_DAEMON,
|
SHRPX_OPTID_DAEMON,
|
||||||
SHRPX_OPTID_DH_PARAM_FILE,
|
SHRPX_OPTID_DH_PARAM_FILE,
|
||||||
|
|
|
@ -549,6 +549,39 @@ unsigned int psk_server_cb(SSL *ssl, const char *identity, unsigned char *psk,
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
unsigned int psk_client_cb(SSL *ssl, const char *hint, char *identity_out,
|
||||||
|
unsigned int max_identity_len, unsigned char *psk,
|
||||||
|
unsigned int max_psk_len) {
|
||||||
|
auto config = get_config();
|
||||||
|
auto &tlsconf = config->tls;
|
||||||
|
|
||||||
|
auto &identity = tlsconf.client_psk.identity;
|
||||||
|
auto &secret = tlsconf.client_psk.secret;
|
||||||
|
|
||||||
|
if (identity.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identity.size() + 1 > max_identity_len) {
|
||||||
|
LOG(ERROR) << "The size of PSK identity is " << identity.size()
|
||||||
|
<< ", but the acceptable maximum size is " << max_identity_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secret.size() > max_psk_len) {
|
||||||
|
LOG(ERROR) << "The size of PSK secret is " << secret.size()
|
||||||
|
<< ", but the acceptable maximum size is " << max_psk_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*std::copy(std::begin(identity), std::end(identity), identity_out) = '\0';
|
||||||
|
std::copy(std::begin(secret), std::end(secret), psk);
|
||||||
|
|
||||||
|
return (unsigned int)secret.size();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
struct TLSProtocol {
|
struct TLSProtocol {
|
||||||
StringRef name;
|
StringRef name;
|
||||||
long int mask;
|
long int mask;
|
||||||
|
@ -899,6 +932,8 @@ SSL_CTX *create_ssl_client_context(
|
||||||
#endif // HAVE_NEVERBLEED
|
#endif // HAVE_NEVERBLEED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb);
|
||||||
|
|
||||||
// NPN selection callback. This is required to set SSL_CTX because
|
// NPN selection callback. This is required to set SSL_CTX because
|
||||||
// OpenSSL does not offer SSL_set_next_proto_select_cb.
|
// OpenSSL does not offer SSL_set_next_proto_select_cb.
|
||||||
SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
|
SSL_CTX_set_next_proto_select_cb(ssl_ctx, next_proto_select_cb, nullptr);
|
||||||
|
@ -1181,8 +1216,10 @@ int verify_hostname(X509 *cert, const StringRef &hostname,
|
||||||
int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
|
int check_cert(SSL *ssl, const Address *addr, const StringRef &host) {
|
||||||
auto cert = SSL_get_peer_certificate(ssl);
|
auto cert = SSL_get_peer_certificate(ssl);
|
||||||
if (!cert) {
|
if (!cert) {
|
||||||
LOG(ERROR) << "No certificate found";
|
// By the protocol definition, TLS server always sends certificate
|
||||||
return -1;
|
// if it has. If certificate cannot be retrieved, authentication
|
||||||
|
// without certificate is used, such as PSK.
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
auto cert_deleter = defer(X509_free, cert);
|
auto cert_deleter = defer(X509_free, cert);
|
||||||
auto verify_res = SSL_get_verify_result(ssl);
|
auto verify_res = SSL_get_verify_result(ssl);
|
||||||
|
|
Loading…
Reference in New Issue