reason: update license to Mulan PSL v2 Signed-off-by: taleintervenor <taleintervenor@aliyun.com>
285 lines
7.9 KiB
Go
285 lines
7.9 KiB
Go
// Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
// syscontainer-tools is licensed under the Mulan PSL v2.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
// You may obtain a copy of Mulan PSL v2 at:
|
|
// http://license.coscl.org.cn/MulanPSL2
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
// PURPOSE.
|
|
// See the Mulan PSL v2 for more details.
|
|
// Description: selinux relabel operation
|
|
// Author: zhangwei
|
|
// Create: 2018-01-18
|
|
|
|
// go base main package
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/docker/docker/pkg/reexec"
|
|
"github.com/opencontainers/runc/libcontainer"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
_ "github.com/opencontainers/runc/libcontainer/nsenter"
|
|
"github.com/opencontainers/runc/libcontainer/selinux"
|
|
"github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/vishvananda/netlink/nl"
|
|
hconfig "isula.org/syscontainer-tools/config"
|
|
"isula.org/syscontainer-tools/libdevice/nsexec"
|
|
"isula.org/syscontainer-tools/utils"
|
|
)
|
|
|
|
var (
|
|
autoRelabel = "/.autorelabel"
|
|
autoRelabelInContainer = "/.autorelabel_in_container"
|
|
containerautoRelabel = "/.container_autorelabel"
|
|
relabelBin = "/usr/bin/autorelabel_container"
|
|
systemdServiceFile = "/etc/systemd/system/multi-user.target.wants/autorelabel.service"
|
|
upstartServiceFile = "/etc/init/autorelabel.conf"
|
|
systemdInit = "systemd"
|
|
appName = "oci-relabel-hook"
|
|
usage = "oci-relabel-hook poststart|poststop"
|
|
relabelRexec = "reexec-relabel"
|
|
|
|
autoRelabelService = `#!/bin/bash
|
|
. /etc/selinux/config
|
|
setenforce 0
|
|
semodule -R
|
|
if [ -f "%s" ]; then
|
|
restorecon -R /
|
|
rm -rf %s
|
|
reboot -f
|
|
else
|
|
if [ "$SELINUX" = "enforcing" ]; then
|
|
setenforce 1
|
|
else
|
|
setenforce 0
|
|
fi
|
|
fi`
|
|
|
|
upstartService = `start on startup
|
|
task
|
|
console output
|
|
script
|
|
logger "upstart-autorelabel start"
|
|
exec %s
|
|
end script`
|
|
|
|
systemdService = `[Unit]
|
|
Description=Relabel all container's filesystems, if necessary
|
|
DefaultDependencies=no
|
|
Requires=local-fs.target
|
|
Conflicts=shutdown.target
|
|
After=local-fs.target
|
|
Before=sysinit.target shutdown.target
|
|
|
|
[Service]
|
|
ExecStart=%s
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
|
|
`
|
|
)
|
|
|
|
func init() {
|
|
reexec.Register(relabelRexec, RelabelInMntNs)
|
|
}
|
|
|
|
// RelabelInMntNs relabel in container mount namespace
|
|
func RelabelInMntNs() {
|
|
var s configs.HookState
|
|
if err := json.NewDecoder(os.Stdin).Decode(&s); err != nil {
|
|
logrus.Errorf("[oci relabel] Failed to decode stdin: %v", err)
|
|
return
|
|
}
|
|
|
|
if err := preStartNs(&s); err != nil {
|
|
logrus.Errorf("[oci relabel] Failed to relabel in mnt ns: %v", err)
|
|
}
|
|
}
|
|
|
|
func relabelSystemd(rootfs string) error {
|
|
logrus.Info("systemd autorelable")
|
|
autoRel := fmt.Sprintf(autoRelabelService, autoRelabelInContainer, autoRelabelInContainer)
|
|
if err := ioutil.WriteFile(filepath.Join(rootfs, relabelBin), []byte(autoRel), 0700); err != nil {
|
|
return err
|
|
}
|
|
systemdService := fmt.Sprintf(systemdService, relabelBin)
|
|
if err := ioutil.WriteFile(filepath.Join(rootfs, systemdServiceFile), []byte(systemdService), 0600); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func relabelUpstart(rootfs string) error {
|
|
logrus.Info("upstart autorelable")
|
|
autoRel := fmt.Sprintf(autoRelabelService, autoRelabelInContainer, autoRelabelInContainer)
|
|
if err := ioutil.WriteFile(filepath.Join(rootfs, relabelBin), []byte(autoRel), 0700); err != nil {
|
|
return err
|
|
}
|
|
|
|
upstartService := fmt.Sprintf(upstartService, relabelBin)
|
|
if err := ioutil.WriteFile(filepath.Join(rootfs, upstartServiceFile), []byte(upstartService), 0600); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func relabel(rootfs string) error {
|
|
if utils.IsSystemdInit(rootfs) {
|
|
return relabelSystemd(rootfs)
|
|
}
|
|
return relabelUpstart(rootfs)
|
|
}
|
|
|
|
func preStartNs(s *configs.HookState) error {
|
|
var (
|
|
se string
|
|
err error
|
|
attr string
|
|
seconfig = "/etc/selinux/config"
|
|
seconfigContainer = s.Root + "/etc/selinux/config"
|
|
)
|
|
|
|
if se, err = utils.SeconfigGet(seconfig, "SELINUX"); err != nil {
|
|
return err
|
|
}
|
|
// don't exec hook's function if SELinux is disabled in host
|
|
if se == "disabled" {
|
|
logrus.Infof("Host SELinux disabled")
|
|
return nil
|
|
}
|
|
|
|
// set permissive to host /etc/selinux/config
|
|
if err = utils.SeconfigSet(seconfig, "SELINUX", "permissive"); err != nil {
|
|
return err
|
|
}
|
|
|
|
if se, err = utils.SeconfigGet(seconfigContainer, "SELINUX"); err != nil {
|
|
return err
|
|
}
|
|
// proposal from it
|
|
// don't exec hook's function if SELinux is disabled in container
|
|
if se == "disabled" {
|
|
logrus.Infof("Container SELinux disabled")
|
|
return nil
|
|
}
|
|
|
|
// mount selinuxfs
|
|
if err = syscall.Mount("none", s.Root+utils.GetSelinuxMountPount(s.Root), "selinuxfs", 0, ""); err != nil {
|
|
return err
|
|
}
|
|
// start a container in the first time, it need relabel, so create a /.autorelabel file
|
|
if !utils.IsExist(s.Root + containerautoRelabel) {
|
|
if err := ioutil.WriteFile(filepath.Join(s.Root, autoRelabel), []byte(""), 0600); err != nil {
|
|
logrus.Errorf("WriteFile err: %v", err)
|
|
}
|
|
if err := ioutil.WriteFile(filepath.Join(s.Root, containerautoRelabel), []byte(""), 0600); err != nil {
|
|
logrus.Errorf("WriteFile err: %v", err)
|
|
}
|
|
}
|
|
|
|
// relabel container' rootfs, just create a systemd service file, and the relabel process is executed in container.
|
|
if err = relabel(s.Root); err != nil {
|
|
return err
|
|
}
|
|
|
|
// make sure relabelBin can execute exactly
|
|
hostRelabelBin := filepath.Join(s.Root, relabelBin)
|
|
if attr, err = selinux.Getfilecon(hostRelabelBin); err != nil {
|
|
logrus.Errorf("Getfilecon %s err", hostRelabelBin)
|
|
return nil
|
|
}
|
|
con := utils.NewContext(attr)
|
|
con.SetType("init_exec_t")
|
|
selinux.Setfilecon(hostRelabelBin, con.Get())
|
|
logrus.Infof("%s [%s]", hostRelabelBin, con.Get())
|
|
|
|
if utils.IsExist(s.Root + autoRelabel) {
|
|
if err := ioutil.WriteFile(filepath.Join(s.Root, autoRelabelInContainer), []byte(""), 0600); err != nil {
|
|
logrus.Errorf("WriteFile err: %v", err)
|
|
}
|
|
if err := syscall.Unlink(filepath.Join(s.Root, autoRelabel)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func preStartClone(s *configs.HookState) error {
|
|
parent, child, err := utils.NewPipe()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
b, err := json.Marshal(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
env := os.Environ()
|
|
env = append(env, fmt.Sprintf("%s=3", nsexec.InitPipe))
|
|
cmd := &exec.Cmd{
|
|
Path: "/proc/self/exe",
|
|
Args: []string{relabelRexec},
|
|
ExtraFiles: []*os.File{child},
|
|
Env: env,
|
|
Stdin: bytes.NewReader(b),
|
|
Stdout: os.Stdout,
|
|
Stderr: os.Stderr,
|
|
}
|
|
|
|
if err := cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
|
|
namespaces := []string{
|
|
fmt.Sprintf("mnt:/proc/%d/ns/mnt", s.Pid),
|
|
}
|
|
r := nl.NewNetlinkRequest(int(libcontainer.InitMsg), 0)
|
|
r.AddData(&libcontainer.Bytemsg{
|
|
Type: libcontainer.NsPathsAttr,
|
|
Value: []byte(strings.Join(namespaces, ",")),
|
|
})
|
|
if _, err := io.Copy(parent, bytes.NewReader(r.Serialize())); err != nil {
|
|
return err
|
|
}
|
|
if err := cmd.Wait(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PrestartRelabel handles oci relabel for prestart state
|
|
func PrestartRelabel(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
if err := preStartClone(state); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PostStopRelabel handles oci relabel for post-stop state
|
|
func PostStopRelabel(state *configs.HookState, hookConfig *hconfig.ContainerHookConfig, spec *specs.Spec) error {
|
|
if utils.IsSystemdInit(state.Root) {
|
|
if err := syscall.Unlink(filepath.Join(state.Root + systemdServiceFile)); err != nil {
|
|
logrus.Errorf("syscall.Unlink state.Root err: %v", err)
|
|
}
|
|
} else {
|
|
if err := syscall.Unlink(filepath.Join(state.Root + upstartServiceFile)); err != nil {
|
|
logrus.Errorf("syscall.Unlink not state.Root err: %v", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|