diff options
| -rw-r--r-- | TODO | 2 | ||||
| -rw-r--r-- | config.cpp | 16 | ||||
| -rw-r--r-- | config.h | 4 | ||||
| -rw-r--r-- | https.cpp | 177 | ||||
| -rw-r--r-- | https.h | 15 | ||||
| -rw-r--r-- | server_certificate.h | 67 | ||||
| -rw-r--r-- | webserver.conf | 6 | 
7 files changed, 181 insertions, 106 deletions
@@ -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 @@ -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; @@ -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 @@ -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(); @@ -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>  | 
