Add the native Go client and finish M3 hardening
The new cmd/tunnel client keeps the API URL and tunnel identity in ~/.config/tunnel/config.json (0600), provisions the key/CSR and certificate, terminates TLS, proxies to local routes, reloads routes without a restart, and runs as a background service. `tunnel import` takes over an existing official CLI profile, preserving its tunnel ID, token, key and certificate. Server hardening: per-IP and per-tunnel rate limits for creates and binds, a CA cool-down after repeated ACME failures, a startup sweep for stale _acme-challenge records, admin metrics, a 60-second half-close timeout, and separate prioritized control / ordered data bridge queues (with `end` ordered after the final bytes). The dashboard shows the new metrics.
This commit is contained in:
@@ -60,10 +60,24 @@ type adminOverview struct {
|
||||
Server adminServer `json:"server"`
|
||||
CA adminCA `json:"ca"`
|
||||
Totals adminTotals `json:"totals"`
|
||||
Metrics adminMetrics `json:"metrics"`
|
||||
Tunnels []adminTunnel `json:"tunnels"`
|
||||
Bridges []adminBridge `json:"bridges"`
|
||||
}
|
||||
|
||||
type adminMetrics struct {
|
||||
CreateRequests uint64 `json:"create_requests"`
|
||||
BindRequests uint64 `json:"bind_requests"`
|
||||
RateLimited uint64 `json:"rate_limited"`
|
||||
IssuanceAttempts uint64 `json:"issuance_attempts"`
|
||||
IssuanceReady uint64 `json:"issuance_ready"`
|
||||
IssuanceFailed uint64 `json:"issuance_failed"`
|
||||
CertificatesReady int `json:"certificates_ready"`
|
||||
CertificatesIssuing int `json:"certificates_issuing"`
|
||||
CertificatesFailed int `json:"certificates_failed"`
|
||||
CertificatesRetrying int `json:"certificates_retrying"`
|
||||
}
|
||||
|
||||
// adminAuthorized enforces the bearer ADMIN_TOKEN; 503 when unset.
|
||||
func (a *API) adminAuthorized(w http.ResponseWriter, r *http.Request) bool {
|
||||
if a.AdminToken == "" {
|
||||
@@ -145,10 +159,28 @@ func (a *API) adminOverviewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
entry.CertificateState = certState(t.Certificate).Type
|
||||
entry.CertificateIssuer = t.Certificate.Issuer
|
||||
entry.CertificateError = t.Certificate.Reason
|
||||
switch {
|
||||
case certReady(t.Certificate):
|
||||
overview.Metrics.CertificatesReady++
|
||||
if t.Certificate.State == "failed" {
|
||||
overview.Metrics.CertificatesRetrying++
|
||||
}
|
||||
case t.Certificate.State == "failed":
|
||||
overview.Metrics.CertificatesFailed++
|
||||
default:
|
||||
overview.Metrics.CertificatesIssuing++
|
||||
}
|
||||
}
|
||||
overview.Tunnels = append(overview.Tunnels, entry)
|
||||
}
|
||||
|
||||
overview.Metrics.CreateRequests = a.createCount.Load()
|
||||
overview.Metrics.BindRequests = a.bindCount.Load()
|
||||
overview.Metrics.RateLimited = a.rateLimited.Load()
|
||||
overview.Metrics.IssuanceAttempts = a.issueAttempts.Load()
|
||||
overview.Metrics.IssuanceReady = a.issueReady.Load()
|
||||
overview.Metrics.IssuanceFailed = a.issueFailed.Load()
|
||||
|
||||
writeJSON(w, http.StatusOK, overview)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user