mirror of https://github.com/containers/podman.git
apparmor: apply default profile at container initialization
Apply the default AppArmor profile at container initialization to cover all possible code paths (i.e., podman-{start,run}) before executing the runtime. This allows moving most of the logic into pkg/apparmor. Also make the loading and application of the default AppArmor profile versio-indepenent by checking for the `libpod-default-` prefix and over-writing the profile in the run-time spec if needed. The intitial run-time spec of the container differs a bit from the applied one when having started the container, which results in displaying a potentially outdated AppArmor profile when inspecting a container. To fix that, load the container config from the file system if present and use it to display the data. Fixes: #2107 Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
parent
c37f731596
commit
edb285d176
|
@ -15,13 +15,11 @@ import (
|
|||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
ann "github.com/containers/libpod/pkg/annotations"
|
||||
"github.com/containers/libpod/pkg/apparmor"
|
||||
"github.com/containers/libpod/pkg/inspect"
|
||||
ns "github.com/containers/libpod/pkg/namespaces"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
cc "github.com/containers/libpod/pkg/spec"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
libpodVersion "github.com/containers/libpod/version"
|
||||
"github.com/docker/docker/pkg/signal"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/docker/go-units"
|
||||
|
@ -162,83 +160,9 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container
|
|||
return ctr, createConfig, nil
|
||||
}
|
||||
|
||||
// Checks if a user-specified AppArmor profile is loaded, or loads the default profile if
|
||||
// AppArmor is enabled.
|
||||
// Any interaction with AppArmor requires root permissions.
|
||||
func loadAppArmor(config *cc.CreateConfig) error {
|
||||
if rootless.IsRootless() {
|
||||
noAAMsg := "AppArmor security is not available in rootless mode"
|
||||
switch config.ApparmorProfile {
|
||||
case "":
|
||||
logrus.Warn(noAAMsg)
|
||||
case "unconfined":
|
||||
default:
|
||||
return fmt.Errorf(noAAMsg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if config.ApparmorProfile == "" && apparmor.IsEnabled() {
|
||||
// Unless specified otherwise, make sure that the default AppArmor
|
||||
// profile is installed. To avoid redundantly loading the profile
|
||||
// on each invocation, check if it's loaded before installing it.
|
||||
// Suffix the profile with the current libpod version to allow
|
||||
// loading the new, potentially updated profile after an update.
|
||||
profile := fmt.Sprintf("%s-%s", apparmor.DefaultLibpodProfile, libpodVersion.Version)
|
||||
|
||||
loadProfile := func() error {
|
||||
isLoaded, err := apparmor.IsLoaded(profile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isLoaded {
|
||||
err = apparmor.InstallDefault(profile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := loadProfile(); err != nil {
|
||||
switch err {
|
||||
case apparmor.ErrApparmorUnsupported:
|
||||
// do not set the profile when AppArmor isn't supported
|
||||
logrus.Debugf("AppArmor is not supported: setting empty profile")
|
||||
default:
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
logrus.Infof("Sucessfully loaded AppAmor profile '%s'", profile)
|
||||
config.ApparmorProfile = profile
|
||||
}
|
||||
} else if config.ApparmorProfile != "" && config.ApparmorProfile != "unconfined" {
|
||||
if !apparmor.IsEnabled() {
|
||||
return fmt.Errorf("Profile specified but AppArmor is disabled on the host")
|
||||
}
|
||||
|
||||
isLoaded, err := apparmor.IsLoaded(config.ApparmorProfile)
|
||||
if err != nil {
|
||||
switch err {
|
||||
case apparmor.ErrApparmorUnsupported:
|
||||
return fmt.Errorf("Profile specified but AppArmor is not supported")
|
||||
default:
|
||||
return fmt.Errorf("Error checking if AppArmor profile is loaded: %v", err)
|
||||
}
|
||||
}
|
||||
if !isLoaded {
|
||||
return fmt.Errorf("The specified AppArmor profile '%s' is not loaded", config.ApparmorProfile)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
|
||||
var (
|
||||
labelOpts []string
|
||||
err error
|
||||
)
|
||||
|
||||
if config.PidMode.IsHost() {
|
||||
|
@ -283,10 +207,6 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
|
|||
}
|
||||
}
|
||||
|
||||
if err := loadAppArmor(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if config.SeccompProfilePath == "" {
|
||||
if _, err := os.Stat(libpod.SeccompOverridePath); err == nil {
|
||||
config.SeccompProfilePath = libpod.SeccompOverridePath
|
||||
|
@ -304,7 +224,7 @@ func parseSecurityOpt(config *cc.CreateConfig, securityOpts []string) error {
|
|||
}
|
||||
}
|
||||
config.LabelOpts = labelOpts
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// isPortInPortBindings determines if an exposed host port is in user
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package libpod
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -407,6 +409,30 @@ func (c *Container) Spec() *spec.Spec {
|
|||
return returnSpec
|
||||
}
|
||||
|
||||
// specFromState returns the unmarshalled json config of the container. If the
|
||||
// config does not exist (e.g., because the container was never started) return
|
||||
// the spec from the config.
|
||||
func (c *Container) specFromState() (*spec.Spec, error) {
|
||||
spec := c.config.Spec
|
||||
|
||||
if f, err := os.Open(c.state.ConfigPath); err == nil {
|
||||
content, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error reading container config")
|
||||
}
|
||||
if err := json.Unmarshal([]byte(content), &spec); err != nil {
|
||||
return nil, errors.Wrapf(err, "error unmarshalling container config")
|
||||
}
|
||||
} else {
|
||||
// ignore when the file does not exist
|
||||
if !os.IsNotExist(err) {
|
||||
return nil, errors.Wrapf(err, "error opening container config")
|
||||
}
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// ID returns the container's ID
|
||||
func (c *Container) ID() string {
|
||||
return c.config.ID
|
||||
|
|
|
@ -12,7 +12,10 @@ import (
|
|||
func (c *Container) getContainerInspectData(size bool, driverData *inspect.Data) (*inspect.ContainerInspectData, error) {
|
||||
config := c.config
|
||||
runtimeInfo := c.state
|
||||
spec := c.config.Spec
|
||||
spec, err := c.specFromState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Process is allowed to be nil in the spec
|
||||
args := []string{}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
cnitypes "github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
crioAnnotations "github.com/containers/libpod/pkg/annotations"
|
||||
"github.com/containers/libpod/pkg/apparmor"
|
||||
"github.com/containers/libpod/pkg/criu"
|
||||
"github.com/containers/libpod/pkg/lookup"
|
||||
"github.com/containers/libpod/pkg/resolvconf"
|
||||
|
@ -185,6 +186,13 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Apply AppArmor checks and load the default profile if needed.
|
||||
updatedProfile, err := apparmor.CheckProfileAndLoadDefault(c.config.Spec.Process.ApparmorProfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.SetProcessApparmorProfile(updatedProfile)
|
||||
|
||||
if err := c.makeBindMounts(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -2,11 +2,16 @@ package apparmor
|
|||
|
||||
import (
|
||||
"errors"
|
||||
libpodVersion "github.com/containers/libpod/version"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultLipodProfilePrefix is used for version-independent presence checks.
|
||||
DefaultLipodProfilePrefix = "libpod-default" + "-"
|
||||
// DefaultLibpodProfile is the name of default libpod AppArmor profile.
|
||||
DefaultLibpodProfile = "libpod-default"
|
||||
DefaultLibpodProfile = DefaultLipodProfilePrefix + libpodVersion.Version
|
||||
// ErrApparmorUnsupported indicates that AppArmor support is not supported.
|
||||
ErrApparmorUnsupported = errors.New("AppArmor is not supported")
|
||||
// ErrApparmorRootless indicates that AppArmor support is not supported in rootless mode.
|
||||
ErrApparmorRootless = errors.New("AppArmor is not supported in rootless mode")
|
||||
)
|
||||
|
|
|
@ -13,7 +13,10 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
runcaa "github.com/opencontainers/runc/libcontainer/apparmor"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// profileDirectory is the file store for apparmor profiles and macros.
|
||||
|
@ -21,6 +24,9 @@ var profileDirectory = "/etc/apparmor.d"
|
|||
|
||||
// IsEnabled returns true if AppArmor is enabled on the host.
|
||||
func IsEnabled() bool {
|
||||
if rootless.IsRootless() {
|
||||
return false
|
||||
}
|
||||
return runcaa.IsEnabled()
|
||||
}
|
||||
|
||||
|
@ -71,6 +77,10 @@ func macroExists(m string) bool {
|
|||
// InstallDefault generates a default profile and loads it into the kernel
|
||||
// using 'apparmor_parser'.
|
||||
func InstallDefault(name string) error {
|
||||
if rootless.IsRootless() {
|
||||
return ErrApparmorRootless
|
||||
}
|
||||
|
||||
p := profileData{
|
||||
Name: name,
|
||||
}
|
||||
|
@ -97,6 +107,10 @@ func InstallDefault(name string) error {
|
|||
// IsLoaded checks if a profile with the given name has been loaded into the
|
||||
// kernel.
|
||||
func IsLoaded(name string) (bool, error) {
|
||||
if name != "" && rootless.IsRootless() {
|
||||
return false, errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name)
|
||||
}
|
||||
|
||||
file, err := os.Open("/sys/kernel/security/apparmor/profiles")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
|
@ -188,3 +202,55 @@ func parseAAParserVersion(output string) (int, error) {
|
|||
return numericVersion, nil
|
||||
|
||||
}
|
||||
|
||||
// CheckProfileAndLoadDefault checks if the specified profile is loaded and
|
||||
// loads the DefaultLibpodProfile if the specified on is prefixed by
|
||||
// DefaultLipodProfilePrefix. This allows to always load and apply the latest
|
||||
// default AppArmor profile. Note that AppArmor requires root. If it's a
|
||||
// default profile, return DefaultLipodProfilePrefix, otherwise the specified
|
||||
// one.
|
||||
func CheckProfileAndLoadDefault(name string) (string, error) {
|
||||
if name == "unconfined" {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
if name != "" && rootless.IsRootless() {
|
||||
return "", errors.Wrapf(ErrApparmorRootless, "cannot load AppArmor profile %q", name)
|
||||
}
|
||||
|
||||
if name != "" && !runcaa.IsEnabled() {
|
||||
return "", fmt.Errorf("profile %q specified but AppArmor is disabled on the host", name)
|
||||
}
|
||||
|
||||
// If the specified name is not empty or is not a default libpod one,
|
||||
// ignore it and return the name.
|
||||
if name != "" && !strings.HasPrefix(name, DefaultLipodProfilePrefix) {
|
||||
isLoaded, err := IsLoaded(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !isLoaded {
|
||||
return "", fmt.Errorf("AppArmor profile %q specified but not loaded")
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
name = DefaultLibpodProfile
|
||||
// To avoid expensive redundant loads on each invocation, check
|
||||
// if it's loaded before installing it.
|
||||
isLoaded, err := IsLoaded(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !isLoaded {
|
||||
err = InstallDefault(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
logrus.Infof("successfully loaded AppAmor profile %q", name)
|
||||
} else {
|
||||
logrus.Infof("AppAmor profile %q is already loaded", name)
|
||||
}
|
||||
|
||||
return name, nil
|
||||
}
|
||||
|
|
|
@ -2,19 +2,25 @@
|
|||
|
||||
package apparmor
|
||||
|
||||
// IsEnabled returns true if AppArmor is enabled on the host.
|
||||
// IsEnabled dummy.
|
||||
func IsEnabled() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// InstallDefault generates a default profile in a temp directory determined by
|
||||
// os.TempDir(), then loads the profile into the kernel using 'apparmor_parser'.
|
||||
// InstallDefault dummy.
|
||||
func InstallDefault(name string) error {
|
||||
return ErrApparmorUnsupported
|
||||
}
|
||||
|
||||
// IsLoaded checks if a profile with the given name has been loaded into the
|
||||
// kernel.
|
||||
// IsLoaded dummy.
|
||||
func IsLoaded(name string) (bool, error) {
|
||||
return false, ErrApparmorUnsupported
|
||||
}
|
||||
|
||||
// CheckProfileAndLoadDefault dummy.
|
||||
func CheckProfileAndLoadDefault(name string) (string, error) {
|
||||
if name == "" {
|
||||
return "", nil
|
||||
}
|
||||
return "", ErrApparmorUnsupported
|
||||
}
|
||||
|
|
|
@ -252,6 +252,7 @@ func CreateConfigToOCISpec(config *CreateConfig) (*spec.Spec, error) { //nolint
|
|||
}
|
||||
// SECURITY OPTS
|
||||
g.SetProcessNoNewPrivileges(config.NoNewPrivs)
|
||||
|
||||
g.SetProcessApparmorProfile(config.ApparmorProfile)
|
||||
|
||||
blockAccessToKernelFilesystems(config, &g)
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
package sysinfo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func findCgroupMountpoints() (map[string]string, error) {
|
||||
cgMounts, err := cgroups.GetCgroupMounts(false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse cgroup information: %v", err)
|
||||
}
|
||||
mps := make(map[string]string)
|
||||
for _, m := range cgMounts {
|
||||
for _, ss := range m.Subsystems {
|
||||
mps[ss] = m.Mountpoint
|
||||
}
|
||||
}
|
||||
return mps, nil
|
||||
}
|
||||
|
||||
// New returns a new SysInfo, using the filesystem to detect which features
|
||||
// the kernel supports. If `quiet` is `false` warnings are printed in logs
|
||||
// whenever an error occurs or misconfigurations are present.
|
||||
func New(quiet bool) *SysInfo {
|
||||
sysInfo := &SysInfo{}
|
||||
cgMounts, err := findCgroupMountpoints()
|
||||
if err != nil {
|
||||
logrus.Warnf("Failed to parse cgroup information: %v", err)
|
||||
} else {
|
||||
sysInfo.cgroupMemInfo = checkCgroupMem(cgMounts, quiet)
|
||||
sysInfo.cgroupCPUInfo = checkCgroupCPU(cgMounts, quiet)
|
||||
sysInfo.cgroupBlkioInfo = checkCgroupBlkioInfo(cgMounts, quiet)
|
||||
sysInfo.cgroupCpusetInfo = checkCgroupCpusetInfo(cgMounts, quiet)
|
||||
sysInfo.cgroupPids = checkCgroupPids(quiet)
|
||||
}
|
||||
|
||||
_, ok := cgMounts["devices"]
|
||||
sysInfo.CgroupDevicesEnabled = ok
|
||||
|
||||
sysInfo.IPv4ForwardingDisabled = !readProcBool("/proc/sys/net/ipv4/ip_forward")
|
||||
sysInfo.BridgeNFCallIPTablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-iptables")
|
||||
sysInfo.BridgeNFCallIP6TablesDisabled = !readProcBool("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
|
||||
|
||||
// Check if AppArmor is supported.
|
||||
if _, err := os.Stat("/sys/kernel/security/apparmor"); !os.IsNotExist(err) {
|
||||
sysInfo.AppArmor = true
|
||||
}
|
||||
|
||||
// Check if Seccomp is supported, via CONFIG_SECCOMP.
|
||||
if err := unix.Prctl(unix.PR_GET_SECCOMP, 0, 0, 0, 0); err != unix.EINVAL {
|
||||
// Make sure the kernel has CONFIG_SECCOMP_FILTER.
|
||||
if err := unix.Prctl(unix.PR_SET_SECCOMP, unix.SECCOMP_MODE_FILTER, 0, 0, 0); err != unix.EINVAL {
|
||||
sysInfo.Seccomp = true
|
||||
}
|
||||
}
|
||||
|
||||
return sysInfo
|
||||
}
|
||||
|
||||
// checkCgroupMem reads the memory information from the memory cgroup mount point.
|
||||
func checkCgroupMem(cgMounts map[string]string, quiet bool) cgroupMemInfo {
|
||||
mountPoint, ok := cgMounts["memory"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Your kernel does not support cgroup memory limit")
|
||||
}
|
||||
return cgroupMemInfo{}
|
||||
}
|
||||
|
||||
swapLimit := cgroupEnabled(mountPoint, "memory.memsw.limit_in_bytes")
|
||||
if !quiet && !swapLimit {
|
||||
logrus.Warn("Your kernel does not support swap memory limit")
|
||||
}
|
||||
memoryReservation := cgroupEnabled(mountPoint, "memory.soft_limit_in_bytes")
|
||||
if !quiet && !memoryReservation {
|
||||
logrus.Warn("Your kernel does not support memory reservation")
|
||||
}
|
||||
oomKillDisable := cgroupEnabled(mountPoint, "memory.oom_control")
|
||||
if !quiet && !oomKillDisable {
|
||||
logrus.Warn("Your kernel does not support oom control")
|
||||
}
|
||||
memorySwappiness := cgroupEnabled(mountPoint, "memory.swappiness")
|
||||
if !quiet && !memorySwappiness {
|
||||
logrus.Warn("Your kernel does not support memory swappiness")
|
||||
}
|
||||
kernelMemory := cgroupEnabled(mountPoint, "memory.kmem.limit_in_bytes")
|
||||
if !quiet && !kernelMemory {
|
||||
logrus.Warn("Your kernel does not support kernel memory limit")
|
||||
}
|
||||
|
||||
return cgroupMemInfo{
|
||||
MemoryLimit: true,
|
||||
SwapLimit: swapLimit,
|
||||
MemoryReservation: memoryReservation,
|
||||
OomKillDisable: oomKillDisable,
|
||||
MemorySwappiness: memorySwappiness,
|
||||
KernelMemory: kernelMemory,
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupCPU reads the cpu information from the cpu cgroup mount point.
|
||||
func checkCgroupCPU(cgMounts map[string]string, quiet bool) cgroupCPUInfo {
|
||||
mountPoint, ok := cgMounts["cpu"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Unable to find cpu cgroup in mounts")
|
||||
}
|
||||
return cgroupCPUInfo{}
|
||||
}
|
||||
|
||||
cpuShares := cgroupEnabled(mountPoint, "cpu.shares")
|
||||
if !quiet && !cpuShares {
|
||||
logrus.Warn("Your kernel does not support cgroup cpu shares")
|
||||
}
|
||||
|
||||
cpuCfsPeriod := cgroupEnabled(mountPoint, "cpu.cfs_period_us")
|
||||
if !quiet && !cpuCfsPeriod {
|
||||
logrus.Warn("Your kernel does not support cgroup cfs period")
|
||||
}
|
||||
|
||||
cpuCfsQuota := cgroupEnabled(mountPoint, "cpu.cfs_quota_us")
|
||||
if !quiet && !cpuCfsQuota {
|
||||
logrus.Warn("Your kernel does not support cgroup cfs quotas")
|
||||
}
|
||||
|
||||
cpuRealtimePeriod := cgroupEnabled(mountPoint, "cpu.rt_period_us")
|
||||
if !quiet && !cpuRealtimePeriod {
|
||||
logrus.Warn("Your kernel does not support cgroup rt period")
|
||||
}
|
||||
|
||||
cpuRealtimeRuntime := cgroupEnabled(mountPoint, "cpu.rt_runtime_us")
|
||||
if !quiet && !cpuRealtimeRuntime {
|
||||
logrus.Warn("Your kernel does not support cgroup rt runtime")
|
||||
}
|
||||
|
||||
return cgroupCPUInfo{
|
||||
CPUShares: cpuShares,
|
||||
CPUCfsPeriod: cpuCfsPeriod,
|
||||
CPUCfsQuota: cpuCfsQuota,
|
||||
CPURealtimePeriod: cpuRealtimePeriod,
|
||||
CPURealtimeRuntime: cpuRealtimeRuntime,
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupBlkioInfo reads the blkio information from the blkio cgroup mount point.
|
||||
func checkCgroupBlkioInfo(cgMounts map[string]string, quiet bool) cgroupBlkioInfo {
|
||||
mountPoint, ok := cgMounts["blkio"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Unable to find blkio cgroup in mounts")
|
||||
}
|
||||
return cgroupBlkioInfo{}
|
||||
}
|
||||
|
||||
weight := cgroupEnabled(mountPoint, "blkio.weight")
|
||||
if !quiet && !weight {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio weight")
|
||||
}
|
||||
|
||||
weightDevice := cgroupEnabled(mountPoint, "blkio.weight_device")
|
||||
if !quiet && !weightDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio weight_device")
|
||||
}
|
||||
|
||||
readBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_bps_device")
|
||||
if !quiet && !readBpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_bps_device")
|
||||
}
|
||||
|
||||
writeBpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_bps_device")
|
||||
if !quiet && !writeBpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_bps_device")
|
||||
}
|
||||
readIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.read_iops_device")
|
||||
if !quiet && !readIOpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.read_iops_device")
|
||||
}
|
||||
|
||||
writeIOpsDevice := cgroupEnabled(mountPoint, "blkio.throttle.write_iops_device")
|
||||
if !quiet && !writeIOpsDevice {
|
||||
logrus.Warn("Your kernel does not support cgroup blkio throttle.write_iops_device")
|
||||
}
|
||||
return cgroupBlkioInfo{
|
||||
BlkioWeight: weight,
|
||||
BlkioWeightDevice: weightDevice,
|
||||
BlkioReadBpsDevice: readBpsDevice,
|
||||
BlkioWriteBpsDevice: writeBpsDevice,
|
||||
BlkioReadIOpsDevice: readIOpsDevice,
|
||||
BlkioWriteIOpsDevice: writeIOpsDevice,
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupCpusetInfo reads the cpuset information from the cpuset cgroup mount point.
|
||||
func checkCgroupCpusetInfo(cgMounts map[string]string, quiet bool) cgroupCpusetInfo {
|
||||
mountPoint, ok := cgMounts["cpuset"]
|
||||
if !ok {
|
||||
if !quiet {
|
||||
logrus.Warn("Unable to find cpuset cgroup in mounts")
|
||||
}
|
||||
return cgroupCpusetInfo{}
|
||||
}
|
||||
|
||||
cpus, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.cpus"))
|
||||
if err != nil {
|
||||
return cgroupCpusetInfo{}
|
||||
}
|
||||
|
||||
mems, err := ioutil.ReadFile(path.Join(mountPoint, "cpuset.mems"))
|
||||
if err != nil {
|
||||
return cgroupCpusetInfo{}
|
||||
}
|
||||
|
||||
return cgroupCpusetInfo{
|
||||
Cpuset: true,
|
||||
Cpus: strings.TrimSpace(string(cpus)),
|
||||
Mems: strings.TrimSpace(string(mems)),
|
||||
}
|
||||
}
|
||||
|
||||
// checkCgroupPids reads the pids information from the pids cgroup mount point.
|
||||
func checkCgroupPids(quiet bool) cgroupPids {
|
||||
_, err := cgroups.FindCgroupMountpoint("pids")
|
||||
if err != nil {
|
||||
if !quiet {
|
||||
logrus.Warn(err)
|
||||
}
|
||||
return cgroupPids{}
|
||||
}
|
||||
|
||||
return cgroupPids{
|
||||
PidsLimit: true,
|
||||
}
|
||||
}
|
||||
|
||||
func cgroupEnabled(mountPoint, name string) bool {
|
||||
_, err := os.Stat(path.Join(mountPoint, name))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func readProcBool(path string) bool {
|
||||
val, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return strings.TrimSpace(string(val)) == "1"
|
||||
}
|
Loading…
Reference in New Issue