-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a pod template generation command (#5)
* Add a template generator command to create bad pods * Add tests for the kgen package * Add documentation about the new gen feature in README
- Loading branch information
Showing
4 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package commands | ||
|
||
import ( | ||
"os" | ||
|
||
"github.com/quarkslab/kdigger/pkg/kgen" | ||
"github.com/spf13/cobra" | ||
"k8s.io/cli-runtime/pkg/printers" | ||
) | ||
|
||
var opts kgen.GenerateOpts | ||
|
||
var genAll bool | ||
|
||
var genCmd = &cobra.Command{ | ||
Use: "gen [name] [flags]", | ||
Aliases: []string{"generate"}, | ||
Short: "Generate template for pod with security features disabled", | ||
Long: `This command generates templates for pod with security features disabled. | ||
You can customize the pods with some of the string flags and activate | ||
boolean flags to disabled security features. Examples: | ||
# Generate a very simple template in json | ||
kdigger gen -o json | ||
# Create a very simple pod | ||
kdigger gen | kubectl apply -f - | ||
# Create a pod named mypod with most security features disabled | ||
kdigger gen -all mypod | kubectl apply -f - | ||
# Create a custom privileged pod | ||
kdigger gen --privileged --image bash --command watch --command date | kubectl apply -f -`, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
// all puts all the boolean flags to true | ||
if genAll { | ||
opts.HostNetwork = true | ||
opts.Privileged = true | ||
opts.HostPath = true | ||
opts.HostPid = true | ||
opts.Tolerations = true | ||
} | ||
if len(args) > 0 { | ||
opts.Name = args[0] | ||
} | ||
|
||
pod := kgen.Generate(opts) | ||
|
||
var p printers.ResourcePrinter | ||
if output == "json" { | ||
p = &printers.JSONPrinter{} | ||
} else { | ||
p = &printers.YAMLPrinter{} | ||
} | ||
err := p.PrintObj(pod, os.Stdout) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.AddCommand(genCmd) | ||
|
||
genCmd.Flags().StringVar(&opts.Image, "image", "busybox", "Container image used") | ||
genCmd.Flags().StringArrayVar(&opts.Command, "command", []string{"sleep", "infinitely"}, "Container command used") | ||
|
||
genCmd.Flags().BoolVar(&opts.Privileged, "privileged", false, "Add the security flag to the security context of the pod") | ||
genCmd.Flags().BoolVar(&opts.HostPath, "hostpath", false, "Add a hostPath volume to the container") | ||
genCmd.Flags().BoolVar(&opts.Tolerations, "tolerations", false, "Add tolerations to be schedulable on most nodes") | ||
genCmd.Flags().BoolVar(&opts.HostPid, "hostpid", false, "Add the hostPid flag on the whole pod") | ||
genCmd.Flags().BoolVar(&opts.HostNetwork, "hostnetwork", false, "Add the hostNetwork flag on the whole pod") | ||
|
||
genCmd.Flags().BoolVar(&genAll, "all", false, "Enable everything") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package kgen | ||
|
||
import ( | ||
v1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
||
"k8s.io/apimachinery/pkg/util/rand" | ||
) | ||
|
||
type GenerateOpts struct { | ||
Name string | ||
Image string | ||
Command []string | ||
Privileged bool | ||
HostPath bool | ||
HostPid bool | ||
HostNetwork bool | ||
Tolerations bool | ||
} | ||
|
||
func Generate(opts GenerateOpts) *v1.Pod { | ||
pod := &v1.Pod{ | ||
TypeMeta: metav1.TypeMeta{ | ||
Kind: "Pod", | ||
APIVersion: "v1", | ||
}, | ||
ObjectMeta: metav1.ObjectMeta{}, | ||
Spec: v1.PodSpec{ | ||
Containers: []v1.Container{ | ||
{ | ||
Command: []string{"sleep", "infinitely"}, | ||
Name: "digger", | ||
Image: "busybox", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
if opts.Name == "" { | ||
pod.ObjectMeta.Name = "digger-" + rand.String(5) | ||
} else { | ||
pod.ObjectMeta.Name = opts.Name | ||
pod.Spec.Containers[0].Name = opts.Name | ||
} | ||
|
||
if opts.Image != "" { | ||
pod.Spec.Containers[0].Image = opts.Image | ||
} | ||
|
||
if len(opts.Command) != 0 { | ||
pod.Spec.Containers[0].Command = opts.Command | ||
} | ||
|
||
pod.Spec.HostPID = opts.HostPid | ||
|
||
pod.Spec.HostNetwork = opts.HostNetwork | ||
|
||
if opts.Privileged { | ||
pod.Spec.Containers[0].SecurityContext = &v1.SecurityContext{ | ||
Privileged: &opts.Privileged, | ||
} | ||
} | ||
|
||
if opts.HostPath { | ||
pod.Spec.Volumes = append(pod.Spec.Volumes, v1.Volume{ | ||
Name: "host", | ||
VolumeSource: v1.VolumeSource{ | ||
HostPath: &v1.HostPathVolumeSource{ | ||
Path: "/", | ||
}, | ||
}, | ||
}) | ||
pod.Spec.Containers[0].VolumeMounts = append(pod.Spec.Containers[0].VolumeMounts, v1.VolumeMount{ | ||
Name: "host", | ||
MountPath: "/hostfs", | ||
}) | ||
} | ||
|
||
if opts.Tolerations { | ||
pod.Spec.Tolerations = append(pod.Spec.Tolerations, []v1.Toleration{ | ||
{ | ||
Key: "NoExecute", | ||
Operator: "Exists", | ||
}, | ||
{ | ||
Key: "NoSchedule", | ||
Operator: "Exists", | ||
}, | ||
{ | ||
Key: "CriticalAddonsOnly", | ||
Operator: "Exists", | ||
}, | ||
}...) | ||
} | ||
|
||
return pod | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package kgen | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
v1 "k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/util/rand" | ||
) | ||
|
||
func TestGenerateManualName(t *testing.T) { | ||
args := GenerateOpts{ | ||
Name: "testname", | ||
} | ||
if got := Generate(args); got.ObjectMeta.Name != args.Name { | ||
t.Errorf("Generate() = %v, want %v", got.ObjectMeta.Name, args.Name) | ||
} | ||
} | ||
|
||
func TestGenerateAutoName(t *testing.T) { | ||
args := GenerateOpts{} | ||
// generate pseudo random | ||
rand.Seed(0) | ||
name := "digger-cbhtc" | ||
if got := Generate(args); got.ObjectMeta.Name != name { | ||
t.Errorf("Generate() = %v, want %v", got.ObjectMeta.Name, name) | ||
} | ||
} | ||
|
||
func TestGenerateManualImage(t *testing.T) { | ||
args := GenerateOpts{ | ||
Image: "testimage", | ||
} | ||
if got := Generate(args); got.Spec.Containers[0].Image != args.Image { | ||
t.Errorf("Generate() = %v, want %v", got.Spec.Containers[0].Image, args.Image) | ||
} | ||
} | ||
|
||
func TestGenerateManualCommand(t *testing.T) { | ||
args := GenerateOpts{ | ||
Command: []string{"test", "command"}, | ||
} | ||
if got := Generate(args); len(got.Spec.Containers[0].Command) != 2 && got.Spec.Containers[0].Command[0] != "test" && got.Spec.Containers[0].Command[1] != "command" { | ||
t.Errorf("Generate() = %v, want %v", got.Spec.Containers[0].Command, args.Command) | ||
} | ||
} | ||
|
||
func TestGenerateHostPID(t *testing.T) { | ||
args := GenerateOpts{ | ||
HostPid: true, | ||
} | ||
if got := Generate(args); !got.Spec.HostPID { | ||
t.Errorf("Generate() = %v, want %v", got.Spec.HostPID, true) | ||
} | ||
args = GenerateOpts{ | ||
HostPid: false, | ||
} | ||
if got := Generate(args); got.Spec.HostPID { | ||
t.Errorf("Generate() = %v, want %v", got.Spec.HostPID, false) | ||
} | ||
} | ||
|
||
func TestGenerateHostNetwork(t *testing.T) { | ||
args := GenerateOpts{ | ||
HostNetwork: true, | ||
} | ||
if got := Generate(args); !got.Spec.HostNetwork { | ||
t.Errorf("Generate() = %v, want %v", got.Spec.HostNetwork, true) | ||
} | ||
args = GenerateOpts{ | ||
HostNetwork: false, | ||
} | ||
if got := Generate(args); got.Spec.HostNetwork { | ||
t.Errorf("Generate() = %v, want %v", got.Spec.HostNetwork, false) | ||
} | ||
} | ||
|
||
func TestGeneratePrivileged(t *testing.T) { | ||
args := []GenerateOpts{ | ||
{ | ||
Privileged: true, | ||
}, | ||
{ | ||
Privileged: false, | ||
}, | ||
} | ||
for _, arg := range args { | ||
if got := Generate(arg); got.Spec.Containers[0].SecurityContext != nil && got.Spec.Containers[0].SecurityContext.Privileged == &arg.Privileged { | ||
t.Errorf("Generate() = %v, want %v", got.Spec.Containers[0].SecurityContext.Privileged, &arg.Privileged) | ||
} | ||
} | ||
} | ||
|
||
func TestGenerateHostPath(t *testing.T) { | ||
args := GenerateOpts{ | ||
HostPath: true, | ||
} | ||
if got := Generate(args); len(got.Spec.Containers[0].VolumeMounts) != 1 && | ||
got.Spec.Containers[0].VolumeMounts[0].Name == "host" && | ||
got.Spec.Containers[0].VolumeMounts[0].MountPath == "hostfs" && | ||
got.Spec.Volumes[0].Name == "host" && | ||
got.Spec.Volumes[0].HostPath.Path == "/" { | ||
t.Errorf("Generate() = %v and %v", got.Spec.Containers[0].VolumeMounts[0], got.Spec.Volumes[0]) | ||
} | ||
} | ||
|
||
func TestGenerateTolerations(t *testing.T) { | ||
args := GenerateOpts{ | ||
Tolerations: true, | ||
} | ||
want := []v1.Toleration{ | ||
{ | ||
Key: "NoExecute", | ||
Operator: "Exists", | ||
}, | ||
{ | ||
Key: "NoSchedule", | ||
Operator: "Exists", | ||
}, | ||
{ | ||
Key: "CriticalAddonsOnly", | ||
Operator: "Exists", | ||
}, | ||
} | ||
if got := Generate(args); reflect.DeepEqual(got, want) { | ||
t.Errorf("Generate() = %v, want %v", got, want) | ||
} | ||
} |