Compare commits
21 Commits
Author | SHA1 | Date |
---|---|---|
|
129ee654eb | |
|
39672d0656 | |
|
b9062551e8 | |
|
ab17c00752 | |
|
e284f508b4 | |
|
5e9c7ff5c5 | |
|
e54ee016b6 | |
|
cbed00787a | |
|
1de6c94faa | |
|
ed237708aa | |
|
2faff03ab6 | |
|
1d05a2741b | |
|
cb8f755e9b | |
|
f536641923 | |
|
ef346f5e25 | |
|
8c54f36172 | |
|
43dfb18ca7 | |
|
b4edb2be0b | |
|
209c3cbdd4 | |
|
6cd7580102 | |
|
1b0abc3709 |
|
@ -0,0 +1,10 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "go"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
name: integration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
integration:
|
||||
runs-on: [ubuntu-latest]
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "1.21.x"
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Start containers
|
||||
run: docker compose up --build --detach
|
||||
- name: run integration test
|
||||
run: go test -v --tags=integration
|
||||
- name: Stop containers
|
||||
if: always()
|
||||
run: docker compose down
|
|
@ -0,0 +1,65 @@
|
|||
name: Build and release
|
||||
on:
|
||||
# Runs automatically when a tag beginning with 'v' (i.e. a versioned release) is pushed.
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build-release:
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.21.4'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: build binary
|
||||
run: go build
|
||||
|
||||
- name: install nfpm
|
||||
run: go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.15.1
|
||||
|
||||
- name: build deb
|
||||
run: nfpm package -p deb -t unbound_exporter.deb
|
||||
|
||||
- name: upload deb
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: unbound_exporter deb artifact
|
||||
path: unbound_exporter.deb
|
||||
|
||||
push-release:
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
needs: build-release
|
||||
runs-on: ubuntu-20.04
|
||||
# Overrides the org default of 'read'. This allows us to upload and post the
|
||||
# resulting package file as part of a release.
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download release artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: unbound_exporter deb artifact
|
||||
|
||||
- name: rename
|
||||
run: mv unbound_exporter.deb unbound_exporter-${GITHUB_REF_NAME}.x86_64.deb
|
||||
|
||||
- name: push release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# https://cli.github.com/manual/gh_release_create
|
||||
run: gh release create "${GITHUB_REF_NAME}" unbound_exporter-${GITHUB_REF_NAME}.x86_64.deb
|
|
@ -0,0 +1,42 @@
|
|||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GO111MODULE: "auto"
|
||||
|
||||
jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version:
|
||||
- 1.20.x
|
||||
- 1.21.x
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
- name: go coverage
|
||||
run: |
|
||||
go test -mod=readonly -v -race -covermode=atomic -coverprofile=coverage.out ./...
|
||||
- uses: codecov/codecov-action@v3
|
||||
if: success()
|
||||
with:
|
||||
file: ./coverage.out
|
||||
flags: unbound_exporter_tests
|
||||
name: unbound_exporter tests
|
|
@ -0,0 +1,20 @@
|
|||
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.21.4-bookworm AS build
|
||||
|
||||
WORKDIR /go/src/app
|
||||
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
|
||||
RUN go mod download
|
||||
|
||||
COPY *.go .
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETPLATFORM go build -v -o /go/bin/unbound_exporter ./...
|
||||
|
||||
FROM gcr.io/distroless/static-debian12
|
||||
|
||||
COPY --from=build /go/bin/unbound_exporter /
|
||||
|
||||
ENTRYPOINT ["/unbound_exporter"]
|
49
README.md
49
README.md
|
@ -9,22 +9,53 @@ names and labels by using a set of regular expressions.
|
|||
|
||||
- - - -
|
||||
|
||||
# Prerequisites
|
||||
|
||||
Go 1.20 or above is required.
|
||||
|
||||
# Installation
|
||||
|
||||
To install this code and in your go environment. You can then add the binary to your `PATH`.
|
||||
go install github.com/letsencrypt/unbound_exporter@latest
|
||||
|
||||
go get github.com/kumina/unbound_exporter
|
||||
go install github.com/kumina/unbound_exporter
|
||||
This will install the binary in `$GOBIN`, or `$HOME/go/bin` if
|
||||
`$GOBIN` is unset.
|
||||
|
||||
# Updating dependencies
|
||||
|
||||
```
|
||||
go get -u
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
- - - -
|
||||
|
||||
# Usage
|
||||
# Usage - Unix socket
|
||||
|
||||
To show all CLI flags available
|
||||
The simplest way to run unbound_exporter is on the same machine as your Unbound instance, connecting via a Unix socket. First, make sure you have this in your unbound.conf:
|
||||
|
||||
unbound_exporter -h
|
||||
remote-control:
|
||||
control-enable: yes
|
||||
control-interface: /run/unbound.ctl
|
||||
|
||||
For extended statistics, you may want to add the following to your unbound.conf
|
||||
Then, arrange to run this on the same machine:
|
||||
|
||||
unbound_exporter -unbound.ca "" -unbound.cert "" -unbound.host "unix:///run/unbound.ctl"
|
||||
|
||||
Metrics will be exported under /metrics, on port 9167, on all interfaces.
|
||||
|
||||
$ curl 127.0.0.1:9167/metrics | grep '^unbound_up'
|
||||
unbound_up 1
|
||||
|
||||
# Usage - TLS
|
||||
|
||||
The more complicated way to run unbound_exporter is to configure unbound's control-interface with a TLS certificate from a private CA, and run unbound_exporter on a separate host. This is more of a hassle because you have to keep the certificate up to date and distribute the private CA to the host that unbound_exporter runs on.
|
||||
|
||||
See https://unbound.docs.nlnetlabs.nl/en/latest/getting-started/configuration.html#set-up-remote-control for instructions on setting up the certificates and keys for remote-control via TLS. On the unbound_exporter side you will need to set the `-unbound.ca`, `-unbound.cert`, and `-unbound.key` flags to point to valid files that will trust the Unbound server's certificate and be trusted by Unbound in return.
|
||||
|
||||
# Extended statistics
|
||||
|
||||
From the Unbound [statistics doc](https://www.nlnetlabs.nl/documentation/unbound/howto-statistics/): Unbound has an option to enable extended statistics collection. If enabled, more statistics are collected, for example what types of queries are sent to the resolver. Otherwise, only the total number of queries is collected. Add the following to your `unbound.conf`.
|
||||
|
||||
server:
|
||||
extended-statistics: yes
|
||||
|
||||
# statistics
|
||||
extended-statistics: yes
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
docker run -i -v `pwd`:/unbound_exporter alpine:edge /bin/sh << 'EOF'
|
||||
set -ex
|
||||
|
||||
# Install prerequisites for the build process.
|
||||
apk update
|
||||
apk add ca-certificates git go libc-dev
|
||||
update-ca-certificates
|
||||
|
||||
# Build the unbound_exporter.
|
||||
cd /unbound_exporter
|
||||
go build --ldflags '-extldflags "-static"'
|
||||
strip unbound_exporter
|
||||
EOF
|
|
@ -0,0 +1,45 @@
|
|||
# Contrib
|
||||
This collection of scripts and files helps us further configure our unbounds and unbound_exporters.
|
||||
|
||||
## unbound-control-setup.sh
|
||||
|
||||
From [Golang 1.15 docs:](https://golang.google.cn/doc/go1.15#commonname)
|
||||
> X.509 CommonName deprecation
|
||||
> The deprecated, legacy behavior of treating the CommonName field on X.509 certificates as a host name when no Subject Alternative Names are present is now disabled by default. It can be temporarily re-enabled by adding the value x509ignoreCN=0 to the GODEBUG environment variable.
|
||||
> Note that if the CommonName is an invalid host name, it's always ignored, regardless of GODEBUG settings. Invalid names include those with any characters other than letters, digits, hyphens and underscores, and those with empty labels or trailing dots.
|
||||
|
||||
Unbound still ships with an `unbound-control-setup` that generates a problematic keypair. This script will generate a keypair that satisfies newer versions of Golang.
|
||||
|
||||
Generate the new keypair
|
||||
```
|
||||
$ bash unbound-control-setup.sh
|
||||
```
|
||||
|
||||
You'll then want to configure `/etc/unbound/unbound.conf` with the following stanza
|
||||
|
||||
```
|
||||
$ cat /etc/unbound/unbound.conf
|
||||
...
|
||||
remote-control:
|
||||
control-enable: yes
|
||||
control-use-cert: yes
|
||||
server-key-file: "/etc/unbound/unbound_server_ec.key"
|
||||
server-cert-file: "/etc/unbound/unbound_server_ec.pem"
|
||||
control-key-file: "/etc/unbound/unbound_control_ec.key"
|
||||
control-cert-file: "/etc/unbound/unbound_control_ec.pem"
|
||||
```
|
||||
|
||||
Test that you can still communicate with unbound via `unbound_control`. You should be able to see metrics.
|
||||
```
|
||||
$ unbound-control stats_noreset
|
||||
thread0.num.queries=35
|
||||
thread0.num.queries_ip_ratelimited=0
|
||||
thread0.num.cachehits=25
|
||||
thread0.num.cachemiss=10
|
||||
thread0.num.prefetch=0
|
||||
thread0.num.expired=0
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
To reconfigure `unbound_exporter` as a systemd service, see [this file](unbound_exporter.service).
|
|
@ -0,0 +1,147 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Generally based on /usr/sbin/unbound-control-setup but adapted to catch
|
||||
# up to ~2010. You know, x509v3, secp384r1, AKIs, stuff like that.
|
||||
|
||||
# directory for files
|
||||
DESTDIR="${UNBOUND_CONFIG_DIR:-/etc/unbound}"
|
||||
|
||||
# validity period for certificates
|
||||
DAYS="${UNBOUND_CERT_LIFETIME:-397}"
|
||||
|
||||
# hash algorithm
|
||||
HASH=sha256
|
||||
|
||||
# base name for unbound CA keys
|
||||
CA_BASE=unbound_ca_ec
|
||||
|
||||
# base name for unbound server keys
|
||||
SVR_BASE=unbound_server_ec
|
||||
|
||||
# base name for unbound-control keys
|
||||
CTL_BASE=unbound_control_ec
|
||||
|
||||
# we want -rw-r----- access (say you run this as root: grp=yes (server), all=no).
|
||||
umask 0027
|
||||
|
||||
# end of options
|
||||
|
||||
# functions:
|
||||
error ( ) {
|
||||
echo "$0 fatal error: ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# go!:
|
||||
echo "setup in directory ${DESTDIR}"
|
||||
cd "${DESTDIR}" || error "could not cd to ${DESTDIR}"
|
||||
|
||||
# create certificate keys; do not recreate if they already exist.
|
||||
if test -f "${CA_BASE}.key"; then
|
||||
echo "${CA_BASE}.key exists"
|
||||
else
|
||||
echo "generating ${CA_BASE}.key"
|
||||
openssl ecparam -genkey -name secp384r1 > ${CA_BASE}.key || error "could not gen ecdsa"
|
||||
fi
|
||||
if test -f "${SVR_BASE}.key"; then
|
||||
echo "${SVR_BASE}.key exists"
|
||||
else
|
||||
echo "generating ${SVR_BASE}.key"
|
||||
openssl ecparam -genkey -name secp384r1 > ${SVR_BASE}.key || error "could not gen ecdsa"
|
||||
fi
|
||||
if test -f "${CTL_BASE}.key"; then
|
||||
echo "${CTL_BASE}.key exists"
|
||||
else
|
||||
echo "generating ${CTL_BASE}.key"
|
||||
openssl ecparam -genkey -name secp384r1 > ${CTL_BASE}.key || error "could not gen ecdsa"
|
||||
fi
|
||||
|
||||
# create self-signed cert CSR for server
|
||||
cat > ca_request.cfg <<EOCAConfig
|
||||
[req]
|
||||
prompt = no
|
||||
distinguished_name = req_distinguished_name
|
||||
x509_extensions = req_v3_extensions
|
||||
|
||||
[req_distinguished_name]
|
||||
commonName = unbound-ca
|
||||
|
||||
[req_v3_extensions]
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer:always
|
||||
basicConstraints = CA:true
|
||||
EOCAConfig
|
||||
test -f ca_request.cfg || error "could not create ca_request.cfg"
|
||||
|
||||
echo "creating ${CA_BASE}.pem (self signed certificate)"
|
||||
openssl req -key "${CA_BASE}.key" -config ca_request.cfg -new -x509 -days "${DAYS}" -out "${CA_BASE}.pem" || error "could not create ${CA_BASE}.pem"
|
||||
|
||||
# --------------
|
||||
|
||||
# create server cert CSR and sign it, piped
|
||||
cat > server_request.cfg <<EOServerConfig
|
||||
[req]
|
||||
prompt = no
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
[req_distinguished_name]
|
||||
commonName = unbound
|
||||
EOServerConfig
|
||||
test -f server_request.cfg || error "could not create server_request.cfg"
|
||||
|
||||
cat > server_exts.cfg <<EOServerConfig
|
||||
subjectAltName = @alt_names
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer:always
|
||||
extendedKeyUsage = serverAuth
|
||||
|
||||
[alt_names]
|
||||
DNS.1 = $(hostname)
|
||||
DNS.2 = unbound
|
||||
DNS.3 = localhost
|
||||
EOServerConfig
|
||||
test -f server_exts.cfg || error "could not create server_exts.cfg"
|
||||
|
||||
echo "create ${SVR_BASE}.pem (signed server certificate)"
|
||||
openssl req -key "${SVR_BASE}.key" -config server_request.cfg -new | openssl x509 -req -days "${DAYS}" -CA "${CA_BASE}.pem" -CAkey "${CA_BASE}.key" -CAcreateserial -"${HASH}" -extfile server_exts.cfg -out "${SVR_BASE}.pem"
|
||||
test -f ${SVR_BASE}.pem || error "could not create ${SVR_BASE}.pem"
|
||||
|
||||
# --------------
|
||||
|
||||
# create client cert CSR and sign it, piped
|
||||
cat > client_request.cfg <<EOCertConfig
|
||||
[req]
|
||||
prompt = no
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
[req_distinguished_name]
|
||||
commonName = unbound-control
|
||||
EOCertConfig
|
||||
test -f client_request.cfg || error "could not create client_request.cfg"
|
||||
|
||||
cat > client_exts.cfg <<EOCertConfig
|
||||
extendedKeyUsage = clientAuth
|
||||
EOCertConfig
|
||||
test -f client_exts.cfg || error "could not create client_exts.cfg"
|
||||
|
||||
echo "create ${CTL_BASE}.pem (signed client certificate)"
|
||||
openssl req -key "${CTL_BASE}.key" -config client_request.cfg -new | openssl x509 -req -days "${DAYS}" -CA "${CA_BASE}.pem" -CAkey "${CA_BASE}.key" -CAcreateserial -"${HASH}" -extfile client_exts.cfg -out "${CTL_BASE}.pem"
|
||||
test -f "${CTL_BASE}.pem" || error "could not create ${CTL_BASE}.pem"
|
||||
|
||||
# --------------
|
||||
|
||||
# set desired permissions
|
||||
chmod 0640 "${CA_BASE}.key" "${SVR_BASE}.key" "${CTL_BASE}.key"
|
||||
chmod 0644 "${CA_BASE}.pem" "${SVR_BASE}.pem" "${CTL_BASE}.pem"
|
||||
|
||||
# cleanup
|
||||
rm -f ca_request.cfg client_request.cfg client_exts.cfg server_request.cfg server_exts.cfg *.srl
|
||||
|
||||
openssl x509 -text -in "${CA_BASE}.pem"
|
||||
openssl x509 -text -in "${SVR_BASE}.pem"
|
||||
openssl x509 -text -in "${CTL_BASE}.pem"
|
||||
|
||||
echo "Satisfy unbound daemon/remote.c SSL_CTX_use_certificate_chain_file by appending ${CA_BASE}.pem to ${SVR_BASE}.pem"
|
||||
cat "${CA_BASE}.pem" >> "${SVR_BASE}.pem"
|
||||
|
||||
echo "Setup success. Certificates created."
|
|
@ -0,0 +1,15 @@
|
|||
[Unit]
|
||||
Description=Prometheus exporter for Unbound metrics, written in Go with pluggable metric collectors. The metrics exporter converts Unbound metric names to Prometheus metric names and labels by using a set of regular expressions.
|
||||
Documentation=https://github.com/letsencrypt/unbound_exporter
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/bin/unbound_exporter \
|
||||
-unbound.ca "/etc/unbound/unbound_ca_ec.pem" \
|
||||
-unbound.cert "/etc/unbound/unbound_control_ec.pem" \
|
||||
-unbound.key "/etc/unbound/unbound_control_ec.key" \
|
||||
-unbound.host "tcp://localhost:8953"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,22 @@
|
|||
services:
|
||||
unbound_exporter:
|
||||
build: .
|
||||
command: [ "-unbound.host=unix:///var/run/socket/unbound.ctl" ]
|
||||
volumes:
|
||||
- socket:/var/run/socket:ro
|
||||
ports:
|
||||
- "9167:9167"
|
||||
depends_on:
|
||||
unbound:
|
||||
condition: service_started
|
||||
unbound:
|
||||
image: "mvance/unbound:1.18.0"
|
||||
volumes:
|
||||
- socket:/var/run/socket:rw
|
||||
- ./unbound-example.conf:/opt/unbound/etc/unbound/unbound.conf
|
||||
- ./droplist.zone:/opt/unbound/etc/unbound/droplist.zone
|
||||
ports:
|
||||
- "1053:1053/udp"
|
||||
- "1053:1053/tcp"
|
||||
volumes:
|
||||
socket:
|
|
@ -0,0 +1,2 @@
|
|||
*.example.com IN A 127.0.0.1
|
||||
*.example.net IN A 127.0.0.1
|
23
go.mod
23
go.mod
|
@ -1,11 +1,20 @@
|
|||
module github.com/kumina/unbound_exporter
|
||||
module github.com/letsencrypt/unbound_exporter
|
||||
|
||||
go 1.12
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect
|
||||
github.com/prometheus/client_golang v1.0.0
|
||||
github.com/prometheus/common v0.5.0
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/prometheus/common v0.45.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
)
|
||||
|
|
95
go.sum
95
go.sum
|
@ -1,72 +1,25 @@
|
|||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0 h1:vrDKnkGzuGvhNAL56c7DBz29ZL+KxnoR0x7enabFceM=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.5.0 h1:2znmQeLeqnfKh7s5Tdg2bjfRzmVBD6JMp6SmWZHwU1E=
|
||||
github.com/prometheus/common v0.5.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
||||
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
||||
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
//go:build integration
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/common/expfmt"
|
||||
)
|
||||
|
||||
// TestIntegration checks that unbound_exporter is running, successfully
|
||||
// scraping and exporting metrics.
|
||||
//
|
||||
// It assumes unbound_exporter is available on localhost:9167, and Unbound on
|
||||
// localhost:1053, as is set up in the docker-compose.yml file.
|
||||
//
|
||||
// A typical invocation of this test would look like
|
||||
//
|
||||
// docker compose up --build -d
|
||||
// go test --tags=integration
|
||||
// docker compose down
|
||||
func TestIntegration(t *testing.T) {
|
||||
resp, err := http.Get("http://localhost:9167/metrics")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to fetch metrics from unbound_exporter: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("Expected a 200 OK from unbound_exporter, got: %v", resp.StatusCode)
|
||||
}
|
||||
|
||||
parser := expfmt.TextParser{}
|
||||
metrics, err := parser.TextToMetricFamilies(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse metrics from unbound_exporter: %v", err)
|
||||
}
|
||||
|
||||
// unbound_up is 1 if we've successfully scraped metrics from it
|
||||
unbound_up := metrics["unbound_up"].Metric[0].Gauge.GetValue()
|
||||
if unbound_up != 1 {
|
||||
t.Errorf("Expected unbound_up to be 1, not: %v", unbound_up)
|
||||
}
|
||||
|
||||
// Check some expected metrics are present
|
||||
for _, metric := range []string{
|
||||
"go_info",
|
||||
"unbound_queries_total",
|
||||
"unbound_response_time_seconds",
|
||||
"unbound_cache_hits_total",
|
||||
"unbound_query_https_total",
|
||||
"unbound_memory_doh_bytes",
|
||||
} {
|
||||
if _, ok := metrics[metric]; !ok {
|
||||
t.Errorf("Expected metric is missing: %s", metric)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
name: "unbound_exporter"
|
||||
arch: "amd64"
|
||||
platform: "linux"
|
||||
version: "${GITHUB_REF_NAME}"
|
||||
description: "Prometheus exporter for Unbound recursive DNS resolver"
|
||||
vendor: "ISRG"
|
||||
maintainer: "ISRG Team <opensource@letsencrypt.org>"
|
||||
homepage: "https://github.com/letsencrypt/unbound_exporter"
|
||||
license: "Apache 2.0"
|
||||
contents:
|
||||
- src: unbound_exporter
|
||||
dst: /usr/bin/unbound_exporter
|
|
@ -0,0 +1,93 @@
|
|||
## This is an example Unbound configuration file
|
||||
## This is needed to use unbound_exporter
|
||||
remote-control:
|
||||
control-enable: yes
|
||||
control-interface: /var/run/socket/unbound.ctl
|
||||
|
||||
# The rest of this file is standard Unbound configuration
|
||||
# There's nothing special here.
|
||||
server:
|
||||
module-config: "respip validator iterator"
|
||||
extended-statistics: yes
|
||||
cache-max-ttl: 86400
|
||||
cache-min-ttl: 300
|
||||
directory: "/opt/unbound/etc/unbound"
|
||||
do-ip4: yes
|
||||
do-ip6: no
|
||||
do-tcp: yes
|
||||
do-udp: yes
|
||||
edns-buffer-size: 1232
|
||||
interface: 0.0.0.0
|
||||
port: 1053
|
||||
prefer-ip6: no
|
||||
rrset-roundrobin: yes
|
||||
username: "_unbound"
|
||||
log-local-actions: no
|
||||
log-queries: no
|
||||
log-replies: no
|
||||
log-servfail: yes
|
||||
logfile: /opt/unbound/etc/unbound/unbound.log
|
||||
verbosity: 2
|
||||
infra-cache-slabs: 4
|
||||
incoming-num-tcp: 10
|
||||
key-cache-slabs: 4
|
||||
msg-cache-size: 142768128
|
||||
msg-cache-slabs: 4
|
||||
num-queries-per-thread: 4096
|
||||
num-threads: 3
|
||||
outgoing-range: 8192
|
||||
rrset-cache-size: 285536256
|
||||
rrset-cache-slabs: 4
|
||||
minimal-responses: yes
|
||||
prefetch: yes
|
||||
prefetch-key: yes
|
||||
serve-expired: yes
|
||||
so-reuseport: yes
|
||||
aggressive-nsec: yes
|
||||
delay-close: 10000
|
||||
do-daemonize: no
|
||||
do-not-query-localhost: no
|
||||
neg-cache-size: 4M
|
||||
qname-minimisation: yes
|
||||
access-control: 127.0.0.1/32 allow
|
||||
access-control: 192.168.0.0/16 allow
|
||||
access-control: 172.16.0.0/12 allow
|
||||
access-control: 10.0.0.0/8 allow
|
||||
access-control: fc00::/7 allow
|
||||
access-control: ::1/128 allow
|
||||
auto-trust-anchor-file: "/opt/unbound/etc/unbound/var/root.key"
|
||||
chroot: ""
|
||||
deny-any: yes
|
||||
harden-algo-downgrade: yes
|
||||
harden-below-nxdomain: yes
|
||||
harden-dnssec-stripped: yes
|
||||
harden-glue: yes
|
||||
harden-large-queries: yes
|
||||
harden-referral-path: no
|
||||
harden-short-bufsize: yes
|
||||
hide-http-user-agent: no
|
||||
hide-identity: yes
|
||||
hide-version: no
|
||||
http-user-agent: "DNS"
|
||||
identity: "DNS"
|
||||
private-address: 10.0.0.0/8
|
||||
private-address: 172.16.0.0/12
|
||||
private-address: 192.168.0.0/16
|
||||
private-address: 169.254.0.0/16
|
||||
private-address: fd00::/8
|
||||
private-address: fe80::/10
|
||||
private-address: ::ffff:0:0/96
|
||||
ratelimit: 1000
|
||||
tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
|
||||
unwanted-reply-threshold: 10000
|
||||
use-caps-for-id: yes
|
||||
val-clean-additional: yes
|
||||
include: /opt/unbound/etc/unbound/a-records.conf
|
||||
include: /opt/unbound/etc/unbound/srv-records.conf
|
||||
|
||||
rpz:
|
||||
name: unbound_exporter_cloak
|
||||
zonefile: /opt/unbound/etc/unbound/droplist.zone
|
||||
rpz-log: yes
|
||||
rpz-log-name: unbound_exporter_cloak
|
||||
rpz-action-override: nxdomain
|
|
@ -20,7 +20,6 @@ import (
|
|||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -31,12 +30,15 @@ import (
|
|||
|
||||
"sort"
|
||||
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/prometheus/common/log"
|
||||
"github.com/prometheus/common/promlog"
|
||||
)
|
||||
|
||||
var (
|
||||
log = promlog.New(&promlog.Config{})
|
||||
|
||||
unboundUpDesc = prometheus.NewDesc(
|
||||
prometheus.BuildFQName("unbound", "", "up"),
|
||||
"Whether scraping Unbound's metrics was successful.",
|
||||
|
@ -78,6 +80,24 @@ var (
|
|||
prometheus.CounterValue,
|
||||
[]string{"thread"},
|
||||
"^thread(\\d+)\\.num\\.cachemiss$"),
|
||||
newUnboundMetric(
|
||||
"queries_cookie_client_total",
|
||||
"Total number of queries with a client cookie.",
|
||||
prometheus.CounterValue,
|
||||
[]string{"thread"},
|
||||
"^thread(\\d+)\\.num\\.queries_cookie_client$"),
|
||||
newUnboundMetric(
|
||||
"queries_cookie_invalid_total",
|
||||
"Total number of queries with a invalid cookie.",
|
||||
prometheus.CounterValue,
|
||||
[]string{"thread"},
|
||||
"^thread(\\d+)\\.num\\.queries_invalid_client$"),
|
||||
newUnboundMetric(
|
||||
"queries_cookie_valid_total",
|
||||
"Total number of queries with a valid cookie.",
|
||||
prometheus.CounterValue,
|
||||
[]string{"thread"},
|
||||
"^thread(\\d+)\\.num\\.queries_cookie_valid$"),
|
||||
newUnboundMetric(
|
||||
"memory_caches_bytes",
|
||||
"Memory in bytes in use by caches.",
|
||||
|
@ -108,6 +128,12 @@ var (
|
|||
prometheus.CounterValue,
|
||||
[]string{"thread"},
|
||||
"^thread(\\d+)\\.num\\.queries$"),
|
||||
newUnboundMetric(
|
||||
"expired_total",
|
||||
"Total number of expired entries served.",
|
||||
prometheus.CounterValue,
|
||||
[]string{"thread"},
|
||||
"^thread(\\d+)\\.num\\.expired$"),
|
||||
newUnboundMetric(
|
||||
"query_classes_total",
|
||||
"Total number of queries with a given query class.",
|
||||
|
@ -146,22 +172,52 @@ var (
|
|||
"^num\\.query\\.edns\\.present$"),
|
||||
newUnboundMetric(
|
||||
"query_tcp_total",
|
||||
"Total number of queries that were made using TCP towards the Unbound server.",
|
||||
"Total number of queries that were made using TCP towards the Unbound server, including DoT and DoH queries.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^num\\.query\\.tcp$"),
|
||||
newUnboundMetric(
|
||||
"query_tcpout_total",
|
||||
"Total number of queries that the Unbound server made using TCP outgoing towards other servers.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^num\\.query\\.tcpout$"),
|
||||
newUnboundMetric(
|
||||
"query_tls_total",
|
||||
"Total number of queries that were made using TCP TLS towards the Unbound server.",
|
||||
"Total number of queries that were made using TCP TLS towards the Unbound server, including DoT and DoH queries.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^num\\.query\\.tls$"),
|
||||
newUnboundMetric(
|
||||
"query_tls_resume_total",
|
||||
"Total number of queries that were made using TCP TLS Resume towards the Unbound server.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^num\\.query\\.tls\\.resume$"),
|
||||
newUnboundMetric(
|
||||
"query_https_total",
|
||||
"Total number of DoH queries that were made towards the Unbound server.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^num\\.query\\.https$"),
|
||||
newUnboundMetric(
|
||||
"query_types_total",
|
||||
"Total number of queries with a given query type.",
|
||||
prometheus.CounterValue,
|
||||
[]string{"type"},
|
||||
"^num\\.query\\.type\\.([\\w]+)$"),
|
||||
newUnboundMetric(
|
||||
"query_udpout_total",
|
||||
"Total number of queries that the Unbound server made using UDP outgoing towardsother servers.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^num\\.query\\.udpout$"),
|
||||
newUnboundMetric(
|
||||
"query_aggressive_nsec",
|
||||
"Total number of queries that the Unbound server generated response using Aggressive NSEC.",
|
||||
prometheus.CounterValue,
|
||||
[]string{"rcode"},
|
||||
"^num\\.query\\.aggressive\\.(\\w+)$"),
|
||||
newUnboundMetric(
|
||||
"request_list_current_all",
|
||||
"Current size of the request list, including internally generated queries.",
|
||||
|
@ -198,6 +254,12 @@ var (
|
|||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^num\\.rrset\\.bogus$"),
|
||||
newUnboundMetric(
|
||||
"rrset_cache_max_collisions_total",
|
||||
"Total number of rrset cache hashtable collisions.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^rrset\\.cache\\.max_collisions$"),
|
||||
newUnboundMetric(
|
||||
"time_elapsed_seconds",
|
||||
"Time since last statistics printout in seconds.",
|
||||
|
@ -246,12 +308,30 @@ var (
|
|||
prometheus.GaugeValue,
|
||||
nil,
|
||||
"^msg\\.cache\\.count$"),
|
||||
newUnboundMetric(
|
||||
"msg_cache_max_collisions_total",
|
||||
"Total number of msg cache hashtable collisions.",
|
||||
prometheus.CounterValue,
|
||||
nil,
|
||||
"^msg\\.cache\\.max_collisions$"),
|
||||
newUnboundMetric(
|
||||
"rrset_cache_count",
|
||||
"The Number of rrset cached",
|
||||
prometheus.GaugeValue,
|
||||
nil,
|
||||
"^rrset\\.cache\\.count$"),
|
||||
newUnboundMetric(
|
||||
"rpz_action_count",
|
||||
"Total number of triggered Response Policy Zone actions, by type.",
|
||||
prometheus.CounterValue,
|
||||
[]string{"type"},
|
||||
"^num\\.rpz\\.action\\.rpz-([\\w-]+)$"),
|
||||
newUnboundMetric(
|
||||
"memory_doh_bytes",
|
||||
"Memory used by DoH buffers, in bytes.",
|
||||
prometheus.GaugeValue,
|
||||
[]string{"buffer"},
|
||||
"^mem\\.http\\.(\\w+)$"),
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -276,7 +356,7 @@ func newUnboundMetric(name string, description string, valueType prometheus.Valu
|
|||
func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error {
|
||||
scanner := bufio.NewScanner(file)
|
||||
scanner.Split(bufio.ScanLines)
|
||||
histogramPattern := regexp.MustCompile("^histogram\\.\\d+\\.\\d+\\.to\\.(\\d+\\.\\d+)$")
|
||||
histogramPattern := regexp.MustCompile(`^histogram\.\d+\.\d+\.to\.(\d+\.\d+)$`)
|
||||
|
||||
histogramCount := uint64(0)
|
||||
histogramAvg := float64(0)
|
||||
|
@ -351,14 +431,6 @@ func CollectFromReader(file io.Reader, ch chan<- prometheus.Metric) error {
|
|||
return scanner.Err()
|
||||
}
|
||||
|
||||
func CollectFromFile(path string, ch chan<- prometheus.Metric) error {
|
||||
conn, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return CollectFromReader(conn, ch)
|
||||
}
|
||||
|
||||
func CollectFromSocket(socketFamily string, host string, tlsConfig *tls.Config, ch chan<- prometheus.Metric) error {
|
||||
var (
|
||||
conn net.Conn
|
||||
|
@ -373,6 +445,7 @@ func CollectFromSocket(socketFamily string, host string, tlsConfig *tls.Config,
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
_, err = conn.Write([]byte("UBCT1 stats_noreset\n"))
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -399,7 +472,7 @@ func NewUnboundExporter(host string, ca string, cert string, key string) (*Unbou
|
|||
}, nil
|
||||
}
|
||||
|
||||
if ca == "" && cert == "" {
|
||||
if ca == "" && cert == "" && key == "" {
|
||||
return &UnboundExporter{
|
||||
socketFamily: u.Scheme,
|
||||
host: u.Host,
|
||||
|
@ -407,7 +480,7 @@ func NewUnboundExporter(host string, ca string, cert string, key string) (*Unbou
|
|||
}
|
||||
|
||||
/* Server authentication. */
|
||||
caData, err := ioutil.ReadFile(ca)
|
||||
caData, err := os.ReadFile(ca)
|
||||
if err != nil {
|
||||
return &UnboundExporter{}, err
|
||||
}
|
||||
|
@ -417,11 +490,11 @@ func NewUnboundExporter(host string, ca string, cert string, key string) (*Unbou
|
|||
}
|
||||
|
||||
/* Client authentication. */
|
||||
certData, err := ioutil.ReadFile(cert)
|
||||
certData, err := os.ReadFile(cert)
|
||||
if err != nil {
|
||||
return &UnboundExporter{}, err
|
||||
}
|
||||
keyData, err := ioutil.ReadFile(key)
|
||||
keyData, err := os.ReadFile(key)
|
||||
if err != nil {
|
||||
return &UnboundExporter{}, err
|
||||
}
|
||||
|
@ -456,7 +529,7 @@ func (e *UnboundExporter) Collect(ch chan<- prometheus.Metric) {
|
|||
prometheus.GaugeValue,
|
||||
1.0)
|
||||
} else {
|
||||
log.Error("Failed to scrape socket: %s", err)
|
||||
_ = level.Error(log).Log("Failed to scrape socket: ", err)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
unboundUpDesc,
|
||||
prometheus.GaugeValue,
|
||||
|
@ -475,7 +548,7 @@ func main() {
|
|||
)
|
||||
flag.Parse()
|
||||
|
||||
log.Info("Starting unbound_exporter")
|
||||
_ = level.Info(log).Log("Starting unbound_exporter")
|
||||
exporter, err := NewUnboundExporter(*unboundHost, *unboundCa, *unboundCert, *unboundKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -484,7 +557,7 @@ func main() {
|
|||
|
||||
http.Handle(*metricsPath, promhttp.Handler())
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`
|
||||
_, _ = w.Write([]byte(`
|
||||
<html>
|
||||
<head><title>Unbound Exporter</title></head>
|
||||
<body>
|
||||
|
@ -493,6 +566,7 @@ func main() {
|
|||
</body>
|
||||
</html>`))
|
||||
})
|
||||
log.Info("Listening on address:port => ", *listenAddress)
|
||||
log.Fatal(http.ListenAndServe(*listenAddress, nil))
|
||||
_ = level.Info(log).Log("Listening on address:port => ", *listenAddress)
|
||||
_ = level.Error(log).Log(http.ListenAndServe(*listenAddress, nil))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestStub(t *testing.T) {
|
||||
if 1 != 1 { //nolint
|
||||
t.Fatal("Math is a lie. We should never have taught computers to think.")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue