github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/specgen/generate/security.go (about) 1 package generate 2 3 import ( 4 "strings" 5 6 "github.com/containers/common/libimage" 7 "github.com/containers/common/pkg/apparmor" 8 "github.com/containers/common/pkg/capabilities" 9 "github.com/containers/common/pkg/config" 10 cutil "github.com/containers/common/pkg/util" 11 "github.com/hanks177/podman/v4/libpod" 12 "github.com/hanks177/podman/v4/libpod/define" 13 "github.com/hanks177/podman/v4/pkg/specgen" 14 "github.com/hanks177/podman/v4/pkg/util" 15 "github.com/opencontainers/runtime-tools/generate" 16 "github.com/opencontainers/selinux/go-selinux/label" 17 "github.com/pkg/errors" 18 "github.com/sirupsen/logrus" 19 ) 20 21 // setLabelOpts sets the label options of the SecurityConfig according to the 22 // input. 23 func setLabelOpts(s *specgen.SpecGenerator, runtime *libpod.Runtime, pidConfig specgen.Namespace, ipcConfig specgen.Namespace) error { 24 if !runtime.EnableLabeling() || s.Privileged { 25 s.SelinuxOpts = label.DisableSecOpt() 26 return nil 27 } 28 29 var labelOpts []string 30 if pidConfig.IsHost() { 31 labelOpts = append(labelOpts, label.DisableSecOpt()...) 32 } else if pidConfig.IsContainer() { 33 ctr, err := runtime.LookupContainer(pidConfig.Value) 34 if err != nil { 35 return errors.Wrapf(err, "container %q not found", pidConfig.Value) 36 } 37 secopts, err := label.DupSecOpt(ctr.ProcessLabel()) 38 if err != nil { 39 return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) 40 } 41 labelOpts = append(labelOpts, secopts...) 42 } 43 44 if ipcConfig.IsHost() { 45 labelOpts = append(labelOpts, label.DisableSecOpt()...) 46 } else if ipcConfig.IsContainer() { 47 ctr, err := runtime.LookupContainer(ipcConfig.Value) 48 if err != nil { 49 return errors.Wrapf(err, "container %q not found", ipcConfig.Value) 50 } 51 secopts, err := label.DupSecOpt(ctr.ProcessLabel()) 52 if err != nil { 53 return errors.Wrapf(err, "failed to duplicate label %q ", ctr.ProcessLabel()) 54 } 55 labelOpts = append(labelOpts, secopts...) 56 } 57 58 s.SelinuxOpts = append(s.SelinuxOpts, labelOpts...) 59 return nil 60 } 61 62 func setupApparmor(s *specgen.SpecGenerator, rtc *config.Config, g *generate.Generator) error { 63 hasProfile := len(s.ApparmorProfile) > 0 64 if !apparmor.IsEnabled() { 65 if hasProfile && s.ApparmorProfile != "unconfined" { 66 return errors.Errorf("Apparmor profile %q specified, but Apparmor is not enabled on this system", s.ApparmorProfile) 67 } 68 return nil 69 } 70 // If privileged and caller did not specify apparmor profiles return 71 if s.Privileged && !hasProfile { 72 return nil 73 } 74 if !hasProfile { 75 s.ApparmorProfile = rtc.Containers.ApparmorProfile 76 } 77 if len(s.ApparmorProfile) > 0 { 78 g.SetProcessApparmorProfile(s.ApparmorProfile) 79 } 80 81 return nil 82 } 83 84 func securityConfigureGenerator(s *specgen.SpecGenerator, g *generate.Generator, newImage *libimage.Image, rtc *config.Config) error { 85 var ( 86 caplist []string 87 err error 88 ) 89 // HANDLE CAPABILITIES 90 // NOTE: Must happen before SECCOMP 91 if s.Privileged { 92 g.SetupPrivileged(true) 93 caplist, err = capabilities.BoundingSet() 94 if err != nil { 95 return err 96 } 97 } else { 98 mergedCaps, err := capabilities.MergeCapabilities(rtc.Containers.DefaultCapabilities, s.CapAdd, s.CapDrop) 99 if err != nil { 100 return err 101 } 102 boundingSet, err := capabilities.BoundingSet() 103 if err != nil { 104 return err 105 } 106 boundingCaps := make(map[string]interface{}) 107 for _, b := range boundingSet { 108 boundingCaps[b] = b 109 } 110 for _, c := range mergedCaps { 111 if _, ok := boundingCaps[c]; ok { 112 caplist = append(caplist, c) 113 } 114 } 115 116 privCapsRequired := []string{} 117 118 // If the container image specifies an label with a 119 // capabilities.ContainerImageLabel then split the comma separated list 120 // of capabilities and record them. This list indicates the only 121 // capabilities, required to run the container. 122 var capsRequiredRequested []string 123 for key, val := range s.Labels { 124 if cutil.StringInSlice(key, capabilities.ContainerImageLabels) { 125 capsRequiredRequested = strings.Split(val, ",") 126 } 127 } 128 if !s.Privileged && len(capsRequiredRequested) > 0 { 129 // Pass capRequiredRequested in CapAdd field to normalize capabilities names 130 capsRequired, err := capabilities.MergeCapabilities(nil, capsRequiredRequested, nil) 131 if err != nil { 132 return errors.Wrapf(err, "capabilities requested by user or image are not valid: %q", strings.Join(capsRequired, ",")) 133 } 134 // Verify all capRequired are in the capList 135 for _, cap := range capsRequired { 136 if !cutil.StringInSlice(cap, caplist) { 137 privCapsRequired = append(privCapsRequired, cap) 138 } 139 } 140 if len(privCapsRequired) == 0 { 141 caplist = capsRequired 142 } else { 143 logrus.Errorf("Capabilities requested by user or image are not allowed by default: %q", strings.Join(privCapsRequired, ",")) 144 } 145 } 146 } 147 148 configSpec := g.Config 149 configSpec.Process.Capabilities.Ambient = []string{} 150 151 // Always unset the inheritable capabilities similarly to what the Linux kernel does 152 // They are used only when using capabilities with uid != 0. 153 configSpec.Process.Capabilities.Inheritable = []string{} 154 configSpec.Process.Capabilities.Bounding = caplist 155 156 user := strings.Split(s.User, ":")[0] 157 158 if (user == "" && s.UserNS.NSMode != specgen.KeepID) || user == "root" || user == "0" { 159 configSpec.Process.Capabilities.Effective = caplist 160 configSpec.Process.Capabilities.Permitted = caplist 161 } else { 162 mergedCaps, err := capabilities.MergeCapabilities(nil, s.CapAdd, nil) 163 if err != nil { 164 return errors.Wrapf(err, "capabilities requested by user are not valid: %q", strings.Join(s.CapAdd, ",")) 165 } 166 boundingSet, err := capabilities.BoundingSet() 167 if err != nil { 168 return err 169 } 170 boundingCaps := make(map[string]interface{}) 171 for _, b := range boundingSet { 172 boundingCaps[b] = b 173 } 174 var userCaps []string 175 for _, c := range mergedCaps { 176 if _, ok := boundingCaps[c]; ok { 177 userCaps = append(userCaps, c) 178 } 179 } 180 configSpec.Process.Capabilities.Effective = userCaps 181 configSpec.Process.Capabilities.Permitted = userCaps 182 183 // Ambient capabilities were added to Linux 4.3. Set ambient 184 // capabilities only when the kernel supports them. 185 if supportAmbientCapabilities() { 186 configSpec.Process.Capabilities.Ambient = userCaps 187 configSpec.Process.Capabilities.Inheritable = userCaps 188 } 189 } 190 191 g.SetProcessNoNewPrivileges(s.NoNewPrivileges) 192 193 if err := setupApparmor(s, rtc, g); err != nil { 194 return err 195 } 196 197 // HANDLE SECCOMP 198 if s.SeccompProfilePath != "unconfined" { 199 seccompConfig, err := getSeccompConfig(s, configSpec, newImage) 200 if err != nil { 201 return err 202 } 203 configSpec.Linux.Seccomp = seccompConfig 204 } 205 206 // Clear default Seccomp profile from Generator for unconfined containers 207 // and privileged containers which do not specify a seccomp profile. 208 if s.SeccompProfilePath == "unconfined" || (s.Privileged && (s.SeccompProfilePath == "" || s.SeccompProfilePath == config.SeccompOverridePath || s.SeccompProfilePath == config.SeccompDefaultPath)) { 209 configSpec.Linux.Seccomp = nil 210 } 211 212 g.SetRootReadonly(s.ReadOnlyFilesystem) 213 214 noUseIPC := s.IpcNS.NSMode == specgen.FromContainer || s.IpcNS.NSMode == specgen.FromPod || s.IpcNS.NSMode == specgen.Host 215 noUseNet := s.NetNS.NSMode == specgen.FromContainer || s.NetNS.NSMode == specgen.FromPod || s.NetNS.NSMode == specgen.Host 216 noUseUTS := s.UtsNS.NSMode == specgen.FromContainer || s.UtsNS.NSMode == specgen.FromPod || s.UtsNS.NSMode == specgen.Host 217 218 // Add default sysctls 219 defaultSysctls, err := util.ValidateSysctls(rtc.Sysctls()) 220 if err != nil { 221 return err 222 } 223 for sysctlKey, sysctlVal := range defaultSysctls { 224 // Ignore mqueue sysctls if --ipc=host 225 if noUseIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { 226 logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace set to %q", sysctlKey, sysctlVal, s.IpcNS.NSMode) 227 228 continue 229 } 230 231 // Ignore net sysctls if --net=host 232 if noUseNet && strings.HasPrefix(sysctlKey, "net.") { 233 logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace set to host", sysctlKey, sysctlVal) 234 continue 235 } 236 237 // Ignore uts sysctls if --uts=host 238 if noUseUTS && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { 239 logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace set to host", sysctlKey, sysctlVal) 240 continue 241 } 242 243 g.AddLinuxSysctl(sysctlKey, sysctlVal) 244 } 245 246 for sysctlKey, sysctlVal := range s.Sysctl { 247 if s.IpcNS.IsHost() && strings.HasPrefix(sysctlKey, "fs.mqueue.") { 248 return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since IPC Namespace set to host", sysctlKey, sysctlVal) 249 } 250 251 // Ignore net sysctls if --net=host 252 if s.NetNS.IsHost() && strings.HasPrefix(sysctlKey, "net.") { 253 return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since Network Namespace set to host", sysctlKey, sysctlVal) 254 } 255 256 // Ignore uts sysctls if --uts=host 257 if s.UtsNS.IsHost() && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { 258 return errors.Wrapf(define.ErrInvalidArg, "sysctl %s=%s can't be set since UTS Namespace set to host", sysctlKey, sysctlVal) 259 } 260 261 g.AddLinuxSysctl(sysctlKey, sysctlVal) 262 } 263 264 return nil 265 }