forked from prometheus-community/prom-label-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
187 lines (162 loc) · 7.15 KB
/
main.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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright 2020 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"errors"
"flag"
"log"
"net"
"net/http"
"net/url"
"os"
"strings"
"syscall"
"github.com/metalmatze/signal/internalserver"
"github.com/oklog/run"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus-community/prom-label-proxy/injectproxy"
)
func main() {
var (
insecureListenAddress string
internalListenAddress string
upstream string
queryParam string
headerName string
label string
labelValue string
enableLabelAPIs bool
unsafePassthroughPaths string // Comma-delimited string.
errorOnReplace bool
)
flagset := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
flagset.StringVar(&insecureListenAddress, "insecure-listen-address", "", "The address the prom-label-proxy HTTP server should listen on.")
flagset.StringVar(&internalListenAddress, "internal-listen-address", "", "The address the internal prom-label-proxy HTTP server should listen on to expose metrics about itself.")
flagset.StringVar(&queryParam, "query-param", "", "Name of the HTTP parameter that contains the tenant value.At most one of -query-param, -header-name and -label-value should be given. If the flag isn't defined and neither -header-name nor -label-value is set, it will default to the value of the -label flag.")
flagset.StringVar(&headerName, "header-name", "", "Name of the HTTP header name that contains the tenant value. At most one of -query-param, -header-name and -label-value should be given.")
flagset.StringVar(&upstream, "upstream", "", "The upstream URL to proxy to.")
flagset.StringVar(&label, "label", "", "The label name to enforce in all proxied PromQL queries.")
flagset.StringVar(&labelValue, "label-value", "", "A fixed label value to enforce in all proxied PromQL queries. At most one of -query-param, -header-name and -label-value should be given.")
flagset.BoolVar(&enableLabelAPIs, "enable-label-apis", false, "When specified proxy allows to inject label to label APIs like /api/v1/labels and /api/v1/label/<name>/values. "+
"NOTE: Enable with care because filtering by matcher is not implemented in older versions of Prometheus (>= v2.24.0 required) and Thanos (>= v0.18.0 required, >= v0.23.0 recommended). If enabled and "+
"any labels endpoint does not support selectors, the injected matcher will have no effect.")
flagset.StringVar(&unsafePassthroughPaths, "unsafe-passthrough-paths", "", "Comma delimited allow list of exact HTTP path segments that should be allowed to hit upstream URL without any enforcement. "+
"This option is checked after Prometheus APIs, you cannot override enforced API endpoints to be not enforced with this option. Use carefully as it can easily cause a data leak if the provided path is an important "+
"API (like /api/v1/configuration) which isn't enforced by prom-label-proxy. NOTE: \"all\" matching paths like \"/\" or \"\" and regex are not allowed.")
flagset.BoolVar(&errorOnReplace, "error-on-replace", false, "When specified, the proxy will return HTTP status code 400 if the query already contains a label matcher that differs from the one the proxy would inject.")
//nolint: errcheck // Parse() will exit on error.
flagset.Parse(os.Args[1:])
if label == "" {
log.Fatalf("-label flag cannot be empty")
}
if labelValue == "" && queryParam == "" && headerName == "" {
queryParam = label
}
if labelValue != "" {
if queryParam != "" || headerName != "" {
log.Fatalf("at most one of -query-param, -header-name and -label-value must be set")
}
} else if queryParam != "" && headerName != "" {
log.Fatalf("at most one of -query-param, -header-name and -label-value must be set")
}
upstreamURL, err := url.Parse(upstream)
if err != nil {
log.Fatalf("Failed to build parse upstream URL: %v", err)
}
if upstreamURL.Scheme != "http" && upstreamURL.Scheme != "https" {
log.Fatalf("Invalid scheme for upstream URL %q, only 'http' and 'https' are supported", upstream)
}
reg := prometheus.NewRegistry()
reg.MustRegister(
collectors.NewGoCollector(),
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
)
opts := []injectproxy.Option{injectproxy.WithPrometheusRegistry(reg)}
if enableLabelAPIs {
opts = append(opts, injectproxy.WithEnabledLabelsAPI())
}
if len(unsafePassthroughPaths) > 0 {
opts = append(opts, injectproxy.WithPassthroughPaths(strings.Split(unsafePassthroughPaths, ",")))
}
if errorOnReplace {
opts = append(opts, injectproxy.WithErrorOnReplace())
}
var extractLabeler injectproxy.ExtractLabeler
switch {
case labelValue != "":
extractLabeler = injectproxy.StaticLabelEnforcer(labelValue)
case queryParam != "":
extractLabeler = injectproxy.HTTPFormEnforcer{ParameterName: queryParam}
case headerName != "":
extractLabeler = injectproxy.HTTPHeaderEnforcer{Name: http.CanonicalHeaderKey(headerName)}
}
var g run.Group
{
// Run the insecure HTTP server.
routes, err := injectproxy.NewRoutes(upstreamURL, label, extractLabeler, opts...)
if err != nil {
log.Fatalf("Failed to create injectproxy Routes: %v", err)
}
mux := http.NewServeMux()
mux.Handle("/", routes)
l, err := net.Listen("tcp", insecureListenAddress)
if err != nil {
log.Fatalf("Failed to listen on insecure address: %v", err)
}
srv := &http.Server{Handler: mux}
g.Add(func() error {
log.Printf("Listening insecurely on %v", l.Addr())
if err := srv.Serve(l); err != nil && err != http.ErrServerClosed {
log.Printf("Server stopped with %v", err)
return err
}
return nil
}, func(error) {
srv.Close()
})
}
if internalListenAddress != "" {
// Run the internal HTTP server.
h := internalserver.NewHandler(
internalserver.WithName("Internal prom-label-proxy API"),
internalserver.WithPrometheusRegistry(reg),
internalserver.WithPProf(),
)
// Run the HTTP server.
l, err := net.Listen("tcp", internalListenAddress)
if err != nil {
log.Fatalf("Failed to listen on internal address: %v", err)
}
srv := &http.Server{Handler: h}
g.Add(func() error {
log.Printf("Listening on %v for metrics and pprof", l.Addr())
if err := srv.Serve(l); err != nil && err != http.ErrServerClosed {
log.Printf("Internal server stopped with %v", err)
return err
}
return nil
}, func(error) {
srv.Close()
})
}
g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
if err := g.Run(); err != nil {
if !errors.As(err, &run.SignalError{}) {
log.Printf("Server stopped with %v", err)
os.Exit(1)
}
log.Print("Caught signal; exiting gracefully...")
}
}