Skip to content

Commit ed44759

Browse files
committed
Add proxy protocol support
1 parent bff607c commit ed44759

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ Flags:
181181
--nginx.ssl-client-cert=""
182182
Path to the PEM encoded client certificate file to use when connecting to the server. ($SSL_CLIENT_CERT)
183183
--nginx.ssl-client-key="" Path to the PEM encoded client certificate key file to use when connecting to the server. ($SSL_CLIENT_KEY)
184+
--[no-]nginx.proxy-protocol
185+
Pass proxy protocol payload to nginx listeners. ($PROXY_PROTOCOL)
184186
--nginx.timeout=5s A timeout for scraping metrics from NGINX or NGINX Plus. ($TIMEOUT)
185187
--prometheus.const-label=PROMETHEUS.CONST-LABEL ...
186188
Label that will be used in every metric. Format is label=value. It can be repeated multiple times. ($CONST_LABELS)

exporter.go

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030

3131
"github.com/prometheus/exporter-toolkit/web"
3232
"github.com/prometheus/exporter-toolkit/web/kingpinflag"
33+
34+
proxyproto "github.com/pires/go-proxyproto"
3335
)
3436

3537
// positiveDuration is a wrapper of time.Duration to ensure only positive values are accepted.
@@ -90,6 +92,7 @@ var (
9092
sslCaCert = kingpin.Flag("nginx.ssl-ca-cert", "Path to the PEM encoded CA certificate file used to validate the servers SSL certificate.").Default("").Envar("SSL_CA_CERT").String()
9193
sslClientCert = kingpin.Flag("nginx.ssl-client-cert", "Path to the PEM encoded client certificate file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_CERT").String()
9294
sslClientKey = kingpin.Flag("nginx.ssl-client-key", "Path to the PEM encoded client certificate key file to use when connecting to the server.").Default("").Envar("SSL_CLIENT_KEY").String()
95+
useProxyProto = kingpin.Flag("nginx.proxy-protocol", "Pass proxy protocol payload to nginx listeners.").Default("false").Envar("PROXY_PROTOCOL").Bool()
9396

9497
// Custom command-line flags.
9598
timeout = createPositiveDurationFlag(kingpin.Flag("nginx.timeout", "A timeout for scraping metrics from NGINX or NGINX Plus.").Default("5s").Envar("TIMEOUT").HintOptions("5s", "10s", "30s", "1m", "5m"))
@@ -223,17 +226,65 @@ func main() {
223226
func registerCollector(logger *slog.Logger, transport *http.Transport,
224227
addr string, labels map[string]string,
225228
) {
229+
var socketPath string
230+
226231
if strings.HasPrefix(addr, "unix:") {
227-
socketPath, requestPath, err := parseUnixSocketAddress(addr)
232+
var err error
233+
var requestPath string
234+
socketPath, requestPath, err = parseUnixSocketAddress(addr)
228235
if err != nil {
229236
logger.Error("parsing unix domain socket scrape address failed", "uri", addr, "error", err.Error())
230237
os.Exit(1)
231238
}
239+
addr = "http://unix" + requestPath
240+
}
232241

242+
if !*useProxyProto && socketPath != "" {
233243
transport.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) {
234244
return net.Dial("unix", socketPath)
235245
}
236-
addr = "http://unix" + requestPath
246+
}
247+
248+
if *useProxyProto {
249+
transport.DialContext = func(_ context.Context, network, addr string) (net.Conn, error) {
250+
if socketPath != "" {
251+
network = "unix"
252+
addr = socketPath
253+
}
254+
255+
conn, err := (&net.Dialer{}).Dial(network, addr)
256+
if err != nil {
257+
return nil, fmt.Errorf("dialing %s %s: %w", network, addr, err)
258+
}
259+
260+
localAddr := conn.LocalAddr()
261+
remoteAddr := conn.RemoteAddr()
262+
transportProtocol := proxyproto.TCPv4
263+
264+
switch addr := remoteAddr.(type) {
265+
case *net.TCPAddr:
266+
if addr.IP.To4() == nil {
267+
transportProtocol = proxyproto.TCPv6
268+
}
269+
case *net.UnixAddr:
270+
transportProtocol = proxyproto.UnixStream
271+
}
272+
273+
header := &proxyproto.Header{
274+
Version: 2,
275+
Command: proxyproto.PROXY,
276+
TransportProtocol: transportProtocol,
277+
SourceAddr: localAddr,
278+
DestinationAddr: remoteAddr,
279+
}
280+
281+
_, err = header.WriteTo(conn)
282+
if err != nil {
283+
return nil, fmt.Errorf("writing proxyproto header: %w", err)
284+
}
285+
286+
return conn, nil
287+
}
237288
}
238289

239290
userAgent := fmt.Sprintf("NGINX-Prometheus-Exporter/v%v", common_version.Version)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/prometheus/client_golang v1.20.5
99
github.com/prometheus/common v0.62.0
1010
github.com/prometheus/exporter-toolkit v0.14.0
11+
github.com/pires/go-proxyproto v0.8.0
1112
)
1213

1314
require (

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+
3434
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
3535
github.com/nginx/nginx-plus-go-client/v2 v2.3.0 h1:ciKh1lwadNzUaOGjLcKWu/BGigASxU6p7v/6US71fhA=
3636
github.com/nginx/nginx-plus-go-client/v2 v2.3.0/go.mod h1:U7G5pqucUS1V4Uecs1xCsJ9knSsfwqhwu8ZEjoCYnmk=
37+
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
38+
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
3739
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
3840
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
3941
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=

0 commit comments

Comments
 (0)