Move files from samos123/pspmigrator into here
This commit is contained in:
95
cmd/migrate.go
Normal file
95
cmd/migrate.go
Normal 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
118
cmd/mutating.go
Normal 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
26
cmd/pspmigrator/main.go
Normal 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
57
cmd/root.go
Normal 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
54
cmd/utils.go
Normal 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())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user