feat(server): Adds server configuration management commands and metric monitoring functionality.

- Add a new `server config` command to display server configuration.
- Supports displaying the full token via the --full flag.
- Add the metrics-token configuration option for monitoring access control.
- Integrate Prometheus metrics monitoring system
- Add the /metrics endpoint to provide monitoring data in Prometheus format.
- Add detailed metric collection for tunnels, connections, traffic, etc.
- Add a link to the metrics endpoint on the homepage
refactor: Refactor the token display logic to support full display options.
- Refactor the token mask logic in the configuration display
- Supports controlling the token display method via the configFull flag.
build: Update dependency versions
- Updated github.com/spf13/cobra from v1.10.1 to v1.10.2
- Updated golang.org/x/crypto from v0.45.0 to v0.46.0
- Updated golang.org/x/net from v0.47.0 to v0.48.0
- Update golang.org/x/sys from v0.38.0 to v0.39.0
- Added several new indirect dependency packages, including Prometheus-related components.
- Update the versions of several existing dependency packages.
This commit is contained in:
Gouryella
2026-01-03 16:50:28 +08:00
parent fa92896d7e
commit 11ca454659
13 changed files with 480 additions and 54 deletions

View File

@@ -20,6 +20,7 @@ import (
"drip/internal/shared/pool"
"drip/internal/shared/protocol"
"github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap"
)
@@ -33,18 +34,20 @@ var bufioReaderPool = sync.Pool{
const openStreamTimeout = 3 * time.Second
type Handler struct {
manager *tunnel.Manager
logger *zap.Logger
domain string
authToken string
manager *tunnel.Manager
logger *zap.Logger
domain string
authToken string
metricsToken string
}
func NewHandler(manager *tunnel.Manager, logger *zap.Logger, domain string, authToken string) *Handler {
func NewHandler(manager *tunnel.Manager, logger *zap.Logger, domain string, authToken string, metricsToken string) *Handler {
return &Handler{
manager: manager,
logger: logger,
domain: domain,
authToken: authToken,
manager: manager,
logger: logger,
domain: domain,
authToken: authToken,
metricsToken: metricsToken,
}
}
@@ -57,6 +60,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.serveStats(w, r)
return
}
if r.URL.Path == "/metrics" {
h.serveMetrics(w, r)
return
}
subdomain := h.extractSubdomain(r.Host)
if subdomain == "" {
@@ -346,7 +353,7 @@ func (h *Handler) serveHomePage(w http.ResponseWriter, r *http.Request) {
<code>drip http 3000</code><br><br>
<code>drip https 443</code><br><br>
<code>drip tcp 5432</code>
<p><a href="/health">Health Check</a> | <a href="/stats">Statistics</a></p>
<p><a href="/health">Health Check</a> | <a href="/stats">Statistics</a> | <a href="/metrics">Prometheus Metrics</a></p>
</body>
</html>`
@@ -375,7 +382,7 @@ func (h *Handler) serveHealth(w http.ResponseWriter, r *http.Request) {
}
func (h *Handler) serveStats(w http.ResponseWriter, r *http.Request) {
if h.authToken != "" {
if h.metricsToken != "" {
// Only accept token via Authorization header (Bearer token)
// URL query parameters are insecure (logged, cached, visible in browser history)
var token string
@@ -384,9 +391,9 @@ func (h *Handler) serveStats(w http.ResponseWriter, r *http.Request) {
token = strings.TrimPrefix(authHeader, "Bearer ")
}
if token != h.authToken {
if token != h.metricsToken {
w.Header().Set("WWW-Authenticate", `Bearer realm="stats"`)
http.Error(w, "Unauthorized: provide token via 'Authorization: Bearer <token>' header", http.StatusUnauthorized)
http.Error(w, "Unauthorized: provide metrics token via 'Authorization: Bearer <token>' header", http.StatusUnauthorized)
return
}
}
@@ -426,6 +433,26 @@ func (h *Handler) serveStats(w http.ResponseWriter, r *http.Request) {
w.Write(data)
}
func (h *Handler) serveMetrics(w http.ResponseWriter, r *http.Request) {
if h.metricsToken != "" {
// Only accept token via Authorization header (Bearer token)
var token string
authHeader := r.Header.Get("Authorization")
if strings.HasPrefix(authHeader, "Bearer ") {
token = strings.TrimPrefix(authHeader, "Bearer ")
}
if token != h.metricsToken {
w.Header().Set("WWW-Authenticate", `Bearer realm="metrics"`)
http.Error(w, "Unauthorized: provide metrics token via 'Authorization: Bearer <token>' header", http.StatusUnauthorized)
return
}
}
// Serve Prometheus metrics
promhttp.Handler().ServeHTTP(w, r)
}
type bufferedReadWriteCloser struct {
*bufio.Reader
net.Conn