diff --git a/server/etc/snapserver.conf b/server/etc/snapserver.conf index 5fc2c0e2..e556f4c5 100644 --- a/server/etc/snapserver.conf +++ b/server/etc/snapserver.conf @@ -52,6 +52,8 @@ [ssl] # https://deliciousbrains.com/ssl-certificate-authority-for-local-https-development/ # https://gist.github.com/fntlnz/cf14feb5a46b2eda428e000157447309 +# Certificate files are either specified by their full or relative path. Certificates with +# relative path are searched for in the current path and in "/etc/snapserver/certs" # Certificate file in PEM format # certificate = diff --git a/server/server_settings.hpp b/server/server_settings.hpp index bf488a00..7bbb81b9 100644 --- a/server/server_settings.hpp +++ b/server/server_settings.hpp @@ -24,6 +24,7 @@ // standard headers #include +#include #include #include @@ -35,20 +36,25 @@ struct ServerSettings int threads{-1}; std::string pid_file{"/var/run/snapserver/pid"}; std::string user{"snapserver"}; - std::string group{""}; - std::string data_dir{""}; + std::string group; + std::string data_dir; }; struct Ssl { - std::string certificate{""}; - std::string certificate_key{""}; - std::string key_password{""}; + std::filesystem::path certificate; + std::filesystem::path certificate_key; + std::string key_password; + + bool enabled() const + { + return !certificate.empty() && !certificate_key.empty(); + } }; struct User { - User(const std::string& user_permissions_password) + explicit User(const std::string& user_permissions_password) { std::string perm; name = utils::string::split_left(user_permissions_password, ':', perm); @@ -71,9 +77,9 @@ struct ServerSettings size_t ssl_port{1788}; std::vector bind_to_address{{"::"}}; std::vector ssl_bind_to_address{{"::"}}; - std::string doc_root{""}; + std::string doc_root; std::string host{""}; - std::string url_prefix{""}; + std::string url_prefix; }; struct Tcp @@ -102,7 +108,7 @@ struct ServerSettings struct Logging { - std::string sink{""}; + std::string sink; std::string filter{"*:info"}; }; diff --git a/server/snapserver.cpp b/server/snapserver.cpp index a5039c2b..929f138e 100644 --- a/server/snapserver.cpp +++ b/server/snapserver.cpp @@ -18,6 +18,7 @@ // local headers #include "common/popl.hpp" +#include #ifdef HAS_DAEMON #include "common/daemon.hpp" #endif @@ -81,12 +82,14 @@ int main(int argc, char* argv[]) conf.add>("", "server.datadir", "directory where persistent data is stored", settings.server.data_dir, &settings.server.data_dir); // SSL settings - conf.add>("", "ssl.certificate", "certificate file (PEM format)", settings.ssl.certificate, &settings.ssl.certificate); - conf.add>("", "ssl.certificate_key", "private key file (PEM format)", settings.ssl.certificate_key, &settings.ssl.certificate_key); + conf.add>("", "ssl.certificate", "certificate file (PEM format)", settings.ssl.certificate, &settings.ssl.certificate); + conf.add>("", "ssl.certificate_key", "private key file (PEM format)", settings.ssl.certificate_key, &settings.ssl.certificate_key); conf.add>("", "ssl.key_password", "key password (for encrypted private key)", settings.ssl.key_password, &settings.ssl.key_password); +#if 0 // feature: users // Users setting auto users_value = conf.add>("", "users.user", "::"); +#endif // HTTP RPC settings conf.add>("", "http.enabled", "enable HTTP Json RPC (HTTP POST and websockets)", settings.http.enabled, &settings.http.enabled); @@ -253,8 +256,40 @@ int main(int argc, char* argv[]) else throw SnapException("Invalid log sink: " + settings.logging.sink); + if (!settings.ssl.certificate.empty() && !settings.ssl.certificate_key.empty()) + { + namespace fs = std::filesystem; + auto make_absolute = [](const fs::path& filename) { + const fs::path cert_path = "/etc/snapserver/certs/"; + if (filename.is_absolute()) + return filename; + if (fs::exists(filename)) + return fs::canonical(filename); + return cert_path / filename; + }; + settings.ssl.certificate = make_absolute(settings.ssl.certificate); + if (!fs::exists(settings.ssl.certificate)) + throw SnapException("SSL certificate file not found: " + settings.ssl.certificate.native()); + settings.ssl.certificate_key = make_absolute(settings.ssl.certificate_key); + if (!fs::exists(settings.ssl.certificate_key)) + throw SnapException("SSL certificate_key file not found: " + settings.ssl.certificate_key.native()); + } + else if (settings.ssl.certificate.empty() != settings.ssl.certificate_key.empty()) + { + throw SnapException("Both SSL 'certificate' and 'certificate_key' must be set or empty"); + } + + if (!settings.ssl.enabled()) + { + if (settings.http.ssl_enabled) + throw SnapException("HTTPS enabled ([http] ssl_enabled), but no certificates specified"); + } + LOG(INFO, LOG_TAG) << "Version " << version::code << (!version::rev().empty() ? (", revision " + version::rev(8)) : ("")) << "\n"; + if (settings.ssl.enabled()) + LOG(INFO, LOG_TAG) << "SSL enabled - certificate file: '" << settings.ssl.certificate.native() << "', certificate key file: '" << settings.ssl.certificate_key.native() << "'\n"; + if (!streamValue->is_set() && !sourceValue->is_set()) settings.stream.sources.push_back(sourceValue->value()); @@ -269,6 +304,7 @@ int main(int argc, char* argv[]) settings.stream.sources.push_back(sourceValue->value(n)); } +#if 0 // feature: users for (size_t n = 0; n < users_value->count(); ++n) { settings.users.emplace_back(users_value->value(n)); @@ -276,7 +312,7 @@ int main(int argc, char* argv[]) << ", permissions: " << utils::string::container_to_string(settings.users.back().permissions) << ", pw: " << settings.users.back().password << "\n"; } - +#endif #ifdef HAS_DAEMON std::unique_ptr daemon;