Move files from samos123/pspmigrator into here

This commit is contained in:
Sam Stoelinga
2022-06-24 10:13:51 -07:00
parent 5134cadbb8
commit 2a0d58fcb5
20 changed files with 2131 additions and 11 deletions

95
cmd/migrate.go Normal file
View File

@ -0,0 +1,95 @@
package cmd
import (
"fmt"
"log"
"os"
"github.com/manifoldco/promptui"
"github.com/olekukonko/tablewriter"
"github.com/kubernetes-sigs/pspmigrator"
"github.com/spf13/cobra"
v1 "k8s.io/api/core/v1"
psaApi "k8s.io/pod-security-admission/api"
)
var MigrateCmd = &cobra.Command{
Use: "migrate",
Short: "Interactive command to migrate from PSP to PSA ",
Long: `The interactive command will help with setting a suggested a
Suggested Pod Security Standard for each namespace. In addition, it also
checks whether a PSP object is mutating pods in every namespace.`,
Run: func(cmd *cobra.Command, args []string) {
pods := GetPods()
fmt.Println("Checking if any pods are being mutated by a PSP object")
mutatedPods := make([]v1.Pod, 0)
for _, pod := range pods.Items {
mutated, _, err := pspmigrator.IsPodBeingMutatedByPSP(&pod, clientset)
if err != nil {
log.Println(err)
}
if mutated {
mutatedPods = append(mutatedPods, pod)
}
}
if len(mutatedPods) > 0 {
fmt.Println("The table below shows the pods that were mutated by a PSP object")
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Namespace", "PSP"})
for _, pod := range mutatedPods {
if pspName, ok := pod.ObjectMeta.Annotations["kubernetes.io/psp"]; ok {
table.Append([]string{pod.Name, pod.Namespace, pspName})
}
}
table.Render()
pod := mutatedPods[0]
fmt.Printf("There were %v pods mutated. Please modify the PodSpec such that PSP no longer needs to mutate your pod.\n", len(mutatedPods))
fmt.Printf("You can run `pspmigrator mutating pod %v -n %v` to learn more why and how your pod is being mutated. ", pod.Name, pod.Namespace)
fmt.Printf("Please re-run the tool again after you've modified your PodSpecs.\n")
os.Exit(1)
}
for _, namespace := range GetNamespaces().Items {
suggestions := make(map[string]bool)
pods := GetPodsByNamespace(namespace.Name).Items
if len(pods) == 0 {
fmt.Printf("There are no pods running in namespace %v. Skipping and going to the next one.\n", namespace.Name)
continue
}
for _, pod := range pods {
level, err := pspmigrator.SuggestedPodSecurityStandard(&pod)
if err != nil {
fmt.Println("error occured checking the suggested pod security standard", err)
}
suggestions[string(level)] = true
}
var suggested psaApi.Level
if suggestions["restricted"] {
suggested = psaApi.LevelRestricted
}
if suggestions["baseline"] {
suggested = psaApi.LevelBaseline
}
if suggestions["privileged"] {
suggested = psaApi.LevelPrivileged
}
fmt.Printf("Suggest using %v in namespace %v\n", suggested, namespace.Name)
skipStr := "skip, continue with next namespace"
prompt := promptui.Select{
Label: fmt.Sprintf("Select control mode for %v on namespace %v", suggested, namespace.Name),
Items: []string{"enforce", "audit", skipStr},
}
_, control, err := prompt.Run()
if err != nil {
fmt.Println("error occured getting enforcement mode", err)
}
if control == skipStr {
continue
}
ApplyPSSLevel(&namespace, suggested, control)
fmt.Printf("Applied pod security level %v on namespace %v in %v control mode\n", suggested, namespace.Name, control)
fmt.Printf("Review the labels by running `kubectl get ns %v -o yaml`\n", namespace.Name)
}
fmt.Println("Done with migrating namespaces with pods to PSA")
},
}

118
cmd/mutating.go Normal file
View File

