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

test

parent 416e23be
Loading
Loading
Loading
Loading
+12 −1
Changes for admin/static/app.js: 12 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -78,7 +78,12 @@ const ProxyPlusAdmin = (() => {
    body.innerHTML = "";
    for (const r of routes) {
      const tr = document.createElement("tr");
      tr.innerHTML = `<td>${r.id}</td><td>${r.path_prefix}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td></td>`;
      const tlsInfo = r.tls_verify_upstream
        ? "geprüft" + (r.tls_pinned_fingerprints_sha256 && r.tls_pinned_fingerprints_sha256.length
            ? " (" + r.tls_pinned_fingerprints_sha256.length + " gepinnt)"
            : "")
        : "keine";
      tr.innerHTML = `<td>${r.id}</td><td>${r.path_prefix}</td><td>${r.upstream}</td><td>${r.upstream_pool}</td><td>${r.required_permission}</td><td>${tlsInfo}</td><td></td>`;
      addDeleteButton(tr, () => deleteRoute(r.id));
      body.appendChild(tr);
    }
@@ -203,12 +208,18 @@ const ProxyPlusAdmin = (() => {
  async function createRoute() {
    document.getElementById("route-error").textContent = "";
    try {
      const pins = document.getElementById("route-tls-pins").value
        .split(",")
        .map((s) => s.trim())
        .filter(Boolean);
      await api("POST", "/api/v1/http/routes", {
        id: document.getElementById("route-id").value,
        path_prefix: document.getElementById("route-path").value,
        upstream: document.getElementById("route-upstream").value,
        upstream_pool: document.getElementById("route-upstream-pool").value,
        required_permission: document.getElementById("route-permission").value,
        tls_verify_upstream: document.getElementById("route-tls-verify").checked,
        tls_pinned_fingerprints_sha256: pins,
      });
      await refreshRoutes();
    } catch (e) {
+3 −1
Changes for admin/static/index.html: 3 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@
  <table id="status-table"></table>

  <h2>HTTP-Routen</h2>
  <table id="http-routes"><thead><tr><th>ID</th><th>Pfad</th><th>Upstream</th><th>Pool</th><th>Berechtigung</th><th>Aktionen</th></tr></thead><tbody></tbody></table>
  <table id="http-routes"><thead><tr><th>ID</th><th>Pfad</th><th>Upstream</th><th>Pool</th><th>Berechtigung</th><th>TLS-Pruefung</th><th>Aktionen</th></tr></thead><tbody></tbody></table>
  <button onclick="ProxyPlusAdmin.showNewRouteForm()">Neue Route</button>
  <div id="new-route-form" hidden>
    <input id="route-id" placeholder="ID">
@@ -32,6 +32,8 @@
    <input id="route-upstream" placeholder="Upstream (direkt), genau eine URL, z.B. http://10.0.0.5:8080">
    <input id="route-upstream-pool" placeholder="oder Pool-ID fuer mehrere Ziele (statt Upstream)">
    <input id="route-permission" placeholder="Berechtigung, z.B. http.proxy.use">
    <label><input type="checkbox" id="route-tls-verify"> Upstream-TLS-Zertifikat pruefen</label>
    <input id="route-tls-pins" placeholder="Gepinnte SHA-256-Fingerprints, kommagetrennt (fuer selbstsignierte Upstreams)">
    <button onclick="ProxyPlusAdmin.createRoute()">Anlegen</button>
    <div id="route-error" class="error"></div>
  </div>
+17 −0
Changes for src/api/JsonApiServer.cpp: 17 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -203,6 +203,14 @@ void JsonApiController::registerRoutes() {
                 json_object_object_add(item, "upstream_pool", json_object_new_string(r.upstreamPool.c_str()));
                 json_object_object_add(item, "required_permission",
                                         json_object_new_string(r.requiredPermission.c_str()));
                 json_object_object_add(item, "tls_verify_upstream",
                                         json_object_new_boolean(r.tlsVerifyUpstream));
                 JsonGuard fpArr(json_object_new_array());
                 for (const auto &fp : r.tlsPinnedFingerprintsSha256) {
                     json_object_array_add(fpArr.obj, json_object_new_string(fp.c_str()));
                 }
                 json_object_get(fpArr.obj);
                 json_object_object_add(item, "tls_pinned_fingerprints_sha256", fpArr.obj);
                 json_object_get(item.obj);
                 json_object_array_add(arr.obj, item.obj);
             }
@@ -219,6 +227,15 @@ void JsonApiController::registerRoutes() {
             rec.upstream = getStringField(body, "upstream");
             rec.upstreamPool = getStringField(body, "upstream_pool");
             rec.requiredPermission = getStringField(body, "required_permission");
             rec.tlsVerifyUpstream = getBoolField(body, "tls_verify_upstream");
             json_object *fpArr = nullptr;
             if (json_object_object_get_ex(body, "tls_pinned_fingerprints_sha256", &fpArr) && fpArr) {
                 int n = json_object_array_length(fpArr);
                 for (int i = 0; i < n; ++i) {
                     const char *fp = json_object_get_string(json_object_array_get_idx(fpArr, i));
                     if (fp) rec.tlsPinnedFingerprintsSha256.emplace_back(fp);
                 }
             }
             if (rec.id.empty() || rec.pathPrefix.empty()) return jsonError("id and path_prefix are required");
             if (rec.upstream.empty() && rec.upstreamPool.empty()) {
                 return jsonError("either upstream or upstream_pool is required");
+39 −1
Changes for src/http/HttpProxyServer.cpp: 39 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include <set>

#include <netplus/socket.h>
#include <netplus/crypto/cert_verify.h>

namespace proxyplus::httpproxy {

@@ -19,6 +20,43 @@ std::string methodName(int requestType) {
    }
}

// Parses a hex-encoded SHA-256 fingerprint (e.g. "AB:CD:..." or "abcd...", as produced by
// `openssl x509 -noout -fingerprint -sha256`) into its raw 32 bytes. Returns empty on any
// malformed input -- callers skip those rather than fail the whole route.
std::vector<uint8_t> parseHexFingerprint(const std::string &hex) {
    std::string clean;
    clean.reserve(hex.size());
    for (char c : hex) {
        if (c != ':' && c != ' ') clean.push_back(c);
    }
    if (clean.size() != 64) return {};
    std::vector<uint8_t> out;
    out.reserve(32);
    for (size_t i = 0; i < clean.size(); i += 2) {
        auto hexVal = [](char c) -> int {
            if (c >= '0' && c <= '9') return c - '0';
            if (c >= 'a' && c <= 'f') return c - 'a' + 10;
            if (c >= 'A' && c <= 'F') return c - 'A' + 10;
            return -1;
        };
        int hi = hexVal(clean[i]);
        int lo = hexVal(clean[i + 1]);
        if (hi < 0 || lo < 0) return {};
        out.push_back(static_cast<uint8_t>((hi << 4) | lo));
    }
    return out;
}

netplus::TlsTrustPolicy trustPolicyFor(const store::HttpRouteRecord &route) {
    netplus::TlsTrustPolicy policy;
    policy.verifyPeer = route.tlsVerifyUpstream;
    for (const auto &hex : route.tlsPinnedFingerprintsSha256) {
        auto raw = parseHexFingerprint(hex);
        if (!raw.empty()) policy.pinnedSha256Fingerprints.push_back(std::move(raw));
    }
    return policy;
}

} // namespace

HttpProxyController::HttpProxyController(std::vector<netplus::socket *> serverSockets,
@@ -278,7 +316,7 @@ void HttpProxyController::RequestEvent(libhttppp::HttpRequest &curreq, const int
            // vers=3: try HTTP/3 first, falling back to HTTP/2 then HTTP/1.1 over
            // TLS (or straight to H1 for a cleartext upstream) -- see HttpClient
            // ctor in libhttppp/src/http.cpp for the fallback ladder.
            libhttppp::HttpClient client(upstream, /*vers=*/3);
            libhttppp::HttpClient client(upstream, /*vers=*/3, /*timeoutSec=*/60, trustPolicyFor(matched));
            libhttppp::HttpRequest outreq;
            outreq.setRequestURL(upstream.getPath());

+7 −0
Changes for src/store/ConfigStore.cpp: 7 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -96,6 +96,9 @@ void encodeHttpRoute(ByteWriter &w, const HttpRouteRecord &r) {
    w.str(r.upstream);
    w.str(r.requiredPermission);
    w.str(r.upstreamPool);
    w.u8(r.tlsVerifyUpstream ? 1 : 0);
    w.u32(static_cast<uint32_t>(r.tlsPinnedFingerprintsSha256.size()));
    for (const auto &fp : r.tlsPinnedFingerprintsSha256) w.str(fp);
}
HttpRouteRecord decodeHttpRoute(ByteReader &r) {
    HttpRouteRecord rec;
@@ -104,6 +107,10 @@ HttpRouteRecord decodeHttpRoute(ByteReader &r) {
    rec.upstream = r.str();
    rec.requiredPermission = r.str();
    rec.upstreamPool = r.str();
    rec.tlsVerifyUpstream = r.u8() != 0;
    uint32_t fpCount = r.u32();
    rec.tlsPinnedFingerprintsSha256.reserve(fpCount);
    for (uint32_t i = 0; i < fpCount; ++i) rec.tlsPinnedFingerprintsSha256.push_back(r.str());
    return rec;
}

Loading