Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

Operator overrides of role capabilities

Andreas Kupries edited this page May 4, 2018 · 8 revisions

Introduction

Some public cloud system require some pods to have more privileges than they are given by default through the role manifest and helm chart generated for it.

This document specifies a number of changes to the generated helm charts which will allow operators to perform scriptable extensions of the default privileges as needed.

Specification

The basics of the change are to provide operators with new keys in the values.yaml they can edit. In their default state the chart will simply use the security context generated from the manifest (SCM). When edited the chart will use the SCM and merge the operator information into it.

  • values.yaml is extended with keys of the form sizing.<rolename>.capabilities. The key is of type list, taking the operator-defined set of capabilities, or ALL. The default is the empty list ([]). Not nil. The list function has crashes on a nil-haystack. To use default we ned something to generate an empty list. The list function list generates nil when called without arguments, not an empty list. It is not possible to use [] in default, i.e. default .foo [] is a syntax error.

  • When capabilities are added to the key for a role they are merged by the role's template into its SCM. The exact rules are dependent on the form of the SCM, of which there are three, called privileged, nil, and cap-list. The list below will show these forms in the named order, and the new forms to be generated by the modified fissile for them to accomplish the merge.

  1. Original SCM is privileged, i.e.

    - securityContext:
        privileged: true
    
    • In this case no merging is necessary, because that role is already at the highest possible privilege.
    • Fissile will keep emitting the original SCM.
  2. Original SCM is nil, i.e.

    - securityContext: ~
    
    • Any capabilities added can be taken as-is, mostly. The only special case case is the capability ALL, which has to be translated to privileged when present, regardless of any other.
    • Fissile is changed to emit
            - securityContext:
            {{ if has "ALL" .Values.sizing.<role>.capabilities -}}
                privileged: true
            {{- end }}
            {{ if not (has "ALL" .Values.sizing.<role>.capabilities) -}}
                capabilities:
                  add: {{ .Values.sizing.<role>.capabilities }}
            {{- end }}
      
  3. Original SCM is cap-list, i.e:

    - securityContext:
        capabilities:
          add:
          - <<role manifest cap 1>>
    
    • Presence of the capability ALL is done like for nil, see above.
    • There is no need to check if the manifest capabilities contain ALL. This is case 2 above.
    • The merging of the user's capability list is done via a range iteration. Note, this may cause duplicate capabilities in the output. These duplicates however do not matter, and while it is possible to concatenate two lists the code for that is very convoluted and non-obvious. See later section.
    • Fissile is changed to emit
            - securityContext:
            {{ if has "ALL" .Values.sizing.<role>.capabilities -}}
                privileged: true
            {{- end }}
            {{ if not (has "ALL" .Values.sizing.<role>.capabilities) -}}
                capabilities:
                  add:
                  - <<role manifest cap 1>>
                  (...)
                  {{- range .Values.sizing.<role>.capabilities }}
                  - {{ . }}
                  {{- end }}
            {{- end }}
      

Notes:

  • As specified the operator-provided capabilities in the values.yaml have to be all uppercase. The templates will not accept mixed-case, nor lower-case. This is different from the manifest where capabilities can be specified in any case, with fissile converting to the all-uppercase form.

List concatenation

The code to concatenate two lists, to allow feeding through uniq is

   {{ $myList := list "11" "12" "31" "14" "15" }}
   {{ $myList2 := list "12" "22" "23" "24" "25" }}
   {{ $concated :=  (( list ($myList | join ":") ($myList2 | join ":") ) | join ":") | splitList ":" }}
   {{ $mys := printf "%+v" ( uniq ((dict "values" $concated) | toJson | fromJson ).values ) }}
   {{ fail $mys }}

The two lists are joined into two strings, placed into a list, joined into string. The resulting single string is flat, and can be split back into one list. This song and dance is all necessary because sprig does not have a plain list concat function.