forked from prometheus-community/prom-label-proxy
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.go
130 lines (113 loc) · 5.13 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
// 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 (
"flag"
"log"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"strings"
"syscall"
"github.com/prometheus-community/prom-label-proxy/injectproxy"
)
func main() {
var (
insecureListenAddress string
upstream string
label string
headerName string
queryParam string
enableLabelAPIs bool
unsafePassthroughPaths string // Comma-delimited string.
errorOnReplace bool
header string
)
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(&upstream, "upstream", "", "The upstream URL to proxy to.")
flagset.StringVar(&queryParam, "query-param", "", "The query parameter to obtain the label value from. This or -header is required.")
flagset.StringVar(&headerName, "headerName", "", "The HTTP header name to obtain the label value from. This or -query-param is required.")
flagset.StringVar(&label, "label", "", "The label to enforce in all proxied PromQL queries. "+
"This label will be also required as the URL parameter to get the value to be injected. For example: -label=tenant will"+
" make it required for this proxy to have URL in form of: <URL>?tenant=abc&other_params...")
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.")
flagset.StringVar(&header, "header", "", "(Optional) An HTTP header to get the label value from.")
//nolint: errcheck // Parse() will exit on error.
flagset.Parse(os.Args[1:])
if label == "" {
log.Fatalf("-label flag cannot be empty")
}
var opts []injectproxy.Option
if (queryParam != "" && header != "") || (queryParam == "" && header == "") {
log.Fatal("exactly one of -query-param and -header must be given")
}
if queryParam != "" {
opts = append(opts, injectproxy.WithValueFromQuery(queryParam))
}
if header != "" {
opts = append(opts, injectproxy.WithValueFromHeader(header))
}
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)
}
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())
}
routes, err := injectproxy.NewRoutes(upstreamURL, label, opts...)
if err != nil {
log.Fatalf("Failed to create injectproxy Routes: %v", err)
}
mux := http.NewServeMux()
mux.Handle("/", routes)
srv := &http.Server{Handler: mux}
l, err := net.Listen("tcp", insecureListenAddress)
if err != nil {
log.Fatalf("Failed to listen on insecure address: %v", err)
}
errCh := make(chan error)
go func() {
log.Printf("Listening insecurely on %v", l.Addr())
errCh <- srv.Serve(l)
}()
term := make(chan os.Signal, 1)
signal.Notify(term, os.Interrupt, syscall.SIGTERM)
select {
case <-term:
log.Print("Received SIGTERM, exiting gracefully...")
srv.Close()
case err := <-errCh:
if err != http.ErrServerClosed {
log.Printf("Server stopped with %v", err)
}
os.Exit(1)
}
}