summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2020-04-05 14:22:31 +0200
committerRoland Reichwein <mail@reichwein.it>2020-04-05 14:22:31 +0200
commite234229ae80da0fa9967b797f7b5f4f381cba4b4 (patch)
tree02bed359b39eb1e8b7f022afb6fdba451292b5c6
parent918685c1c09de1e3cd14c41bb8cc8b89a177ccd2 (diff)
All certificates configurable per site
-rw-r--r--TODO2
-rw-r--r--config.cpp16
-rw-r--r--config.h4
-rw-r--r--https.cpp177
-rw-r--r--https.h15
-rw-r--r--server_certificate.h67
-rw-r--r--webserver.conf6
7 files changed, 181 insertions, 106 deletions
diff --git a/TODO b/TODO
index 5ec5b0e..26cd06d 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,7 @@
Certbot: https://certbot.eff.org/lets-encrypt/debianbuster-other
Webbox
Debian 10
-alternative hosts www, lists, ...
drop privileges: www-data,www-data, ...?
Speed up DocRoot, use string_view
+read: The socket was closed due to a timeout
diff --git a/config.cpp b/config.cpp
index 6050768..072b1bd 100644
--- a/config.cpp
+++ b/config.cpp
@@ -58,6 +58,10 @@ void Config::readConfigfile(std::string filename)
path.params[param.first.data()] = param.second.data();
}
site_struct.paths.push_back(path);
+ } else if (x.first == "certpath"s) {
+ site_struct.cert_path = x.second.data();
+ } else if (x.first == "keypath"s) {
+ site_struct.key_path = x.second.data();
} else
throw std::runtime_error("Unknown element: "s + x.first);
}
@@ -80,10 +84,6 @@ void Config::readConfigfile(std::string filename)
socket_struct.protocol = SocketProtocol::HTTPS;
else
throw std::runtime_error("Unknown protocol: "s + x.second.data());
- } else if (x.first == "certpath"s) {
- socket_struct.cert_path = x.second.data();
- } else if (x.first == "keypath"s) {
- socket_struct.key_path = x.second.data();
} else
throw std::runtime_error("Unknown element: "s + x.first);
}
@@ -165,14 +165,14 @@ void Config::dump() const
std::cout << " " << param.first << ": " << param.second << std::endl;
}
}
+ if (site.key_path != ""s) {
+ std::cout << " Key: " << site.key_path.generic_string() << std::endl;
+ std::cout << " Cert: " << site.cert_path.generic_string() << std::endl;
+ }
}
for (const auto& socket: m_sockets) {
std::cout << "Socket: " << socket.address << ":" << socket.port << " (" << (socket.protocol == SocketProtocol::HTTP ? "HTTP" : "HTTPS") << ")" << std::endl;
- if (socket.protocol == SocketProtocol::HTTPS) {
- std::cout << " Key: " << socket.key_path.generic_string() << std::endl;
- std::cout << " Cert: " << socket.cert_path.generic_string() << std::endl;
- }
std::cout << " Serving:";
for (const auto& site: socket.serve_sites) {
std::cout << " " << site;
diff --git a/config.h b/config.h
index 1c938c0..801b5fa 100644
--- a/config.h
+++ b/config.h
@@ -26,6 +26,8 @@ struct Site
std::string name;
std::unordered_set<std::string> hosts;
std::vector<Path> paths;
+ fs::path cert_path;
+ fs::path key_path;
};
enum class SocketProtocol
@@ -40,8 +42,6 @@ struct Socket
std::string port;
SocketProtocol protocol;
std::vector<std::string> serve_sites; // if empty, automatically expand to all configured sites
- fs::path cert_path;
- fs::path key_path;
};
class Config
diff --git a/https.cpp b/https.cpp
index 9d0784e..0e45272 100644
--- a/https.cpp
+++ b/https.cpp
@@ -1,22 +1,27 @@
#include "https.h"
+#include "config.h"
+#include "file.h"
#include "server.h"
#include <boost/beast/version.hpp>
#if BOOST_VERSION == 107100
-#include "server_certificate.h"
-
#include <openssl/ssl.h>
+
+#include <boost/asio/buffer.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/asio/dispatch.hpp>
+#include <boost/asio/ssl/context.hpp>
#include <boost/asio/strand.hpp>
#include <boost/config.hpp>
+
#include <algorithm>
+#include <cstddef>
#include <cstdlib>
#include <functional>
#include <iostream>
@@ -519,39 +524,161 @@ private:
}
};
-} // anonymous namespace
-//------------------------------------------------------------------------------
+/* Load a signed certificate into the ssl context, and configure
+ the context for use with a server.
-namespace HTTPS {
+ For this to work with the browser or operating system, it is
+ necessary to import the "Beast Test CA" certificate into
+ the local certificate store, browser, or operating system
+ depending on your environment Please see the documentation
+ accompanying the Beast certificate for more details.
+*/
+void load_server_certificate(boost::asio::ssl::context& ctx, fs::path cert_path, fs::path key_path)
+{
+ /*
+ The certificate was generated from CMD.EXE on Windows 10 using:
+
+ winpty openssl dhparam -out dh.pem 2048
+ winpty openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 10000 -out cert.pem -subj "//C=US\ST=CA\L=Los Angeles\O=Beast\CN=www.example.com"
+ */
+
+ std::string const dh =
+ "-----BEGIN DH PARAMETERS-----\n"
+ "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n"
+ "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n"
+ "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n"
+ "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n"
+ "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n"
+ "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n"
+ "-----END DH PARAMETERS-----\n";
+
+ ctx.set_password_callback(
+ [](std::size_t,
+ boost::asio::ssl::context_base::password_purpose)
+ {
+ return "test";
+ });
+
+ ctx.set_options(
+ boost::asio::ssl::context::default_workarounds |
+ boost::asio::ssl::context::no_sslv2 |
+ boost::asio::ssl::context::single_dh_use);
+
+ std::string cert;
+ if (cert_path == "") {
+ cert =
+ "-----BEGIN CERTIFICATE-----\n"
+ "MIIDnTCCAoWgAwIBAgIULkYtO+2Ddeg+qLZ+aDQpmA5b4L0wDQYJKoZIhvcNAQEL\n"
+ "BQAwXjELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0JhdmFyaWExDzANBgNVBAcMBk11\n"
+ "bmljaDEVMBMGA1UECgwMUmVpY2h3ZWluIElUMRUwEwYDVQQDDAxyZWljaHdlaW4u\n"
+ "aXQwHhcNMjAwNDA1MDgwNzIyWhcNNDcwODIyMDgwNzIyWjBeMQswCQYDVQQGEwJE\n"
+ "RTEQMA4GA1UECAwHQmF2YXJpYTEPMA0GA1UEBwwGTXVuaWNoMRUwEwYDVQQKDAxS\n"
+ "ZWljaHdlaW4gSVQxFTATBgNVBAMMDHJlaWNod2Vpbi5pdDCCASIwDQYJKoZIhvcN\n"
+ "AQEBBQADggEPADCCAQoCggEBALJNb0WLbz+xP+YITMMk+eeK/SIOCRFs/9aZIAyK\n"
+ "ParGauxa+8d25mlfJTAo6/G0h3sA240JHyNpOzVOogPU+v4dRWyGO0w5vHVD0caB\n"
+ "rDb1eEfmLtqfKLLUL9iPDReUh6WAE7qoNDtfoT551uSMIae1cpPUduVTnSkEgw8k\n"
+ "NjJSHYT800jSB2R+e7tJG3ErXDM63R3B8RbitZPoWACjpBxDT+Qrj0fBFS4AWw6b\n"
+ "z09uitv0RrgI6CW7xRh3UAdRwEBGHiU6HTIthX6LNgez1UL0sfu1iZ22wNmYZP/S\n"
+ "sL3b20WtSH9LN2PRJ4q3AGt6RMbmSGr65ljha9xkTFna0Y8CAwEAAaNTMFEwHQYD\n"
+ "VR0OBBYEFKd5/MGFZUAUV502vJ/Kcswax8WVMB8GA1UdIwQYMBaAFKd5/MGFZUAU\n"
+ "V502vJ/Kcswax8WVMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB\n"
+ "AIBS4AfM7wiunQ2UZQQ5A0Un99+BLax9e+h11h/jGeJ+/9maY/E9MK6UG9LXoOv2\n"
+ "z32Q7Ta2xKeRu6GC/qupwYJ0Xt3LENOfogsaNCAgxKlAN48LGlRyCTvzWsEMh28j\n"
+ "RaelWonh2qQoiryKLVnRwrg8g1Bu4v+V437cIBmeZPxf0spEL9EVqlN+iS8plmel\n"
+ "7/F4ULdybKGq39tgicuS7JhnY21ZzOFoq0bWnKBbAeTndmuROdb3pEppxW6pwu0q\n"
+ "TFdMrSJE38kiQh2O9IchPQbTZ+Rdj0HE9NxStlrNr5bu6rjikRm50/G3JoXpzYdp\n"
+ "AN4ZI2QZ6R6Y+TzDixKecNk=\n"
+ "-----END CERTIFICATE-----\n"
+ ;
+ } else {
+ cert = File::getFile(cert_path);
+ }
+
+ ctx.use_certificate_chain(
+ boost::asio::buffer(cert.data(), cert.size()));
+
+ std::string key;
+ if (key_path == "") {
+ key =
+ "-----BEGIN PRIVATE KEY-----\n"
+ "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyTW9Fi28/sT/m\n"
+ "CEzDJPnniv0iDgkRbP/WmSAMij2qxmrsWvvHduZpXyUwKOvxtId7ANuNCR8jaTs1\n"
+ "TqID1Pr+HUVshjtMObx1Q9HGgaw29XhH5i7anyiy1C/Yjw0XlIelgBO6qDQ7X6E+\n"
+ "edbkjCGntXKT1HblU50pBIMPJDYyUh2E/NNI0gdkfnu7SRtxK1wzOt0dwfEW4rWT\n"
+ "6FgAo6QcQ0/kK49HwRUuAFsOm89Pborb9Ea4COglu8UYd1AHUcBARh4lOh0yLYV+\n"
+ "izYHs9VC9LH7tYmdtsDZmGT/0rC929tFrUh/Szdj0SeKtwBrekTG5khq+uZY4Wvc\n"
+ "ZExZ2tGPAgMBAAECggEBAK9bJKIa3dCgPB257/TEOtsTgJyrfROcRYkCk9iBZOC9\n"
+ "v46wdIrZTwY2wtY4iMPwLoY0c7ijTfJ/nfFxYjmujyK4Gvz+jvcKmWQizP8TrRFo\n"
+ "HWFo6o+slFQ8BspO9itIspd7/OtIXgY+qNBO959Sig7sjsEA5eXoc9pRS6vqizq0\n"
+ "j4G/UO5Amr/l3ciEJiqMJgZsIVLDKaGlqFTymydSqkB8UHQYWK1kunQxhK4Ldycu\n"
+ "hTooQE7tXM0zvoFVV6v1fldV5OFsZk2kPMNtvMO6ZEpOM4rNMlg+vJy8kB1fb3Gs\n"
+ "iFE/DCUpZsMSserQMU9/hfrYlndgsFD5Sr1EVGEebhECgYEA1gc9qx+ugdhYTY5j\n"
+ "tJDXjOsnw8KY/l/1y+mQ8XNJ9MVdBGy1WB+uWB4teiyJchV49gn2XlKUK2rcCBvZ\n"
+ "vC5CwPmFi2t70JezQgnXtDlbR0bARPlRd741i4rBpD7hEiZNCTOd2HFBpUg/CGWN\n"
+ "E4n1ksazBm6jvv3Jo6WAa07Z390CgYEA1USrFqmc/fKGQpTCdH0qYZv3hQtrb1TQ\n"
+ "9YnrbhtaC0haPpintZKjvhU3tCd1fPuIDXtMAgaaKSyoGiE2aMvLxt1/eV08BkMi\n"
+ "kGIss9poYNi5+6ZD9QAHmHJhzZtVGj8U5L8379XmwxAByiBRVVE8CW1X/e6+iJpz\n"
+ "+CLgN+zEVlsCgYEAsuOAdtxXJm4meERwL8b0cvNF3Eh1Sf/42MPTAwzCntSrh3w5\n"
+ "InvwY/RtPHWnN/ScksEG7BWHhLafTCPDHJdp8hNcvIhNB68UBDln0loyYePP5pag\n"
+ "sj4IUSbb7SUlR989elhrMTKQlM5K6QDAJrmjyVdM4S5urL9A3wgAyzAvyP0CgYAO\n"
+ "paGuc8WxdzebWQYl4/bGL2UHgSpGwid7xZYiwMQlZDm2dNuHz+NpCaICwHcEN243\n"
+ "ptEojnWGAGgnK0LGXcDIDqxTlICr2W6FRgjV7Vkf1aKoUtn1+KOM58YpzdJBdDWm\n"
+ "JC/eS+2GVhIZZLDRUDv0VcsmSIBTd3AhiZumm588YwKBgBZfNqfmHAwIP2pM1wml\n"
+ "Ck3vaLLvonghj3iQW9CFJ/SqLOnfT4KJkFObR6oGbxY0RtXsCrmSqidIKgDd0Kkq\n"
+ "L6QbHp2j3+16GBdmLNUJlfjBTNPJp69IDKztjeCX7/8JZs79p/LAv+I9Sh4lVw4O\n"
+ "IrDprlB0yzP5zigcsAZeViYJ\n"
+ "-----END PRIVATE KEY-----\n"
+ ;
+ } else {
+ key = File::getFile(key_path);
+ }
+ ctx.use_private_key(
+ boost::asio::buffer(key.data(), key.size()),
+ boost::asio::ssl::context::file_format::pem);
+
+ ctx.use_tmp_dh(
+ boost::asio::buffer(dh.data(), dh.size()));
+}
int servername_callback(SSL *s, int *al, void *arg)
{
int type {SSL_get_servername_type(s)};
std::string server_name {SSL_get_servername(s, type)};
- Server* server = (Server*)arg;
- if (server_name == "lists.antcom.de"s) {
- SSL_set_SSL_CTX(s, server->m_ctx.native_handle());
- } else {
- SSL_set_SSL_CTX(s, server->m_ctx2.native_handle());
- }
+ HTTPS::Server::ctx_type* ctx_map = (HTTPS::Server::ctx_type*)arg;
+
+ ssl::context& ctx = *(ctx_map->at(server_name));
+
+ SSL_set_SSL_CTX(s, ctx.native_handle());
+
return SSL_TLSEXT_ERR_OK;
}
+} // anonymous namespace
+//------------------------------------------------------------------------------
+
+namespace HTTPS {
+
Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket): ::Server(config, ioc), m_socket(socket)
{
- for (const auto& site: socket.serve_sites) {
- std::cout << "Creating SSL context/cert for site " << site << std::endl;
- }
+ for (const auto& serve_site: socket.serve_sites) {
+ for (const auto& site: config.Sites()) {
+ if (site.name == serve_site) {
+ std::shared_ptr<ssl::context> ctx {std::make_shared<ssl::context>(tls_method)};
- load_server_certificate(m_ctx, m_socket.cert_path, m_socket.key_path);
- load_server_certificate(m_ctx2, "/home/ernie/code/webserver/cert.pem", "/home/ernie/code/webserver/key.pem");
-
- SSL_CTX_set_tlsext_servername_callback(m_ctx.native_handle(), servername_callback);
- SSL_CTX_set_tlsext_servername_arg(m_ctx.native_handle(), this);
-
- SSL_CTX_set_tlsext_servername_callback(m_ctx2.native_handle(), servername_callback);
- SSL_CTX_set_tlsext_servername_arg(m_ctx2.native_handle(), this);
+ std::cout << "Creating SSL context/cert for site " << serve_site << std::endl;
+
+ load_server_certificate(*ctx, site.cert_path, site.key_path);
+ SSL_CTX_set_tlsext_servername_callback(ctx->native_handle(), servername_callback);
+ SSL_CTX_set_tlsext_servername_arg(ctx->native_handle(), &m_ctx);
+
+ for (const auto& host: site.hosts) {
+ std::cout << " Adding Host " << host << std::endl;
+ m_ctx.emplace(host, ctx);
+ }
+ }
+ }
+ }
}
Server::~Server()
@@ -563,10 +690,14 @@ int Server::start()
auto const address = net::ip::make_address(m_socket.address);
auto const port = static_cast<unsigned short>(std::atoi(m_socket.port.data()));
+ load_server_certificate(m_ctx_dummy, "", ""); // initial dummy, before we can add specific ctx w/ certificate
+ SSL_CTX_set_tlsext_servername_callback(m_ctx_dummy.native_handle(), servername_callback);
+ SSL_CTX_set_tlsext_servername_arg(m_ctx_dummy.native_handle(), &m_ctx);
+
// Create and launch a listening port
std::make_shared<listener>(
m_ioc,
- m_ctx,
+ m_ctx_dummy,
tcp::endpoint{address, port},
m_config,
m_socket)->run();
diff --git a/https.h b/https.h
index 13a67a7..e009b78 100644
--- a/https.h
+++ b/https.h
@@ -1,5 +1,9 @@
#pragma once
+#include <memory>
+#include <string>
+#include <unordered_map>
+
#include <boost/asio/dispatch.hpp>
#include <boost/asio/strand.hpp>
#include <boost/beast/ssl.hpp>
@@ -12,12 +16,17 @@ namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
namespace HTTPS {
+static const ssl::context_base::method tls_method {ssl::context::tlsv13};
+
class Server: public ::Server
{
- // The SSL context is required, and holds certificates
public:
- ssl::context m_ctx{ssl::context::tlsv13};
- ssl::context m_ctx2{ssl::context::tlsv13};
+ typedef std::unordered_map<std::string, std::shared_ptr<ssl::context>> ctx_type;
+
+private:
+ ctx_type m_ctx;
+ ssl::context m_ctx_dummy{tls_method}; // Initial use, will be replaced by host specific context (with specific certificate)
+
const Socket& m_socket;
public:
diff --git a/server_certificate.h b/server_certificate.h
deleted file mode 100644
index 1dc12a4..0000000
--- a/server_certificate.h
+++ /dev/null
@@ -1,67 +0,0 @@
-#ifndef BOOST_BEAST_EXAMPLE_COMMON_SERVER_CERTIFICATE_HPP
-#define BOOST_BEAST_EXAMPLE_COMMON_SERVER_CERTIFICATE_HPP
-
-#include <boost/asio/buffer.hpp>
-#include <boost/asio/ssl/context.hpp>
-#include <cstddef>
-#include <memory>
-
-#include "config.h"
-#include "file.h"
-
-/* Load a signed certificate into the ssl context, and configure
- the context for use with a server.
-
- For this to work with the browser or operating system, it is
- necessary to import the "Beast Test CA" certificate into
- the local certificate store, browser, or operating system
- depending on your environment Please see the documentation
- accompanying the Beast certificate for more details.
-*/
-inline
-void
-load_server_certificate(boost::asio::ssl::context& ctx, fs::path cert_path, fs::path key_path)
-{
- /*
- The certificate was generated from CMD.EXE on Windows 10 using:
-
- winpty openssl dhparam -out dh.pem 2048
- winpty openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 10000 -out cert.pem -subj "//C=US\ST=CA\L=Los Angeles\O=Beast\CN=www.example.com"
- */
-
- std::string const cert = File::getFile(cert_path);
- std::string const key = File::getFile(key_path);
- std::string const dh =
- "-----BEGIN DH PARAMETERS-----\n"
- "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n"
- "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n"
- "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n"
- "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n"
- "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n"
- "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n"
- "-----END DH PARAMETERS-----\n";
-
- ctx.set_password_callback(
- [](std::size_t,
- boost::asio::ssl::context_base::password_purpose)
- {
- return "test";
- });
-
- ctx.set_options(
- boost::asio::ssl::context::default_workarounds |
- boost::asio::ssl::context::no_sslv2 |
- boost::asio::ssl::context::single_dh_use);
-
- ctx.use_certificate_chain(
- boost::asio::buffer(cert.data(), cert.size()));
-
- ctx.use_private_key(
- boost::asio::buffer(key.data(), key.size()),
- boost::asio::ssl::context::file_format::pem);
-
- ctx.use_tmp_dh(
- boost::asio::buffer(dh.data(), dh.size()));
-}
-
-#endif
diff --git a/webserver.conf b/webserver.conf
index 46d4120..38af8ab 100644
--- a/webserver.conf
+++ b/webserver.conf
@@ -22,6 +22,8 @@
<target>/var/lib/webbox</target>
</path>
-->
+ <certpath>/home/ernie/code/webserver/fullchain.pem</certpath>
+ <keypath>/home/ernie/code/webserver/privkey.pem</keypath>
</site>
<site>
<name>marx</name>
@@ -29,6 +31,8 @@
<path requested="/" type="files">
<target>/home/ernie/homepage/test1</target>
</path>
+ <certpath>/home/ernie/code/webserver/cert.pem</certpath>
+ <keypath>/home/ernie/code/webserver/key.pem</keypath>
</site>
<!--
reichwein.it
@@ -51,8 +55,6 @@
<address>127.0.0.1</address>
<port>8081</port>
<protocol>https</protocol>
- <certpath>/home/ernie/code/webserver/fullchain.pem</certpath>
- <keypath>/home/ernie/code/webserver/privkey.pem</keypath>
</socket>
</sockets>
</webserver>