1 #pragma once 2 3 #include "http_connection.hpp" 4 #include "logging.hpp" 5 #include "ssl_key_handler.hpp" 6 7 #include <boost/asio/ip/address.hpp> 8 #include <boost/asio/ip/tcp.hpp> 9 #include <boost/asio/signal_set.hpp> 10 #include <boost/asio/ssl/context.hpp> 11 #include <boost/asio/ssl/stream.hpp> 12 #include <boost/asio/steady_timer.hpp> 13 14 #include <atomic> 15 #include <chrono> 16 #include <cstdint> 17 #include <filesystem> 18 #include <future> 19 #include <memory> 20 #include <string> 21 #include <utility> 22 #include <vector> 23 24 namespace crow 25 { 26 27 template <typename Handler, typename Adaptor = boost::asio::ip::tcp::socket> 28 class Server 29 { 30 public: 31 Server(Handler* handlerIn, boost::asio::ip::tcp::acceptor&& acceptorIn, 32 std::shared_ptr<boost::asio::ssl::context> adaptorCtxIn, 33 std::shared_ptr<boost::asio::io_context> io) : 34 ioService(std::move(io)), 35 acceptor(std::move(acceptorIn)), 36 signals(*ioService, SIGINT, SIGTERM, SIGHUP), handler(handlerIn), 37 adaptorCtx(std::move(adaptorCtxIn)) 38 {} 39 40 void updateDateStr() 41 { 42 time_t lastTimeT = time(nullptr); 43 tm myTm{}; 44 45 gmtime_r(&lastTimeT, &myTm); 46 47 dateStr.resize(100); 48 size_t dateStrSz = strftime(dateStr.data(), dateStr.size() - 1, 49 "%a, %d %b %Y %H:%M:%S GMT", &myTm); 50 dateStr.resize(dateStrSz); 51 } 52 53 void run() 54 { 55 loadCertificate(); 56 updateDateStr(); 57 58 getCachedDateStr = [this]() -> std::string { 59 static std::chrono::time_point<std::chrono::steady_clock> 60 lastDateUpdate = std::chrono::steady_clock::now(); 61 if (std::chrono::steady_clock::now() - lastDateUpdate >= 62 std::chrono::seconds(10)) 63 { 64 lastDateUpdate = std::chrono::steady_clock::now(); 65 updateDateStr(); 66 } 67 return dateStr; 68 }; 69 70 BMCWEB_LOG_INFO("bmcweb server is running, local endpoint {}", 71 acceptor.local_endpoint().address().to_string()); 72 startAsyncWaitForSignal(); 73 doAccept(); 74 } 75 76 void loadCertificate() 77 { 78 if constexpr (BMCWEB_INSECURE_DISABLE_SSL) 79 { 80 return; 81 } 82 namespace fs = std::filesystem; 83 // Cleanup older certificate file existing in the system 84 fs::path oldCert = "/home/root/server.pem"; 85 if (fs::exists(oldCert)) 86 { 87 fs::remove("/home/root/server.pem"); 88 } 89 fs::path certPath = "/etc/ssl/certs/https/"; 90 // if path does not exist create the path so that 91 // self signed certificate can be created in the 92 // path 93 if (!fs::exists(certPath)) 94 { 95 fs::create_directories(certPath); 96 } 97 fs::path certFile = certPath / "server.pem"; 98 BMCWEB_LOG_INFO("Building SSL Context file={}", certFile.string()); 99 std::string sslPemFile(certFile); 100 std::string cert = 101 ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile); 102 if (cert.empty()) 103 { 104 throw std::runtime_error("Failed to load string"); 105 } 106 std::shared_ptr<boost::asio::ssl::context> sslContext = 107 ensuressl::getSslContext(cert); 108 if (sslContext == nullptr) 109 { 110 throw std::runtime_error("Failed to load certificate"); 111 } 112 BMCWEB_LOG_DEBUG("Replaced certificate"); 113 adaptorCtx = sslContext; 114 handler->ssl(std::move(sslContext)); 115 } 116 117 void startAsyncWaitForSignal() 118 { 119 signals.async_wait( 120 [this](const boost::system::error_code& ec, int signalNo) { 121 if (ec) 122 { 123 BMCWEB_LOG_INFO("Error in signal handler{}", ec.message()); 124 } 125 else 126 { 127 if (signalNo == SIGHUP) 128 { 129 BMCWEB_LOG_INFO("Receivied reload signal"); 130 loadCertificate(); 131 boost::system::error_code ec2; 132 acceptor.cancel(ec2); 133 if (ec2) 134 { 135 BMCWEB_LOG_ERROR( 136 "Error while canceling async operations:{}", 137 ec2.message()); 138 } 139 startAsyncWaitForSignal(); 140 } 141 else 142 { 143 stop(); 144 } 145 } 146 }); 147 } 148 149 void stop() 150 { 151 ioService->stop(); 152 } 153 154 void doAccept() 155 { 156 if (ioService == nullptr) 157 { 158 BMCWEB_LOG_CRITICAL("IoService was null"); 159 return; 160 } 161 boost::asio::steady_timer timer(*ioService); 162 std::shared_ptr<Connection<Adaptor, Handler>> connection; 163 if constexpr (std::is_same<Adaptor, 164 boost::asio::ssl::stream< 165 boost::asio::ip::tcp::socket>>::value) 166 { 167 if (adaptorCtx == nullptr) 168 { 169 BMCWEB_LOG_CRITICAL( 170 "Asked to lauch TLS socket but no context available"); 171 return; 172 } 173 connection = std::make_shared<Connection<Adaptor, Handler>>( 174 handler, std::move(timer), getCachedDateStr, 175 Adaptor(*ioService, *adaptorCtx)); 176 } 177 else 178 { 179 connection = std::make_shared<Connection<Adaptor, Handler>>( 180 handler, std::move(timer), getCachedDateStr, 181 Adaptor(*ioService)); 182 } 183 acceptor.async_accept( 184 boost::beast::get_lowest_layer(connection->socket()), 185 [this, connection](const boost::system::error_code& ec) { 186 if (!ec) 187 { 188 boost::asio::post(*ioService, 189 [connection] { connection->start(); }); 190 } 191 doAccept(); 192 }); 193 } 194 195 private: 196 std::shared_ptr<boost::asio::io_context> ioService; 197 std::function<std::string()> getCachedDateStr; 198 boost::asio::ip::tcp::acceptor acceptor; 199 boost::asio::signal_set signals; 200 201 std::string dateStr; 202 203 Handler* handler; 204 205 std::shared_ptr<boost::asio::ssl::context> adaptorCtx; 206 }; 207 } // namespace crow 208