Don't use setuid on chkmq*
Also add new tests for chkmqhealthy and privileges
This commit is contained in:
committed by
Arthur J Barr
parent
94ad66661e
commit
e8d26aa79e
@@ -98,7 +98,7 @@ COPY web /etc/mqm/web
|
||||
COPY etc/mqm/*.tpl /etc/mqm/
|
||||
RUN chmod ug+x /usr/local/bin/runmqserver \
|
||||
&& 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/* \
|
||||
&& install --directory --mode 2775 --owner 1001 --group root /run/runmqserver \
|
||||
&& touch /run/termination-log \
|
||||
|
||||
10
Makefile
10
Makefile
@@ -40,7 +40,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 ?= $(MQ_ARCHIVE_DEV_$(MQ_VERSION))
|
||||
# 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 ?=ibm-mqadvanced-server
|
||||
# MQ_IMAGE_DEVSERVER is the name of the built MQ Advanced for Developers image
|
||||
@@ -78,7 +80,7 @@ MQ_ARCHIVE_DEV_TYPE=Linux
|
||||
BUILD_SERVER_CONTAINER=build-server
|
||||
# NUM_CPU is the number of CPUs available to Docker. Used to control how many
|
||||
# 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=$(lastword $(subst /, ,$(subst :,-,$(BASE_IMAGE))))
|
||||
#BASE_IMAGE_TAG=$(subst /,-,$(subst :,-,$(BASE_IMAGE)))
|
||||
@@ -241,7 +243,7 @@ test-unit:
|
||||
test-advancedserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG_CACHED_$(ARCH)) on $(shell docker --version)"$(END)))
|
||||
docker inspect $(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG_CACHED_$(ARCH))
|
||||
cd test/docker && TEST_IMAGE=$(MQ_IMAGE_ADVANCEDSERVER):$(MQ_TAG_CACHED_$(ARCH)) EXPECTED_LICENSE=Production go test -parallel $(NUM_CPU) $(TEST_OPTS_DOCKER)
|
||||
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)
|
||||
|
||||
.PHONY: build-devjmstest
|
||||
build-devjmstest:
|
||||
@@ -252,7 +254,7 @@ build-devjmstest:
|
||||
test-devserver: test/docker/vendor
|
||||
$(info $(SPACER)$(shell printf $(TITLE)"Test $(MQ_IMAGE_DEVSERVER):$(MQ_TAG_CACHED_$(ARCH)) on $(shell docker --version)"$(END)))
|
||||
docker inspect $(MQ_IMAGE_DEVSERVER):$(MQ_TAG_CACHED_$(ARCH))
|
||||
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)
|
||||
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)
|
||||
|
||||
.PHONY: 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");
|
||||
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)
|
||||
// Run the command and wait for completion
|
||||
out, err := cmd.CombinedOutput()
|
||||
fmt.Printf("%s", out)
|
||||
if err != nil {
|
||||
fmt.Println(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)") {
|
||||
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");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -1434,3 +1434,44 @@ func TestTraceStrmqm(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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))
|
||||
// getDefaultHostConfig creates a HostConfig and populates it with the defaults used in testing
|
||||
func getDefaultHostConfig(t *testing.T, cli *client.Client) *container.HostConfig {
|
||||
hostConfig := container.HostConfig{
|
||||
Binds: []string{
|
||||
coverageBind(t),
|
||||
@@ -299,6 +287,37 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
|
||||
} else {
|
||||
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 {
|
||||
port := nat.Port(fmt.Sprintf("%v/tcp", p))
|
||||
hostConfig.PortBindings[port] = []nat.PortBinding{
|
||||
@@ -307,14 +326,7 @@ func runContainerWithPorts(t *testing.T, cli *client.Client, containerConfig *co
|
||||
},
|
||||
}
|
||||
}
|
||||
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
|
||||
return runContainerWithHostConfig(t, cli, containerConfig, hostConfig)
|
||||
}
|
||||
|
||||
// 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
|
||||
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{
|
||||
User: user,
|
||||
Privileged: false,
|
||||
|
||||
Reference in New Issue
Block a user