1 package securitypolicy
2
3
7
8 import (
9 _ "embed"
10 "encoding/json"
11 "fmt"
12 "strings"
13 "syscall"
14 )
15
16 type marshalFunc func(
17 allowAll bool,
18 containers []*Container,
19 externalProcesses []ExternalProcessConfig,
20 fragments []FragmentConfig,
21 allowPropertiesAccess bool,
22 allowDumpStacks bool,
23 allowRuntimeLogging bool,
24 allowEnvironmentVariableDropping bool,
25 allowUnencryptedScratch bool,
26 allowCapabilityDropping bool,
27 ) (string, error)
28
29 const (
30 jsonMarshaller = "json"
31 regoMarshaller = "rego"
32 )
33
34 var (
35 registeredMarshallers = map[string]marshalFunc{}
36 defaultMarshaller = jsonMarshaller
37 )
38
39 func init() {
40 registeredMarshallers[jsonMarshaller] = marshalJSON
41 registeredMarshallers[regoMarshaller] = marshalRego
42 }
43
44
45 var policyRegoTemplate string
46
47
48 var openDoorRegoTemplate string
49
50 var openDoorRego = strings.Replace(openDoorRegoTemplate, "@@API_VERSION@@", apiVersion, 1)
51
52 func marshalJSON(
53 allowAll bool,
54 containers []*Container,
55 _ []ExternalProcessConfig,
56 _ []FragmentConfig,
57 _ bool,
58 _ bool,
59 _ bool,
60 _ bool,
61 _ bool,
62 _ bool,
63 ) (string, error) {
64 var policy *SecurityPolicy
65 if allowAll {
66 if len(containers) > 0 {
67 return "", ErrInvalidOpenDoorPolicy
68 }
69
70 policy = NewOpenDoorPolicy()
71 } else {
72 policy = NewSecurityPolicy(allowAll, containers)
73 }
74
75 policyCode, err := json.Marshal(policy)
76 if err != nil {
77 return "", err
78 }
79
80 return string(policyCode), nil
81 }
82
83 func marshalRego(
84 allowAll bool,
85 containers []*Container,
86 externalProcesses []ExternalProcessConfig,
87 fragments []FragmentConfig,
88 allowPropertiesAccess bool,
89 allowDumpStacks bool,
90 allowRuntimeLogging bool,
91 allowEnvironmentVariableDropping bool,
92 allowUnencryptedScratch bool,
93 allowCapabilityDropping bool,
94 ) (string, error) {
95 if allowAll {
96 if len(containers) > 0 {
97 return "", ErrInvalidOpenDoorPolicy
98 }
99
100 return openDoorRego, nil
101 }
102
103 policy, err := newSecurityPolicyInternal(
104 containers,
105 externalProcesses,
106 fragments,
107 allowPropertiesAccess,
108 allowDumpStacks,
109 allowRuntimeLogging,
110 allowEnvironmentVariableDropping,
111 allowUnencryptedScratch,
112 allowCapabilityDropping,
113 )
114 if err != nil {
115 return "", err
116 }
117
118 return policy.marshalRego(), nil
119 }
120
121 func MarshalFragment(
122 namespace string,
123 svn string,
124 containers []*Container,
125 externalProcesses []ExternalProcessConfig,
126 fragments []FragmentConfig) (string, error) {
127 fragment, err := newSecurityPolicyFragment(namespace, svn, containers, externalProcesses, fragments)
128 if err != nil {
129 return "", err
130 }
131
132 return fragment.marshalRego(), nil
133 }
134
135 func MarshalPolicy(
136 marshaller string,
137 allowAll bool,
138 containers []*Container,
139 externalProcesses []ExternalProcessConfig,
140 fragments []FragmentConfig,
141 allowPropertiesAccess bool,
142 allowDumpStacks bool,
143 allowRuntimeLogging bool,
144 allowEnvironmentVariableDropping bool,
145 allowUnencryptedScratch bool,
146 allowCapbilitiesDropping bool,
147 ) (string, error) {
148 if marshaller == "" {
149 marshaller = defaultMarshaller
150 }
151
152 if marshal, ok := registeredMarshallers[marshaller]; !ok {
153 return "", fmt.Errorf("unknown marshaller: %q", marshaller)
154 } else {
155 return marshal(
156 allowAll,
157 containers,
158 externalProcesses,
159 fragments,
160 allowPropertiesAccess,
161 allowDumpStacks,
162 allowRuntimeLogging,
163 allowEnvironmentVariableDropping,
164 allowUnencryptedScratch,
165 allowCapbilitiesDropping,
166 )
167 }
168 }
169
170
171
172
173 func (c Containers) MarshalJSON() ([]byte, error) {
174 type Alias Containers
175 return json.Marshal(&struct {
176 Length int `json:"length"`
177 *Alias
178 }{
179 Length: len(c.Elements),
180 Alias: (*Alias)(&c),
181 })
182 }
183
184 func (e EnvRules) MarshalJSON() ([]byte, error) {
185 type Alias EnvRules
186 return json.Marshal(&struct {
187 Length int `json:"length"`
188 *Alias
189 }{
190 Length: len(e.Elements),
191 Alias: (*Alias)(&e),
192 })
193 }
194
195 func (s StringArrayMap) MarshalJSON() ([]byte, error) {
196 type Alias StringArrayMap
197 return json.Marshal(&struct {
198 Length int `json:"length"`
199 *Alias
200 }{
201 Length: len(s.Elements),
202 Alias: (*Alias)(&s),
203 })
204 }
205
206 func (c CommandArgs) MarshalJSON() ([]byte, error) {
207 return json.Marshal(StringArrayMap(c))
208 }
209
210 func (l Layers) MarshalJSON() ([]byte, error) {
211 return json.Marshal(StringArrayMap(l))
212 }
213
214 func (o Options) MarshalJSON() ([]byte, error) {
215 return json.Marshal(StringArrayMap(o))
216 }
217
218 func (m Mounts) MarshalJSON() ([]byte, error) {
219 type Alias Mounts
220 return json.Marshal(&struct {
221 Length int `json:"length"`
222 *Alias
223 }{
224 Length: len(m.Elements),
225 Alias: (*Alias)(&m),
226 })
227 }
228
229
230
231 var indentUsing string = " "
232
233 type stringArray []string
234 type signalArray []syscall.Signal
235
236 func (array stringArray) marshalRego() string {
237 values := make([]string, len(array))
238 for i, value := range array {
239 values[i] = fmt.Sprintf(`"%s"`, value)
240 }
241
242 return fmt.Sprintf("[%s]", strings.Join(values, ","))
243 }
244
245 func (array signalArray) marshalRego() string {
246 values := make([]string, len(array))
247 for i, value := range array {
248 values[i] = fmt.Sprintf("%d", value)
249 }
250
251 return fmt.Sprintf("[%s]", strings.Join(values, ","))
252 }
253
254 func writeLine(builder *strings.Builder, format string, args ...interface{}) {
255 builder.WriteString(fmt.Sprintf(format, args...) + "\n")
256 }
257
258 func writeCommand(builder *strings.Builder, command []string, indent string) {
259 array := (stringArray(command)).marshalRego()
260 writeLine(builder, `%s"command": %s,`, indent, array)
261 }
262
263 func (e EnvRuleConfig) marshalRego() string {
264 return fmt.Sprintf("{\"pattern\": `%s`, \"strategy\": \"%s\", \"required\": %v}", e.Rule, e.Strategy, e.Required)
265 }
266
267 type envRuleArray []EnvRuleConfig
268
269 func (array envRuleArray) marshalRego() string {
270 values := make([]string, len(array))
271 for i, env := range array {
272 values[i] = env.marshalRego()
273 }
274
275 return fmt.Sprintf("[%s]", strings.Join(values, ","))
276 }
277
278 func writeEnvRules(builder *strings.Builder, envRules []EnvRuleConfig, indent string) {
279 writeLine(builder, `%s"env_rules": %s,`, indent, envRuleArray(envRules).marshalRego())
280 }
281
282 func writeLayers(builder *strings.Builder, layers []string, indent string) {
283 writeLine(builder, `%s"layers": %s,`, indent, (stringArray(layers)).marshalRego())
284 }
285
286 func writeCapabilities(builder *strings.Builder, capabilities *capabilitiesInternal, indent string) {
287 if capabilities != nil {
288 writeLine(builder, `%s"capabilities": {`, indent)
289 writeLine(builder, `%s"bounding": %s,`, indent+indentUsing, (stringArray(capabilities.Bounding)).marshalRego())
290 writeLine(builder, `%s"effective": %s,`, indent+indentUsing, (stringArray(capabilities.Effective)).marshalRego())
291 writeLine(builder, `%s"inheritable": %s,`, indent+indentUsing, (stringArray(capabilities.Inheritable)).marshalRego())
292 writeLine(builder, `%s"permitted": %s,`, indent+indentUsing, (stringArray(capabilities.Permitted)).marshalRego())
293 writeLine(builder, `%s"ambient": %s,`, indent+indentUsing, (stringArray(capabilities.Ambient)).marshalRego())
294 writeLine(builder, `%s},`, indent)
295 } else {
296 writeLine(builder, `%s"capabilities": null,`, indent)
297 }
298 }
299
300 func (m mountInternal) marshalRego() string {
301 options := stringArray(m.Options).marshalRego()
302 return fmt.Sprintf(`{"destination": "%s", "options": %s, "source": "%s", "type": "%s"}`, m.Destination, options, m.Source, m.Type)
303 }
304
305 func writeMounts(builder *strings.Builder, mounts []mountInternal, indent string) {
306 values := make([]string, len(mounts))
307 for i, mount := range mounts {
308 values[i] = mount.marshalRego()
309 }
310
311 writeLine(builder, `%s"mounts": [%s],`, indent, strings.Join(values, ","))
312 }
313
314 func (p containerExecProcess) marshalRego() string {
315 command := stringArray(p.Command).marshalRego()
316 signals := signalArray(p.Signals).marshalRego()
317
318 return fmt.Sprintf(`{"command": %s, "signals": %s}`, command, signals)
319 }
320
321 func writeExecProcesses(builder *strings.Builder, execProcesses []containerExecProcess, indent string) {
322 values := make([]string, len(execProcesses))
323 for i, process := range execProcesses {
324 values[i] = process.marshalRego()
325 }
326 writeLine(builder, `%s"exec_processes": [%s],`, indent, strings.Join(values, ","))
327 }
328
329 func writeSignals(builder *strings.Builder, signals []syscall.Signal, indent string) {
330 array := (signalArray(signals)).marshalRego()
331 writeLine(builder, `%s"signals": %s,`, indent, array)
332 }
333
334 func (n IDNameConfig) marshalRego() string {
335 return fmt.Sprintf("{\"pattern\": `%s`, \"strategy\": \"%s\"}", n.Rule, n.Strategy)
336 }
337
338 type idConfigArray []IDNameConfig
339
340 func (array idConfigArray) marshalRego() string {
341 values := make([]string, len(array))
342 for i, name := range array {
343 values[i] = name.marshalRego()
344 }
345
346 return fmt.Sprintf("[%s]", strings.Join(values, ","))
347 }
348
349 func writeUser(builder *strings.Builder, user UserConfig, indent string) {
350 groupIDNames := idConfigArray(user.GroupIDNames).marshalRego()
351 writeLine(builder, `%s"user": {`, indent)
352 writeLine(builder, `%s"user_idname": %s,`, indent+indentUsing, user.UserIDName.marshalRego())
353 writeLine(builder, `%s"group_idnames": %s,`, indent+indentUsing, groupIDNames)
354 writeLine(builder, `%s"umask": "%s"`, indent+indentUsing, user.Umask)
355 writeLine(builder, `%s},`, indent)
356 }
357
358 func writeContainer(builder *strings.Builder, container *securityPolicyContainer, indent string) {
359 writeLine(builder, "%s{", indent)
360 writeCommand(builder, container.Command, indent+indentUsing)
361 writeEnvRules(builder, container.EnvRules, indent+indentUsing)
362 writeLayers(builder, container.Layers, indent+indentUsing)
363 writeMounts(builder, container.Mounts, indent+indentUsing)
364 writeExecProcesses(builder, container.ExecProcesses, indent+indentUsing)
365 writeSignals(builder, container.Signals, indent+indentUsing)
366 writeUser(builder, container.User, indent+indentUsing)
367 writeCapabilities(builder, container.Capabilities, indent+indentUsing)
368 writeLine(builder, `%s"seccomp_profile_sha256": "%s",`, indent+indentUsing, container.SeccompProfileSHA256)
369 writeLine(builder, `%s"allow_elevated": %t,`, indent+indentUsing, container.AllowElevated)
370 writeLine(builder, `%s"working_dir": "%s",`, indent+indentUsing, container.WorkingDir)
371 writeLine(builder, `%s"allow_stdio_access": %t,`, indent+indentUsing, container.AllowStdioAccess)
372 writeLine(builder, `%s"no_new_privileges": %t,`, indent+indentUsing, container.NoNewPrivileges)
373 writeLine(builder, "%s},", indent)
374 }
375
376 func addContainers(builder *strings.Builder, containers []*securityPolicyContainer) {
377 if len(containers) == 0 {
378 return
379 }
380
381 writeLine(builder, "containers := [")
382 for _, container := range containers {
383 writeContainer(builder, container, indentUsing)
384 }
385 writeLine(builder, "]")
386 }
387
388 func (p externalProcess) marshalRego() string {
389 command := stringArray(p.command).marshalRego()
390 envRules := envRuleArray(p.envRules).marshalRego()
391 return fmt.Sprintf(`{"command": %s, "env_rules": %s, "working_dir": "%s", "allow_stdio_access": %t}`, command, envRules, p.workingDir, p.allowStdioAccess)
392 }
393
394 func addExternalProcesses(builder *strings.Builder, processes []*externalProcess) {
395 if len(processes) == 0 {
396 return
397 }
398
399 writeLine(builder, "external_processes := [")
400
401 for _, process := range processes {
402 writeLine(builder, `%s%s,`, indentUsing, process.marshalRego())
403 }
404
405 writeLine(builder, "]")
406 }
407
408 func (f fragment) marshalRego() string {
409 includes := stringArray(f.includes).marshalRego()
410 return fmt.Sprintf(`{"issuer": "%s", "feed": "%s", "minimum_svn": "%s", "includes": %s}`,
411 f.issuer, f.feed, f.minimumSVN, includes)
412 }
413
414 func addFragments(builder *strings.Builder, fragments []*fragment) {
415 if len(fragments) == 0 {
416 return
417 }
418
419 writeLine(builder, "fragments := [")
420
421 for _, fragment := range fragments {
422 writeLine(builder, "%s%s,", indentUsing, fragment.marshalRego())
423 }
424
425 writeLine(builder, "]")
426 }
427
428 func (p securityPolicyInternal) marshalRego() string {
429 builder := new(strings.Builder)
430 addFragments(builder, p.Fragments)
431 addContainers(builder, p.Containers)
432 addExternalProcesses(builder, p.ExternalProcesses)
433 writeLine(builder, `allow_properties_access := %t`, p.AllowPropertiesAccess)
434 writeLine(builder, `allow_dump_stacks := %t`, p.AllowDumpStacks)
435 writeLine(builder, `allow_runtime_logging := %t`, p.AllowRuntimeLogging)
436 writeLine(builder, "allow_environment_variable_dropping := %t", p.AllowEnvironmentVariableDropping)
437 writeLine(builder, "allow_unencrypted_scratch := %t", p.AllowUnencryptedScratch)
438 writeLine(builder, "allow_capability_dropping := %t", p.AllowCapabilityDropping)
439 result := strings.Replace(policyRegoTemplate, "@@OBJECTS@@", builder.String(), 1)
440 result = strings.Replace(result, "@@API_VERSION@@", apiVersion, 1)
441 result = strings.Replace(result, "@@FRAMEWORK_VERSION@@", frameworkVersion, 1)
442 return result
443 }
444
445 func (p securityPolicyFragment) marshalRego() string {
446 builder := new(strings.Builder)
447 addFragments(builder, p.Fragments)
448 addContainers(builder, p.Containers)
449 addExternalProcesses(builder, p.ExternalProcesses)
450 return fmt.Sprintf("package %s\n\nsvn := \"%s\"\nframework_version := \"%s\"\n\n%s", p.Namespace, p.SVN, frameworkVersion, builder.String())
451 }
452
View as plain text