Skip to content

Commit 8c6250a

Browse files
authored
Merge pull request #203 from Luzilla/system-resolver
system resolver
2 parents f2233cc + ac61f63 commit 8c6250a

File tree

7 files changed

+177
-64
lines changed

7 files changed

+177
-64
lines changed

.github/workflows/integration.yml

Lines changed: 50 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,67 @@
1-
---
21
name: integration
32

43
on:
54
pull_request:
65

76
jobs:
8-
e2e:
7+
snapshot:
98
runs-on: ubuntu-latest
109
steps:
11-
- name: Checkout
12-
uses: actions/checkout@v4
13-
with:
14-
fetch-depth: 0
15-
- name: Set up Go
16-
uses: actions/setup-go@v5
17-
with:
18-
go-version-file: go.mod
19-
cache: false
20-
- name: Run GoReleaser
21-
uses: goreleaser/goreleaser-action@v5
22-
with:
23-
version: latest
24-
args: release --config ./.goreleaser.ci.yml --clean --snapshot
25-
- name: Copy .ini files
26-
run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
27-
- name: Upload artifact
28-
uses: actions/upload-artifact@v4
29-
with:
30-
name: dnsbl_exporter
31-
path: dist/dnsbl_exporter_linux_amd64_v1
10+
- name: Checkout
11+
uses: actions/checkout@v4
12+
with:
13+
fetch-depth: 0
14+
- name: Set up Go
15+
uses: actions/setup-go@v5
16+
with:
17+
go-version-file: go.mod
18+
cache: false
19+
- name: Run GoReleaser
20+
uses: goreleaser/goreleaser-action@v5
21+
with:
22+
version: latest
23+
args: release --config ./.goreleaser.ci.yml --clean --snapshot
24+
- name: Copy .ini files
25+
run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
26+
- name: Upload artifact
27+
uses: actions/upload-artifact@v4
28+
with:
29+
name: dnsbl_exporter
30+
path: dist/dnsbl_exporter_linux_amd64_v1
3231