@ -0,0 +1,118 @@
package cmd
import (
"context"
"fmt"
"log"
"os"
"strconv"
"github.com/olekukonko/tablewriter"
"github.com/kubernetes-sigs/pspmigrator"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var MutatingCmd = &cobra.Command{
Use: "mutating",
Short: "Check if pods or PSP objects are mutating",
}
func initMutating() {
podCmd := cobra.Command{
Use: "pod [name of pod]",
Short: "Check if a pod is being mutated by a PSP policy",
Run: func(cmd *cobra.Command, args []string) {
// Examples for error handling:
// - Use helper functions like e.g. errors.IsNotFound()
// - And/or cast to StatusError and use its properties like e.g. ErrStatus.Message
pod := args[0]
podObj, err := clientset.CoreV1().Pods(Namespace).Get(context.TODO(), pod, metav1.GetOptions{})
if errors.IsNotFound(err) {
fmt.Printf("Pod %s in namespace %s not found\n", pod, Namespace)
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
fmt.Printf("Error getting pod %s in namespace %s: %v\n",
pod, Namespace, statusError.ErrStatus.Message)
} else if err != nil {
panic(err.Error())
} else {
mutated, diff, err := pspmigrator.IsPodBeingMutatedByPSP(podObj, clientset)
if err != nil {
log.Println(err)
}
if pspName, ok := podObj.ObjectMeta.Annotations["kubernetes.io/psp"]; ok {
fmt.Printf("Pod %v is mutated by PSP %v: %v, diff: %v\n", podObj.Name, pspName, mutated, diff)
pspObj, err := clientset.PolicyV1beta1().PodSecurityPolicies().Get(context.TODO(), pspName, metav1.GetOptions{})
if errors.IsNotFound(err) {
fmt.Printf("PodSecurityPolicy %s not found\n", pspName)
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
fmt.Printf("Error getting PodSecurityPolicy %s: %v\n",
pspName, statusError.ErrStatus.Message)
} else if err != nil {
panic(err.Error())
} else {
_, fields, annotations := pspmigrator.IsPSPMutating(pspObj)
fmt.Printf("PSP profile %v has the following mutating fields: %v and annotations: %v\n", pspName, fields, annotations)
}
}
}
},
Args: cobra.ExactArgs(1),
}
podCmd.Flags().StringVarP(&Namespace, "namespace", "n", "", "K8s namespace (required)")
podCmd.MarkFlagRequired("namespace")
podsCmd := cobra.Command{
Use: "pods",
Short: "Check all pods across all namespaces in a cluster are being mutated by a PSP policy",
Run: func(cmd *cobra.Command, args []string) {
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"Name", "Namespace", "Mutated", "PSP"})
pods := GetPods()
fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))
for _, pod := range pods.Items {
if pspName, ok := pod.ObjectMeta.Annotations["kubernetes.io/psp"]; ok {
mutated, _, err := pspmigrator.IsPodBeingMutatedByPSP(&pod, clientset)
if err != nil {
log.Println("error occured checking if pod is mutated:", err)
}
table.Append([]string{pod.Name, pod.Namespace, strconv.FormatBool(mutated), pspName})
}
}
table.Render() // Send output
},
Args: cobra.NoArgs,
}
pspCmd := cobra.Command{
Use: "psp [name of PSP object]",
Short: "Check if a PSP object is potentially mutating pods",
Run: func(cmd *cobra.Command, args []string) {
// Examples for error handling:
// - Use helper functions like e.g. errors.IsNotFound()
// - And/or cast to StatusError and use its properties like e.g. ErrStatus.Message
pspName := args[0]
pspObj, err := clientset.PolicyV1beta1().PodSecurityPolicies().Get(context.TODO(), pspName, metav1.GetOptions{})
if errors.IsNotFound(err) {
fmt.Printf("PodSecurityPolicy %s not found\n", pspName)
} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
fmt.Printf("Error getting PodSecurityPolicy %s: %v\n",
pspName, statusError.ErrStatus.Message)
} else if err != nil {
panic(err.Error())
} else {
_, fields, annotations := pspmigrator.IsPSPMutating(pspObj)
fmt.Printf("PSP profile %v has the following mutating fields: %v and annotations: %v\n", pspName, fields, annotations)
}
},
Args: cobra.ExactArgs(1),
}
MutatingCmd.AddCommand(&podCmd)
MutatingCmd.AddCommand(&podsCmd)
MutatingCmd.AddCommand(&pspCmd)
}

26
cmd/pspmigrator/main.go Normal file
View File

@ -0,0 +1,26 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Note: the example only works with the code within the same release/branch.
package main
import (
"github.com/kubernetes-sigs/pspmigrator/cmd"
)
func main() {
cmd.RootCmd.Execute()
}

57
cmd/root.go Normal file
View File

@ -0,0 +1,57 @@
package cmd
import (
"path/filepath"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
_ "k8s.io/client-go/plugin/pkg/client/auth"
//
// Or uncomment to load specific auth plugins
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
_ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)
var RootCmd = &cobra.Command{
Use: "pspmigrator",
Short: "pspmigrator is a tool to help migrate from PSP to PSA",
}
var (
clientset *kubernetes.Clientset
err error
Namespace string
)
func init() {
initMutating()
RootCmd.AddCommand(MutatingCmd)
RootCmd.AddCommand(MigrateCmd)
var kubeconfig string
if home := homedir.HomeDir(); home != "" {
RootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k",
filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
} else {
RootCmd.PersistentFlags().StringVarP(&kubeconfig, "kubeconfig", "k", "", "absolute path to the kubeconfig file")
}
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
config.WarningHandler = rest.NoWarnings{}
if err != nil {
panic(err.Error())
}
// create the clientset
clientset, err = kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
}

54
cmd/utils.go Normal file
View File

@ -0,0 +1,54 @@
package cmd
import (
"context"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
psaApi "k8s.io/pod-security-admission/api"
)
func IgnoreNamespaceSelector(field string) string {
ignoredNamespaces := []string{"kube-system", "kube-public", "kube-node-lease"}
selectors := make([]fields.Selector, 0)
for _, n := range ignoredNamespaces {
selectors = append(selectors, fields.OneTermNotEqualSelector(field, n))
}
return fields.AndSelectors(selectors...).String()
}
func GetPods() *v1.PodList {
listOptions := metav1.ListOptions{FieldSelector: IgnoreNamespaceSelector("metadata.namespace")}
pods, err := clientset.CoreV1().Pods("").List(context.TODO(), listOptions)
if err != nil {
panic(err.Error())
}
return pods
}
func GetPodsByNamespace(namespace string) *v1.PodList {
listOptions := metav1.ListOptions{}
pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), listOptions)
if err != nil {
panic(err.Error())
}
return pods
}
func GetNamespaces() *v1.NamespaceList {
listOptions := metav1.ListOptions{FieldSelector: IgnoreNamespaceSelector("metadata.name")}
namespaces, err := clientset.CoreV1().Namespaces().List(context.TODO(), listOptions)
if err != nil {
panic(err.Error())
}
return namespaces
}
func ApplyPSSLevel(namespace *v1.Namespace, level psaApi.Level, control string) {
namespace.Labels["pod-security.kubernetes.io/"+control] = string(level)
namespace, err := clientset.CoreV1().Namespaces().Update(context.TODO(), namespace, metav1.UpdateOptions{})
if err != nil {
panic(err.Error())
}
}