Skip to content

Commit 4908523

Browse files
authored
Merge pull request #29 from sysdiglabs/allow-psp-gen-files
Add ability to convert K8s Obj to PSP
2 parents df1989f + cc6425d commit 4908523

File tree

7 files changed

+887
-571
lines changed

7 files changed

+887
-571
lines changed

README.MD

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
# Kube PodSecurityPolicy Advisor
22

3+
kube-psp-advisor is a tool that makes it easier to create K8s Pod Security Policies (PSPs) from either a live K8s environment or from a single .yaml file containing a pod specification (Deployment, DaemonSet, Pod, etc).
4+
5+
It has 2 subcommands, `kube-psp-advisor inspect` and `kube-psp-advisor convert`. `inspect` connects to a K8s API server, downloads all Pod-related objects in a given namespace, and generates a PSP based on the properties of those objects. `convert` works without connecting to an API Server, reading a single .yaml file containing a object with a pod spec and generating a PSP based on the file.
6+
37
## Build and Run locally
48
1. ```make build```
5-
2. ```./kube-psp-advisor``` to generate Pod Security Policy based on running cluster configuration
6-
3. ```./kube-psp-advisor --report``` to print the details reports (why this PSP is recommended for the cluster)
9+
2. ```./kube-psp-advisor inspect``` to generate Pod Security Policy based on running cluster configuration
10+
3. ```./kube-psp-advisor inspect --report``` to print the details reports (why this PSP is recommended for the cluster)
11+
4. ```./kube-psp-advisor convert --podFile <path> --pspFile <path>``` to generate a PSP from a single .yaml file.
712

