Skip to content

Commit 9243030

Browse files
committed
added custom host path support
1 parent f8c8f22 commit 9243030

File tree

7 files changed

+128
-51
lines changed

7 files changed

+128
-51
lines changed

Diff for: chart/templates/deployment.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ spec:
4141
value: '{{ .Values.global.cattle.clusterName }}'
4242
- name: CIS_OPERATOR_DEBUG
4343
value: '{{ .Values.image.cisoperator.debug }}'
44+
- name: CUSTOM_SCAN_HOST_PATHS
45+
value: '{{ .Values.customScanHostPaths | toJson }}'
4446
{{- if .Values.securityScanJob.overrideTolerations }}
4547
- name: SECURITY_SCAN_JOB_TOLERATIONS
4648
value: '{{ .Values.securityScanJob.tolerations | toJson }}'

Diff for: chart/values.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ securityScanJob:
3939

4040
affinity: {}
4141

42+
customScanHostPaths: []
43+
4244
global:
4345
cattle:
4446
systemDefaultRegistry: ""

Diff for: main.go

+55-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"errors"
1111
"fmt"
1212
"os"
13+
"path/filepath"
14+
"strings"
1315
"time"
1416

1517
"github.com/rancher/wrangler/v3/pkg/kubeconfig"
@@ -52,6 +54,7 @@ var (
5254
sonobuoyImageTag string
5355
clusterName string
5456
securityScanJobTolerationsVal string
57+
customScanHostPathsVal string
5558
)
5659

5760
func main() {
@@ -134,6 +137,12 @@ func main() {
134137
Name: "alertEnabled",
135138
EnvVars: []string{"CIS_ALERTS_ENABLED"},
136139
},
140+
&cli.StringFlag{
141+
Name: "custom-scan-host-paths",
142+
EnvVar: "CUSTOM_SCAN_HOST_PATHS",
143+
Value: "",
144+
Destination: &customScanHostPathsVal,
145+
},
137146
}
138147
app.Action = run
139148

@@ -176,6 +185,22 @@ func run(c *cli.Context) error {
176185
}
177186
}
178187

188+
customHostPaths := []string{}
189+
customScanHostPathsVal = c.String("custom-scan-host-paths")
190+
191+
if customScanHostPathsVal != "" {
192+
193+
err := json.Unmarshal([]byte(customScanHostPathsVal), &customHostPaths)
194+
if err != nil {
195+
logrus.Fatalf("invalid value received for custom-scan-host-paths flag:%s", err.Error())
196+
}
197+
198+
err = validateCustomScanHostPaths(customHostPaths)
199+
if err != nil {
200+
logrus.Fatalf("validation failed for custom-scan-host-paths:%s", err.Error())
201+
}
202+
}
203+
179204
kubeConfig, err := kubeconfig.GetNonInteractiveClientConfig(kubeConfig).ClientConfig()
180205
if err != nil {
181206
logrus.Fatalf("failed to find kubeconfig: %v", err)
@@ -195,7 +220,7 @@ func run(c *cli.Context) error {
195220
logrus.Fatalf("Error starting CIS-Operator: %v", err)
196221
}
197222

198-
ctl, err := cisoperator.NewController(ctx, kubeConfig, cisoperatorapiv1.ClusterScanNS, name, imgConfig, securityScanJobTolerations)
223+
ctl, err := cisoperator.NewController(ctx, kubeConfig, cisoperatorapiv1.ClusterScanNS, name, imgConfig, securityScanJobTolerations, customHostPaths)
199224
if err != nil {
200225
logrus.Fatalf("Error building controller: %s", err.Error())
201226
}
@@ -224,3 +249,32 @@ func validateConfig(imgConfig *cisoperatorapiv1.ScanImageConfig) error {
224249
}
225250
return nil
226251
}
252+
253+
func validateCustomScanHostPaths(hostPaths []string) error {
254+
protectedDirs := []string{"/bin", "/boot", "/dev", "/etc", "/lib", "/lib64", "/proc", "/root", "/run", "/sbin", "/selinux", "/sys", "/tmp", "/usr", "/var"}
255+
256+
hostPathSet := make(map[string]bool)
257+
258+
for _, path := range hostPaths {
259+
if !filepath.IsAbs(path) {
260+
return fmt.Errorf("path must be absolute: %s", path)
261+
}
262+
263+
if path == "/" {
264+
return fmt.Errorf("root path '/' is not allowed")
265+
}
266+
267+
for _, protected := range protectedDirs {
268+
if path == protected || strings.HasPrefix(path, protected+"/") {
269+
return fmt.Errorf("path %s is not allowed as it affects protected directory %s", path, protected)
270+
}
271+
}
272+
273+
if _, exists := hostPathSet[path]; exists {
274+
return fmt.Errorf("duplicate path detected: %s", path)
275+
}
276+
hostPathSet[path] = true
277+
}
278+
279+
return nil
280+
}

