metrics: Added prometheus metrics for rules and challenges

This commit is contained in:
WeebDataHoarder
2025-04-25 11:26:44 +02:00
parent 6f3d81618c
commit 47f9f6fee6
11 changed files with 156 additions and 16 deletions

View File

@@ -112,6 +112,9 @@ func (a Challenge) Handle(logger *slog.Logger, w http.ResponseWriter, r *http.Re
data := challenge.RequestDataFromContext(r.Context())
for _, reg := range a.Challenges {
if data.HasValidChallenge(reg.Id()) {
data.State.ChallengeChecked(r, reg, r.URL.String(), logger)
if a.Continue {
return true, nil
}

View File

@@ -97,6 +97,10 @@ type StateInterface interface {
ChallengeFailed(r *http.Request, reg *Registration, err error, redirect string, logger *slog.Logger)
ChallengePassed(r *http.Request, reg *Registration, redirect string, logger *slog.Logger)
ChallengeIssued(r *http.Request, reg *Registration, redirect string, logger *slog.Logger)
ChallengeChecked(r *http.Request, reg *Registration, redirect string, logger *slog.Logger)
RuleHit(r *http.Request, name string, logger *slog.Logger)
RuleMiss(r *http.Request, name string, logger *slog.Logger)
Logger(r *http.Request) *slog.Logger

View File

@@ -43,7 +43,7 @@ func (state *State) ChallengeFailed(r *http.Request, reg *challenge.Registration
}
logger.Warn("challenge failed", "challenge", reg.Name, "err", err, "redirect", redirect)
//TODO: metrics
state.metrics.Challenge(reg.Name, "fail")
}
func (state *State) ChallengePassed(r *http.Request, reg *challenge.Registration, redirect string, logger *slog.Logger) {
@@ -52,7 +52,7 @@ func (state *State) ChallengePassed(r *http.Request, reg *challenge.Registration
}
logger.Warn("challenge passed", "challenge", reg.Name, "redirect", redirect)
//TODO: metrics
state.metrics.Challenge(reg.Name, "pass")
}
func (state *State) ChallengeIssued(r *http.Request, reg *challenge.Registration, redirect string, logger *slog.Logger) {
@@ -61,7 +61,19 @@ func (state *State) ChallengeIssued(r *http.Request, reg *challenge.Registration
}
logger.Info("challenge issued", "challenge", reg.Name, "redirect", redirect)
//TODO: metrics
state.metrics.Challenge(reg.Name, "issue")
}
func (state *State) ChallengeChecked(r *http.Request, reg *challenge.Registration, redirect string, logger *slog.Logger) {
state.metrics.Challenge(reg.Name, "check")
}
func (state *State) RuleHit(r *http.Request, name string, logger *slog.Logger) {
state.metrics.Rule(name, "hit")
}
func (state *State) RuleMiss(r *http.Request, name string, logger *slog.Logger) {
state.metrics.Rule(name, "miss")
}
func (state *State) Logger(r *http.Request) *slog.Logger {

32
lib/metrics.go Normal file
View File

@@ -0,0 +1,32 @@
package lib
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
type stateMetrics struct {
rules *prometheus.CounterVec
challenges *prometheus.CounterVec
}
func newMetrics() *stateMetrics {
return &stateMetrics{
rules: promauto.NewCounterVec(prometheus.CounterOpts{
Name: "go-away_rule_results",
Help: "The number of rule hits or misses",
}, []string{"rule", "result"}),
challenges: promauto.NewCounterVec(prometheus.CounterOpts{
Name: "go-away_challenge_actions",
Help: "The number of challenges issued, passed or explicitly failed",
}, []string{"challenge", "action"}),
}
}
func (metrics *stateMetrics) Rule(name, result string) {
metrics.rules.With(prometheus.Labels{"rule": name, "result": result}).Inc()
}
func (metrics *stateMetrics) Challenge(name, action string) {
metrics.challenges.With(prometheus.Labels{"challenge": name, "action": action}).Inc()
}

View File

@@ -107,6 +107,8 @@ func (rule RuleState) Evaluate(logger *slog.Logger, w http.ResponseWriter, r *ht
return false, fmt.Errorf("error: evaluating administrative rule %s/%s: %w", data.Id.String(), rule.Hash, err)
} else if out != nil && out.Type() == types.BoolType {
if out.Equal(types.True) == types.True {
data.State.RuleHit(r, rule.Name, logger)
next, err = rule.Handler.Handle(lg, w, r, func() http.Handler {
r.Header.Set("X-Away-Rule", rule.Name)
r.Header.Set("X-Away-Hash", rule.Hash)
@@ -134,7 +136,13 @@ func (rule RuleState) Evaluate(logger *slog.Logger, w http.ResponseWriter, r *ht
return next, nil
}
}
} else {
data.State.RuleMiss(r, rule.Name, logger)
}
} else if out != nil {
err := fmt.Errorf("return type not Bool, got %s", out.Type().TypeName())
lg.Error(err.Error())
return false, fmt.Errorf("error: evaluating administrative rule %s/%s: %w", data.Id.String(), rule.Hash, err)
}
return true, nil

View File

@@ -20,9 +20,16 @@ type Backend struct {
// TLSSkipVerify Disable TLS certificate verification, if any
TLSSkipVerify bool `yaml:"tls-skip-verify"`
// IpHeader HTTP header to set containing the IP header. Set - to forcefully ignore global defaults.
IpHeader string `yaml:"ip-header"`
}
func (b Backend) Create() (*httputil.ReverseProxy, error) {
if b.IpHeader == "-" {
b.IpHeader = ""
}
proxy, err := utils.MakeReverseProxy(b.URL)
if err != nil {
return nil, err
@@ -40,9 +47,19 @@ func (b Backend) Create() (*httputil.ReverseProxy, error) {
if b.Host != "" {
transport.TLSClientConfig.ServerName = b.Host
}
if b.IpHeader != "" || b.Host != "" {
director := proxy.Director
proxy.Director = func(req *http.Request) {
req.Host = b.Host
if b.IpHeader != "" {
if ip := utils.GetRemoteAddress(req.Context()); ip != nil {
req.Header.Set(b.IpHeader, ip.Addr().Unmap().String())
}
}
if b.Host != "" {
req.Host = b.Host
}
director(req)
}
}

View File

@@ -44,6 +44,8 @@ type State struct {
close chan struct{}
Mux *http.ServeMux
metrics *stateMetrics
}
func NewState(p policy.Policy, opt settings.Settings, settings policy.StateSettings) (handler http.Handler, err error) {
@@ -51,6 +53,7 @@ func NewState(p policy.Policy, opt settings.Settings, settings policy.StateSetti
state.close = make(chan struct{})
state.settings = settings
state.opt = opt
state.metrics = newMetrics()
state.client = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
@@ -214,7 +217,6 @@ func NewState(p policy.Policy, opt settings.Settings, settings policy.StateSetti
}
for _, r := range p.Rules {
rule, err := NewRuleState(state, r, conditionReplacer, nil)
if err != nil {
return nil, fmt.Errorf("rule %s: %w", r.Name, err)