diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go index ef574139e1..ce5c859c58 100644 --- a/pkg/nfd-master/nfd-master.go +++ b/pkg/nfd-master/nfd-master.go @@ -465,30 +465,15 @@ func (m *nfdMaster) updateMasterNode() error { // arriving through the gRPC API. func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResources) { outLabels := Labels{} - for label, value := range labels { + for name, value := range labels { // Add possibly missing default ns - label := addNs(label, nfdv1alpha1.FeatureLabelNs) - - ns, name := splitNs(label) - - // Check label namespace, filter out if ns is not whitelisted - if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs && - !strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) { - // If the namespace is denied, and not present in the extraLabelNs, label will be ignored - if isNamespaceDenied(ns, m.deniedNs.wildcard, m.deniedNs.normal) { - if _, ok := m.config.ExtraLabelNs[ns]; !ok { - klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label) - continue - } - } - } + name := addNs(name, nfdv1alpha1.FeatureLabelNs) - // Skip if label doesn't match labelWhiteList - if !m.config.LabelWhiteList.Regexp.MatchString(name) { - klog.Errorf("%s (%s) does not match the whitelist (%s) and will not be published.", name, label, m.config.LabelWhiteList.Regexp.String()) - continue + if err := m.filterFeatureLabel(name); err != nil { + klog.Errorf("ignoring label %s=%v: %v", name, value, err) + } else { + outLabels[name] = value } - outLabels[label] = value } // Remove labels which are intended to be extended resources @@ -510,28 +495,52 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels) (Labels, ExtendedResource return outLabels, extendedResources } +func (m *nfdMaster) filterFeatureLabel(name string) error { + // Check label namespace, filter out if ns is not whitelisted + ns, base := splitNs(name) + if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs && + !strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) { + // If the namespace is denied, and not present in the extraLabelNs, label will be ignored + if isNamespaceDenied(ns, m.deniedNs.wildcard, m.deniedNs.normal) { + if _, ok := m.config.ExtraLabelNs[ns]; !ok { + return fmt.Errorf("namespace %q is not allowed", ns) + } + } + } + + // Skip if label doesn't match labelWhiteList + if !m.config.LabelWhiteList.Regexp.MatchString(base) { + return fmt.Errorf("%s (%s) does not match the whitelist (%s)", base, name, m.config.LabelWhiteList.Regexp.String()) + } + return nil +} + func filterTaints(taints []corev1.Taint) []corev1.Taint { outTaints := []corev1.Taint{} for _, taint := range taints { - ns, _ := splitNs(taint.Key) - - // Check prefix of the key, filter out disallowed ones - if ns == "" { - klog.Errorf("taint keys without namespace (prefix/) are not allowed. Ignoring taint %v", ns, taint) - continue - } - if ns != nfdv1alpha1.TaintNs && !strings.HasSuffix(ns, nfdv1alpha1.TaintSubNsSuffix) && - (ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io")) { - klog.Errorf("Prefix %q is not allowed for taint key. Ignoring taint %v", ns, taint) - continue + if err := filterTaint(&taint); err != nil { + klog.Errorf("ignoring taint %q: %w", taint.ToString(), err) + } else { + outTaints = append(outTaints, taint) } - outTaints = append(outTaints, taint) } - return outTaints } +func filterTaint(taint *corev1.Taint) error { + // Check prefix of the key, filter out disallowed ones + ns, _ := splitNs(taint.Key) + if ns == "" { + return fmt.Errorf("taint keys without namespace (prefix/) are not allowed") + } + if ns != nfdv1alpha1.TaintNs && !strings.HasSuffix(ns, nfdv1alpha1.TaintSubNsSuffix) && + (ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io")) { + return fmt.Errorf("prefix %q is not allowed for taint key", ns) + } + return nil +} + func verifyNodeName(cert *x509.Certificate, nodeName string) error { if cert.Subject.CommonName == nodeName { return nil @@ -558,20 +567,6 @@ func isNamespaceDenied(labelNs string, wildcardDeniedNs map[string]struct{}, nor return false } -func isNamespaceAllowed(labelNs string, wildcardAllowedNs map[string]struct{}, normalAllowedNs map[string]struct{}) bool { - for allowedNs := range normalAllowedNs { - if labelNs == allowedNs { - return true - } - } - for allowedNs := range wildcardAllowedNs { - if strings.HasSuffix(labelNs, allowedNs) { - 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) @@ -696,64 +691,60 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error { // filterExtendedResources filters extended resources and returns a map // of valid extended resources. -func (m *nfdMaster) filterExtendedResources(features *nfdv1alpha1.Features, extendedResources ExtendedResources) ExtendedResources { +func filterExtendedResources(features *nfdv1alpha1.Features, extendedResources ExtendedResources) ExtendedResources { outExtendedResources := ExtendedResources{} - deniedNs := map[string]struct{}{"kubernetes.io": {}} - deniedWildCarNs := map[string]struct{}{".kubernetes.io": {}} - allowedNs := map[string]struct{}{nfdv1alpha1.ExtendedResourceNs: {}} - allowedWildCardNs := map[string]struct{}{nfdv1alpha1.ExtendedResourceSubNsSuffix: {}} - for extendedResource, capacity := range extendedResources { - if strings.Contains(extendedResource, "/") { - // Check if given NS is allowed - ns, _ := splitNs(extendedResource) - if isNamespaceDenied(ns, deniedWildCarNs, deniedNs) { - if !isNamespaceAllowed(ns, allowedWildCardNs, allowedNs) { - klog.Errorf("namespace %q is not allowed. Ignoring Extended Resource %q", ns, extendedResource) - continue - } - } + for name, value := range extendedResources { + // Add possibly missing default ns + name = addNs(name, nfdv1alpha1.ExtendedResourceNs) + + capacity, err := filterExtendedResource(name, value, features) + if err != nil { + klog.Errorf("failed to create extended resources %s=%s: %v", name, value, err) } else { - // Add possibly missing default ns - extendedResource = path.Join(nfdv1alpha1.ExtendedResourceNs, extendedResource) + outExtendedResources[name] = capacity } + } + return outExtendedResources +} - // Dynamic Value - if strings.HasPrefix(capacity, "@") { - // capacity is a string in the form of attribute.featureset.elements - split := strings.SplitN(capacity[1:], ".", 3) - if len(split) != 3 { - klog.Errorf("capacity %s is not in the form of '@domain.feature.element',. Ignoring Extended Resource %q", capacity, extendedResource) - continue - } - featureName := split[0] + "." + split[1] - elementName := split[2] - attrFeatureSet, ok := features.Attributes[featureName] - if !ok { - klog.Errorf("feature %s not found. Ignoring Extended Resource %q", featureName, extendedResource) - continue - } - element, ok := attrFeatureSet.Elements[elementName] - if !ok { - klog.Errorf("element %s not found on feature %s. Ignoring Extended Resource %q", elementName, featureName, extendedResource) - continue - } - q, err := k8sQuantity.ParseQuantity(element) - if err != nil { - klog.Errorf("bad label value %s encountered for extended resource: %s", q.String(), extendedResource, err) - continue - } - outExtendedResources[extendedResource] = q.String() - continue +func filterExtendedResource(name, value string, features *nfdv1alpha1.Features) (string, error) { + // Check if given NS is allowed + ns, _ := splitNs(name) + if ns != nfdv1alpha1.ExtendedResourceNs && !strings.HasPrefix(ns, nfdv1alpha1.ExtendedResourceSubNsSuffix) { + if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") { + return "", fmt.Errorf("namespace %q is not allowed", ns) } - // Static Value (Pre-Defined at the NodeFeatureRule) - q, err := k8sQuantity.ParseQuantity(capacity) + } + + // Dynamic Value + if strings.HasPrefix(value, "@") { + // value is a string in the form of attribute.featureset.elements + split := strings.SplitN(value[1:], ".", 3) + if len(split) != 3 { + return "", fmt.Errorf("value %s is not in the form of '@domain.feature.element'", value) + } + featureName := split[0] + "." + split[1] + elementName := split[2] + attrFeatureSet, ok := features.Attributes[featureName] + if !ok { + return "", fmt.Errorf("feature %s not found", featureName) + } + element, ok := attrFeatureSet.Elements[elementName] + if !ok { + return "", fmt.Errorf("element %s not found on feature %s", elementName, featureName) + } + q, err := k8sQuantity.ParseQuantity(element) if err != nil { - klog.Errorf("bad label value %s encountered for extended resource: %s", capacity, extendedResource, err) - continue + return "", fmt.Errorf("invalid value %s (from %s): %w", element, value, err) } - outExtendedResources[extendedResource] = q.String() + return q.String(), nil } - return outExtendedResources + // Static Value (Pre-Defined at the NodeFeatureRule) + q, err := k8sQuantity.ParseQuantity(value) + if err != nil { + return "", fmt.Errorf("invalid value %s: %w", value, err) + } + return q.String(), nil } func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName string, annotations Annotations, labels map[string]string, features *nfdv1alpha1.Features) error { @@ -777,7 +768,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName stri for k, v := range crExtendedResources { extendedResources[k] = v } - extendedResources = m.filterExtendedResources(features, extendedResources) + extendedResources = filterExtendedResources(features, extendedResources) var taints []corev1.Taint if m.config.EnableTaints { @@ -1157,6 +1148,7 @@ func (m *nfdMaster) configure(filepath string, overrides string) error { m.deniedNs.normal = normalDeniedNs m.deniedNs.wildcard = wildcardDeniedNs // We forcibly deny kubernetes.io + m.deniedNs.normal[""] = struct{}{} m.deniedNs.normal["kubernetes.io"] = struct{}{} m.deniedNs.wildcard[".kubernetes.io"] = struct{}{}