Skip to content

Commit

Permalink
feat: add deny-label-ns flag which supports wildcard
Browse files Browse the repository at this point in the history
Signed-off-by: AhmedGrati <[email protected]>
  • Loading branch information
TessaIO committed Feb 11, 2023
1 parent 94ab0dd commit 5182050
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 9 deletions.
23 changes: 23 additions & 0 deletions cmd/nfd-master/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"os"
"regexp"
"strings"

"k8s.io/klog/v2"

Expand Down Expand Up @@ -85,12 +86,15 @@ func main() {
func initFlags(flagset *flag.FlagSet) *master.Args {
args := &master.Args{
LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")},
DenyLabelNs: map[string]struct{}{"kubernetes.io": {}},
}

flagset.StringVar(&args.CaFile, "ca-file", "",
"Root certificate for verifying connections")
flagset.StringVar(&args.CertFile, "cert-file", "",
"Certificate used for authenticating connections")
flagset.Var(&args.DenyLabelNs, "deny-label-ns",
"Comma separated list of denied label namespaces")
flagset.Var(&args.ExtraLabelNs, "extra-label-ns",
"Comma separated list of allowed extra label namespaces")
flagset.StringVar(&args.Instance, "instance", "",
Expand Down Expand Up @@ -122,5 +126,24 @@ func initFlags(flagset *flag.FlagSet) *master.Args {
"Verify worker node name against the worker's TLS certificate. "+
"Only takes effect when TLS authentication has been enabled.")

normalDeniedNs, wildcardDeniedNs := preProcessDeniedNamespaces(args.DenyLabelNs)
args.NormalDenyLabelNs = normalDeniedNs
args.WildcardDenyLabelNs = wildcardDeniedNs

return args
}

// Seperate denied namespaces into two lists:
// one contains wildcard namespaces the other contains normal namespaces
func preProcessDeniedNamespaces(deniedNs map[string]struct{}) (normalDeniedNs map[string]struct{}, wildcardDeniedNs map[string]struct{}) {
normalDeniedNs = map[string]struct{}{}
wildcardDeniedNs = map[string]struct{}{}
for ns := range deniedNs {
if strings.HasPrefix(ns, "*") {
wildcardDeniedNs[ns] = struct{}{}
} else {
normalDeniedNs[ns] = struct{}{}
}
}
return
}
3 changes: 3 additions & 0 deletions deployment/helm/node-feature-discovery/templates/master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ spec:
{{- if .Values.master.extraLabelNs | empty | not }}
- "-extra-label-ns={{- join "," .Values.master.extraLabelNs }}"
{{- end }}
{{- if .Values.master.denyLabelNs | empty | not }}
- "-deny-label-ns={{- join "," .Values.master.denyLabelNs }}"
{{- end }}
{{- if .Values.master.resourceLabels | empty | not }}
- "-resource-labels={{- join "," .Values.master.resourceLabels }}"
{{- end }}
Expand Down
1 change: 1 addition & 0 deletions deployment/helm/node-feature-discovery/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ master:
port: 8080
instance:
featureApi:
denyLabelNs: []
extraLabelNs: []
resourceLabels: []
crdController: null
Expand Down
16 changes: 16 additions & 0 deletions docs/reference/master-commandline-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,22 @@ Example:
nfd-master -extra-label-ns=vendor-1.com,vendor-2.io
```

### -deny-label-ns

The `-deny-label-ns` flag specifies a comma-separated list of excluded
label namespaces. By default, nfd-master allows creating labels in all
namespaces, excluding `kubernetes.io` namespace and its sub-namespaces
(e.g. `sub.ns.kubernetes.io`).
This option can be used to exclude some vendors or application specific namespaces.

Default: *kubernetes.io*

Example:

```bash
nfd-master -deny-label-ns=kubernetes.io,vendor-2.io
```

### -resource-labels

The `-resource-labels` flag specifies a comma-separated list of features to be
Expand Down
5 changes: 5 additions & 0 deletions docs/usage/customization-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ namespace must be explicitly allowed with the `-extra-label-ns` command line
flag of nfd-master if using something else than
`[<sub-ns>.]feature.node.kubernetes.io` or
`[<sub-ns>.]profile.node.kubernetes.io`.
Also, the namespace can be excluded using the
`-deny-label-ns` command line flag of nfd-master.

### Mounts

Expand Down Expand Up @@ -421,6 +423,9 @@ The namespace part (i.e. prefix) of the labels is controlled by nfd:
- Additional namespaces may be enabled with the
[`-extra-label-ns`](../reference/master-commandline-reference.md#-extra-label-ns)
command line flag of nfd-master
- Namespaces may be excluded with the
[`-deny-label-ns`](../reference/master-commandline-reference.md#-deny-label-ns)
command line flag of nfd-master

## Label rule format

Expand Down
12 changes: 7 additions & 5 deletions pkg/nfd-master/nfd-master-internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,16 +352,17 @@ func TestSetLabels(t *testing.T) {
})
})

Convey("When -extra-label-ns and -instance are specified", func() {
Convey("When -extra-label-ns, -deny-label-ns and -instance are specified", func() {
// In the gRPC request the label names may omit the default ns
instance := "foo"
vendorFeatureLabel := "vendor." + nfdv1alpha1.FeatureLabelNs + "/feature-4"
vendorProfileLabel := "vendor." + nfdv1alpha1.ProfileLabelNs + "/feature-5"
mockLabels := map[string]string{"feature-1": "val-1",
"valid.ns/feature-2": "val-2",
"invalid.ns/feature-3": "val-3",
vendorFeatureLabel: " val-4",
vendorProfileLabel: " val-5"}
"valid.ns/feature-2": "val-2",
"invalid.ns/feature-3": "val-3",
"random.denied.ns/feature-3": "val-4",
vendorFeatureLabel: " val-5",
vendorProfileLabel: " val-6"}
expectedPatches := []apihelper.JsonPatch{
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+nfdv1alpha1.WorkerVersionAnnotation, workerVer),
apihelper.NewJsonPatch("add", "/metadata/annotations",
Expand All @@ -374,6 +375,7 @@ func TestSetLabels(t *testing.T) {
apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
}

mockMaster.args.DenyLabelNs = map[string]struct{}{"random.denied.ns": {}}
mockMaster.args.ExtraLabelNs = map[string]struct{}{"valid.ns": {}}
mockMaster.args.Instance = instance
mockHelper.On("GetClient").Return(mockClient, nil)
Expand Down
26 changes: 24 additions & 2 deletions pkg/nfd-master/nfd-master.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ type Annotations map[string]string
type Args struct {
CaFile string
CertFile string
DenyLabelNs utils.StringSetVal
WildcardDenyLabelNs utils.StringSetVal
NormalDenyLabelNs utils.StringSetVal
ExtraLabelNs utils.StringSetVal
Instance string
KeyFile string
Expand Down Expand Up @@ -392,7 +395,7 @@ func (m *nfdMaster) updateMasterNode() error {
// into extended resources. This function also handles proper namespacing of
// labels and ERs, i.e. adds the possibly missing default namespace for labels
// arriving through the gRPC API.
func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelWhiteList regexp.Regexp, extendedResourceNames map[string]struct{}) (Labels, ExtendedResources) {
func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, wildcardDeniedLabelNs map[string]struct{}, normalDeniedLabelsNs map[string]struct{}, labelWhiteList regexp.Regexp, extendedResourceNames map[string]struct{}) (Labels, ExtendedResources) {
outLabels := Labels{}

for label, value := range labels {
Expand All @@ -408,6 +411,10 @@ func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelW
klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label)
continue
}
if isNamespaceDenied(ns, wildcardDeniedLabelNs, normalDeniedLabelsNs) {
klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label)
continue
}
}

// Skip if label doesn't match labelWhiteList
Expand Down Expand Up @@ -449,6 +456,21 @@ func verifyNodeName(cert *x509.Certificate, nodeName string) error {
return nil
}

func isNamespaceDenied(labelNs string, wildcardDeniedNs map[string]struct{}, normalDeniedNs map[string]struct{}) bool {
for deniedNs := range normalDeniedNs {
if labelNs == deniedNs {
return true
}
}
for deniedNs := range wildcardDeniedNs {
deniedSuffix := strings.TrimLeft(deniedNs, "*")
if strings.HasSuffix(labelNs, deniedSuffix) {
return true
}
}
return false
}

// SetLabels implements LabelerServer
func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.SetLabelsReply, error) {
err := authorizeClient(c, m.args.VerifyNodeName, r.NodeName)
Expand Down Expand Up @@ -583,7 +605,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName stri
labels[k] = v
}

labels, extendedResources := filterFeatureLabels(labels, m.args.ExtraLabelNs, m.args.LabelWhiteList.Regexp, m.args.ResourceLabels)
labels, extendedResources := filterFeatureLabels(labels, m.args.ExtraLabelNs, m.args.WildcardDenyLabelNs, m.args.NormalDenyLabelNs, m.args.LabelWhiteList.Regexp, m.args.ResourceLabels)

var taints []corev1.Taint
if m.args.EnableTaints {
Expand Down
3 changes: 1 addition & 2 deletions pkg/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ func (a *StringSetVal) String() string {
if *a == nil {
return ""
}

vals := make([]string, len(*a), 0)
vals := make([]string, 0, len(*a))
for val := range *a {
vals = append(vals, val)
}
Expand Down

0 comments on commit 5182050

Please sign in to comment.