taleintervenor dfaef9d393 syscontainer-tools: update license to Mulan PSL v2
reason: update license to Mulan PSL v2

Signed-off-by: taleintervenor <taleintervenor@aliyun.com>
2020-04-27 14:54:17 +08:00

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
}