...

Source file src/github.com/Microsoft/hcsshim/pkg/securitypolicy/securitypolicy_marshal.go

Documentation: github.com/Microsoft/hcsshim/pkg/securitypolicy

     1  package securitypolicy
     2  
     3  /** TODO
     4   *  Once JSON output/input functionality is removed, this code should be
     5   *  moved to the securitypolicy tool.
     6   */
     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  //go:embed policy.rego
    45  var policyRegoTemplate string
    46  
    47  //go:embed open_door.rego
    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  // Custom JSON marshalling to add `length` field that matches the number of
   171  // elements present in the `elements` field.
   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  // Marshaling for creating Rego policy code
   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