diff --git a/Dockerfile-server b/Dockerfile-server index d21e5a0..7222dcd 100644 --- a/Dockerfile-server +++ b/Dockerfile-server @@ -18,14 +18,15 @@ WORKDIR /go/src/github.com/ibm-messaging/mq-container/ COPY cmd/ ./cmd COPY pkg/ ./pkg COPY vendor/ ./vendor -RUN GOOS=linux go build ./cmd/runmqserver/ -RUN GOOS=linux go build ./cmd/chkmqready/ -RUN GOOS=linux go build ./cmd/chkmqhealthy/ +RUN go build ./cmd/runmqserver/ +RUN go build ./cmd/chkmqready/ +RUN go build ./cmd/chkmqhealthy/ # Build stage to run Go unit tests FROM golang:1.9 as tester COPY pkg/ ./pkg -RUN cd pkg/name && GOOS=linux go test +RUN cd pkg/name && go test +RUN cd pkg/linux/capabilities && go test # Main build stage, to build MQ image FROM ubuntu:16.04 diff --git a/cmd/runmqserver/mqconfig.go b/cmd/runmqserver/mqconfig.go index c66fc55..7a90fd2 100644 --- a/cmd/runmqserver/mqconfig.go +++ b/cmd/runmqserver/mqconfig.go @@ -22,6 +22,7 @@ import ( "runtime" "strings" + "github.com/ibm-messaging/mq-container/pkg/linux/capabilities" "golang.org/x/sys/unix" ) @@ -62,6 +63,18 @@ func logUser() { } } +func logCapabilities() { + status, err := readProc("/proc/1/status") + if err != nil { + // Ignore + return + } + caps, err := capabilities.DetectCapabilities(status) + if err == nil { + log.Printf("Detected capabilities: %v", strings.Join(caps, ",")) + } +} + func readProc(filename string) (value string, err error) { buf, err := ioutil.ReadFile(filename) if err != nil { @@ -130,6 +143,7 @@ func logConfig() { log.Printf("Maximum file handles: %v", fileMax) } logUser() + logCapabilities() readMounts() } else { log.Fatalf("Unsupported platform: %v", runtime.GOOS) diff --git a/pkg/linux/capabilities/capabilities.go b/pkg/linux/capabilities/capabilities.go new file mode 100644 index 0000000..2cdbc3a --- /dev/null +++ b/pkg/linux/capabilities/capabilities.go @@ -0,0 +1,158 @@ +/* +© Copyright IBM Corporation 2017 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// capabilities allows querying of information on Linux capabilities +package capabilities + +import ( + "errors" + "strconv" + "strings" +) + +// DetectCapabilities determines Linux capabilities, based on the contents of a Linux "status" file. +// For example, the contents of file `/proc/1/status` +func DetectCapabilities(status string) ([]string, error) { + lines := strings.Split(status, "\n") + for _, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "CapPrm:") { + words := strings.Fields(line) + cap, err := strconv.ParseUint(words[1], 16, 64) + if err != nil { + return nil, err + } + return getCapabilities(cap), nil + } + } + return nil, errors.New("Unable to detect capabilities") +} + +// getCapabilities converts an encoded uint64 into a slice of string names of Linux capabilities +func getCapabilities(cap uint64) []string { + caps := make([]string, 0, 37) + if cap&0x0000000040000000 == 0x0000000040000000 { + caps = append(caps, "AUDIT_CONTROL") + } + if cap&0x0000000020000000 == 0x0000000020000000 { + caps = append(caps, "AUDIT_WRITE") + } + if cap&0x0000001000000000 == 0x0000001000000000 { + caps = append(caps, "BLOCK_SUSPEND") + } + if cap&0x0000000000000001 == 0x0000000000000001 { + caps = append(caps, "CHOWN") + } + if cap&0x0000000000000002 == 0x0000000000000002 { + caps = append(caps, "DAC_OVERRIDE") + } + if cap&0x0000000000000004 == 0x0000000000000004 { + caps = append(caps, "DAC_READ_SEARCH") + } + if cap&0x0000000000000008 == 0x0000000000000008 { + caps = append(caps, "FOWNER") + } + if cap&0x0000000000000010 == 0x0000000000000010 { + caps = append(caps, "FSETID") + } + if cap&0x0000000000004000 == 0x0000000000004000 { + caps = append(caps, "IPC_LOCK") + } + if cap&0x0000000000008000 == 0x0000000000008000 { + caps = append(caps, "IPC_OWNER") + } + if cap&0x0000000000000020 == 0x0000000000000020 { + caps = append(caps, "KILL") + } + if cap&0x0000000010000000 == 0x0000000010000000 { + caps = append(caps, "LEASE") + } + if cap&0x0000000000000200 == 0x0000000000000200 { + caps = append(caps, "LINUX_IMMUTABLE") + } + if cap&0x0000000200000000 == 0x0000000200000000 { + caps = append(caps, "MAC_ADMIN") + } + if cap&0x0000000100000000 == 0x0000000100000000 { + caps = append(caps, "MAC_OVERRIDE") + } + if cap&0x0000000008000000 == 0x0000000008000000 { + caps = append(caps, "MKNOD") + } + if cap&0x0000000000001000 == 0x0000000000001000 { + caps = append(caps, "NET_ADMIN") + } + if cap&0x0000000000000400 == 0x0000000000000400 { + caps = append(caps, "NET_BIND_SERVICE") + } + if cap&0x0000000000000800 == 0x0000000000000800 { + caps = append(caps, "NET_BROADCAST") + } + if cap&0x0000000000002000 == 0x0000000000002000 { + caps = append(caps, "NET_RAW") + } + if cap&0x0000000080000000 == 0x0000000080000000 { + caps = append(caps, "SETFCAP") + } + if cap&0x0000000000000040 == 0x0000000000000040 { + caps = append(caps, "SETGID") + } + if cap&0x0000000000000100 == 0x0000000000000100 { + caps = append(caps, "SETPCAP") + } + if cap&0x0000000000000080 == 0x0000000000000080 { + caps = append(caps, "SETUID") + } + if cap&0x0000000400000000 == 0x0000000400000000 { + caps = append(caps, "SYSLOG") + } + if cap&0x0000000000200000 == 0x0000000000200000 { + caps = append(caps, "SYS_ADMIN") + } + if cap&0x0000000000400000 == 0x0000000000400000 { + caps = append(caps, "SYS_BOOT") + } + if cap&0x0000000000040000 == 0x0000000000040000 { + caps = append(caps, "SYS_CHROOT") + } + if cap&0x0000000000010000 == 0x0000000000010000 { + caps = append(caps, "SYS_MODULE") + } + if cap&0x0000000000800000 == 0x0000000000800000 { + caps = append(caps, "SYS_NICE") + } + if cap&0x0000000000100000 == 0x0000000000100000 { + caps = append(caps, "SYS_PACCT") + } + if cap&0x0000000000080000 == 0x0000000000080000 { + caps = append(caps, "SYS_PTRACE") + } + if cap&0x0000000000020000 == 0x0000000000020000 { + caps = append(caps, "SYS_RAWIO") + } + if cap&0x0000000001000000 == 0x0000000001000000 { + caps = append(caps, "SYS_RESOURCE") + } + if cap&0x0000000002000000 == 0x0000000002000000 { + caps = append(caps, "SYS_TIME") + } + if cap&0x0000000004000000 == 0x0000000004000000 { + caps = append(caps, "SYS_TTY_CONFIG") + } + if cap&0x0000000800000000 == 0x0000000800000000 { + caps = append(caps, "WAKE_ALARM") + } + return caps +} diff --git a/pkg/linux/capabilities/capabilities_test.go b/pkg/linux/capabilities/capabilities_test.go new file mode 100644 index 0000000..dae2973 --- /dev/null +++ b/pkg/linux/capabilities/capabilities_test.go @@ -0,0 +1,39 @@ +/* +© Copyright IBM Corporation 2017 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package capabilities + +import ( + "reflect" + "testing" +) + +var capTests = []struct { + in uint64 + out []string +}{ + {0x0000000040000000, []string{"AUDIT_CONTROL"}}, + // Default values when you run a Docker container without changing capabilities: + {0x00000000a80425fb, []string{"AUDIT_WRITE", "CHOWN", "DAC_OVERRIDE", "FOWNER", "FSETID", "KILL", "MKNOD", "NET_BIND_SERVICE", "NET_RAW", "SETFCAP", "SETGID", "SETPCAP", "SETUID", "SYS_CHROOT"}}, +} + +func TestGetCapabilities(t *testing.T) { + for _, table := range capTests { + caps := getCapabilities(table.in) + if !reflect.DeepEqual(caps, table.out) { + t.Errorf("getCapabilities(%v) - expected %v, got %v", table.in, table.out, caps) + } + } +}