Change for running as a non-root user (#276)
* Enable running container as mqm * Fix merge problem * Don't force root usage * RHEL image runs as mqm instead of root * Build on host with SELinux enabled * Enable building on node in an OpenShift cluster * Enable running container as mqm * Fix merge problem * Don't force root usage * Merge lastest changes from master * RHEL image runs as mqm instead of root * Fix merge issues * Test changes for non-root * Make timeout properly, and more non-root test fixes * Run tests with fewer/no capabilities * Correct usage docs for non-root * Add security docs * Add temporary debug output * Remove debug code * Fixes for termination-log * Allow init container to run as root * Fixes for CentOS build * Fixes for RHEL build * Logging improvements * Fix Dockerfile RHEL/CentOS build * Fix bash error * Make all builds specify UID * Use redist client for Go SDK * Inspect image before running tests * New test for init container * Log container runtime in runmqdevserver * Add extra capabilities if using a RHEL image
This commit is contained in:
63
cmd/runmqdevserver/logruntime.go
Normal file
63
cmd/runmqdevserver/logruntime.go
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
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 main
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||
"github.com/ibm-messaging/mq-container/internal/user"
|
||||
)
|
||||
|
||||
func logContainerDetails() {
|
||||
log.Printf("CPU architecture: %v", runtime.GOARCH)
|
||||
kv, err := containerruntime.GetKernelVersion()
|
||||
if err == nil {
|
||||
log.Printf("Linux kernel version: %v", kv)
|
||||
}
|
||||
cr, err := containerruntime.GetContainerRuntime()
|
||||
if err == nil {
|
||||
log.Printf("Container runtime: %v", cr)
|
||||
}
|
||||
bi, err := containerruntime.GetBaseImage()
|
||||
if err == nil {
|
||||
log.Printf("Base image: %v", bi)
|
||||
}
|
||||
u, err := user.GetUser()
|
||||
if err == nil {
|
||||
if len(u.SupplementalGID) == 0 {
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v", u.UID, u.Name, u.PrimaryGID)
|
||||
} else {
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.UID, u.Name, u.PrimaryGID, strings.Join(u.SupplementalGID, ","))
|
||||
}
|
||||
}
|
||||
caps, err := containerruntime.GetCapabilities()
|
||||
if err == nil {
|
||||
for k, v := range caps {
|
||||
if len(v) > 0 {
|
||||
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Errorf("Error getting capabilities: %v", err)
|
||||
}
|
||||
sc, err := containerruntime.GetSeccomp()
|
||||
if err == nil {
|
||||
log.Printf("seccomp enforcing mode: %v", sc)
|
||||
}
|
||||
log.Printf("Process security attributes: %v", containerruntime.GetSecurityAttributes())
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -31,7 +31,7 @@ var log *logger.Logger
|
||||
|
||||
func setPassword(user string, password string) error {
|
||||
// #nosec G204
|
||||
cmd := exec.Command("chpasswd")
|
||||
cmd := exec.Command("sudo", "chpasswd")
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -41,9 +41,10 @@ func setPassword(user string, password string) error {
|
||||
if err != nil {
|
||||
log.Errorf("Error closing password stdin: %v", err)
|
||||
}
|
||||
_, _, err = command.RunCmd(cmd)
|
||||
out, _, err := command.RunCmd(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
// Include the command output in the error
|
||||
return fmt.Errorf("%v: %v", err.Error(), out)
|
||||
}
|
||||
log.Printf("Set password for \"%v\" user", user)
|
||||
return nil
|
||||
@@ -93,16 +94,16 @@ func configureWeb(qmName string) error {
|
||||
}
|
||||
|
||||
func logTerminationf(format string, args ...interface{}) {
|
||||
logTermination(fmt.Sprintf(format, args))
|
||||
logTermination(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// TODO: Duplicated code
|
||||
func logTermination(args ...interface{}) {
|
||||
msg := fmt.Sprint(args)
|
||||
// Write the message to the termination log. This is the default place
|
||||
msg := fmt.Sprint(args...)
|
||||
// Write the message to the termination log. This is not the default place
|
||||
// that Kubernetes will look for termination information.
|
||||
log.Debugf("Writing termination message: %v", msg)
|
||||
err := ioutil.WriteFile("/dev/termination-log", []byte(msg), 0660)
|
||||
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
}
|
||||
@@ -115,6 +116,9 @@ func doMain() error {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
logContainerDetails()
|
||||
|
||||
adminPassword, set := os.LookupEnv("MQ_ADMIN_PASSWORD")
|
||||
if set {
|
||||
err = setPassword("admin", adminPassword)
|
||||
@@ -170,7 +174,7 @@ func main() {
|
||||
} else {
|
||||
// Replace this process with runmqserver
|
||||
// #nosec G204
|
||||
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver"}, os.Environ())
|
||||
err = syscall.Exec("/usr/local/bin/runmqserver", []string{"runmqserver", "-dev"}, os.Environ())
|
||||
if err != nil {
|
||||
log.Errorf("Error replacing this process with runmqserver: %v", err)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -36,7 +36,7 @@ func processTemplateFile(templateFile, destFile string, data interface{}) error
|
||||
_, err = os.Stat(dir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(dir, 0660)
|
||||
err = os.MkdirAll(dir, 0770)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -36,15 +36,15 @@ var log *logger.Logger
|
||||
var collectDiagOnFail = false
|
||||
|
||||
func logTerminationf(format string, args ...interface{}) {
|
||||
logTermination(fmt.Sprintf(format, args))
|
||||
logTermination(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func logTermination(args ...interface{}) {
|
||||
msg := fmt.Sprint(args)
|
||||
// Write the message to the termination log. This is the default place
|
||||
msg := fmt.Sprint(args...)
|
||||
// Write the message to the termination log. This is not the default place
|
||||
// that Kubernetes will look for termination information.
|
||||
log.Debugf("Writing termination message: %v", msg)
|
||||
err := ioutil.WriteFile("/dev/termination-log", []byte(msg), 0660)
|
||||
err := ioutil.WriteFile("/run/termination-log", []byte(msg), 0660)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
}
|
||||
|
||||
79
cmd/runmqserver/logruntime.go
Normal file
79
cmd/runmqserver/logruntime.go
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
containerruntime "github.com/ibm-messaging/mq-container/internal/containerruntime"
|
||||
"github.com/ibm-messaging/mq-container/internal/user"
|
||||
)
|
||||
|
||||
func logContainerDetails() error {
|
||||
if runtime.GOOS != "linux" {
|
||||
return fmt.Errorf("Unsupported platform: %v", runtime.GOOS)
|
||||
}
|
||||
log.Printf("CPU architecture: %v", runtime.GOARCH)
|
||||
kv, err := containerruntime.GetKernelVersion()
|
||||
if err == nil {
|
||||
log.Printf("Linux kernel version: %v", kv)
|
||||
}
|
||||
cr, err := containerruntime.GetContainerRuntime()
|
||||
if err == nil {
|
||||
log.Printf("Container runtime: %v", cr)
|
||||
}
|
||||
bi, err := containerruntime.GetBaseImage()
|
||||
if err == nil {
|
||||
log.Printf("Base image: %v", bi)
|
||||
}
|
||||
u, err := user.GetUser()
|
||||
if err == nil {
|
||||
if len(u.SupplementalGID) == 0 {
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v", u.UID, u.Name, u.PrimaryGID)
|
||||
} else {
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.UID, u.Name, u.PrimaryGID, strings.Join(u.SupplementalGID, ","))
|
||||
}
|
||||
}
|
||||
caps, err := containerruntime.GetCapabilities()
|
||||
if err == nil {
|
||||
for k, v := range caps {
|
||||
if len(v) > 0 {
|
||||
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
|
||||
}
|
||||
}
|
||||
}
|
||||
sc, err := containerruntime.GetSeccomp()
|
||||
if err == nil {
|
||||
log.Printf("seccomp enforcing mode: %v", sc)
|
||||
}
|
||||
log.Printf("Process security attributes: %v", containerruntime.GetSecurityAttributes())
|
||||
m, err := containerruntime.GetMounts()
|
||||
if err == nil {
|
||||
if len(m) == 0 {
|
||||
log.Print("No volume detected. Persistent messages may be lost")
|
||||
} else {
|
||||
for mountPoint, fsType := range m {
|
||||
log.Printf("Detected '%v' volume mounted to %v", fsType, mountPoint)
|
||||
if !containerruntime.SupportedFilesystem(fsType) {
|
||||
return fmt.Errorf("%v uses unsupported filesystem type: %v", mountPoint, fsType)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
© Copyright IBM Corporation 2017, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -30,10 +30,11 @@ import (
|
||||
)
|
||||
|
||||
func doMain() error {
|
||||
var initFlag = flag.Bool("i", false, "initialize volume only, then exit")
|
||||
var infoFlag = flag.Bool("info", false, "Display debug info, then exit")
|
||||
var devFlag = flag.Bool("dev", false, "used when running this program from runmqdevserver to control log output")
|
||||
flag.Parse()
|
||||
|
||||
// Configure the logger so we can output messages
|
||||
name, nameErr := name.GetQueueManagerName()
|
||||
mf, err := configureLogger(name)
|
||||
if err != nil {
|
||||
@@ -44,7 +45,7 @@ func doMain() error {
|
||||
// Check whether they only want debug info
|
||||
if *infoFlag {
|
||||
logVersionInfo()
|
||||
logConfig()
|
||||
logContainerDetails()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -81,16 +82,12 @@ func doMain() error {
|
||||
// Enable diagnostic collecting on failure
|
||||
collectDiagOnFail = true
|
||||
|
||||
err = verifyCurrentUser()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = logConfig()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
if *devFlag == false {
|
||||
err = logContainerDetails()
|
||||
if err != nil {
|
||||
logTermination(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = createVolume("/mnt/mqm")
|
||||
@@ -104,6 +101,11 @@ func doMain() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// If init flag is set, exit now
|
||||
if *initFlag {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Print out versioning information
|
||||
logVersionInfo()
|
||||
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
|
||||
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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/genuinetools/amicontained/container"
|
||||
)
|
||||
|
||||
func logContainerRuntime() {
|
||||
r, err := container.DetectRuntime()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get container runtime: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("Container runtime: %v", r)
|
||||
}
|
||||
|
||||
func logBaseImage() {
|
||||
buf, err := ioutil.ReadFile("/etc/os-release")
|
||||
if err != nil {
|
||||
log.Printf("Failed to read /etc/os-release: %v", err)
|
||||
return
|
||||
}
|
||||
lines := strings.Split(string(buf), "\n")
|
||||
for _, l := range lines {
|
||||
if strings.HasPrefix(l, "PRETTY_NAME=") {
|
||||
words := strings.Split(l, "\"")
|
||||
if len(words) >= 2 {
|
||||
log.Printf("Base image: %v", words[1])
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logCapabilities logs the Linux capabilities (e.g. setuid, setgid). See https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
|
||||
func logCapabilities() {
|
||||
caps, err := container.Capabilities()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get container capabilities: %v", err)
|
||||
return
|
||||
}
|
||||
for k, v := range caps {
|
||||
if len(v) > 0 {
|
||||
log.Printf("Capabilities (%s set): %v", strings.ToLower(k), strings.Join(v, ","))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logSeccomp logs the seccomp enforcing mode, which affects which kernel calls can be made
|
||||
func logSeccomp() {
|
||||
s, err := container.SeccompEnforcingMode()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get container SeccompEnforcingMode: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("seccomp enforcing mode: %v", s)
|
||||
}
|
||||
|
||||
// logSecurityAttributes logs the security attributes of the current process.
|
||||
// The security attributes indicate whether AppArmor or SELinux are being used,
|
||||
// and what the level of confinement is.
|
||||
func logSecurityAttributes() {
|
||||
a, err := readProc("/proc/self/attr/current")
|
||||
// On some systems, if AppArmor or SELinux are not installed, you get an
|
||||
// error when you try and read `/proc/self/attr/current`, even though the
|
||||
// file exists.
|
||||
if err != nil || a == "" {
|
||||
a = "none"
|
||||
}
|
||||
log.Printf("Process security attributes: %v", a)
|
||||
}
|
||||
|
||||
func readProc(filename string) (value string, err error) {
|
||||
// #nosec G304
|
||||
buf, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(buf)), nil
|
||||
}
|
||||
|
||||
func readMounts() error {
|
||||
all, err := readProc("/proc/mounts")
|
||||
if err != nil {
|
||||
log.Print("Error: Couldn't read /proc/mounts")
|
||||
return err
|
||||
}
|
||||
lines := strings.Split(all, "\n")
|
||||
detected := false
|
||||
for i := range lines {
|
||||
parts := strings.Split(lines[i], " ")
|
||||
//dev := parts[0]
|
||||
mountPoint := parts[1]
|
||||
fsType := parts[2]
|
||||
if strings.Contains(mountPoint, "/mnt/mqm") {
|
||||
log.Printf("Detected '%v' volume mounted to %v", fsType, mountPoint)
|
||||
detected = true
|
||||
}
|
||||
}
|
||||
if !detected {
|
||||
log.Print("No volume detected. Persistent messages may be lost")
|
||||
} else {
|
||||
return checkFS("/mnt/mqm")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func logConfig() error {
|
||||
log.Printf("CPU architecture: %v", runtime.GOARCH)
|
||||
if runtime.GOOS == "linux" {
|
||||
var err error
|
||||
osr, err := readProc("/proc/sys/kernel/osrelease")
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
log.Printf("Linux kernel version: %v", osr)
|
||||
}
|
||||
logContainerRuntime()
|
||||
logBaseImage()
|
||||
fileMax, err := readProc("/proc/sys/fs/file-max")
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
} else {
|
||||
log.Printf("Maximum file handles: %v", fileMax)
|
||||
}
|
||||
logUser()
|
||||
logCapabilities()
|
||||
logSeccomp()
|
||||
logSecurityAttributes()
|
||||
err = readMounts()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Unsupported platform: %v", runtime.GOOS)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// +build linux
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2017, 2018
|
||||
|
||||
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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// fsTypes contains file system identifier codes.
|
||||
// This code will not compile on some operating systems - Linux only.
|
||||
var fsTypes = map[int64]string{
|
||||
0x61756673: "aufs",
|
||||
0xef53: "ext",
|
||||
0x6969: "nfs",
|
||||
0x65735546: "fuse",
|
||||
0x9123683e: "btrfs",
|
||||
0x01021994: "tmpfs",
|
||||
0x794c7630: "overlayfs",
|
||||
0x58465342: "xfs",
|
||||
// less popular codes
|
||||
0xadf5: "adfs",
|
||||
0xadff: "affs",
|
||||
0x5346414F: "afs",
|
||||
0x0187: "autofs",
|
||||
0x73757245: "coda",
|
||||
0x28cd3d45: "cramfs",
|
||||
0x453dcd28: "cramfs",
|
||||
0x64626720: "debugfs",
|
||||
0x73636673: "securityfs",
|
||||
0xf97cff8c: "selinux",
|
||||
0x43415d53: "smack",
|
||||
0x858458f6: "ramfs",
|
||||
0x958458f6: "hugetlbfs",
|
||||
0x73717368: "squashfs",
|
||||
0xf15f: "ecryptfs",
|
||||
0x414A53: "efs",
|
||||
0xabba1974: "xenfs",
|
||||
0x3434: "nilfs",
|
||||
0xF2F52010: "f2fs",
|
||||
0xf995e849: "hpfs",
|
||||
0x9660: "isofs",
|
||||
0x72b6: "jffs2",
|
||||
0x6165676C: "pstorefs",
|
||||
0xde5e81e4: "efivarfs",
|
||||
0x00c0ffee: "hostfs",
|
||||
0x137F: "minix_14", // minix v1 fs, 14 char names
|
||||
0x138F: "minix_30", // minix v1 fs, 30 char names
|
||||
0x2468: "minix2_14", // minix v2 fs, 14 char names
|
||||
0x2478: "minix2_30", // minix v2 fs, 30 char names
|
||||
0x4d5a: "minix3_60", // minix v3 fs, 60 char names
|
||||
0x4d44: "msdos",
|
||||
0x564c: "ncp",
|
||||
0x7461636f: "ocfs2",
|
||||
0x9fa1: "openprom",
|
||||
0x002f: "qnx4",
|
||||
0x68191122: "qnx6",
|
||||
0x6B414653: "afs_fs",
|
||||
0x52654973: "reiserfs",
|
||||
0x517B: "smb",
|
||||
0x27e0eb: "cgroup",
|
||||
0x63677270: "cgroup2",
|
||||
0x7655821: "rdtgroup",
|
||||
0x57AC6E9D: "stack_end",
|
||||
0x74726163: "tracefs",
|
||||
0x01021997: "v9fs",
|
||||
0x62646576: "bdevfs",
|
||||
0x64646178: "daxfs",
|
||||
0x42494e4d: "binfmtfs",
|
||||
0x1cd1: "devpts",
|
||||
0xBAD1DEA: "futexfs",
|
||||
0x50495045: "pipefs",
|
||||
0x9fa0: "proc",
|
||||
0x534F434B: "sockfs",
|
||||
0x62656572: "sysfs",
|
||||
0x9fa2: "usbdevice",
|
||||
0x11307854: "mtd_inode",
|
||||
0x09041934: "anon_inode",
|
||||
0x73727279: "btrfs",
|
||||
0x6e736673: "nsfs",
|
||||
0xcafe4a11: "bpf",
|
||||
0x5a3c69f0: "aafs",
|
||||
0x15013346: "udf",
|
||||
0x13661366: "balloon_kvm",
|
||||
0x58295829: "zsmalloc",
|
||||
}
|
||||
|
||||
func checkFS(path string) error {
|
||||
statfs := &unix.Statfs_t{}
|
||||
err := unix.Statfs(path, statfs)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil
|
||||
}
|
||||
// Use a type conversion to make type an int64. On s390x it's a uint32.
|
||||
t, ok := fsTypes[int64(statfs.Type)]
|
||||
if !ok {
|
||||
log.Printf("WARNING: detected %v has unknown filesystem type %x", path, statfs.Type)
|
||||
return nil
|
||||
}
|
||||
switch t {
|
||||
case "aufs", "overlayfs", "tmpfs":
|
||||
return fmt.Errorf("%v uses unsupported filesystem type: %v", path, t)
|
||||
default:
|
||||
log.Printf("Detected %v has filesystem type '%v'", path, t)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// +build !linux
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
|
||||
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 main
|
||||
|
||||
// Dummy version of this function, only for non-Linux systems.
|
||||
// Having this allows unit tests to be run on other platforms (e.g. macOS)
|
||||
func checkFS(path string) error {
|
||||
return nil
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func configureQueueManager() error {
|
||||
// Run the command and wait for completion
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
log.Errorf("Error running MQSC file %v: %v", file.Name(), err)
|
||||
log.Errorf("Error running MQSC file %v (%v):\n\t%v", file.Name(), err, strings.Replace(string(out), "\n", "\n\t", -1))
|
||||
return err
|
||||
}
|
||||
// Print the runmqsc output, adding tab characters to make it more readable as part of the log
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
|
||||
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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/user"
|
||||
"strings"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
)
|
||||
|
||||
const groupName string = "supplgrp"
|
||||
|
||||
func verifyCurrentUser() error {
|
||||
log.Debug("Verifying current user information")
|
||||
curUser, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("Detected current user as: %v+", curUser)
|
||||
if curUser.Username == "mqm" {
|
||||
// Not supported yet
|
||||
return fmt.Errorf("Container is running as mqm user which is not supported. Please run this container as root")
|
||||
} else if curUser.Username == "root" {
|
||||
// We're running as root so need to check for supplementary groups.
|
||||
// We can't use the golang User.GroupIDs as it doesn't seem to detect container supplementary groups..
|
||||
groups, err := getCurrentUserGroups()
|
||||
for _, e := range groups {
|
||||
_, _, testGroup := command.Run("getent", "group", e)
|
||||
if testGroup != nil {
|
||||
log.Printf("Group %s does not exist on the system... Adding to system and MQM user", e)
|
||||
_, _, err = command.Run("groupadd", "-g", e, groupName)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create group %s as %s", e, groupName)
|
||||
return err
|
||||
}
|
||||
_, _, err = command.Run("usermod", "-aG", groupName, "mqm")
|
||||
if err != nil {
|
||||
log.Errorf("Failed to add group %s(%s) to the mqm user.", groupName, e)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We're running as an unknown user...
|
||||
return fmt.Errorf("Container is running as %s user which is not supported. Please run this container as root", curUser.Username)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func logUser() {
|
||||
u, usererr := user.Current()
|
||||
if usererr == nil {
|
||||
g, err := getCurrentUserGroups()
|
||||
if err != nil && len(g) == 0 {
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v", u.Uid, u.Name, u.Gid)
|
||||
} else {
|
||||
// Look for the primary group in the list of group IDs
|
||||
for i, v := range g {
|
||||
if v == u.Gid {
|
||||
// Remove the element from the slice
|
||||
g = append(g[:i], g[i+1:]...)
|
||||
}
|
||||
}
|
||||
log.Printf("Running as user ID %v (%v) with primary group %v, and supplementary groups %v", u.Uid, u.Name, u.Gid, strings.Join(g, ","))
|
||||
}
|
||||
}
|
||||
|
||||
if usererr == nil && u.Username != "mqm" {
|
||||
mqm, err := user.Lookup("mqm")
|
||||
// Need to print out mqm user details as well.
|
||||
g, err := getUserGroups(mqm)
|
||||
if err != nil && len(g) == 0 {
|
||||
log.Printf("MQM user ID %v (%v) has primary group %v", mqm.Uid, "mqm", mqm.Gid)
|
||||
} else {
|
||||
// Look for the primary group in the list of group IDs
|
||||
for i, v := range g {
|
||||
if v == mqm.Gid {
|
||||
// Remove the element from the slice
|
||||
g = append(g[:i], g[i+1:]...)
|
||||
}
|
||||
}
|
||||
log.Printf("MQM user ID %v (%v) has primary group %v, and supplementary groups %v", mqm.Uid, "mqm", mqm.Gid, strings.Join(g, ","))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getCurrentUserGroups() ([]string, error) {
|
||||
var nilArray []string
|
||||
out, _, err := command.Run("id", "--groups")
|
||||
if err != nil {
|
||||
log.Debug("Unable to get current user groups")
|
||||
return nilArray, err
|
||||
}
|
||||
|
||||
out = strings.TrimSpace(out)
|
||||
if out == "" {
|
||||
// we don't have any groups?
|
||||
return nilArray, fmt.Errorf("Unable to determine groups for current user")
|
||||
}
|
||||
|
||||
groups := strings.Split(out, " ")
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func getUserGroups(usr *user.User) ([]string, error) {
|
||||
var nilArray []string
|
||||
out, _, err := command.Run("id", "--groups", usr.Uid)
|
||||
if err != nil {
|
||||
log.Debugf("Unable to get user %s groups", usr.Uid)
|
||||
return nilArray, err
|
||||
}
|
||||
|
||||
out = strings.TrimSpace(out)
|
||||
if out == "" {
|
||||
// we don't have any groups?
|
||||
return nilArray, fmt.Errorf("Unable to determine groups for user %s", usr.Uid)
|
||||
}
|
||||
|
||||
groups := strings.Split(out, " ")
|
||||
return groups, nil
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// +build mqdev
|
||||
|
||||
/*
|
||||
© Copyright IBM Corporation 2018
|
||||
© Copyright IBM Corporation 2018, 2019
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,9 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/ibm-messaging/mq-container/internal/command"
|
||||
@@ -42,12 +44,23 @@ func startWebServer() error {
|
||||
// Take all current environment variables, and add the app password
|
||||
cmd.Env = append(os.Environ(), "MQ_APP_PASSWORD=passw0rd")
|
||||
}
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
uid, gid, err := command.LookupMQM()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentUID, err := strconv.Atoi(u.Uid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting UID to string: %v", err)
|
||||
}
|
||||
// Add credentials to run as 'mqm', only if we aren't already 'mqm'
|
||||
if currentUID != uid {
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)}
|
||||
}
|
||||
out, rc, err := command.RunCmd(cmd)
|
||||
if err != nil {
|
||||
log.Printf("Error %v starting web server: %v", rc, string(out))
|
||||
|
||||
Reference in New Issue
Block a user