3332
integration:
3433
runs-on: ubuntu-latest
3534
needs:
36-
- snapshot
35+
- snapshot
3736
services:
3837
unbound:
3938
image: klutchell/unbound:latest
4039
ports:
41-
- 5053:5053/tcp
42-
- 5053:5053/udp
40+
- 5053:5053/tcp
41+
- 5053:5053/udp
4342
steps:
44-
- uses: actions/checkout@v4
45-
with:
46-
fetch-depth: 0
47-
- uses: actions/setup-go@v5
48-
with:
49-
go-version-file: go.mod
50-
cache: false
51-
- uses: goreleaser/goreleaser-action@v5
52-
with:
53-
version: latest
54-
args: release --config ./.goreleaser.ci.yml --clean --snapshot
55-
- run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
56-
- uses: JarvusInnovations/background-action@v1
57-
with:
58-
run: |
59-
ls -lah && ./dnsbl-exporter --log.debug --config.dns-resolver=localhost:5053 &
60-
wait-on: |
61-
http-get://localhost:9211/
62-
http-get://localhost:9211/metrics
63-
http-get://localhost:9211/prober?target=github.com
64-
tail: true # true = stderr,stdout
65-
log-output-resume: stderr
66-
wait-for: 1m
67-
log-output: stderr,stdout
68-
working-directory: ./dist/dnsbl_exporter_linux_amd64_v1
43+
- uses: actions/checkout@v4
44+
with:
45+
fetch-depth: 0
46+
- uses: actions/setup-go@v5
47+
with:
48+
go-version-file: go.mod
49+
cache: false
50+
- uses: goreleaser/goreleaser-action@v5
51+
with:
52+
version: latest
53+
args: release --config ./.goreleaser.ci.yml --clean --snapshot
54+
- run: cp targets.ini rbls.ini ./dist/dnsbl_exporter_linux_amd64_v1
55+
- uses: JarvusInnovations/background-action@v1
56+
with:
57+
run: |
58+
ls -lah && ./dnsbl-exporter --log.debug --config.dns-resolver=localhost:5053 &
59+
wait-on: |
60+
http-get://localhost:9211/
61+
http-get://localhost:9211/metrics
62+
http-get://localhost:9211/prober?target=github.com
63+
tail: true # true = stderr,stdout
64+
log-output-resume: stderr
65+
wait-for: 1m
66+
log-output: stderr,stdout
67+
working-directory: ./dist/dnsbl_exporter_linux_amd64_v1

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ $ sudo unbound -d -vvvv
109109
192.42.118.104
110110
```
111111

112+
### Use /etc/resolv.conf
113+
114+
Use `system` as a value and the exporter will pick the **first** resolver from `/etc/resolv.conf`.
115+
116+
Adequate permissions need to be set by yourself so the exporter can read the file.
117+
118+
- `--config.dns-resolver=system`
119+
- `DNSBL_EXP_RESOLVER=system`
120+
112121
## License / Author
113122

114123
This code is Apache 2.0 licensed.

app/app.go

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/Luzilla/dnsbl_exporter/internal/index"
1313
"github.com/Luzilla/dnsbl_exporter/internal/metrics"
1414
"github.com/Luzilla/dnsbl_exporter/internal/prober"
15+
"github.com/Luzilla/dnsbl_exporter/internal/resolvconf"
1516
"github.com/Luzilla/dnsbl_exporter/internal/setup"
1617
"github.com/Luzilla/dnsbl_exporter/pkg/dns"
1718
"github.com/prometheus/client_golang/prometheus/collectors"
@@ -30,6 +31,8 @@ var (
3031
resolver string
3132
)
3233

34+
const resolvConfFile = "/etc/resolv.conf"
35+
3336
// NewApp ...
3437
func NewApp(name string, version string) DNSBLApp {
3538
appName = name
@@ -42,7 +45,7 @@ func NewApp(name string, version string) DNSBLApp {
4245
&cli.StringFlag{
4346
Name: "config.dns-resolver",
4447
Value: "127.0.0.1:53",
45-
Usage: "IP address[:port] of the resolver to use.",
48+
Usage: "IP address[:port] of the resolver to use, use `system` to use a resolve from " + resolvConfFile,
4649
EnvVars: []string{"DNSBL_EXP_RESOLVER"},
4750
Destination: &resolver,
4851
},
@@ -102,6 +105,17 @@ func NewApp(name string, version string) DNSBLApp {
102105
return nil
103106
},
104107
},
108+
&cli.StringFlag{
109+
Name: "log.format",
110+
Value: "text",
111+
Usage: "format, text is logfmt or use json",
112+
Action: func(cCtx *cli.Context, v string) error {
113+
if v != "text" && v != "json" {
114+
return cli.Exit("We currently support only text and json: --log.format", 2)
115+
}
116+
return nil
117+
},
118+
},
105119
}
106120

107121
return DNSBLApp{
@@ -110,29 +124,36 @@ func NewApp(name string, version string) DNSBLApp {
110124
}
111125

112126
func (a *DNSBLApp) Bootstrap() {
113-
a.App.Action = func(ctx *cli.Context) error {
127+
a.App.Action = func(cCtx *cli.Context) error {
114128
// setup logging
115129
handler := &slog.HandlerOptions{}
116130
var writer io.Writer
117131

118-
if ctx.Bool("log.debug") {
132+
if cCtx.Bool("log.debug") {
119133
handler.Level = slog.LevelDebug
120134
}
121135

122-
switch ctx.String("log.output") {
136+
switch cCtx.String("log.output") {
123137
case "stdout":
124138
writer = os.Stdout
125139
case "stderr":
126140
writer = os.Stderr
127141
}
128142

129-
log := slog.New(handler.NewTextHandler(writer))
143+
var logHandler slog.Handler
144+
if cCtx.String("log.format") == "text" {
145+
logHandler = handler.NewTextHandler(writer)
146+
} else {
147+
logHandler = handler.NewJSONHandler(writer)
148+
}
149+
150+
log := slog.New(logHandler)
130151

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

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

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

179+
// use the system's resolver
180+
if resolver == "system" {
181+
log.Info("fetching resolver from " + resolvConfFile)
182+
servers, err := resolvconf.GetServers(resolvConfFile)
183+
if err != nil {
184+
return err
185+
}
186+
if len(servers) == 0 {
187+
return fmt.Errorf("unable to return a server from %s", resolvConfFile)
188+
}
189+
190+
// pick the first
191+
resolver = servers[0]
192+
log.Info("using resolver: " + resolver)
193+
}
194+
158195
iHandler := index.IndexHandler{
159196
Name: appName,
160197
Version: appVersion,
@@ -174,12 +211,12 @@ func (a *DNSBLApp) Bootstrap() {
174211
return err
175212
}
176213

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

180217
registryExporter := setup.CreateRegistry()
181218

182-
if ctx.Bool("web.include-exporter-metrics") {
219+
if cCtx.Bool("web.include-exporter-metrics") {
183220
log.Info("Exposing exporter metrics")
184221

185222
registryExporter.MustRegister(
@@ -193,21 +230,21 @@ func (a *DNSBLApp) Bootstrap() {
193230
RegistryExporter: registryExporter,
194231
}
195232

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

198235
pHandler := prober.ProberHandler{
199236
DNS: dnsUtil,
200237
Rbls: rbls,
201-
DomainBased: ctx.Bool("config.domain-based"),
238+
DomainBased: cCtx.Bool("config.domain-based"),
202239
Logger: log.With("area", "prober"),
203240
}
204241
http.Handle("/prober", pHandler)
205242

206243
log.Info("starting exporter",
207-
slog.String("web.listen-address", ctx.String("web.listen-address")),
244+
slog.String("web.listen-address", cCtx.String("web.listen-address")),
208245
slog.String("resolver", resolver),
209246
)
210-
err = http.ListenAndServe(ctx.String("web.listen-address"), nil)
247+
err = http.ListenAndServe(cCtx.String("web.listen-address"), nil)
211248
if err != nil {
212249
return err
213250
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
search anything.local
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# two servers
2+
nameserver 1.1.1.1
3+
nameserver 8.8.8.8

internal/resolvconf/resolvconf.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package resolvconf
2+
3+
import (
4+
"bufio"
5+
"os"
6+
"strings"
7+
)
8+
9+
func GetServers(path string) (servers []string, err error) {
10+
file, err := os.Open(path)
11+
if err != nil {
12+
return
13+
}
14+
defer file.Close()
15+
16+
scanner := bufio.NewScanner(file)
17+
for scanner.Scan() {
18+
line := scanner.Text()
19+
if !strings.HasPrefix(line, "nameserver ") {
20+
continue
21+
}
22+
23+
server, ok := strings.CutPrefix(line, "nameserver ")
24+
if !ok {
25+
continue
26+
}
27+
28+
servers = append(servers, server)
29+
}
30+
31+
err = scanner.Err()
32+
return
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package resolvconf_test
2+
3+
import (
4+
"path/filepath"
5+
"testing"
6+
7+
"github.com/Luzilla/dnsbl_exporter/internal/resolvconf"
8+
"github.com/stretchr/testify/assert"
9+
)
10+
11+
func TestGetServers(t *testing.T) {
12+
testCases := []struct {
13+
path string
14+
expected []string
15+
}{
16+
{
17+
path: "no-server.conf",
18+
expected: []string(nil),
19+
},
20+
{
21+
path: "two-servers.conf",
22+
expected: []string{"1.1.1.1", "8.8.8.8"},
23+
},
24+
}
25+
26+
for _, tc := range testCases {
27+
servers, err := resolvconf.GetServers(filepath.Join("fixtures", tc.path))
28+
assert.NoError(t, err)
29+
assert.Equal(t, tc.expected, servers)
30+
}
31+
}

0 commit comments

Comments
 (0)