Don't use setuid on chkmq*
Also add new tests for chkmqhealthy and privileges
This commit is contained in:
@@ -97,7 +97,7 @@ COPY web /etc/mqm/web
|
|||||||
COPY etc/mqm/*.tpl /etc/mqm/
|
COPY etc/mqm/*.tpl /etc/mqm/
|
||||||
RUN chmod ug+x /usr/local/bin/runmqserver \
|
RUN chmod ug+x /usr/local/bin/runmqserver \
|
||||||
&& chown 1001:root /usr/local/bin/*mq* \
|
&& chown 1001:root /usr/local/bin/*mq* \
|
||||||
&& chmod ug+xs /usr/local/bin/chkmq* \
|
&& chmod ug+x /usr/local/bin/chkmq* \
|
||||||
&& chown -R 1001:root /etc/mqm/* \
|
&& chown -R 1001:root /etc/mqm/* \
|
||||||
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
|
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
|
||||||
&& touch /run/termination-log \
|
&& touch /run/termination-log \
|
||||||
|
|||||||
11
Makefile
11
Makefile
@@ -37,7 +37,9 @@ MQ_ARCHIVE_DEV ?= $(MQ_VERSION)-IBM-MQ-Advanced-for-Developers-Non-Install-$(MQ_
|
|||||||
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
|
# MQ_SDK_ARCHIVE specifies the archive to use for building the golang programs. Defaults vary on developer or advanced.
|
||||||
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
MQ_SDK_ARCHIVE ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||||
# Options to `go test` for the Docker tests
|
# Options to `go test` for the Docker tests
|
||||||
TEST_OPTS_DOCKER ?=
|
TEST_OPTS_DOCKER ?=
|
||||||
|
# Timeout for the Docker tests
|
||||||
|
TEST_TIMEOUT_DOCKER ?= 30m
|
||||||
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
# MQ_IMAGE_ADVANCEDSERVER is the name of the built MQ Advanced image
|
||||||
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
|
MQ_IMAGE_ADVANCEDSERVER ?=ibm-mqadvanced-server
|
||||||
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
||||||
@@ -73,7 +75,7 @@ MQ_ARCHIVE_DEV_TYPE=Linux
|
|||||||
BUILD_SERVER_CONTAINER=build-server
|
BUILD_SERVER_CONTAINER=build-server
|
||||||
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
||||||
# test run in parallel
|
# test run in parallel
|
||||||
NUM_CPU = $(or $(shell docker info --format "{{ .NCPU }}"),2)
|
NUM_CPU ?= $(or $(shell docker info --format "{{ .NCPU }}"),2)
|
||||||
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
# BASE_IMAGE_TAG is a normalized version of BASE_IMAGE, suitable for use in a Docker tag
|
||||||
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
BASE_IMAGE_TAG=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
||||||
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||||
@@ -183,7 +185,7 @@ test-unit:
|
|||||||
test-advancedserver: test/docker/vendor
|
test-advancedserver: test/docker/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
||||||
docker inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
|
docker inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG)
|
||||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
|
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) -timeout $(TEST_TIMEOUT_DOCKER) $(TEST_OPTS_DOCKER)
|
||||||
|
|
||||||
.PHONY: build-devjmstest
|
.PHONY: build-devjmstest
|
||||||
build-devjmstest:
|
build-devjmstest:
|
||||||
@@ -193,8 +195,9 @@ build-devjmstest:
|
|||||||
.PHONY: test-devserver
|
.PHONY: test-devserver
|
||||||
test-devserver: test/docker/vendor
|
test-devserver: test/docker/vendor
|
||||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG) on $(shell docker --version)"$(END)))
|
||||||
|
|
||||||
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG)
|
||||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) EXPECTED_LICENSE=Developer DEV_JMS_IMAGE=$(DEV_JMS_IMAGE) IBMJRE=true go test -parallel $(NUM_CPU) -tags mqdev $(TEST_OPTS_DOCKER)
|
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_DEVSERVER):$(MQ_TAG) 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)
|
||||||
|
|
||||||
.PHONY: coverage
|
.PHONY: coverage
|
||||||
coverage:
|
coverage:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2019
|
© Copyright IBM Corporation 2017, 2020
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -36,11 +36,11 @@ func queueManagerHealthy() (bool, error) {
|
|||||||
cmd := exec.Command("dspmq", "-n", "-m", name)
|
cmd := exec.Command("dspmq", "-n", "-m", name)
|
||||||
// Run the command and wait for completion
|
// Run the command and wait for completion
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
|
fmt.Printf("%s", out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
fmt.Printf("%s", out)
|
|
||||||
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") {
|
if !strings.Contains(string(out), "(RUNNING)") && !strings.Contains(string(out), "(RUNNING AS STANDBY)") && !strings.Contains(string(out), "(STARTING)") {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
© Copyright IBM Corporation 2017, 2019
|
© Copyright IBM Corporation 2017, 2020
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -1433,3 +1433,44 @@ func TestTraceStrmqm(t *testing.T) {
|
|||||||
t.Fatalf("No trace files found in trace directory /var/mqm/trace. RC=%d.", rc)
|
t.Fatalf("No trace files found in trace directory /var/mqm/trace. RC=%d.", rc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// utilTestHealthCheck is used by TestHealthCheck* to run a container with
|
||||||
|
// privileges enabled or disabled. Otherwise the same as the golden path tests.
|
||||||
|
func utilTestHealthCheck(t *testing.T, nonewpriv bool) {
|
||||||
|
t.Parallel()
|
||||||
|
cli, err := client.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
containerConfig := container.Config{
|
||||||
|
Env: []string{"LICENSE=accept", "MQ_QMGR_NAME=qm1"},
|
||||||
|
}
|
||||||
|
hostConfig := getDefaultHostConfig(t, cli)
|
||||||
|
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("no-new-privileges:%v", nonewpriv))
|
||||||
|
id := runContainerWithHostConfig(t, cli, &containerConfig, hostConfig)
|
||||||
|
defer cleanContainer(t, cli, id)
|
||||||
|
waitForReady(t, cli, id)
|
||||||
|
rc, out := execContainer(t, cli, id, "", []string{"chkmqhealthy"})
|
||||||
|
t.Log(out)
|
||||||
|
if rc != 0 {
|
||||||
|
t.Errorf("Expected chkmqhealthy to return with exit code 0; got \"%v\"", rc)
|
||||||
|
t.Logf("Output from chkmqhealthy:\n%v", out)
|
||||||
|
}
|
||||||
|
// Stop the container cleanly
|
||||||
|
stopContainer(t, cli, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHealthCheckWithNoNewPrivileges tests golden path start/stop plus
|
||||||
|
// chkmqhealthy, when running in a container where no new privileges are
|
||||||
|
// allowed (i.e. setuid is disabled)
|
||||||
|
func TestHealthCheckWithNoNewPrivileges(t *testing.T) {
|
||||||
|
utilTestHealthCheck(t, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestHealthCheckWithNoNewPrivileges tests golden path start/stop plus
|
||||||
|
// chkmqhealthy when running in a container where new privileges are
|
||||||
|
// allowed (i.e. setuid is allowed)
|
||||||
|
// See https://github.com/ibm-messaging/mq-container/issues/428
|
||||||
|
func TestHealthCheckWithNewPrivileges(t *testing.T) {
|
||||||
|
utilTestHealthCheck(t, false)
|
||||||
|
}
|
||||||
|
|||||||
@@ -267,20 +267,8 @@ func generateRandomUID() string {
|
|||||||
return fmt.Sprint(rand.Intn(max-min) + min)
|
return fmt.Sprint(rand.Intn(max-min) + min)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runContainerWithPorts creates and starts a container, exposing the specified ports on the host.
|
// getDefaultHostConfig creates a HostConfig and populates it with the defaults used in testing
|
||||||
// If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE
|
func getDefaultHostConfig(t *testing.T, cli *client.Client) *container.HostConfig {
|
||||||
// environment variable.
|
|
||||||
func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *container.Config, ports []int) string {
|
|
||||||
if containerConfig.Image == "" {
|
|
||||||
containerConfig.Image = imageName()
|
|
||||||
}
|
|
||||||
// Always run as a random user, unless the test has specified otherwise
|
|
||||||
if containerConfig.User == "" {
|
|
||||||
containerConfig.User = generateRandomUID()
|
|
||||||
}
|
|
||||||
// if coverage
|
|
||||||
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
|
|
||||||
containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t))
|
|
||||||
hostConfig := container.HostConfig{
|
hostConfig := container.HostConfig{
|
||||||
Binds: []string{
|
Binds: []string{
|
||||||
coverageBind(t),
|
coverageBind(t),
|
||||||
@@ -299,6 +287,37 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
|
|||||||
} else {
|
} else {
|
||||||
t.Logf("Detected MQ Advanced image - dropping all capabilities")
|
t.Logf("Detected MQ Advanced image - dropping all capabilities")
|
||||||
}
|
}
|
||||||
|
return &hostConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainerWithHostConfig creates and starts a container, using the supplied HostConfig.
|
||||||
|
// Note that a default HostConfig can be created using getDefaultHostConfig.
|
||||||
|
func runContainerWithHostConfig(t *testing.T, cli *client.Client, containerConfig *container.Config, hostConfig *container.HostConfig) string {
|
||||||
|
if containerConfig.Image == "" {
|
||||||
|
containerConfig.Image = imageName()
|
||||||
|
}
|
||||||
|
// Always run as a random user, unless the test has specified otherwise
|
||||||
|
if containerConfig.User == "" {
|
||||||
|
containerConfig.User = generateRandomUID()
|
||||||
|
}
|
||||||
|
// if coverage
|
||||||
|
containerConfig.Env = append(containerConfig.Env, "COVERAGE_FILE="+t.Name()+".cov")
|
||||||
|
containerConfig.Env = append(containerConfig.Env, "EXIT_CODE_FILE="+getExitCodeFilename(t))
|
||||||
|
networkingConfig := network.NetworkingConfig{}
|
||||||
|
t.Logf("Running container (%s)", containerConfig.Image)
|
||||||
|
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, hostConfig, &networkingConfig, t.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
startContainer(t, cli, ctr.ID)
|
||||||
|
return ctr.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// runContainerWithPorts creates and starts a container, exposing the specified ports on the host.
|
||||||
|
// If no image is specified in the container config, then the image name is retrieved from the TEST_IMAGE
|
||||||
|
// environment variable.
|
||||||
|
func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *container.Config, ports []int) string {
|
||||||
|
hostConfig := getDefaultHostConfig(t, cli)
|
||||||
for _, p := range ports {
|
for _, p := range ports {
|
||||||
port := nat.Port(fmt.Sprintf("%v/tcp", p))
|
port := nat.Port(fmt.Sprintf("%v/tcp", p))
|
||||||
hostConfig.PortBindings[port] = []nat.PortBinding{
|
hostConfig.PortBindings[port] = []nat.PortBinding{
|
||||||
@@ -307,14 +326,7 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
networkingConfig := network.NetworkingConfig{}
|
return runContainerWithHostConfig(t, cli, containerConfig, hostConfig)
|
||||||
t.Logf("Running container (%s)", containerConfig.Image)
|
|
||||||
ctr, err := cli.ContainerCreate(context.Background(), containerConfig, &hostConfig, &networkingConfig, t.Name())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
startContainer(t, cli, ctr.ID)
|
|
||||||
return ctr.ID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// runContainer creates and starts a container. If no image is specified in
|
// runContainer creates and starts a container. If no image is specified in
|
||||||
@@ -526,6 +538,7 @@ func waitForContainer(t *testing.T, cli *client.Client, ID string, timeout time.
|
|||||||
|
|
||||||
// execContainer runs a command in a running container, and returns the exit code and output
|
// execContainer runs a command in a running container, and returns the exit code and output
|
||||||
func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd []string) (int, string) {
|
func execContainer(t *testing.T, cli *client.Client, ID string, user string, cmd []string) (int, string) {
|
||||||
|
t.Logf("Running command: %v", cmd)
|
||||||
config := types.ExecConfig{
|
config := types.ExecConfig{
|
||||||
User: user,
|
User: user,
|
||||||
Privileged: false,
|
Privileged: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user