813
## Build and Run as Container
914
1. ```docker build -t <Image Name> -f container/Dockerfile .```
@@ -37,9 +42,9 @@ Some attributes(capabilities, host ports etc.) required gathering runtime inform
3742
- [x] Basic functionalities;
3843
- [ ] Create PSP's for common charts
3944
- [ ] Kubectl plugin
40-
45+
`
4146
## Sample Pod Security Policy
42-
Command: `./kube-psp-advisor --namespace=psp-test`
47+
Command: `./kube-psp-advisor inspect --namespace=psp-test`
4348
```
4449
apiVersion: policy/v1beta1
4550
kind: PodSecurityPolicy
@@ -73,8 +78,8 @@ spec:
7378
- secret
7479
```
7580

76-
## Sample Report
77-
Command: `./kube-psp-advisor --namespace=psp-test --report | jq .podSecuritySpecs`
81+
## Sample Report
82+
Command: `./kube-psp-advisor inspect --namespace=psp-test --report | jq .podSecuritySpecs`
7883
```
7984
{
8085
"hostIPC": [

advisor/processor/generate.go

Lines changed: 9 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ package processor
22

33
import (
44
"fmt"
5-
"time"
65

6+
"github.com/sysdiglabs/kube-psp-advisor/generator"
77
"github.com/sysdiglabs/kube-psp-advisor/advisor/report"
88
"github.com/sysdiglabs/kube-psp-advisor/advisor/types"
9-
"github.com/sysdiglabs/kube-psp-advisor/utils"
109

1110
v1 "k8s.io/api/core/v1"
1211
"k8s.io/api/policy/v1beta1"
@@ -20,10 +19,16 @@ type Processor struct {
2019
namespace string
2120
serviceAccountMap map[string]v1.ServiceAccount
2221
serverGitVersion string
22+
gen *generator.Generator
2323
}
2424

2525
// NewProcessor returns a new processor
2626
func NewProcessor(kubeconfig string) (*Processor, error) {
27+
28+
gen, err := generator.NewGenerator(); if err != nil {
29+
return nil, fmt.Errorf("Could not create generator: %v", err)
30+
}
31+
2732
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
2833
if err != nil {
2934
return nil, err
@@ -43,6 +48,7 @@ func NewProcessor(kubeconfig string) (*Processor, error) {
4348
k8sClient: clientset,
4449
resourceNamePrefix: map[string]bool{},
4550
serverGitVersion: info.GitVersion,
51+
gen: gen,
4652
}, nil
4753
}
4854

@@ -52,186 +58,8 @@ func (p *Processor) SetNamespace(ns string) {
5258

5359
// GeneratePSP generate Pod Security Policy
5460
func (p *Processor) GeneratePSP(cssList []types.ContainerSecuritySpec, pssList []types.PodSecuritySpec) *v1beta1.PodSecurityPolicy {
55-
var ns string
56-
// no PSP will be generated if no security spec is provided
57-
if len(cssList) == 0 && len(pssList) == 0 {
58-
return nil
59-
}
60-
61-
psp := &v1beta1.PodSecurityPolicy{}
62-
63-
psp.APIVersion = "policy/v1beta1"
64-
psp.Kind = "PodSecurityPolicy"
65-
66-
addedCap := map[string]int{}
67-
droppedCap := map[string]int{}
68-
69-
effectiveCap := map[string]bool{}
70-
71-
runAsUser := map[int64]bool{}
72-
73-
volumeTypes := map[string]bool{}
74-
75-
hostPaths := map[string]bool{}
76-
77-
runAsUserCount := 0
78-
79-
runAsNonRootCount := 0
80-
81-
notAllowPrivilegeEscationCount := 0
82-
83-
ns = p.namespace
84-
85-
if ns == "" {
86-
ns = "all"
87-
}
88-
89-
psp.Name = fmt.Sprintf("%s-%s-%s", "pod-security-policy", ns, time.Now().Format("20060102150405"))
90-
91-
for _, sc := range pssList {
92-
psp.Spec.HostPID = psp.Spec.HostPID || sc.HostPID
93-
psp.Spec.HostIPC = psp.Spec.HostIPC || sc.HostIPC
94-
psp.Spec.HostNetwork = psp.Spec.HostNetwork || sc.HostNetwork
95-
96-
for _, t := range sc.VolumeTypes {
97-
volumeTypes[t] = true
98-
}
99-
100-
for path, readOnly := range sc.MountHostPaths {
101-
if _, exists := hostPaths[path]; !exists {
102-
hostPaths[path] = readOnly
103-
} else {
104-
hostPaths[path] = readOnly && hostPaths[path]
105-
}
106-
}
107-
}
108-
109-
for _, sc := range cssList {
110-
for _, cap := range sc.Capabilities {
111-
effectiveCap[cap] = true
112-
}
113-
114-
for _, cap := range sc.AddedCap {
115-
addedCap[cap]++
116-
}
117-
118-
for _, cap := range sc.DroppedCap {
119-
droppedCap[cap]++
120-
}
121-
122-
psp.Spec.Privileged = psp.Spec.Privileged || sc.Privileged
123-
124-
psp.Spec.ReadOnlyRootFilesystem = psp.Spec.ReadOnlyRootFilesystem || sc.ReadOnlyRootFS
125-
126-
if sc.RunAsNonRoot != nil && *sc.RunAsNonRoot {
127-
runAsNonRootCount++
128-
}
129-
130-
if sc.RunAsUser != nil {
131-
runAsUser[*sc.RunAsUser] = true
132-
runAsUserCount++
133-
}
134-
135-
if sc.AllowPrivilegeEscalation != nil && !*sc.AllowPrivilegeEscalation {
136-
notAllowPrivilegeEscationCount++
137-
}
138-
139-
// set host ports
140-
// TODO: need to integrate with listening port during the runtime, might cause false positive.
141-
//for _, port := range sc.HostPorts {
142-
// psp.Spec.HostPorts = append(psp.Spec.HostPorts, v1beta1.HostPortRange{Min: port, Max: port})
143-
//}
144-
}
145-
146-
// set allowedPrivilegeEscalation
147-
if notAllowPrivilegeEscationCount == len(cssList) {
148-
notAllowed := false
149-
psp.Spec.AllowPrivilegeEscalation = &notAllowed
150-
}
151-
152-
// set runAsUser strategy
153-
if runAsNonRootCount == len(cssList) {
154-
psp.Spec.RunAsUser.Rule = v1beta1.RunAsUserStrategyMustRunAsNonRoot
155-
}
156-
157-
if runAsUserCount == len(cssList) {
158-
psp.Spec.RunAsUser.Rule = v1beta1.RunAsUserStrategyMustRunAs
159-
for uid := range runAsUser {
160-
if psp.Spec.RunAsUser.Rule == v1beta1.RunAsUserStrategyMustRunAsNonRoot && uid != 0 {
161-
psp.Spec.RunAsUser.Ranges = append(psp.Spec.RunAsUser.Ranges, v1beta1.IDRange{
162-
Min: uid,
163-
Max: uid,
164-
})
165-
}
166-
}
167-
}
168-
169-
// set allowed host path
170-
enforceReadOnly, _ := utils.CompareVersion(p.serverGitVersion, types.Version1_11)
171-
172-
for path, readOnly := range hostPaths {
173-
psp.Spec.AllowedHostPaths = append(psp.Spec.AllowedHostPaths, v1beta1.AllowedHostPath{
174-
PathPrefix: path,
175-
ReadOnly: readOnly || enforceReadOnly,
176-
})
177-
}
178-
179-
// set limit volumes
180-
volumeTypeList := utils.MapToArray(volumeTypes)
181-
182-
for _, v := range volumeTypeList {
183-
psp.Spec.Volumes = append(psp.Spec.Volumes, v1beta1.FSType(v))
184-
}
185-
186-
// set allowedCapabilities
187-
defaultCap := utils.ArrayToMap(types.DefaultCaps)
188-
for cap := range defaultCap {
189-
if _, exists := effectiveCap[cap]; exists {
190-
delete(effectiveCap, cap)
191-
}
192-
}
193-
194-
// set allowedAddCapabilities
195-
for cap := range effectiveCap {
196-
psp.Spec.AllowedCapabilities = append(psp.Spec.AllowedCapabilities, v1.Capability(cap))
197-
}
198-
199-
// set defaultAddCapabilities
200-
for k, v := range addedCap {
201-
if v == len(cssList) {
202-
psp.Spec.DefaultAddCapabilities = append(psp.Spec.DefaultAddCapabilities, v1.Capability(k))
203-
}
204-
}
205-
206-
// set requiredDroppedCapabilities
207-
for k, v := range droppedCap {
208-
if v == len(cssList) {
209-
psp.Spec.RequiredDropCapabilities = append(psp.Spec.RequiredDropCapabilities, v1.Capability(k))
210-
}
211-
}
212-
213-
// set to default values
214-
if string(psp.Spec.RunAsUser.Rule) == "" {
215-
psp.Spec.RunAsUser.Rule = v1beta1.RunAsUserStrategyRunAsAny
216-
}
217-
218-
if psp.Spec.RunAsGroup != nil && string(psp.Spec.RunAsGroup.Rule) == "" {
219-
psp.Spec.RunAsGroup.Rule = v1beta1.RunAsGroupStrategyRunAsAny
220-
}
221-
222-
if string(psp.Spec.FSGroup.Rule) == "" {
223-
psp.Spec.FSGroup.Rule = v1beta1.FSGroupStrategyRunAsAny
224-
}
225-
226-
if string(psp.Spec.SELinux.Rule) == "" {
227-
psp.Spec.SELinux.Rule = v1beta1.SELinuxStrategyRunAsAny
228-
}
229-
230-
if string(psp.Spec.SupplementalGroups.Rule) == "" {
231-
psp.Spec.SupplementalGroups.Rule = v1beta1.SupplementalGroupsStrategyRunAsAny
232-
}
23361

234-
return psp
62+
return p.gen.GeneratePSP(cssList, pssList, p.namespace, p.serverGitVersion)
23563
}
23664

23765
// GenerateReport generate a JSON report

0 commit comments

Comments
 (0)