Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

system resolver #203

Merged
merged 3 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 50 additions & 51 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
@@ -1,68 +1,67 @@
---
name: integration

on:
pull_request:

jobs:
e2e:
snapshot:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: false
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --config ./.goreleaser.ci.yml --clean --snapshot
- name: Copy .ini files
run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: dnsbl_exporter
path: dist/dnsbl_exporter_linux_amd64_v1
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: false
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --config ./.goreleaser.ci.yml --clean --snapshot
- name: Copy .ini files
run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: dnsbl_exporter
path: dist/dnsbl_exporter_linux_amd64_v1

integration:
runs-on: ubuntu-latest
needs:
- snapshot
- snapshot
services:
unbound:
image: klutchell/unbound:latest
ports:
- 5053:5053/tcp
- 5053:5053/udp
- 5053:5053/tcp
- 5053:5053/udp
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: false
- uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --config ./.goreleaser.ci.yml --clean --snapshot
- run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
- uses: JarvusInnovations/background-action@v1
with:
run: |
ls -lah && ./dnsbl-exporter --log.debug --config.dns-resolver=localhost:5053 &
wait-on: |
http-get://localhost:9211/
http-get://localhost:9211/metrics
http-get://localhost:9211/prober?target=github.com
tail: true # true = stderr,stdout
log-output-resume: stderr
wait-for: 1m
log-output: stderr,stdout
working-directory: ./dist/dnsbl_exporter_linux_amd64_v1
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: false
- uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --config ./.goreleaser.ci.yml --clean --snapshot
- run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
- uses: JarvusInnovations/background-action@v1
with:
run: |
ls -lah && ./dnsbl-exporter --log.debug --config.dns-resolver=localhost:5053 &
wait-on: |
http-get://localhost:9211/
http-get://localhost:9211/metrics
http-get://localhost:9211/prober?target=github.com
tail: true # true = stderr,stdout
log-output-resume: stderr
wait-for: 1m
log-output: stderr,stdout
working-directory: ./dist/dnsbl_exporter_linux_amd64_v1
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ $ sudo unbound -d -vvvv
192.42.118.104
```

### Use /etc/resolv.conf

Use `system` as a value and the exporter will pick the **first** resolver from `/etc/resolv.conf`.

Adequate permissions need to be set by yourself so the exporter can read the file.

- `--config.dns-resolver=system`
- `DNSBL_EXP_RESOLVER=system`

## License / Author

This code is Apache 2.0 licensed.
Expand Down
63 changes: 50 additions & 13 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/Luzilla/dnsbl_exporter/internal/index"
"github.com/Luzilla/dnsbl_exporter/internal/metrics"
"github.com/Luzilla/dnsbl_exporter/internal/prober"
"github.com/Luzilla/dnsbl_exporter/internal/resolvconf"
"github.com/Luzilla/dnsbl_exporter/internal/setup"
"github.com/Luzilla/dnsbl_exporter/pkg/dns"
"github.com/prometheus/client_golang/prometheus/collectors"
Expand All @@ -30,6 +31,8 @@ var (
resolver string
)

const resolvConfFile = "/etc/resolv.conf"

// NewApp ...
func NewApp(name string, version string) DNSBLApp {
appName = name
Expand All @@ -42,7 +45,7 @@ func NewApp(name string, version string) DNSBLApp {
&cli.StringFlag{
Name: "config.dns-resolver",
Value: "127.0.0.1:53",
Usage: "IP address[:port] of the resolver to use.",
Usage: "IP address[:port] of the resolver to use, use `system` to use a resolve from " + resolvConfFile,
EnvVars: []string{"DNSBL_EXP_RESOLVER"},
Destination: &resolver,
},
Expand Down Expand Up @@ -102,6 +105,17 @@ func NewApp(name string, version string) DNSBLApp {
return nil
},
},
&cli.StringFlag{
Name: "log.format",
Value: "text",
Usage: "format, text is logfmt or use json",
Action: func(cCtx *cli.Context, v string) error {
if v != "text" && v != "json" {
return cli.Exit("We currently support only text and json: --log.format", 2)
}
return nil
},
},
}

return DNSBLApp{
Expand All @@ -110,29 +124,36 @@ func NewApp(name string, version string) DNSBLApp {
}

func (a *DNSBLApp) Bootstrap() {
a.App.Action = func(ctx *cli.Context) error {
a.App.Action = func(cCtx *cli.Context) error {
// setup logging
handler := &slog.HandlerOptions{}
var writer io.Writer

if ctx.Bool("log.debug") {
if cCtx.Bool("log.debug") {
handler.Level = slog.LevelDebug
}

switch ctx.String("log.output") {
switch cCtx.String("log.output") {
case "stdout":
writer = os.Stdout
case "stderr":
writer = os.Stderr
}

log := slog.New(handler.NewTextHandler(writer))
var logHandler slog.Handler
if cCtx.String("log.format") == "text" {
logHandler = handler.NewTextHandler(writer)
} else {
logHandler = handler.NewJSONHandler(writer)
}

log := slog.New(logHandler)

c := config.Config{
Logger: log.With("area", "config"),
}

cfgRbls, err := c.LoadFile(ctx.String("config.rbls"))
cfgRbls, err := c.LoadFile(cCtx.String("config.rbls"))
if err != nil {
return err
}
Expand All @@ -142,7 +163,7 @@ func (a *DNSBLApp) Bootstrap() {
return fmt.Errorf("unable to load the rbls from the config: %w", err)
}

cfgTargets, err := c.LoadFile(ctx.String("config.targets"))
cfgTargets, err := c.LoadFile(cCtx.String("config.targets"))
if err != nil {
return err
}
Expand All @@ -155,6 +176,22 @@ func (a *DNSBLApp) Bootstrap() {
log.Info("starting exporter without targets — check the /prober endpoint or correct the .ini file")
}

// use the system's resolver
if resolver == "system" {
log.Info("fetching resolver from " + resolvConfFile)
servers, err := resolvconf.GetServers(resolvConfFile)
if err != nil {
return err
}
if len(servers) == 0 {
return fmt.Errorf("unable to return a server from %s", resolvConfFile)
}

// pick the first
resolver = servers[0]
log.Info("using resolver: " + resolver)
}

iHandler := index.IndexHandler{
Name: appName,
Version: appVersion,
Expand All @@ -174,12 +211,12 @@ func (a *DNSBLApp) Bootstrap() {
return err
}

rblCollector := setup.CreateCollector(rbls, targets, ctx.Bool("config.domain-based"), dnsUtil, log.With("area", "metrics"))
rblCollector := setup.CreateCollector(rbls, targets, cCtx.Bool("config.domain-based"), dnsUtil, log.With("area", "metrics"))
registry.MustRegister(rblCollector)

registryExporter := setup.CreateRegistry()

if ctx.Bool("web.include-exporter-metrics") {
if cCtx.Bool("web.include-exporter-metrics") {
log.Info("Exposing exporter metrics")

registryExporter.MustRegister(
Expand All @@ -193,21 +230,21 @@ func (a *DNSBLApp) Bootstrap() {
RegistryExporter: registryExporter,
}

http.Handle(ctx.String("web.telemetry-path"), mHandler.Handler())
http.Handle(cCtx.String("web.telemetry-path"), mHandler.Handler())

pHandler := prober.ProberHandler{
DNS: dnsUtil,
Rbls: rbls,
DomainBased: ctx.Bool("config.domain-based"),
DomainBased: cCtx.Bool("config.domain-based"),
Logger: log.With("area", "prober"),
}
http.Handle("/prober", pHandler)

log.Info("starting exporter",
slog.String("web.listen-address", ctx.String("web.listen-address")),
slog.String("web.listen-address", cCtx.String("web.listen-address")),
slog.String("resolver", resolver),
)
err = http.ListenAndServe(ctx.String("web.listen-address"), nil)
err = http.ListenAndServe(cCtx.String("web.listen-address"), nil)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions internal/resolvconf/fixtures/no-server.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
search anything.local
3 changes: 3 additions & 0 deletions internal/resolvconf/fixtures/two-servers.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# two servers
nameserver 1.1.1.1
nameserver 8.8.8.8
33 changes: 33 additions & 0 deletions internal/resolvconf/resolvconf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package resolvconf

import (
"bufio"
"os"
"strings"
)

func GetServers(path string) (servers []string, err error) {
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if !strings.HasPrefix(line, "nameserver ") {
continue
}

server, ok := strings.CutPrefix(line, "nameserver ")
if !ok {
continue
}

servers = append(servers, server)
}

err = scanner.Err()
return
}
31 changes: 31 additions & 0 deletions internal/resolvconf/resolvconf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package resolvconf_test

import (
"path/filepath"
"testing"

"github.com/Luzilla/dnsbl_exporter/internal/resolvconf"
"github.com/stretchr/testify/assert"
)

func TestGetServers(t *testing.T) {
testCases := []struct {
path string
expected []string
}{
{
path: "no-server.conf",
expected: []string(nil),
},
{
path: "two-servers.conf",
expected: []string{"1.1.1.1", "8.8.8.8"},
},
}

for _, tc := range testCases {
servers, err := resolvconf.GetServers(filepath.Join("fixtures", tc.path))
assert.NoError(t, err)
assert.Equal(t, tc.expected, servers)
}
}
Loading