Skip to content

Commit 3b25c2e

Browse files
authored
PMM-13748 gssapi support (#1042)
* build with gssapi tag * set up kerberos test env * improve setup * create separate exporter keytab * prepare test environment * include test tags * create conf file before starting containers * use tmp dir for config * re-run script * add logrus reporter * install libkrb5-dev in runner * drop installation * do not mount tmp directly * install krb5 libraries * use one liners for mongo commands * fix permissions * fix up linter * split test function to satisfy linter * fix formatting * remove unused changes * use prebuilt docker image and volume * rename 'docker' folder to 'test-setup' * fix image in use * build docker image * enable CGO in goreleaser * fix releaser tags * force cgo during build * add timeout to script * remove logrus * apply linter suggestion
1 parent d3bd2f1 commit 3b25c2e

19 files changed

+240
-25
lines changed

.github/workflows/go.yml

+5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ jobs:
4646
with:
4747
go-version-file: ${{ github.workspace }}/go.mod
4848

49+
- name: Install kerberos development libraries
50+
run: |
51+
sudo apt-get update
52+
sudo apt-get install libkrb5-dev
53+
4954
- name: Run tests with code coverage
5055
run: |
5156
TEST_MONGODB_IMAGE=${{ matrix.image }} make test-cluster

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ dist
1111
cover.out
1212
mongodb_exporter
1313
.DS_Store
14+

.goreleaser.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ version: 2
33
builds:
44
- binary: mongodb_exporter
55
env:
6-
- CGO_ENABLED=0
6+
- CGO_ENABLED=1
77
goos:
88
- linux
99
- darwin
@@ -19,6 +19,7 @@ builds:
1919
goarch: arm
2020
flags:
2121
- -v
22+
- -tags=gssapi
2223
ldflags:
2324
- -s -w -X main.version=v{{.Version}} -X main.commit={{.ShortCommit}} -X main.buildDate={{.Date}}
2425
archives:

Makefile

+6-5
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ init: ## Install linters
7171
cd tools && go generate -x -tags=tools
7272

7373
build: ## Compile using plain go build
74-
go build -ldflags="$(GO_BUILD_LDFLAGS)" -o $(PMM_RELEASE_PATH)/mongodb_exporter
74+
CGO_ENABLED=1 go build -ldflags="$(GO_BUILD_LDFLAGS)" -o $(PMM_RELEASE_PATH)/mongodb_exporter -tags gssapi
7575

7676
release: ## Build the binaries using goreleaser
7777
docker run --rm --privileged \
@@ -99,17 +99,18 @@ help: ## Display this help message
9999
awk -F ':.*?## ' 'NF==2 {printf " %-26s%s\n", $$1, $$2}'
100100

101101
test: env ## Run all tests
102-
go test -v -count 1 -timeout 30s ./...
102+
go test -tags gssapi -v -count 1 -timeout 30s ./...
103103

104104
test-race: env ## Run all tests with race flag
105-
go test -race -v -timeout 30s ./...
105+
go test -tags gssapi -race -v -timeout 30s ./...
106106

107107
test-cover: env ## Run tests and collect cross-package coverage information
108-
go test -race -timeout 30s -coverprofile=cover.out -covermode=atomic -coverpkg=./... ./...
108+
go test -tags gssapi -race -timeout 30s -coverprofile=cover.out -covermode=atomic -coverpkg=./... ./...
109109

110110
test-cluster: env ## Starts MongoDB test cluster. Use env var TEST_MONGODB_IMAGE to set flavor and version. Example: TEST_MONGODB_IMAGE=mongo:3.6 make test-cluster
111111
docker compose up --build -d
112-
./docker/scripts/setup-pbm.sh
112+
./test-setup/scripts/init-psmdb-kerberos.sh
113+
./test-setup/scripts/init-pbm.sh
113114

114115
test-cluster-clean: env ## Stops MongoDB test cluster.
115116
docker compose down --remove-orphans --volumes

docker-compose.yml

+61-17
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ services:
4848
- "mongo-1-3"
4949
- "mongo-1-arbiter"
5050
volumes:
51-
- ./docker/scripts:/scripts
51+
- ./test-setup/scripts:/scripts
5252
environment:
5353
- MONGO1=mongo-1-1
5454
- MONGO2=mongo-1-2
@@ -63,7 +63,7 @@ services:
6363
mongo-2-2:
6464
container_name: "mongo-2-2"
6565
build:
66-
dockerfile: ./docker/mongodb-auth.dockerfile
66+
dockerfile: test-setup/mongodb-auth.dockerfile
6767
environment:
6868
- MONGO_INITDB_ROOT_USERNAME=${TEST_MONGODB_USERNAME:-admin}
6969
- MONGO_INITDB_ROOT_PASSWORD=${TEST_MONGODB_PASSWORD:-admin}
@@ -76,7 +76,7 @@ services:
7676
mongo-2-3:
7777
container_name: "mongo-2-3"
7878
build:
79-
dockerfile: ./docker/mongodb-auth.dockerfile
79+
dockerfile: test-setup/mongodb-auth.dockerfile
8080
ports:
8181
- "${TEST_MONGODB_S2_SECONDARY1_PORT:-17005}:27017"
8282
command: mongod --replSet rs2 --port 27017 --oplogSize 16 --auth --keyFile=/opt/keyfile
@@ -86,7 +86,7 @@ services:
8686
mongo-2-1:
8787
container_name: "mongo-2-1"
8888
build:
89-
dockerfile: ./docker/mongodb-auth.dockerfile
89+
dockerfile: test-setup/mongodb-auth.dockerfile
9090
ports:
9191
- "${TEST_MONGODB_S2_SECONDARY2_PORT:-17006}:27017"
9292
command: mongod --replSet rs2 --port 27017 --oplogSize 16 --auth --keyFile=/opt/keyfile
@@ -96,7 +96,7 @@ services:
9696
mongo-2-arbiter:
9797
container_name: "mongo-2-arbiter"
9898
build:
99-
dockerfile: ./docker/mongodb-auth.dockerfile
99+
dockerfile: test-setup/mongodb-auth.dockerfile
100100
ports:
101101
- "${TEST_MONGODB_S2_ARBITER:-17012}:27017"
102102
command: mongod --replSet rs2 --port 27017 --oplogSize 16 --auth --keyFile=/opt/keyfile
@@ -112,8 +112,8 @@ services:
112112
environment:
113113
- PBM_MONGODB_URI=mongodb://admin:admin@mongo-2-1:27017
114114
volumes:
115-
- ./docker/pbm/config:/etc/config
116-
- ./docker/scripts:/scripts
115+
- ./test-setup/pbm/config:/etc/config
116+
- ./test-setup/scripts:/scripts
117117
- pbm-backups:/opt/backups
118118
networks:
119119
- rs2
@@ -127,8 +127,8 @@ services:
127127
environment:
128128
- PBM_MONGODB_URI=mongodb://admin:admin@mongo-2-2:27017
129129
volumes:
130-
- ./docker/pbm/config:/etc/config
131-
- ./docker/scripts:/scripts
130+
- ./test-setup/pbm/config:/etc/config
131+
- ./test-setup/scripts:/scripts
132132
- pbm-backups:/opt/backups
133133
networks:
134134
- rs2
@@ -142,8 +142,8 @@ services:
142142
environment:
143143
- PBM_MONGODB_URI=mongodb://admin:admin@mongo-2-3:27017
144144
volumes:
145-
- ./docker/pbm/config:/etc/config
146-
- ./docker/scripts:/scripts
145+
- ./test-setup/pbm/config:/etc/config
146+
- ./test-setup/scripts:/scripts
147147
- pbm-backups:/opt/backups
148148
networks:
149149
- rs2
@@ -157,7 +157,7 @@ services:
157157
- "mongo-2-3"
158158
- "mongo-2-arbiter"
159159
volumes:
160-
- ./docker/scripts:/scripts
160+
- ./test-setup/scripts:/scripts
161161
environment:
162162
- MONGO1=mongo-2-2
163163
- MONGO2=mongo-2-1
@@ -210,7 +210,7 @@ services:
210210
- "mongo-cnf-2"
211211
- "mongo-cnf-3"
212212
volumes:
213-
- ./docker/scripts:/scripts
213+
- ./test-setup/scripts:/scripts
214214
environment:
215215
- MONGO1=mongo-cnf-1
216216
- MONGO2=mongo-cnf-2
@@ -249,7 +249,7 @@ services:
249249
- rs2
250250
- cnf-serv
251251
volumes:
252-
- ./docker/scripts:/scripts
252+
- ./test-setup/scripts:/scripts
253253
environment:
254254
- MONGOS=mongos
255255
- MONGO11=mongo-1-1
@@ -265,7 +265,7 @@ services:
265265
- PORT2=27017
266266
- PORT3=27017
267267
- VERSION=${TEST_MONGODB_IMAGE}
268-
entrypoint: [ "/scripts/init-shard.sh" ]
268+
entrypoint: [ "/scripts/setup-shard.sh" ]
269269
restart: on-failure:20
270270

271271
standalone:
@@ -282,12 +282,56 @@ services:
282282
ports:
283283
- "${TEST_MONGODB_STANDALONE_ENCRYPTED_PORT:-27027}:27017"
284284
volumes:
285-
- ./docker/secret/mongodb_secrets.txt:/secret/mongodb_secrets.txt
286-
- ./docker/scripts:/scripts
285+
- ./test-setup/secret/mongodb_secrets.txt:/secret/mongodb_secrets.txt
286+
- ./test-setup/scripts:/scripts
287287
command: /scripts/run-mongodb-encrypted.sh
288288

289+
kerberos:
290+
build:
291+
dockerfile: test-setup/kerberos.dockerfile
292+
container_name: kerberos
293+
hostname: kerberos
294+
environment:
295+
- KRB5_TRACE=/dev/stderr
296+
- MONGO_HOST=psmdb-kerberos
297+
- KERBEROS_HOST=kerberos
298+
- KRB5_CONFIG=/krb5/krb5.conf
299+
- MONGO_KERBEROS_USERNAME=pmm-test
300+
- MONGO_KERBEROS_PASSWORD=password1
301+
depends_on:
302+
- psmdb-kerberos
303+
volumes:
304+
- kerberos-config:/krb5
305+
- ./test-setup/scripts:/scripts
306+
ports:
307+
- "88:88/udp"
308+
entrypoint: [ "sh", "/scripts/setup-krb5-server.sh" ]
309+
healthcheck:
310+
test: [ "CMD", "kadmin.local", "-q", "listprincs" ]
311+
interval: 5s
312+
timeout: 5s
313+
retries: 5
314+
315+
psmdb-kerberos:
316+
image: percona/percona-server-mongodb:8.0
317+
container_name: psmdb-kerberos
318+
hostname: psmdb-kerberos
319+
ports:
320+
- 29017:27017
321+
environment:
322+
- KRB5_KTNAME=/krb5/mongodb.keytab
323+
- KRB5CCNAME=/krb5/krb5cc_0
324+
- KRB5_CONFIG=/krb5/krb5.conf
325+
- MONGO_INITDB_ROOT_USERNAME=admin
326+
- MONGO_INITDB_ROOT_PASSWORD=adminpassword
327+
volumes:
328+
- ./test-setup/scripts:/scripts
329+
- kerberos-config:/krb5
330+
command: >
331+
mongod --bind_ip_all --auth --setParameter authenticationMechanisms=GSSAPI,SCRAM-SHA-256
289332
volumes:
290333
pbm-backups:
334+
kerberos-config:
291335

292336
networks:
293337
rs1:

exporter/exporter_test.go

+88
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"net"
2323
"net/http"
2424
"net/http/httptest"
25+
"os"
2526
"strconv"
2627
"strings"
2728
"sync"
@@ -30,6 +31,7 @@ import (
3031
"github.com/prometheus/client_golang/prometheus/testutil"
3132
"github.com/prometheus/common/promslog"
3233
"github.com/stretchr/testify/assert"
34+
"github.com/stretchr/testify/require"
3335

3436
"github.com/percona/mongodb_exporter/internal/tu"
3537
)
@@ -197,6 +199,92 @@ func TestMongoS(t *testing.T) {
197199
}
198200
}
199201

202+
func generateKerberosConfigFile(t *testing.T) *os.File {
203+
t.Helper()
204+
kerberosHost, err := tu.IPForContainer("kerberos")
205+
require.NoError(t, err)
206+
207+
config := fmt.Sprintf(`
208+
[libdefaults]
209+
default_realm = PERCONATEST.COM
210+
forwardable = true
211+
dns_lookup_realm = false
212+
dns_lookup_kdc = false
213+
ignore_acceptor_hostname = true
214+
rdns = false
215+
[realms]
216+
PERCONATEST.COM = {
217+
kdc_ports = 88
218+
kdc = %s
219+
}
220+
[domain_realm]
221+
.perconatest.com = PERCONATEST.COM
222+
perconatest.com = PERCONATEST.COM
223+
%s = PERCONATEST.COM
224+
`, kerberosHost, kerberosHost)
225+
configFile, err := os.Create(t.TempDir() + "/krb5.conf")
226+
require.NoError(t, err)
227+
228+
_, err = configFile.WriteString(config)
229+
require.NoError(t, err)
230+
231+
return configFile
232+
}
233+
234+
func TestGSSAPIAuth(t *testing.T) {
235+
logger := promslog.New(&promslog.Config{})
236+
237+
mongoHost, err := tu.IPForContainer("psmdb-kerberos")
238+
require.NoError(t, err)
239+
240+
configFile := generateKerberosConfigFile(t)
241+
require.NoError(t, err)
242+
defer func() {
243+
_ = configFile.Close()
244+
t.Setenv("KRB5_CONFIG", "")
245+
}()
246+
247+
t.Setenv("KRB5_CONFIG", configFile.Name())
248+
ctx := context.Background()
249+
250+
username := "pmm-test%40PERCONATEST.COM"
251+
password := "password1"
252+
uri := fmt.Sprintf("mongodb://%s:%s@%s/?authSource=$external&authMechanism=GSSAPI",
253+
username,
254+
password,
255+
net.JoinHostPort(mongoHost, "27017"),
256+
)
257+
exporterOpts := &Opts{
258+
URI: uri,
259+
Logger: logger,
260+
CollectAll: true,
261+
GlobalConnPool: false,
262+
DirectConnect: true,
263+
}
264+
265+
client, err := connect(ctx, exporterOpts)
266+
assert.NoError(t, err)
267+
268+
e := New(exporterOpts)
269+
nodeType, _ := getNodeType(ctx, client)
270+
gc := newGeneralCollector(ctx, client, nodeType, e.opts.Logger)
271+
r := e.makeRegistry(ctx, client, new(labelsGetterMock), *e.opts)
272+
273+
expected := strings.NewReader(`
274+
# HELP mongodb_up Whether MongoDB is up.
275+
# TYPE mongodb_up gauge
276+
mongodb_up {cluster_role="mongod"} 1` + "\n")
277+
278+
filter := []string{
279+
"mongodb_up",
280+
}
281+
err = testutil.CollectAndCompare(gc, expected, filter...)
282+
require.NoError(t, err, "mongodb_up metric should be 1")
283+
284+
res := r.Unregister(gc)
285+
assert.True(t, res)
286+
}
287+
200288
func TestMongoUpMetric(t *testing.T) {
201289
ctx := context.Background()
202290

internal/tu/testutils.go

+14
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,20 @@ func PortForContainer(name string) (string, error) {
199199
return ports[0].HostPort, nil
200200
}
201201

202+
// IPForContainer returns the IP address of a running container.
203+
func IPForContainer(name string) (string, error) {
204+
di, err := InspectContainer(name)
205+
if err != nil {
206+
return "", errors.Wrapf(err, "cannot get error for container %q", name)
207+
}
208+
209+
if len(di) == 0 {
210+
return "", errors.Wrapf(err, "cannot get error for container %q (empty array)", name)
211+
}
212+
213+
return di[0].NetworkSettings.Networks.MongodbExporterDefault.IPAddress, nil
214+
}
215+
202216
// SetupFakeResolver sets up Fake DNS server to resolve SRV records.
203217
func SetupFakeResolver() *mockdns.Server {
204218
p1, err1 := strconv.ParseInt(GetenvDefault("TEST_MONGODB_S1_PRIMARY_PORT", "17001"), 10, 64)

test-setup/kerberos.dockerfile

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM alpine
2+
RUN apk add --no-cache bash krb5 krb5-server krb5-pkinit
3+
EXPOSE 88/udp
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
ARG TEST_MONGODB_IMAGE=mongo:4.4
22
FROM ${TEST_MONGODB_IMAGE}
33
USER root
4-
COPY docker/secret/keyfile /opt/keyfile
4+
COPY test-setup/secret/keyfile /opt/keyfile
55
RUN chown mongodb /opt/keyfile && chmod 400 /opt/keyfile && mkdir -p /home/mongodb/ && chown mongodb /home/mongodb
66
RUN mkdir /opt/backups && touch /opt/backups/.gitkeep && chown mongodb /opt/backups
77
USER mongodb
File renamed without changes.
File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
3+
docker exec --user root psmdb-kerberos bash -c 'chown mongodb:root /krb5/mongodb.keytab'
4+
docker exec psmdb-kerberos bash -c '/scripts/setup-krb5-mongo.sh'

0 commit comments

Comments
 (0)