feat: add Prometheus metrics endpoint and Docker image (PLAN.md 4.2)
Add internal/metrics package with dedicated Prometheus registry exposing SSH connection, auth attempt, session, and build info metrics. Wire into SSH server (4 instrumentation points) and web server (/metrics endpoint). Add dockerImage output to flake.nix via dockerTools.buildLayeredImage. Bump version to 0.7.0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
93
internal/metrics/metrics.go
Normal file
93
internal/metrics/metrics.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// Metrics holds all Prometheus collectors for the honeypot.
|
||||
type Metrics struct {
|
||||
registry *prometheus.Registry
|
||||
|
||||
SSHConnectionsTotal *prometheus.CounterVec
|
||||
SSHConnectionsActive prometheus.Gauge
|
||||
AuthAttemptsTotal *prometheus.CounterVec
|
||||
SessionsTotal *prometheus.CounterVec
|
||||
SessionsActive prometheus.Gauge
|
||||
SessionDuration prometheus.Histogram
|
||||
BuildInfo *prometheus.GaugeVec
|
||||
}
|
||||
|
||||
// New creates a new Metrics instance with all collectors registered.
|
||||
func New(version string) *Metrics {
|
||||
reg := prometheus.NewRegistry()
|
||||
|
||||
m := &Metrics{
|
||||
registry: reg,
|
||||
SSHConnectionsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "oubliette_ssh_connections_total",
|
||||
Help: "Total SSH connections received.",
|
||||
}, []string{"outcome"}),
|
||||
SSHConnectionsActive: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "oubliette_ssh_connections_active",
|
||||
Help: "Current active SSH connections.",
|
||||
}),
|
||||
AuthAttemptsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "oubliette_auth_attempts_total",
|
||||
Help: "Total authentication attempts.",
|
||||
}, []string{"result", "reason"}),
|
||||
SessionsTotal: prometheus.NewCounterVec(prometheus.CounterOpts{
|
||||
Name: "oubliette_sessions_total",
|
||||
Help: "Total sessions created.",
|
||||
}, []string{"shell"}),
|
||||
SessionsActive: prometheus.NewGauge(prometheus.GaugeOpts{
|
||||
Name: "oubliette_sessions_active",
|
||||
Help: "Current active sessions.",
|
||||
}),
|
||||
SessionDuration: prometheus.NewHistogram(prometheus.HistogramOpts{
|
||||
Name: "oubliette_session_duration_seconds",
|
||||
Help: "Session duration in seconds.",
|
||||
Buckets: []float64{1, 5, 10, 30, 60, 120, 300, 600, 1800, 3600},
|
||||
}),
|
||||
BuildInfo: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||
Name: "oubliette_build_info",
|
||||
Help: "Build information. Always 1.",
|
||||
}, []string{"version"}),
|
||||
}
|
||||
|
||||
reg.MustRegister(
|
||||
collectors.NewGoCollector(),
|
||||
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
||||
m.SSHConnectionsTotal,
|
||||
m.SSHConnectionsActive,
|
||||
m.AuthAttemptsTotal,
|
||||
m.SessionsTotal,
|
||||
m.SessionsActive,
|
||||
m.SessionDuration,
|
||||
m.BuildInfo,
|
||||
)
|
||||
|
||||
m.BuildInfo.WithLabelValues(version).Set(1)
|
||||
|
||||
// Initialize label combinations so they appear in Gather/output.
|
||||
for _, outcome := range []string{"accepted", "rejected_handshake", "rejected_max_connections"} {
|
||||
m.SSHConnectionsTotal.WithLabelValues(outcome)
|
||||
}
|
||||
for _, reason := range []string{"static_credential", "remembered_credential", "threshold_reached", "rejected"} {
|
||||
m.AuthAttemptsTotal.WithLabelValues("accepted", reason)
|
||||
m.AuthAttemptsTotal.WithLabelValues("rejected", reason)
|
||||
}
|
||||
for _, shell := range []string{"bash", "fridge", "banking", "adventure"} {
|
||||
m.SessionsTotal.WithLabelValues(shell)
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Handler returns an http.Handler that serves Prometheus metrics.
|
||||
func (m *Metrics) Handler() http.Handler {
|
||||
return promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{})
|
||||
}
|
||||
Reference in New Issue
Block a user