Skip to content

Commit e8d26aa

Browse files
committed
Don't use setuid on chkmq*
Also add new tests for chkmqhealthy and privileges
1 parent 94ad666 commit e8d26aa

File tree

5 files changed

+86
-30
lines changed

5 files changed

+86
-30
lines changed

Dockerfile-server

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ COPY web /etc/mqm/web
9898
COPY etc/mqm/*.tpl /etc/mqm/
9999
RUN chmod ug+x /usr/local/bin/runmqserver \
100100
&& chown 1001:root /usr/local/bin/*mq* \
101-
&& chmod ug+xs /usr/local/bin/chkmq* \
101+
&& chmod ug+x /usr/local/bin/chkmq* \
102102
&& chown -R 1001:root /etc/mqm/* \
103103
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
104104
&& touch /run/termination-log \

Makefile

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_
4040
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
4141
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
4242
# Options to `go test` for the Docker tests
43-
TEST_OPTS_DOCKER ?=
43+
TEST_OPTS_DOCKER ?=
44+
# Timeout for the Docker tests
45+
TEST_TIMEOUT_DOCKER ?= 30m
4446
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
4547
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
4648
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
@@ -78,7 +80,7 @@ MQ_ARCHIVE_DEV_TYPE=Linux
7880
BUILD_SERVER_CONTAINER=build-server
7981
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
8082
# test run in parallel
81-
NUM_CPU = $(or $(shell docker info --format "{{ .NCPU }}"),2)
83+
NUM_CPU ?= $(or $(shell docker info --format "{{ .NCPU }}"),2)
8284
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
8385
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
8486
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
@@ -241,7 +243,7 @@ test-unit:
241243
test-advancedserver: test/docker/vendor
242244
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG_CACHED_$(ARCH)) on $(shell docker --version)"$(END)))
243245
docker inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG_CACHED_$(ARCH))
244-
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG_CACHED_$(ARCH)) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
246+
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG_CACHED_$(ARCH)) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) $(TEST_OPTS_DOCKER)
245247

246248
.PHONY: build-devjmstest
247249
build-devjmstest:
@@ -252,7 +254,7 @@ build-devjmstest:
252254
test-devserver: test/docker/vendor
253255
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG_CACHED_$(ARCH)) on $(shell docker --version)"$(END)))
254256
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG_CACHED_$(ARCH))
255-
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG_CACHED_$(ARCH)) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
257+
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG_CACHED_$(ARCH)) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) -tags mqdev $(TEST_OPTS_DOCKER)
256258

257259
.PHONY: coverage
258260
coverage:

cmd/chkmqhealthy/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
© Copyright IBM Corporation 2017, 2019
2+
© Copyright IBM Corporation 2017, 2020
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -36,11 +36,11 @@ func queueManagerHealthy() (bool, error) {
3636
cmd := exec.Command("dspmq", "-n", "-m", name)
3737
// Run the command and wait for completion
3838
out, err := cmd.CombinedOutput()
39+
fmt.Printf("%s", out)
3940
if err != nil {
4041
fmt.Println(err)
4142
return false, err
4243
}
43-
fmt.Printf("%s", out)
4444
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") {
4545
return false, nil
4646
}

test/docker/docker_api_test.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
© Copyright IBM Corporation 2017, 2019
2+
© Copyright IBM Corporation 2017, 2020
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -1434,3 +1434,44 @@ func TestTraceStrmqm(t *testing.T) {
14341434
t.Fatalf("No trace files found in trace directory /var/mqm/trace. RC=%d.", rc)
14351435
}
14361436
}
1437+
1438+
// utilTestHealthCheck is used by TestHealthCheck* to run a container with
1439+
// privileges enabled or disabled. Otherwise the same as the golden path tests.
1440+
func utilTestHealthCheck(t *testing.T, nonewpriv bool) {
1441+
t.Parallel()
1442+
cli, err := client.NewEnvClient()
1443+
if err != nil {
1444+
t.Fatal(err)
1445+
}
1446+
containerConfig := container.Config{
1447+
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
1448+
}
1449+
hostConfig := getDefaultHostConfig(t, cli)
1450+
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("no-new-privileges:%v", nonewpriv))
1451+
id := runContainerWithHostConfig(t, cli, &containerConfig, hostConfig)
1452+
defer cleanContainer(t, cli, id)
1453+
waitForReady(t, cli, id)
1454+
rc, out := execContainer(t, cli, id, "", []string{"chkmqhealthy"})
1455+
t.Log(out)
1456+
if rc != 0 {
1457+
t.Errorf("Expected chkmqhealthy to return with exit code 0; got \"%v\"", rc)
1458+
t.Logf("Output from chkmqhealthy:\n%v", out)
1459+
}
1460+
// Stop the container cleanly
1461+
stopContainer(t, cli, id)
1462+
}
1463+
1464+
// TestHealthCheckWithNoNewPrivileges tests golden path start/stop plus
1465+
// chkmqhealthy, when running in a container where no new privileges are
1466+
// allowed (i.e. setuid is disabled)
1467+
func TestHealthCheckWithNoNewPrivileges(t *testing.T) {
1468+
utilTestHealthCheck(t, true)
1469+
}
1470+
1471+
// TestHealthCheckWithNoNewPrivileges tests golden path start/stop plus
1472+
// chkmqhealthy when running in a container where new privileges are
1473+
// allowed (i.e. setuid is allowed)
1474+
// See https://github.com/ibm-messaging/mq-container/issues/428
1475+
func TestHealthCheckWithNewPrivileges(t *testing.T) {
1476+
utilTestHealthCheck(t, false)
1477+
}

test/docker/docker_api_test_util.go

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -267,20 +267,8 @@ func generateRandomUID() string {
267267
return fmt.Sprint(rand.Intn(max-min) + min)
268268
}
269269

270-
// runContainerWithPorts creates and starts a container, exposing the specified ports on the host.
271-
// If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE
272-
// environment variable.
273-
func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *container.Config, ports []int) string {
274-
if containerConfig.Image == "" {
275-
containerConfig.Image = imageName()
276-
}
277-
// Always run as a random user, unless the test has specified otherwise
278-
if containerConfig.User == "" {
279-
containerConfig.User = generateRandomUID()
280-
}
281-
// if coverage
282-
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
283-
containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t))
270+
// getDefaultHostConfig creates a HostConfig and populates it with the defaults used in testing
271+
func getDefaultHostConfig(t *testing.T, cli *client.Client) *container.HostConfig {
284272
hostConfig := container.HostConfig{
285273
Binds: []string{
286274
coverageBind(t),
@@ -299,24 +287,48 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
299287
} else {
300288
t.Logf("Detected MQ Advanced image - dropping all capabilities")
301289
}
302-
for _, p := range ports {
303-
port := nat.Port(fmt.Sprintf("%v/tcp", p))
304-
hostConfig.PortBindings[port] = []nat.PortBinding{
305-
{
306-
HostIP: "0.0.0.0",
307-
},
308-
}
290+
return &hostConfig
291+
}
292+
293+
// runContainerWithHostConfig creates and starts a container, using the supplied HostConfig.
294+
// Note that a default HostConfig can be created using getDefaultHostConfig.
295+
func runContainerWithHostConfig(t *testing.T, cli *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig) string {
296+
if containerConfig.Image == "" {
297+
containerConfig.Image = imageName()
298+
}
299+
// Always run as a random user, unless the test has specified otherwise
300+
if containerConfig.User == "" {
301+
containerConfig.User = generateRandomUID()
309302
}
303+
// if coverage
304+
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
305+
containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t))
310306
networkingConfig := network.NetworkingConfig{}
311307
t.Logf("Running container (%s)", containerConfig.Image)
312-
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, &hostConfig, &networkingConfig, t.Name())
308+
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, hostConfig, &networkingConfig, t.Name())
313309
if err != nil {
314310
t.Fatal(err)
315311
}
316312
startContainer(t, cli, ctr.ID)
317313
return ctr.ID
318314
}
319315

316+
// runContainerWithPorts creates and starts a container, exposing the specified ports on the host.
317+
// If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE
318+
// environment variable.
319+
func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *container.Config, ports []int) string {
320+
hostConfig := getDefaultHostConfig(t, cli)
321+
for _, p := range ports {
322+
port := nat.Port(fmt.Sprintf("%v/tcp", p))
323+
hostConfig.PortBindings[port] = []nat.PortBinding{
324+
{
325+
HostIP: "0.0.0.0",
326+
},
327+
}
328+
}
329+
return runContainerWithHostConfig(t, cli, containerConfig, hostConfig)
330+
}
331+
320332
// runContainer creates and starts a container. If no image is specified in
321333
// the container config, then the image name is retrieved from the TEST_IMAGE
322334
// environment variable.
@@ -526,6 +538,7 @@ func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout time.
526538

527539
// execContainer runs a command in a running container, and returns the exit code and output
528540
func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd []string) (int, string) {
541+
t.Logf("Running command: %v", cmd)
529542
config := types.ExecConfig{
530543
User: user,
531544
Privileged: false,

0 commit comments

Comments
 (0)