mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-28 17:57:05 +02:00
Mutual SSL authentication
This commit is contained in:
parent
be301c6931
commit
85e8d02e5b
9 changed files with 164 additions and 30 deletions
|
@ -535,7 +535,7 @@ ssl_websocket& ClientConnectionWss::getWs()
|
||||||
return ssl_ws_.value();
|
return ssl_ws_.value();
|
||||||
|
|
||||||
ssl_ws_.emplace(strand_, ssl_context_);
|
ssl_ws_.emplace(strand_, ssl_context_);
|
||||||
if (server_.certificate.has_value())
|
if (server_.server_certificate.has_value())
|
||||||
{
|
{
|
||||||
ssl_ws_->next_layer().set_verify_mode(boost::asio::ssl::verify_peer);
|
ssl_ws_->next_layer().set_verify_mode(boost::asio::ssl::verify_peer);
|
||||||
ssl_ws_->next_layer().set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx)
|
ssl_ws_->next_layer().set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx)
|
||||||
|
@ -551,7 +551,7 @@ ssl_websocket& ClientConnectionWss::getWs()
|
||||||
char subject_name[256];
|
char subject_name[256];
|
||||||
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
|
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
|
||||||
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
|
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
|
||||||
LOG(INFO, LOG_TAG) << "verifying cert: '" << subject_name << "', pre verified: " << preverified << "\n";
|
LOG(INFO, LOG_TAG) << "Verifying cert: '" << subject_name << "', pre verified: " << preverified << "\n";
|
||||||
|
|
||||||
return preverified;
|
return preverified;
|
||||||
});
|
});
|
||||||
|
|
|
@ -67,7 +67,13 @@ struct ClientSettings
|
||||||
/// server port
|
/// server port
|
||||||
size_t port{1704};
|
size_t port{1704};
|
||||||
/// server certificate
|
/// server certificate
|
||||||
std::optional<std::filesystem::path> certificate;
|
std::optional<std::filesystem::path> server_certificate;
|
||||||
|
/// Certificate file
|
||||||
|
std::filesystem::path certificate;
|
||||||
|
/// Private key file
|
||||||
|
std::filesystem::path certificate_key;
|
||||||
|
/// Password for encrypted key file
|
||||||
|
std::string key_password;
|
||||||
/// Is ssl in use?
|
/// Is ssl in use?
|
||||||
bool isSsl() const
|
bool isSsl() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -80,17 +80,42 @@ Controller::Controller(boost::asio::io_context& io_context, const ClientSettings
|
||||||
: io_context_(io_context), ssl_context_(boost::asio::ssl::context::tlsv12_client), timer_(io_context), settings_(settings), stream_(nullptr),
|
: io_context_(io_context), ssl_context_(boost::asio::ssl::context::tlsv12_client), timer_(io_context), settings_(settings), stream_(nullptr),
|
||||||
decoder_(nullptr), player_(nullptr), serverSettings_(nullptr)
|
decoder_(nullptr), player_(nullptr), serverSettings_(nullptr)
|
||||||
{
|
{
|
||||||
if (settings.server.isSsl() && settings.server.certificate.has_value())
|
if (settings.server.isSsl())
|
||||||
{
|
{
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
ssl_context_.set_default_verify_paths(ec);
|
if (settings.server.server_certificate.has_value())
|
||||||
if (ec.failed())
|
|
||||||
LOG(WARNING, LOG_TAG) << "Failed to load system certificates: " << ec << "\n";
|
|
||||||
if (!settings.server.certificate->empty())
|
|
||||||
{
|
{
|
||||||
ssl_context_.load_verify_file(settings.server.certificate.value().string(), ec);
|
LOG(DEBUG, LOG_TAG) << "Loading server certificate\n";
|
||||||
|
ssl_context_.set_default_verify_paths(ec);
|
||||||
if (ec.failed())
|
if (ec.failed())
|
||||||
throw SnapException("Failed to load certificate: " + settings.server.certificate.value().string() + ": " + ec.message());
|
LOG(WARNING, LOG_TAG) << "Failed to load system certificates: " << ec << "\n";
|
||||||
|
if (!settings.server.server_certificate->empty())
|
||||||
|
{
|
||||||
|
ssl_context_.load_verify_file(settings.server.server_certificate.value().string(), ec);
|
||||||
|
if (ec.failed())
|
||||||
|
throw SnapException("Failed to load server certificate: " + settings.server.server_certificate.value().string() + ": " + ec.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.server.certificate.empty() && !settings.server.certificate_key.empty())
|
||||||
|
{
|
||||||
|
if (!settings.server.key_password.empty())
|
||||||
|
{
|
||||||
|
ssl_context_.set_password_callback(
|
||||||
|
[pw = settings.server.key_password](size_t max_length, boost::asio::ssl::context_base::password_purpose purpose) -> string
|
||||||
|
{
|
||||||
|
LOG(DEBUG, LOG_TAG) << "getPassword, purpose: " << purpose << ", max length: " << max_length << "\n";
|
||||||
|
return pw;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
LOG(DEBUG, LOG_TAG) << "Loading certificate file: " << settings.server.certificate << "\n";
|
||||||
|
ssl_context_.use_certificate_chain_file(settings.server.certificate.string(), ec);
|
||||||
|
if (ec.failed())
|
||||||
|
throw SnapException("Failed to load certificate: " + settings.server.certificate.string() + ": " + ec.message());
|
||||||
|
LOG(DEBUG, LOG_TAG) << "Loading certificate key file: " << settings.server.certificate_key << "\n";
|
||||||
|
ssl_context_.use_private_key_file(settings.server.certificate_key.string(), boost::asio::ssl::context::pem, ec);
|
||||||
|
if (ec.failed())
|
||||||
|
throw SnapException("Failed to load private key file: " + settings.server.certificate_key.string() + ": " + ec.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,12 @@ int main(int argc, char** argv)
|
||||||
auto port_opt = op.add<Value<size_t>>("p", "port", "(deprecated, use [url]) Server port", 1704, &settings.server.port);
|
auto port_opt = op.add<Value<size_t>>("p", "port", "(deprecated, use [url]) Server port", 1704, &settings.server.port);
|
||||||
op.add<Value<size_t>>("i", "instance", "Instance id when running multiple instances on the same host", 1, &settings.instance);
|
op.add<Value<size_t>>("i", "instance", "Instance id when running multiple instances on the same host", 1, &settings.instance);
|
||||||
op.add<Value<string>>("", "hostID", "Unique host id, default is MAC address", "", &settings.host_id);
|
op.add<Value<string>>("", "hostID", "Unique host id, default is MAC address", "", &settings.host_id);
|
||||||
auto server_cert_opt = op.add<Implicit<std::filesystem::path>>("", "server-cert", "Verify server with certificate", "default certificates");
|
auto server_cert_opt =
|
||||||
|
op.add<Implicit<std::filesystem::path>>("", "server-cert", "Verify server with certificate (PEM format)", "default certificates");
|
||||||
|
op.add<Value<std::filesystem::path>>("", "cert", "Client certificate file (PEM format)", settings.server.certificate, &settings.server.certificate);
|
||||||
|
op.add<Value<std::filesystem::path>>("", "cert-key", "Client private key file (PEM format)", settings.server.certificate_key,
|
||||||
|
&settings.server.certificate_key);
|
||||||
|
op.add<Value<string>>("", "key-password", "Key password (for encrypted private key)", settings.server.key_password, &settings.server.key_password);
|
||||||
|
|
||||||
// PCM device specific
|
// PCM device specific
|
||||||
#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI)
|
#if defined(HAS_ALSA) || defined(HAS_PULSE) || defined(HAS_WASAPI)
|
||||||
|
@ -349,13 +354,28 @@ int main(int argc, char** argv)
|
||||||
if (server_cert_opt->is_set())
|
if (server_cert_opt->is_set())
|
||||||
{
|
{
|
||||||
if (server_cert_opt->get_default() == server_cert_opt->value())
|
if (server_cert_opt->get_default() == server_cert_opt->value())
|
||||||
settings.server.certificate = "";
|
settings.server.server_certificate = "";
|
||||||
else
|
else
|
||||||
settings.server.certificate = std::filesystem::weakly_canonical(server_cert_opt->value());
|
settings.server.server_certificate = std::filesystem::weakly_canonical(server_cert_opt->value());
|
||||||
if (settings.server.certificate.value_or("").empty())
|
if (settings.server.server_certificate.value_or("").empty())
|
||||||
LOG(INFO, LOG_TAG) << "Server certificate: default certificates\n";
|
LOG(INFO, LOG_TAG) << "Server certificate: default certificates\n";
|
||||||
else
|
else
|
||||||
LOG(INFO, LOG_TAG) << "Server certificate: " << settings.server.certificate.value_or("") << "\n";
|
LOG(INFO, LOG_TAG) << "Server certificate: " << settings.server.server_certificate.value_or("") << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!settings.server.certificate.empty() && !settings.server.certificate_key.empty())
|
||||||
|
{
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
settings.server.certificate = fs::weakly_canonical(settings.server.certificate);
|
||||||
|
if (!fs::exists(settings.server.certificate))
|
||||||
|
throw SnapException("Certificate file not found: " + settings.server.certificate.native());
|
||||||
|
settings.server.certificate_key = fs::weakly_canonical(settings.server.certificate_key);
|
||||||
|
if (!fs::exists(settings.server.certificate_key))
|
||||||
|
throw SnapException("Certificate_key file not found: " + settings.server.certificate_key.native());
|
||||||
|
}
|
||||||
|
else if (settings.server.certificate.empty() != settings.server.certificate_key.empty())
|
||||||
|
{
|
||||||
|
throw SnapException("Both SSL 'certificate' and 'certificate_key' must be set or empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(HAS_AVAHI) && !defined(HAS_BONJOUR)
|
#if !defined(HAS_AVAHI) && !defined(HAS_BONJOUR)
|
||||||
|
@ -500,6 +520,7 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
int num_threads = 0;
|
int num_threads = 0;
|
||||||
std::vector<std::thread> threads;
|
std::vector<std::thread> threads;
|
||||||
|
threads.reserve(num_threads);
|
||||||
for (int n = 0; n < num_threads; ++n)
|
for (int n = 0; n < num_threads; ++n)
|
||||||
threads.emplace_back([&] { io_context.run(); });
|
threads.emplace_back([&] { io_context.run(); });
|
||||||
io_context.run();
|
io_context.run();
|
||||||
|
|
|
@ -483,7 +483,7 @@ public:
|
||||||
std::string print(const Attribute& max_attribute = Attribute::optional) const override;
|
std::string print(const Attribute& max_attribute = Attribute::optional) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string to_string(Option_ptr option) const;
|
std::string to_string(const Option_ptr& option) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ public:
|
||||||
std::string print(const Attribute& max_attribute = Attribute::optional) const override;
|
std::string print(const Attribute& max_attribute = Attribute::optional) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string to_string(Option_ptr option) const;
|
std::string to_string(const Option_ptr& option) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1122,7 +1122,7 @@ inline ConsoleOptionPrinter::ConsoleOptionPrinter(const OptionParser* option_par
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline std::string ConsoleOptionPrinter::to_string(Option_ptr option) const
|
inline std::string ConsoleOptionPrinter::to_string(const Option_ptr& option) const
|
||||||
{
|
{
|
||||||
std::stringstream line;
|
std::stringstream line;
|
||||||
if (option->short_name() != 0)
|
if (option->short_name() != 0)
|
||||||
|
@ -1142,7 +1142,7 @@ inline std::string ConsoleOptionPrinter::to_string(Option_ptr option) const
|
||||||
std::stringstream defaultStr;
|
std::stringstream defaultStr;
|
||||||
if (option->get_default(defaultStr))
|
if (option->get_default(defaultStr))
|
||||||
{
|
{
|
||||||
if (!defaultStr.str().empty())
|
if (!defaultStr.str().empty() && (defaultStr.str() != "\"\""))
|
||||||
line << " (=" << defaultStr.str() << ")";
|
line << " (=" << defaultStr.str() << ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1216,7 +1216,7 @@ inline GroffOptionPrinter::GroffOptionPrinter(const OptionParser* option_parser)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline std::string GroffOptionPrinter::to_string(Option_ptr option) const
|
inline std::string GroffOptionPrinter::to_string(const Option_ptr& option) const
|
||||||
{
|
{
|
||||||
std::stringstream line;
|
std::stringstream line;
|
||||||
if (option->short_name() != 0)
|
if (option->short_name() != 0)
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
// local headers
|
// local headers
|
||||||
#include "common/aixlog.hpp"
|
#include "common/aixlog.hpp"
|
||||||
#include "common/json.hpp"
|
#include "common/json.hpp"
|
||||||
|
#include "common/snap_exception.hpp"
|
||||||
#include "control_session_http.hpp"
|
#include "control_session_http.hpp"
|
||||||
#include "control_session_tcp.hpp"
|
#include "control_session_tcp.hpp"
|
||||||
#include "server_settings.hpp"
|
#include "server_settings.hpp"
|
||||||
|
@ -54,10 +55,50 @@ ControlServer::ControlServer(boost::asio::io_context& io_context, const ServerSe
|
||||||
return pw;
|
return pw;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ssl.certificate.empty() && !ssl.certificate_key.empty())
|
if (!ssl.certificate.empty() && !ssl.certificate_key.empty())
|
||||||
{
|
{
|
||||||
ssl_context_.use_certificate_chain_file(ssl.certificate);
|
boost::system::error_code ec;
|
||||||
ssl_context_.use_private_key_file(ssl.certificate_key, boost::asio::ssl::context::pem);
|
ssl_context_.use_certificate_chain_file(ssl.certificate, ec);
|
||||||
|
if (ec.failed())
|
||||||
|
throw SnapException("Failed to load certificate: " + settings.ssl.certificate.string() + ": " + ec.message());
|
||||||
|
ssl_context_.use_private_key_file(ssl.certificate_key, boost::asio::ssl::context::pem, ec);
|
||||||
|
if (ec.failed())
|
||||||
|
throw SnapException("Failed to load private key file: " + settings.ssl.certificate_key.string() + ": " + ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.ssl.verify_clients)
|
||||||
|
{
|
||||||
|
boost::system::error_code ec;
|
||||||
|
ssl_context_.set_default_verify_paths(ec);
|
||||||
|
if (ec.failed())
|
||||||
|
LOG(WARNING, LOG_TAG) << "Failed to load system certificates: " << ec << "\n";
|
||||||
|
for (const auto& cert_path : settings_.ssl.client_certs)
|
||||||
|
{
|
||||||
|
LOG(DEBUG, LOG_TAG) << "Loading client certificate: " << cert_path << "\n";
|
||||||
|
ssl_context_.load_verify_file(cert_path.string(), ec);
|
||||||
|
if (ec.failed())
|
||||||
|
throw SnapException("Failed to load client certificate: " + cert_path.string() + ": " + ec.message());
|
||||||
|
}
|
||||||
|
|
||||||
|
ssl_context_.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert);
|
||||||
|
ssl_context_.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx)
|
||||||
|
{
|
||||||
|
// The verify callback can be used to check whether the certificate that is
|
||||||
|
// being presented is valid for the peer. For example, RFC 2818 describes
|
||||||
|
// the steps involved in doing this for HTTPS. Consult the OpenSSL
|
||||||
|
// documentation for more details. Note that the callback is called once
|
||||||
|
// for each certificate in the certificate chain, starting from the root
|
||||||
|
// certificate authority.
|
||||||
|
|
||||||
|
// In this example we will simply print the certificate's subject name.
|
||||||
|
char subject_name[256];
|
||||||
|
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
|
||||||
|
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
|
||||||
|
LOG(INFO, LOG_TAG) << "Verifying cert: '" << subject_name << "', pre verified: " << preverified << "\n";
|
||||||
|
|
||||||
|
return preverified;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// ssl_context_.use_tmp_dh_file("dh4096.pem");
|
// ssl_context_.use_tmp_dh_file("dh4096.pem");
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,13 @@
|
||||||
# Password for decryption of the certificate_key (only needed for encrypted certificate_key file)
|
# Password for decryption of the certificate_key (only needed for encrypted certificate_key file)
|
||||||
#key_password =
|
#key_password =
|
||||||
|
|
||||||
|
# Verify client certificates
|
||||||
|
#verify_clients = false
|
||||||
|
|
||||||
|
# List of client CA certificate files, can be configured multiple times
|
||||||
|
#client_cert =
|
||||||
|
#client_cert =
|
||||||
|
|
||||||
#
|
#
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
|
|
@ -31,27 +31,43 @@
|
||||||
|
|
||||||
struct ServerSettings
|
struct ServerSettings
|
||||||
{
|
{
|
||||||
|
/// Launch settings
|
||||||
struct Server
|
struct Server
|
||||||
{
|
{
|
||||||
|
/// Number of worker threads
|
||||||
int threads{-1};
|
int threads{-1};
|
||||||
|
/// PID file, if running as daemon
|
||||||
std::string pid_file{"/var/run/snapserver/pid"};
|
std::string pid_file{"/var/run/snapserver/pid"};
|
||||||
|
/// User when running as deaemon
|
||||||
std::string user{"snapserver"};
|
std::string user{"snapserver"};
|
||||||
|
/// Group when running as deaemon
|
||||||
std::string group;
|
std::string group;
|
||||||
|
/// Server data dir
|
||||||
std::string data_dir;
|
std::string data_dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// SSL settings
|
||||||
struct Ssl
|
struct Ssl
|
||||||
{
|
{
|
||||||
|
/// Certificate file
|
||||||
std::filesystem::path certificate;
|
std::filesystem::path certificate;
|
||||||
|
/// Private key file
|
||||||
std::filesystem::path certificate_key;
|
std::filesystem::path certificate_key;
|
||||||
|
/// Password for encrypted key file
|
||||||
std::string key_password;
|
std::string key_password;
|
||||||
|
/// Verify client certificates
|
||||||
|
bool verify_clients = false;
|
||||||
|
/// Client CA certificates
|
||||||
|
std::vector<std::filesystem::path> client_certs;
|
||||||
|
|
||||||
|
/// @return if SSL is enabled
|
||||||
bool enabled() const
|
bool enabled() const
|
||||||
{
|
{
|
||||||
return !certificate.empty() && !certificate_key.empty();
|
return !certificate.empty() && !certificate_key.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// User settings
|
||||||
struct User
|
struct User
|
||||||
{
|
{
|
||||||
explicit User(const std::string& user_permissions_password)
|
explicit User(const std::string& user_permissions_password)
|
||||||
|
@ -67,8 +83,8 @@ struct ServerSettings
|
||||||
std::string password;
|
std::string password;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<User> users;
|
|
||||||
|
|
||||||
|
/// HTTP settings
|
||||||
struct Http
|
struct Http
|
||||||
{
|
{
|
||||||
bool enabled{true};
|
bool enabled{true};
|
||||||
|
@ -82,6 +98,7 @@ struct ServerSettings
|
||||||
std::string url_prefix;
|
std::string url_prefix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// TCP streaming client settings
|
||||||
struct Tcp
|
struct Tcp
|
||||||
{
|
{
|
||||||
bool enabled{true};
|
bool enabled{true};
|
||||||
|
@ -89,6 +106,7 @@ struct ServerSettings
|
||||||
std::vector<std::string> bind_to_address{{"::"}};
|
std::vector<std::string> bind_to_address{{"::"}};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Stream settings
|
||||||
struct Stream
|
struct Stream
|
||||||
{
|
{
|
||||||
size_t port{1704};
|
size_t port{1704};
|
||||||
|
@ -102,22 +120,28 @@ struct ServerSettings
|
||||||
std::vector<std::string> bind_to_address{{"::"}};
|
std::vector<std::string> bind_to_address{{"::"}};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Client settings
|
||||||
struct StreamingClient
|
struct StreamingClient
|
||||||
{
|
{
|
||||||
|
/// Initial volume of new clients
|
||||||
uint16_t initialVolume{100};
|
uint16_t initialVolume{100};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Logging settings
|
||||||
struct Logging
|
struct Logging
|
||||||
{
|
{
|
||||||
|
/// log sing
|
||||||
std::string sink;
|
std::string sink;
|
||||||
|
/// log filter
|
||||||
std::string filter{"*:info"};
|
std::string filter{"*:info"};
|
||||||
};
|
};
|
||||||
|
|
||||||
Server server;
|
Server server; ///< Server settings
|
||||||
Ssl ssl;
|
Ssl ssl; ///< SSL settings
|
||||||
Http http;
|
std::vector<User> users; ///< User settings
|
||||||
Tcp tcp;
|
Http http; ///< HTTP settings
|
||||||
Stream stream;
|
Tcp tcp; ///< TCP settings
|
||||||
StreamingClient streamingclient;
|
Stream stream; ///< Stream settings
|
||||||
Logging logging;
|
StreamingClient streamingclient; ///< Client settings
|
||||||
|
Logging logging; ///< Logging settings
|
||||||
};
|
};
|
||||||
|
|
|
@ -86,6 +86,9 @@ int main(int argc, char* argv[])
|
||||||
conf.add<Value<std::filesystem::path>>("", "ssl.certificate_key", "private key file (PEM format)", settings.ssl.certificate_key,
|
conf.add<Value<std::filesystem::path>>("", "ssl.certificate_key", "private key file (PEM format)", settings.ssl.certificate_key,
|
||||||
&settings.ssl.certificate_key);
|
&settings.ssl.certificate_key);
|
||||||
conf.add<Value<string>>("", "ssl.key_password", "key password (for encrypted private key)", settings.ssl.key_password, &settings.ssl.key_password);
|
conf.add<Value<string>>("", "ssl.key_password", "key password (for encrypted private key)", settings.ssl.key_password, &settings.ssl.key_password);
|
||||||
|
conf.add<Value<bool>>("", "ssl.verify_clients", "Verify client certificates", settings.ssl.verify_clients, &settings.ssl.verify_clients);
|
||||||
|
auto client_cert_opt =
|
||||||
|
conf.add<Value<std::filesystem::path>>("", "ssl.client_cert", "List of client CA certificate files, can be configured multiple times", "");
|
||||||
|
|
||||||
#if 0 // feature: users
|
#if 0 // feature: users
|
||||||
// Users setting
|
// Users setting
|
||||||
|
@ -276,6 +279,13 @@ int main(int argc, char* argv[])
|
||||||
settings.ssl.certificate_key = make_absolute(settings.ssl.certificate_key);
|
settings.ssl.certificate_key = make_absolute(settings.ssl.certificate_key);
|
||||||
if (!fs::exists(settings.ssl.certificate_key))
|
if (!fs::exists(settings.ssl.certificate_key))
|
||||||
throw SnapException("SSL certificate_key file not found: " + settings.ssl.certificate_key.native());
|
throw SnapException("SSL certificate_key file not found: " + settings.ssl.certificate_key.native());
|
||||||
|
for (size_t n = 0; n < client_cert_opt->count(); ++n)
|
||||||
|
{
|
||||||
|
auto cert_file = std::filesystem::weakly_canonical(client_cert_opt->value(n));
|
||||||
|
if (!fs::exists(cert_file))
|
||||||
|
throw SnapException("Client certificate file not found: " + cert_file.string());
|
||||||
|
settings.ssl.client_certs.push_back(std::move(cert_file));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (settings.ssl.certificate.empty() != settings.ssl.certificate_key.empty())
|
else if (settings.ssl.certificate.empty() != settings.ssl.certificate_key.empty())
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue