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

test

parent 68c40019
Loading
Loading
Loading
Loading
+31 −7
Changes for src/api/JsonApiServer.cpp: 31 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -272,13 +272,37 @@ json_object *tlsProfileToJson(const store::TlsProfileRecord &r) {
// -- never the unsealed private key. Restoring these on the same deployment (same
// cluster.key) round-trips exactly; restoring onto a different deployment's cluster won't
// decrypt (see src/tls/CertSeal.h) and needs cert_pem/key_pem supplied fresh instead.
json_object *tlsProfileToExportJson(const store::TlsProfileRecord &r) {
//
// r.certPem is only populated in ConfigStore for the manual case, or for self-signed once
// CertificateManager has published it there (cluster mode only, see generateSelfSigned()) --
// single-node self-signed/manual certs live purely on local disk and would otherwise be
// silently missing from every export. Fall back to disk (sealing on the fly with the same
// cluster-wide key CertSeal always uses, cluster mode or not) for exactly that case; skip it
// for ACME profiles, which don't need backing up at all -- they just reissue automatically.
json_object *tlsProfileToExportJson(const config::AppConfig &cfg, const store::TlsProfileRecord &r) {
    json_object *item = tlsProfileToJson(r);
    json_object_object_add(item, "cert_pem", json_object_new_string(r.certPem.c_str()));
    json_object_object_add(item, "encrypted_key_pem_base64",
                            json_object_new_string(toBase64(r.encryptedKeyPem).c_str()));
    json_object_object_add(item, "key_nonce_base64", json_object_new_string(toBase64(r.keyNonce).c_str()));
    json_object_object_add(item, "key_tag_base64", json_object_new_string(toBase64(r.keyTag).c_str()));

    std::string certPem = r.certPem;
    std::vector<uint8_t> encKeyPem = r.encryptedKeyPem, keyNonce = r.keyNonce, keyTag = r.keyTag;
    if (certPem.empty() && !r.acmeEnabled) {
        std::ifstream certFile(cfg.dataPath + "/tls/" + r.profile + ".crt", std::ios::binary);
        std::ifstream keyFile(cfg.dataPath + "/tls/" + r.profile + ".key", std::ios::binary);
        if (certFile && keyFile) {
            std::string diskCert((std::istreambuf_iterator<char>(certFile)), std::istreambuf_iterator<char>());
            std::string diskKey((std::istreambuf_iterator<char>(keyFile)), std::istreambuf_iterator<char>());
            auto clusterKey = cluster::ClusterManager::loadOrCreateSharedKey(cfg.dataPath);
            auto sealed = tls::sealPrivateKey(clusterKey, diskKey);
            certPem = diskCert;
            encKeyPem = sealed.ciphertext;
            keyNonce = sealed.nonce;
            keyTag = sealed.tag;
        }
    }

    json_object_object_add(item, "cert_pem", json_object_new_string(certPem.c_str()));
    json_object_object_add(item, "encrypted_key_pem_base64", json_object_new_string(toBase64(encKeyPem).c_str()));
    json_object_object_add(item, "key_nonce_base64", json_object_new_string(toBase64(keyNonce).c_str()));
    json_object_object_add(item, "key_tag_base64", json_object_new_string(toBase64(keyTag).c_str()));
    return item;
}

@@ -660,7 +684,7 @@ void JsonApiController::registerRoutes() {
             // is a far more privileged permission than tls.profiles.read.
             json_object *profiles = json_object_new_array();
             for (const auto &r : _configStore.listTlsProfiles()) {
                 json_object_array_add(profiles, tlsProfileToExportJson(r));
                 json_object_array_add(profiles, tlsProfileToExportJson(_cfg, r));
             }
             json_object_object_add(root, "tls_profiles", profiles);

+10 −1
Changes for src/cluster/ClusterManager.cpp: 10 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -185,16 +185,25 @@ void ClusterManager::pushLocalSnapshot() {
}

void ClusterManager::pullPeerSnapshots(const std::vector<std::string> &roster) {
    bool anyApplied = false;
    for (const auto &peerId : roster) {
        if (peerId == _cfg.nodeId) continue;
        try {
            auto blob = _client->retrieve(groupIdForNode(peerId));
            _configStore.applySnapshotIfNewer(blob);
            if (_configStore.applySnapshotIfNewer(blob)) anyApplied = true;
        } catch (const std::exception &e) {
            std::lock_guard lk(_statusMutex);
            _status.lastSyncError = std::string("pull from ") + peerId + " failed: " + e.what();
        }
    }
    if (!anyApplied) return;
    std::lock_guard lk(_configChangeMutex);
    for (const auto &cb : _configChangeCallbacks) cb();
}

void ClusterManager::registerConfigChangeCallback(std::function<void()> cb) {
    std::lock_guard lk(_configChangeMutex);
    _configChangeCallbacks.push_back(std::move(cb));
}

void ClusterManager::pollLoop() {
+11 −0
Changes for src/cluster/ClusterManager.h: 11 added lines, 0 removed lines.
Original line number Diff line number Diff line
#pragma once

#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
@@ -51,6 +53,12 @@ public:

    ClusterStatus status() const;

    // Invoked whenever pullPeerSnapshots() actually applies a newer snapshot from some peer
    // (i.e. applySnapshotIfNewer() returned true) -- so e.g. CertificateManager can reconcile
    // TLS profiles right after a peer's edit lands here, instead of only noticing on this
    // node's own next (up to several-hours-away) background check. Register before start().
    void registerConfigChangeCallback(std::function<void()> cb);

private:
    void pollLoop();
    void announceSelfInRoster();
@@ -71,6 +79,9 @@ private:

    mutable std::mutex _statusMutex;
    ClusterStatus _status;

    std::mutex _configChangeMutex;
    std::vector<std::function<void()>> _configChangeCallbacks;
};

} // namespace proxyplus::cluster
+6 −0
Changes for src/main.cpp: 6 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -194,6 +194,12 @@ int main(int argc, char **argv) {
        proxyplus::tls::AcmeChallengeStore acmeChallenges;
        proxyplus::tls::CertificateManager certManager(cfg, configStore, pluginManager,
                                                        acmeChallenges);
        if (clusterManager) {
            // Otherwise a peer's tls/profiles edit only reaches this node's ConfigStore (via
            // the periodic pull above) without this node's CertificateManager noticing until
            // its own next background check, up to kCheckIntervalSeconds (6h) later.
            clusterManager->registerConfigChangeCallback([&certManager] { certManager.checkAllOnce(); });
        }
        registerAutogenSelfSignedProfiles(configStore, cfg.httpListeners);

        // Self-signed profiles must have a cert on disk before any listener binds below --
+6 −2
Changes for src/tls/CertificateManager.cpp: 6 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -247,8 +247,7 @@ void CertificateManager::removeCert(const std::string &profile) {
    ::remove((dir + "/" + profile + ".key").c_str());
}

void CertificateManager::checkLoop() {
    while (_running.load()) {
void CertificateManager::checkAllOnce() {
    for (const auto &rec : _configStore.listTlsProfiles()) {
        if (rec.acmeEnabled) {
            if (needsRenewal(rec.profile)) renew(rec);
@@ -258,6 +257,11 @@ void CertificateManager::checkLoop() {
            materializeManualCert(rec);
        }
    }
}

void CertificateManager::checkLoop() {
    while (_running.load()) {
        checkAllOnce();
        for (int i = 0; i < kCheckIntervalSeconds && _running.load(); ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
Loading