forked from gepaplexx/multena-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathenforcer_logql.go
91 lines (81 loc) · 2.96 KB
/
enforcer_logql.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package main
import (
"fmt"
"strings"
"github.com/rs/zerolog/log"
logqlv2 "github.com/observatorium/api/logql/v2"
"github.com/prometheus/prometheus/model/labels"
)
// LogQLEnforcer manipulates and enforces tenant isolation on LogQL queries.
type LogQLEnforcer struct{}
// Enforce modifies a LogQL query string to enforce tenant isolation based on provided tenant labels and a label match string.
// If the input query is empty, a new query is constructed to match provided tenant labels.
// If the input query is non-empty, it is parsed and modified to ensure tenant isolation.
// Returns the modified query or an error if parsing or modification fails.
func (LogQLEnforcer) Enforce(query string, tenantLabels map[string]bool, labelMatch string) (string, error) {
log.Trace().Str("function", "enforcer").Str("query", query).Msg("input")
if query == "" {
operator := "="
if len(tenantLabels) > 1 {
operator = "=~"
}
query = fmt.Sprintf("{%s%s\"%s\"}", labelMatch, operator, strings.Join(MapKeysToArray(tenantLabels), "|"))
log.Trace().Str("function", "enforcer").Str("query", query).Msg("enforcing")
return query, nil
}
log.Trace().Str("function", "enforcer").Str("query", query).Msg("enforcing")
expr, err := logqlv2.ParseExpr(query)
if err != nil {
return "", err
}
errMsg := error(nil)
expr.Walk(func(expr interface{}) {
switch labelExpression := expr.(type) {
case *logqlv2.StreamMatcherExpr:
matchers, err := matchNamespaceMatchers(labelExpression.Matchers(), tenantLabels, labelMatch)
if err != nil {
errMsg = err
return
}
labelExpression.SetMatchers(matchers)
default:
// Do nothing
}
})
if errMsg != nil {
return "", errMsg
}
log.Trace().Str("function", "enforcer").Str("query", expr.String()).Msg("enforcing")
return expr.String(), nil
}
// matchNamespaceMatchers ensures tenant label matchers in a LogQL query adhere to provided tenant labels.
// It verifies that the tenant label exists in the query matchers, validating or modifying its values based on tenantLabels.
// If the tenant label is absent in the matchers, it's added along with all values from tenantLabels.
// Returns an error for an unauthorized namespace and nil on success.
func matchNamespaceMatchers(queryMatches []*labels.Matcher, tenantLabels map[string]bool, labelMatch string) ([]*labels.Matcher, error) {
foundNamespace := false
for _, match := range queryMatches {
if match.Name == labelMatch {
foundNamespace = true
queryLabels := strings.Split(match.Value, "|")
for _, queryLabel := range queryLabels {
_, ok := tenantLabels[queryLabel]
if !ok {
return nil, fmt.Errorf("unauthorized namespace %s", queryLabel)
}
}
}
}
if !foundNamespace {
matchType := labels.MatchEqual
if len(tenantLabels) > 1 {
matchType = labels.MatchRegexp
}
queryMatches = append(queryMatches, &labels.Matcher{
Type: matchType,
Name: labelMatch,
Value: strings.Join(MapKeysToArray(tenantLabels), "|"),
})
}
return queryMatches, nil
}