1+ /*
2+ The MIT License (MIT)
3+ Copyright (c) 2018 Jessica Frazelle
4+ Permission is hereby granted, free of charge, to any person obtaining a copy
5+ of this software and associated documentation files (the "Software"), to deal
6+ in the Software without restriction, including without limitation the rights
7+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ copies of the Software, and to permit persons to whom the Software is
9+ furnished to do so, subject to the following conditions:
10+ The above copyright notice and this permission notice shall be included in all
11+ copies or substantial portions of the Software.
12+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18+ SOFTWARE.
19+ */
20+
21+ /*
22+ The code for amicontained.go is forked from
23+ https://github.com/genuinetools/bpfd/blob/434b609b3d4a5aeb461109b1167b68e000b72f69/proc/proc.go
24+ The code was forked when the latest details are as "Latest commit 871fc34 on Sep 18, 2018"
25+ */
26+
27+ package containerruntime
28+
29+ import (
30+ "fmt"
31+ "io/ioutil"
32+ "os"
33+ "path/filepath"
34+ "regexp"
35+ "strings"
36+
37+ "github.com/syndtr/gocapability/capability"
38+ "golang.org/x/sys/unix"
39+ )
40+
41+ // ContainerRuntime is the type for the various container runtime strings.
42+ type ContainerRuntime string
43+
44+ // SeccompMode is the type for the various seccomp mode strings.
45+ type SeccompMode string
46+
47+ const (
48+ // RuntimeDocker is the string for the docker runtime.
49+ RuntimeDocker ContainerRuntime = "docker"
50+ // RuntimeRkt is the string for the rkt runtime.
51+ RuntimeRkt ContainerRuntime = "rkt"
52+ // RuntimeNspawn is the string for the systemd-nspawn runtime.
53+ RuntimeNspawn ContainerRuntime = "systemd-nspawn"
54+ // RuntimeLXC is the string for the lxc runtime.
55+ RuntimeLXC ContainerRuntime = "lxc"
56+ // RuntimeLXCLibvirt is the string for the lxc-libvirt runtime.
57+ RuntimeLXCLibvirt ContainerRuntime = "lxc-libvirt"
58+ // RuntimeOpenVZ is the string for the openvz runtime.
59+ RuntimeOpenVZ ContainerRuntime = "openvz"
60+ // RuntimeKubernetes is the string for the kubernetes runtime.
61+ RuntimeKubernetes ContainerRuntime = "kube"
62+ // RuntimeGarden is the string for the garden runtime.
63+ RuntimeGarden ContainerRuntime = "garden"
64+ // RuntimePodman is the string for the podman runtime.
65+ RuntimePodman ContainerRuntime = "podman"
66+ // RuntimeNotFound is the string for when no container runtime is found.
67+ RuntimeNotFound ContainerRuntime = "not-found"
68+
69+ // SeccompModeDisabled is equivalent to "0" in the /proc/{pid}/status file.
70+ SeccompModeDisabled SeccompMode = "disabled"
71+ // SeccompModeStrict is equivalent to "1" in the /proc/{pid}/status file.
72+ SeccompModeStrict SeccompMode = "strict"
73+ // SeccompModeFiltering is equivalent to "2" in the /proc/{pid}/status file.
74+ SeccompModeFiltering SeccompMode = "filtering"
75+
76+ apparmorUnconfined = "unconfined"
77+
78+ uint32Max = 4294967295
79+
80+ statusFileValue = ":(.*)"
81+ )
82+
83+ var (
84+ // ContainerRuntimes contains all the container runtimes.
85+ ContainerRuntimes = []ContainerRuntime {
86+ RuntimeDocker ,
87+ RuntimeRkt ,
88+ RuntimeNspawn ,
89+ RuntimeLXC ,
90+ RuntimeLXCLibvirt ,
91+ RuntimeOpenVZ ,
92+ RuntimeKubernetes ,
93+ RuntimeGarden ,
94+ RuntimePodman ,
95+ }
96+
97+ seccompModes = map [string ]SeccompMode {
98+ "0" : SeccompModeDisabled ,
99+ "1" : SeccompModeStrict ,
100+ "2" : SeccompModeFiltering ,
101+ }
102+
103+ statusFileValueRegex = regexp .MustCompile (statusFileValue )
104+ )
105+
106+ // GetContainerRuntime returns the container runtime the process is running in.
107+ // If pid is less than one, it returns the runtime for "self".
108+ func GetContainerRuntime (tgid , pid int ) ContainerRuntime {
109+ file := "/proc/self/cgroup"
110+ if pid > 0 {
111+ if tgid > 0 {
112+ file = fmt .Sprintf ("/proc/%d/task/%d/cgroup" , tgid , pid )
113+ } else {
114+ file = fmt .Sprintf ("/proc/%d/cgroup" , pid )
115+ }
116+ }
117+
118+ // read the cgroups file
119+ a := readFileString (file )
120+ runtime := getContainerRuntime (a )
121+ if runtime != RuntimeNotFound {
122+ return runtime
123+ }
124+
125+ // /proc/vz exists in container and outside of the container, /proc/bc only outside of the container.
126+ if fileExists ("/proc/vz" ) && ! fileExists ("/proc/bc" ) {
127+ return RuntimeOpenVZ
128+ }
129+
130+ a = os .Getenv ("container" )
131+ runtime = getContainerRuntime (a )
132+ if runtime != RuntimeNotFound {
133+ return runtime
134+ }
135+
136+ // PID 1 might have dropped this information into a file in /run.
137+ // Read from /run/systemd/container since it is better than accessing /proc/1/environ,
138+ // which needs CAP_SYS_PTRACE
139+ a = readFileString ("/run/systemd/container" )
140+ runtime = getContainerRuntime (a )
141+ if runtime != RuntimeNotFound {
142+ return runtime
143+ }
144+
145+ return RuntimeNotFound
146+ }
147+
148+ func getContainerRuntime (input string ) ContainerRuntime {
149+ if len (strings .TrimSpace (input )) < 1 {
150+ return RuntimeNotFound
151+ }
152+
153+ for _ , runtime := range ContainerRuntimes {
154+ if strings .Contains (input , string (runtime )) {
155+ return runtime
156+ }
157+ }
158+
159+ return RuntimeNotFound
160+ }
161+
162+ func fileExists (file string ) bool {
163+ if _ , err := os .Stat (file ); ! os .IsNotExist (err ) {
164+ return true
165+ }
166+ return false
167+ }
168+
169+ func readFile (file string ) []byte {
170+ if ! fileExists (file ) {
171+ return nil
172+ }
173+ // filepath.clean was added to resolve the gosec build failure
174+ // with error "Potential file inclusion via variable"
175+ b , err := ioutil .ReadFile (filepath .Clean (file ))
176+ if err != nil {
177+ return nil
178+ }
179+ return b
180+ }
181+
182+ // GetCapabilities returns the allowed capabilities for the process.
183+ // If pid is less than one, it returns the capabilities for "self".
184+ func GetCapabilities (pid int ) (map [string ][]string , error ) {
185+ allCaps := capability .List ()
186+
187+ caps , err := capability .NewPid (pid )
188+ if err != nil {
189+ return nil , err
190+ }
191+
192+ allowedCaps := map [string ][]string {}
193+ allowedCaps ["EFFECTIVE | PERMITTED | INHERITABLE" ] = []string {}
194+ allowedCaps ["BOUNDING" ] = []string {}
195+ allowedCaps ["AMBIENT" ] = []string {}
196+
197+ for _ , cap := range allCaps {
198+ if caps .Get (capability .CAPS , cap ) {
199+ allowedCaps ["EFFECTIVE | PERMITTED | INHERITABLE" ] = append (allowedCaps ["EFFECTIVE | PERMITTED | INHERITABLE" ], cap .String ())
200+ }
201+ if caps .Get (capability .BOUNDING , cap ) {
202+ allowedCaps ["BOUNDING" ] = append (allowedCaps ["BOUNDING" ], cap .String ())
203+ }
204+ if caps .Get (capability .AMBIENT , cap ) {
205+ allowedCaps ["AMBIENT" ] = append (allowedCaps ["AMBIENT" ], cap .String ())
206+ }
207+ }
208+
209+ return allowedCaps , nil
210+ }
211+
212+ // GetSeccompEnforcingMode returns the seccomp enforcing level (disabled, filtering, strict)
213+ // for a process.
214+ // If pid is less than one, it returns the seccomp enforcing mode for "self".
215+ func GetSeccompEnforcingMode (pid int ) SeccompMode {
216+ file := "/proc/self/status"
217+ if pid > 0 {
218+ file = fmt .Sprintf ("/proc/%d/status" , pid )
219+ }
220+
221+ return getSeccompEnforcingMode (readFileString (file ))
222+ }
223+
224+ func getSeccompEnforcingMode (input string ) SeccompMode {
225+ mode := getStatusEntry (input , "Seccomp:" )
226+ sm , ok := seccompModes [mode ]
227+ if ok {
228+ return sm
229+ }
230+
231+ // Pre linux 3.8, check if Seccomp is supported, via CONFIG_SECCOMP.
232+ if err := unix .Prctl (unix .PR_GET_SECCOMP , 0 , 0 , 0 , 0 ); err != unix .EINVAL {
233+ // Make sure the kernel has CONFIG_SECCOMP_FILTER.
234+ if err := unix .Prctl (unix .PR_SET_SECCOMP , unix .SECCOMP_MODE_FILTER , 0 , 0 , 0 ); err != unix .EINVAL {
235+ return SeccompModeStrict
236+ }
237+ }
238+
239+ return SeccompModeDisabled
240+ }
241+
242+ // TODO: make this function more efficient and read the file line by line.
243+ func getStatusEntry (input , find string ) string {
244+ // Split status file string by line
245+ statusMappings := strings .Split (input , "\n " )
246+ statusMappings = deleteEmpty (statusMappings )
247+
248+ for _ , line := range statusMappings {
249+ if strings .Contains (line , find ) {
250+ matches := statusFileValueRegex .FindStringSubmatch (line )
251+ if len (matches ) > 1 {
252+ return strings .TrimSpace (matches [1 ])
253+ }
254+ }
255+ }
256+
257+ return ""
258+ }
259+
260+ func deleteEmpty (s []string ) []string {
261+ var r []string
262+ for _ , str := range s {
263+ if strings .TrimSpace (str ) != "" {
264+ r = append (r , strings .TrimSpace (str ))
265+ }
266+ }
267+ return r
268+ }
269+
270+ func readFileString (file string ) string {
271+ b := readFile (file )
272+ if b == nil {
273+ return ""
274+ }
275+ return strings .TrimSpace (string (b ))
276+ }
0 commit comments