1 package helpers
2
3 import (
4 "encoding/json"
5 "fmt"
6 "log"
7 "os"
8 "strconv"
9 "strings"
10
11 "github.com/google/go-containerregistry/pkg/authn"
12 "github.com/google/go-containerregistry/pkg/name"
13 v1 "github.com/google/go-containerregistry/pkg/v1"
14 "github.com/google/go-containerregistry/pkg/v1/remote"
15 "github.com/opencontainers/runtime-spec/specs-go"
16
17 "github.com/Microsoft/hcsshim/ext4/tar2ext4"
18 sp "github.com/Microsoft/hcsshim/pkg/securitypolicy"
19 )
20
21
22
23 func RemoteImageFromImageName(imageName string, opts ...remote.Option) (v1.Image, error) {
24 ref, err := name.ParseReference(imageName)
25 if err != nil {
26 return nil, err
27 }
28
29 return remote.Image(ref, opts...)
30 }
31
32
33
34 func ComputeLayerHashes(img v1.Image) ([]string, error) {
35 imgLayers, err := img.Layers()
36 if err != nil {
37 return nil, err
38 }
39
40 var layerHashes []string
41
42 for _, layer := range imgLayers {
43 r, err := layer.Uncompressed()
44 if err != nil {
45 return nil, err
46 }
47
48 hashString, err := tar2ext4.ConvertAndComputeRootDigest(r)
49 if err != nil {
50 return nil, err
51 }
52 layerHashes = append(layerHashes, hashString)
53 }
54 return layerHashes, nil
55 }
56
57
58
59
60 func ParseEnvFromImage(img v1.Image) ([]string, error) {
61 imgConfig, err := img.ConfigFile()
62 if err != nil {
63 return nil, err
64 }
65
66 return imgConfig.Config.Env, nil
67 }
68
69
70
71
72 func DefaultContainerConfigs() []sp.ContainerConfig {
73 pause := sp.ContainerConfig{
74 ImageName: "k8s.gcr.io/pause:3.1",
75 Command: []string{"/pause"},
76 }
77 return []sp.ContainerConfig{pause}
78 }
79
80
81
82 func ParseWorkingDirFromImage(img v1.Image) (string, error) {
83 imgConfig, err := img.ConfigFile()
84 if err != nil {
85 return "", err
86 }
87
88 if imgConfig.Config.WorkingDir != "" {
89 return imgConfig.Config.WorkingDir, nil
90 }
91 return "/", nil
92 }
93
94
95
96 func ParseCommandFromImage(img v1.Image) ([]string, error) {
97 imgConfig, err := img.ConfigFile()
98 if err != nil {
99 return nil, err
100 }
101
102 cmdArgs := imgConfig.Config.Entrypoint
103 cmdArgs = append(cmdArgs, imgConfig.Config.Cmd...)
104 return cmdArgs, nil
105 }
106
107
108 func ParseUserFromImage(img v1.Image) (sp.IDNameConfig, sp.IDNameConfig, error) {
109 imgConfig, err := img.ConfigFile()
110 var user sp.IDNameConfig
111 var group sp.IDNameConfig
112 if err != nil {
113 return user, group, err
114 }
115
116 userString := imgConfig.Config.User
117
118
119
120
121
122 if userString == "" {
123
124 user.Strategy = sp.IDNameStrategyAny
125 group.Strategy = sp.IDNameStrategyAny
126 } else {
127 parts := strings.Split(userString, ":")
128 if len(parts) == 1 {
129
130 group.Strategy = sp.IDNameStrategyAny
131 user.Rule = parts[0]
132 _, err := strconv.ParseUint(parts[0], 10, 32)
133 if err == nil {
134 user.Strategy = sp.IDNameStrategyID
135 } else {
136 user.Strategy = sp.IDNameStrategyName
137 }
138 } else if len(parts) == 2 {
139 _, err := strconv.ParseUint(parts[0], 10, 32)
140 user.Rule = parts[0]
141 if err == nil {
142 user.Strategy = sp.IDNameStrategyID
143 } else {
144 user.Strategy = sp.IDNameStrategyName
145 }
146
147 _, err = strconv.ParseUint(parts[1], 10, 32)
148 group.Rule = parts[1]
149 if err == nil {
150 group.Strategy = sp.IDNameStrategyID
151 } else {
152 group.Strategy = sp.IDNameStrategyName
153 }
154 }
155 }
156
157 return user, group, nil
158 }
159
160
161
162 func PolicyContainersFromConfigs(containerConfigs []sp.ContainerConfig) ([]*sp.Container, error) {
163 var policyContainers []*sp.Container
164 for _, containerConfig := range containerConfigs {
165 var imageOptions []remote.Option
166
167 if containerConfig.Auth.Username != "" && containerConfig.Auth.Password != "" {
168 auth := authn.Basic{
169 Username: containerConfig.Auth.Username,
170 Password: containerConfig.Auth.Password}
171 c, _ := auth.Authorization()
172 authOption := remote.WithAuth(authn.FromConfig(*c))
173 imageOptions = append(imageOptions, authOption)
174 }
175
176 img, err := RemoteImageFromImageName(containerConfig.ImageName, imageOptions...)
177 if err != nil {
178 return nil, fmt.Errorf("unable to fetch image: %w", err)
179 }
180
181 layerHashes, err := ComputeLayerHashes(img)
182 if err != nil {
183 return nil, err
184 }
185
186 commandArgs := containerConfig.Command
187 if len(commandArgs) == 0 {
188 commandArgs, err = ParseCommandFromImage(img)
189 if err != nil {
190 return nil, err
191 }
192 }
193
194
195 envVars, err := ParseEnvFromImage(img)
196 if err != nil {
197 return nil, err
198 }
199
200
201
202 envRules := sp.NewEnvVarRules(envVars, true)
203
204
205
206 envRules = append(envRules, sp.EnvRuleConfig{
207 Rule: "TERM=xterm",
208 Strategy: sp.EnvVarRuleString,
209 Required: false,
210 })
211
212 envRules = append(envRules, containerConfig.EnvRules...)
213
214 workingDir, err := ParseWorkingDirFromImage(img)
215 if err != nil {
216 return nil, err
217 }
218
219 if containerConfig.WorkingDir != "" {
220 workingDir = containerConfig.WorkingDir
221 }
222
223 user, group, err := ParseUserFromImage(img)
224 if err != nil {
225 return nil, err
226 }
227
228 container, err := sp.CreateContainerPolicy(
229 commandArgs,
230 layerHashes,
231 envRules,
232 workingDir,
233 containerConfig.Mounts,
234 containerConfig.AllowElevated,
235 containerConfig.ExecProcesses,
236 containerConfig.Signals,
237 containerConfig.AllowStdioAccess,
238 !containerConfig.AllowPrivilegeEscalation,
239 setDefaultUser(containerConfig.User, user, group),
240 setDefaultCapabilities(containerConfig.Capabilities),
241 setDefaultSeccomp(containerConfig.SeccompProfilePath),
242 )
243 if err != nil {
244 return nil, err
245 }
246 policyContainers = append(policyContainers, container)
247 }
248
249 return policyContainers, nil
250 }
251
252 func setDefaultUser(config *sp.UserConfig, user, group sp.IDNameConfig) sp.UserConfig {
253 if config != nil {
254 return *config
255 }
256
257
258 return sp.UserConfig{
259 UserIDName: user,
260 GroupIDNames: []sp.IDNameConfig{group},
261 Umask: "0022",
262 }
263 }
264
265 func setDefaultCapabilities(config *sp.CapabilitiesConfig) *sp.CapabilitiesConfig {
266 if config != nil {
267
268
269
270
271 if config.Bounding == nil {
272 config.Bounding = make([]string, 0)
273 }
274 if config.Effective == nil {
275 config.Effective = make([]string, 0)
276 }
277 if config.Inheritable == nil {
278 config.Inheritable = make([]string, 0)
279 }
280 if config.Permitted == nil {
281 config.Permitted = make([]string, 0)
282 }
283 if config.Ambient == nil {
284 config.Ambient = make([]string, 0)
285 }
286 }
287
288 return config
289 }
290
291 func setDefaultSeccomp(seccompProfilePath string) string {
292 if len(seccompProfilePath) == 0 {
293 return ""
294 }
295
296 var seccomp specs.LinuxSeccomp
297
298 buff, err := os.ReadFile(seccompProfilePath)
299 if err != nil {
300 log.Fatalf("unable to read seccomp profile: %v", err)
301 }
302
303 err = json.Unmarshal(buff, &seccomp)
304 if err != nil {
305 log.Fatalf("unable to parse seccomp profile: %v", err)
306 }
307
308 profileSHA256, err := sp.MeasureSeccompProfile(&seccomp)
309 if err != nil {
310 log.Fatalf("unable to measure seccomp profile: %v", err)
311 }
312
313 return profileSHA256
314 }
315
View as plain text