Commit db31e681 authored by jan.koester's avatar jan.koester
Browse files

test

parent e3dc81d7
Loading
Loading
Loading
Loading
+5 −0
Changes for config/proxyplus.yaml: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -61,6 +61,11 @@ proxyplus:
  process:
    user: "proxy"
    group: "proxy"
    # Open-file ceiling, raised via setrlimit() before privileges are dropped (needs to run
    # while still root). Covers every long-lived fd: HTTP/1.1+H2+H3 client sockets,
    # load-balanced upstream connections, and each WebSocket tunnel's socket pair + its own
    # kqueue/epoll fd. 0 = leave the OS/login-class default alone.
    max_open_files: 65536

  pid_file: "/run/proxyplus.pid"
  recovery_mode: false
+2 −0
Changes for src/config/BootstrapConfig.cpp: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ const std::set<std::string> &knownLeafPatterns() {
        "/proxyplus/logging/targets",
        "/proxyplus/process/user",
        "/proxyplus/process/group",
        "/proxyplus/process/max_open_files",
        "/proxyplus/pid_file",
        "/proxyplus/recovery_mode",
        "/proxyplus/maintenance_mode",
@@ -221,6 +222,7 @@ void BootstrapConfig::loadAndValidate() {

    cfg.processUser = readValue(conf, "/proxyplus/process/user", "");
    cfg.processGroup = readValue(conf, "/proxyplus/process/group", "");
    cfg.maxOpenFiles = readInt(conf, "/proxyplus/process/max_open_files", 65536);
    cfg.pidFile = readValue(conf, "/proxyplus/pid_file", "");

    cfg.recoveryMode = readBool(conf, "/proxyplus/recovery_mode", false) ? RecoveryMode::Recover
+10 −0
Changes for src/config/BootstrapConfig.h: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -78,6 +78,16 @@ struct AppConfig {
    std::string processGroup;
    std::string pidFile;

    // Raised via setrlimit(RLIMIT_NOFILE, ...) before privileges are dropped (see main.cpp's
    // raiseFileDescriptorLimit() -- only root/CAP_SYS_RESOURCE can raise the hard limit past
    // whatever the OS/login-class default is). Needs to cover every long-lived fd this process
    // holds at once: per-client sockets across HTTP/1.1, H2 and H3/QUIC, load-balanced upstream
    // connections, and each WebSocket tunnel's socket *pair* plus its own dedicated kqueue/epoll
    // fd (see WebSocketProxy.cpp's kMaxConcurrentWsTunnels) -- exactly the ceiling that produced
    // "socketwait: kqueue() failed: Too many open files" in production. 0 = leave the OS default
    // alone (opt-out escape hatch for a platform where this is managed externally).
    int maxOpenFiles = 65536;

    RecoveryMode recoveryMode = RecoveryMode::Normal;
    MaintenanceMode maintenanceMode = MaintenanceMode::Off;
};
+29 −0
Changes for src/main.cpp: 29 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -8,9 +8,11 @@
#include <thread>
#include <vector>

#include <cstring>
#include <fcntl.h>
#include <grp.h>
#include <pwd.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <unistd.h>

@@ -141,6 +143,31 @@ std::pair<std::string, std::string> readServiceCredential(const std::string &dat
    return {line.substr(0, sep), line.substr(sep + 1)};
}

// Raises this process's open-file ceiling while it can still do so -- only root/
// CAP_SYS_RESOURCE may raise rlim_max past its current value, and dropPrivileges() gives that
// up for good, so this must run before it (and as early as possible generally, since
// ConfigStore/AuthDBAdapter/CertificateManager/every listener bind below all consume fds
// against whatever ceiling is in effect at the time they open one). Left at the OS/login-class
// default otherwise, which is easy to exhaust once long-lived connections pile up -- see
// AppConfig::maxOpenFiles's comment for exactly which fds count against it. Never fatal: a
// process that can't raise its own ceiling should still try to run at whatever it's got, not
// refuse to start.
void raiseFileDescriptorLimit(const proxyplus::config::AppConfig &cfg) {
    if (cfg.maxOpenFiles <= 0) return;
    struct rlimit rl;
    if (::getrlimit(RLIMIT_NOFILE, &rl) != 0) {
        std::cerr << "[main] WARNING: getrlimit(RLIMIT_NOFILE) failed: " << strerror(errno) << std::endl;
        return;
    }
    rlim_t want = static_cast<rlim_t>(cfg.maxOpenFiles);
    if (::geteuid() == 0 && rl.rlim_max < want) rl.rlim_max = want;
    rl.rlim_cur = std::min(want, rl.rlim_max);
    if (::setrlimit(RLIMIT_NOFILE, &rl) != 0) {
        std::cerr << "[main] WARNING: setrlimit(RLIMIT_NOFILE, " << rl.rlim_cur
                   << ") failed: " << strerror(errno) << std::endl;
    }
}

void dropPrivileges(const proxyplus::config::AppConfig &cfg) {
    if (::geteuid() != 0) return;
    if (cfg.processGroup.empty() || cfg.processUser.empty()) {
@@ -233,6 +260,8 @@ int main(int argc, char **argv) {
        proxyplus::config::BootstrapConfig bootstrap(argv[1]);
        auto cfg = bootstrap.get();

        raiseFileDescriptorLimit(cfg);

        if (cfg.maintenanceMode == proxyplus::config::MaintenanceMode::On) {
            std::cerr << "[main] maintenance mode: only the admin API will be started" << std::endl;
        }