Diff for: pkg/securityscan/controller.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,11 @@ type Controller struct {
7070
daemonsets appsctlv1.DaemonSetController
7171
daemonsetCache appsctlv1.DaemonSetCache
7272
securityScanJobTolerations []corev1.Toleration
73+
customScanHostPaths []string
7374
}
7475

7576
func NewController(ctx context.Context, cfg *rest.Config, namespace, name string,
76-
imgConfig *cisoperatorapiv1.ScanImageConfig, securityScanJobTolerations []corev1.Toleration) (ctl *Controller, err error) {
77+
imgConfig *cisoperatorapiv1.ScanImageConfig, securityScanJobTolerations []corev1.Toleration, customScanHostPaths []string) (ctl *Controller, err error) {
7778
if cfg == nil {
7879
cfg, err = rest.InClusterConfig()
7980
if err != nil {
@@ -154,6 +155,7 @@ func NewController(ctx context.Context, cfg *rest.Config, namespace, name string
154155
ctl.daemonsets = ctl.appsFactory.Apps().V1().DaemonSet()
155156
ctl.daemonsetCache = ctl.appsFactory.Apps().V1().DaemonSet().Cache()
156157
ctl.securityScanJobTolerations = securityScanJobTolerations
158+
ctl.customScanHostPaths = customScanHostPaths
157159
return ctl, nil
158160
}
159161

Diff for: pkg/securityscan/core/configmap.go

+56-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"bytes"
55
_ "embed" // nolint
66
"encoding/json"
7+
"fmt"
8+
"strings"
79
"text/template"
810

911
corev1 "k8s.io/api/core/v1"
@@ -16,6 +18,17 @@ import (
1618
cisoperatorapiv1 "github.com/rancher/cis-operator/pkg/apis/cis.cattle.io/v1"
1719
)
1820

21+
var requiredPaths = map[string]string{
22+
"var-rancher": "/var/lib/rancher",
23+
"etc-rancher": "/etc/rancher",
24+
"etc-cni": "/etc/cni/net.d",
25+
"var-cni": "/var/lib/cni",
26+
"var-log": "/var/log",
27+
"run-log": "/run/log",
28+
"etc-kubelet": "/etc/kubernetes/kubelet",
29+
"var-kubelet": "/var/lib/kubelet",
30+
}
31+
1932
//go:embed templates/pluginConfig.template
2033
var pluginConfigTemplate string
2134

@@ -31,7 +44,8 @@ const (
3144
ConfigFileName = "config.json"
3245
)
3346

34-
func NewConfigMaps(clusterscan *cisoperatorapiv1.ClusterScan, clusterscanprofile *cisoperatorapiv1.ClusterScanProfile, clusterscanbenchmark *cisoperatorapiv1.ClusterScanBenchmark, _ string, imageConfig *cisoperatorapiv1.ScanImageConfig, configmapsClient wcorev1.ConfigMapController) (cmMap map[string]*corev1.ConfigMap, err error) {
47+
func NewConfigMaps(clusterscan *cisoperatorapiv1.ClusterScan, clusterscanprofile *cisoperatorapiv1.ClusterScanProfile, clusterscanbenchmark *cisoperatorapiv1.ClusterScanBenchmark, _ string, imageConfig *cisoperatorapiv1.ScanImageConfig,
48+
configmapsClient wcorev1.ConfigMapController, customScanHostPaths []string) (cmMap map[string]*corev1.ConfigMap, err error) {
3549
cmMap = make(map[string]*corev1.ConfigMap)
3650

3751
configdata := map[string]interface{}{
@@ -62,6 +76,8 @@ func NewConfigMaps(clusterscan *cisoperatorapiv1.ClusterScan, clusterscanprofile
6276
customBenchmarkConfigMapName = customcm.Name
6377
}
6478

79+
hostPathVolumes, hostPathVolumeMounts := pluginConfigHostPathVolumesData(customScanHostPaths)
80+
6581
plugindata := map[string]interface{}{
6682
"namespace": cisoperatorapiv1.ClusterScanNS,
6783
"name": name.SafeConcatName(cisoperatorapiv1.ClusterScanPluginsConfigMap, clusterscan.Name),
@@ -74,6 +90,8 @@ func NewConfigMaps(clusterscan *cisoperatorapiv1.ClusterScan, clusterscanprofile
7490
"configDir": cisoperatorapiv1.CustomBenchmarkBaseDir,
7591
"customBenchmarkConfigMapName": customBenchmarkConfigMapName,
7692
"customBenchmarkConfigMapData": customBenchmarkConfigMapData,
93+
"hostPathVolumes": hostPathVolumes,
94+
"hostPathVolumeMounts": hostPathVolumeMounts,
7795
}
7896
plugincm, err := generateConfigMap(clusterscan, "pluginConfig.template", pluginConfigTemplate, plugindata)
7997
if err != nil {
@@ -176,3 +194,40 @@ func getCustomBenchmarkConfigMap(benchmark *cisoperatorapiv1.ClusterScanBenchmar
176194
}
177195
return configmapsClient.Create(&configmapCopy)
178196
}
197+
198+
func pluginConfigHostPathVolumesData(customScanHostPaths []string) ([]*corev1.Volume, []*corev1.VolumeMount) {
199+
volumes := make([]*corev1.Volume, 0, len(requiredPaths)+len(customScanHostPaths))
200+
volumeMounts := make([]*corev1.VolumeMount, 0, len(requiredPaths)+len(customScanHostPaths))
201+
hostPaths := make(map[string]bool, len(requiredPaths))
202+
203+
// Add required volumes
204+
for name, path := range requiredPaths {
205+
path = strings.TrimSuffix(path, "/")
206+
volumes = append(volumes, &corev1.Volume{
207+
Name: name,
208+
VolumeSource: corev1.VolumeSource{
209+
HostPath: &corev1.HostPathVolumeSource{Path: path},
210+
},
211+
})
212+
volumeMounts = append(volumeMounts, &corev1.VolumeMount{Name: name, MountPath: path, ReadOnly: true})
213+
hostPaths[path] = true
214+
}
215+
216+
// Add custom volumes if they are not already included
217+
for idx, path := range customScanHostPaths {
218+
if !hostPaths[path] {
219+
path = strings.TrimSuffix(path, "/")
220+
name := fmt.Sprintf("custom-volume-%d", idx)
221+
volumes = append(volumes, &corev1.Volume{
222+
Name: name,
223+
VolumeSource: corev1.VolumeSource{
224+
HostPath: &corev1.HostPathVolumeSource{Path: path},
225+
},
226+
})
227+
volumeMounts = append(volumeMounts, &corev1.VolumeMount{Name: name, MountPath: path, ReadOnly: true})
228+
hostPaths[path] = true
229+
}
230+
}
231+
232+
return volumes, volumeMounts
233+
}

Diff for: pkg/securityscan/core/templates/pluginConfig.template

+9-47
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,11 @@ data:
2929
key: CriticalAddonsOnly
3030
operator: Exists
3131
volumes:
32+
{{- range .hostPathVolumes }}
3233
- hostPath:
33-
path: /var/lib/rancher
34-
name: var-rancher
35-
- hostPath:
36-
path: /etc/rancher
37-
name: etc-rancher
38-
- hostPath:
39-
path: /etc/cni/net.d
40-
name: etc-cni
41-
- hostPath:
42-
path: /var/lib/cni
43-
name: var-cni
44-
- hostPath:
45-
path: /var/log
46-
name: var-log
47-
- hostPath:
48-
path: /run/log
49-
name: run-log
50-
- hostPath:
51-
path: /etc/kubernetes/kubelet
52-
name: etc-kubelet
53-
- hostPath:
54-
path: /var/lib/kubelet
55-
name: var-kubelet
34+
path: {{ .VolumeSource.HostPath.Path }}
35+
name: {{ .Name }}
36+
{{- end }}
5637
{{- if .isCustomBenchmark }}
5738
- configMap:
5839
defaultMode: 420
@@ -102,30 +83,11 @@ data:
10283
- mountPath: /tmp/results
10384
name: results
10485
readOnly: false
105-
- mountPath: /var/lib/rancher
106-
name: var-rancher
107-
readOnly: true
108-
- mountPath: /etc/rancher
109-
name: etc-rancher
110-
readOnly: true
111-
- mountPath: /etc/cni/net.d
112-
name: etc-cni
113-
readOnly: true
114-
- mountPath: /var/lib/cni
115-
name: var-cni
116-
readOnly: true
117-
- mountPath: /var/log/
118-
name: var-log
119-
readOnly: true
120-
- mountPath: /run/log/
121-
name: run-log
122-
readOnly: true
123-
- mountPath: /etc/kubernetes/kubelet
124-
name: etc-kubelet
125-
readOnly: true
126-
- mountPath: /var/lib/kubelet
127-
name: var-kubelet
128-
readOnly: true
86+
{{- range .hostPathVolumeMounts }}
87+
- mountPath: {{ .MountPath }}
88+
name: {{ .Name }}
89+
readOnly: {{ .ReadOnly }}
90+
{{- end }}
12991
{{- if .isCustomBenchmark }}
13092
- mountPath: /etc/kbs/custombenchmark/cfg
13193
name: custom-benchmark-volume

Diff for: pkg/securityscan/scanHandler.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func (c *Controller) handleClusterScans(ctx context.Context) error {
9696
v1.ClusterScanConditionReconciling.True(obj)
9797
return objects, obj.Status, fmt.Errorf("Error when getting Benchmark: %w", err)
9898
}
99-
cmMap, err := ciscore.NewConfigMaps(obj, profile, benchmark, c.Name, c.ImageConfig, c.configmaps)
99+
cmMap, err := ciscore.NewConfigMaps(obj, profile, benchmark, c.Name, c.ImageConfig, c.configmaps, c.customScanHostPaths)
100100
if err != nil {
101101
v1.ClusterScanConditionFailed.True(obj)
102102
message := fmt.Sprintf("Error when creating ConfigMaps: %v", err)

0 commit comments

Comments
 (0)