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

test

parent 73dfc23f
Loading
Loading
Loading
Loading
Loading
+2 −0
Changes for README.md: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -14,6 +14,8 @@ HTTP is available without TLS configuration. To enable HTTPS, set both
## Master/slave replication
A master can feed any number of read-only slaves. Master and slave can each be
a single node (file backend) or a cluster (`FENRIRDS/CLUSTER/ENABLE`).
A cluster always has at least three nodes in `CLUSTER/PEERS` (this node
included); fenrirds refuses to start, and the web interface to save, with fewer.

Slaves poll the master over the normal fenrirds HTTP(S) port. The master
publishes a content hash per domain (admin db and every domain); a slave
+1 −1
Changes for data/serverconfig.js: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ var ServerConfig = (function() {
            { key: "CLUSTER/PARITY_BLOCKS", label: "Parity blocks", type: "number" },
            { key: "CLUSTER/SSLVERIFY", label: "Verify TLS", type: "bool" },
            { key: "CLUSTER/PEERS", label: "Peers", type: "list",
              hint: "one ip:port per line, this node included" }
              hint: "one ip:port per line, this node included; at least 3 nodes" }
        ]},
        { title: "Maintenance", fields: [
            { key: "VACUUM_INTERVAL", label: "Vacuum interval (h)", type: "number" }
+1 −0
Changes for src/CMakeLists.txt: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ add_executable(fenrirds
    fenrirds.cpp
    mdns.cpp
    nodejoin.cpp
    selfcert.cpp
    api.cpp
    log.cpp
)
+83 −56
Changes for src/confhelper.cpp: 83 added lines, 56 removed lines.
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@
#include <confplus/conf.h>

#include "confhelper.h"
#include "selfcert.h"

#ifndef _WIN32
#include <unistd.h>
@@ -129,6 +130,7 @@ namespace fenrirds::confhelper {
            "  CLUSTER:\n"
            "    ENABLE: \"false\"\n"
            "    PATH: \"" STATEPATH "/cluster\"\n"
            "    SSLVERIFY: \"false\"\n"
            "  KDC:\n"
            "    BIND: \"0.0.0.0\"\n"
            "    PORT: 88\n"
@@ -137,9 +139,11 @@ namespace fenrirds::confhelper {
            "    PORT: 389\n"
            "    MAXCONN: 1024\n"
            "  REPLICATION:\n"
            "    ROLE: \"none\"\n";
            "    ROLE: \"none\"\n"
            "    SSLVERIFY: \"false\"\n";

        const size_t MAXMSG = 65536;
        const size_t MIN_CLUSTER_NODES = 3;

        std::string specBackend(const std::string &spec) {
            size_t d = spec.find(':');
@@ -220,9 +224,10 @@ namespace fenrirds::confhelper {
                return false;
            }
#ifndef _WIN32
            /* a missing file is generated, see generateCertificate() */
            struct stat st;
            if (stat(v.c_str(), &st) != 0 || !S_ISREG(st.st_mode)) {
                err = v + ": no such file";
            if (stat(v.c_str(), &st) == 0 && !S_ISREG(st.st_mode)) {
                err = v + ": not a file";
                return false;
            }
#endif
@@ -437,60 +442,13 @@ namespace fenrirds::confhelper {
            return jres;
        }

#ifndef _WIN32
        bool runProgram(const std::vector<std::string> &argv) {
            pid_t pid = fork();
            if (pid < 0)
                return false;
            if (pid == 0) {
                std::vector<char *> args;
                for (const auto &a : argv)
                    args.push_back(const_cast<char *>(a.c_str()));
                args.push_back(nullptr);
                execvp(args[0], args.data());
                _exit(127);
            }
            int status = 0;
            while (waitpid(pid, &status, 0) < 0 && errno == EINTR) {}
            return WIFEXITED(status) && WEXITSTATUS(status) == 0;
        }
#endif

        /* a self-signed certificate in <config dir>/ssl (DER, as the
         * Debian package creates it) unless one is there already */
        /* the default certificate in <config dir>/ssl, generated if missing */
        void ensureCertificate(const std::string &confDir, std::string &cert, std::string &key) {
            std::string dir = confDir + "/ssl";
            cert = dir + "/cert.der";
            key = dir + "/key.der";
#ifndef _WIN32
            struct stat st;
            if (stat(cert.c_str(), &st) == 0 && stat(key.c_str(), &st) == 0)
                return;
            if (stat(confDir.c_str(), &st) != 0 && mkdir(confDir.c_str(), 0755) != 0)
                throw std::runtime_error("mkdir " + confDir + ": " + strerror(errno));
            if (stat(dir.c_str(), &st) != 0 && mkdir(dir.c_str(), 0700) != 0)
                throw std::runtime_error("mkdir " + dir + ": " + strerror(errno));
            char host[256] = "fenrirds";
            gethostname(host, sizeof(host) - 1);
            std::string kpem = dir + "/key.pem.tmp", cpem = dir + "/cert.pem.tmp";
            bool ok = runProgram({ "openssl", "req", "-x509", "-newkey", "rsa:4096", "-nodes",
                                   "-keyout", kpem, "-out", cpem, "-days", "3650",
                                   "-subj", std::string("/CN=") + host + "/O=fenrirds/OU=auto-generated" })
                   && runProgram({ "openssl", "x509", "-in", cpem, "-outform", "DER", "-out", cert })
                   && runProgram({ "openssl", "rsa", "-in", kpem, "-outform", "DER", "-out", key });
            ::unlink(kpem.c_str());
            ::unlink(cpem.c_str());
            if (!ok) {
                ::unlink(cert.c_str());
                ::unlink(key.c_str());
                throw std::runtime_error("generating a TLS certificate with openssl failed");
            }
            chmod(key.c_str(), 0600);
            chmod(cert.c_str(), 0644);
            std::cerr << "[CONFHELPER] generated self-signed certificate " << cert << std::endl;
#else
            throw std::runtime_error("certificate generation is not supported on this platform");
#endif
            cert = confDir + "/ssl/cert.der";
            key = confDir + "/ssl/key.der";
            std::string err;
            if (!generateCertificate(cert, key, err))
                throw std::runtime_error(err);
        }

        /* validates one field; on success vals holds what goes into the file
@@ -627,6 +585,17 @@ namespace fenrirds::confhelper {
                else
                    cf.put(f.key, vals);
            }
            /* a cluster needs at least three nodes (this one included) */
            if (cf.values("CLUSTER/ENABLE") == std::vector<std::string>{ "true" }
                && cf.values("CLUSTER/PEERS").size() < MIN_CLUSTER_NODES) {
                err = "CLUSTER/PEERS: a cluster needs at least " + std::to_string(MIN_CLUSTER_NODES)
                    + " nodes, this one included";
                return nullptr;
            }
            /* a certificate/key that is configured but not there yet */
            std::vector<std::string> cert = cf.values("SSL/CERT"), key = cf.values("SSL/KEY");
            if (!cert.empty() && !key.empty() && !generateCertificate(cert[0], key[0], err))
                return nullptr;
            cf.save();
            json_object *jres = json_object_new_object();
            json_object_object_add(jres, "ok", json_object_new_boolean(true));
@@ -828,6 +797,64 @@ namespace fenrirds::confhelper {
#endif
    }

    bool generateCertificate(const std::string &cert, const std::string &key, std::string &err) {
#ifndef _WIN32
        struct stat st;
        if (stat(cert.c_str(), &st) == 0 && stat(key.c_str(), &st) == 0)
            return true;
        /* creates the directories of both files (the key's private) */
        auto mkdirs = [&err](const std::string &dir, mode_t mode) {
            for (size_t p = 1; p != std::string::npos; ) {
                p = dir.find('/', p + 1);
                std::string d = dir.substr(0, p);
                struct stat ds;
                if (stat(d.c_str(), &ds) != 0 && mkdir(d.c_str(), p == std::string::npos ? mode : 0755) != 0) {
                    err = "mkdir " + d + ": " + strerror(errno);
                    return false;
                }
            }
            return true;
        };
        if (!mkdirs(parentDir(cert), 0755) || !mkdirs(parentDir(key), 0700))
            return false;
        char host[256] = "fenrirds";
        gethostname(host, sizeof(host) - 1);
        selfcert::Result sc;
        try {
            sc = selfcert::create(host);
        } catch (std::exception &e) {
            err = std::string("generating a TLS certificate failed: ") + e.what();
            return false;
        }
        /* key first and private from the start: write to .tmp, rename */
        auto writeFile = [&err](const std::string &path, const std::vector<uint8_t> &data, mode_t mode) {
            std::string tmp = path + ".tmp";
            int fd = ::open(tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
            bool ok = fd >= 0 && ::write(fd, data.data(), data.size()) == (ssize_t)data.size();
            if (fd >= 0) {
                ok = ok && ::fchmod(fd, mode) == 0;
                ::close(fd);
            }
            if (!ok || ::rename(tmp.c_str(), path.c_str()) != 0) {
                err = "writing " + path + ": " + strerror(errno);
                ::unlink(tmp.c_str());
                return false;
            }
            return true;
        };
        if (!writeFile(key, sc.key, 0600) || !writeFile(cert, sc.cert, 0644)) {
            ::unlink(key.c_str());
            return false;
        }
        std::cerr << "[FENRIRDS] generated self-signed certificate " << cert << " / " << key << std::endl;
        return true;
#else
        (void)cert; (void)key;
        err = "certificate generation is not supported on this platform";
        return false;
#endif
    }

    void start(const std::string &configSpec, const std::vector<std::string> &restartArgv) {
#ifndef _WIN32
        int sv[2];
+5 −0
Changes for src/confhelper.h: 5 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -50,6 +50,11 @@ namespace fenrirds::confhelper {
     * stays false. */
    void start(const std::string &configSpec, const std::vector<std::string> &restartArgv);

    /* Creates a self-signed certificate (DER, RSA, libnetplus) at
     * cert/key unless both files exist. Needs write access there: the
     * helper, or the server while it is still root. */
    bool generateCertificate(const std::string &cert, const std::string &key, std::string &err);

    /* true in the server once start() forked the helper */
    bool available();

Loading