-
Notifications
You must be signed in to change notification settings - Fork 200
/
kubernetes.go
112 lines (97 loc) · 3.2 KB
/
kubernetes.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package providers
import (
"context"
"github.com/datadog/stratus-red-team/v2/internal/utils"
"github.com/google/uuid"
authv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
"log"
"os"
"path/filepath"
)
const (
KubeconfigDefaultPath = ".kube/config"
)
type K8sProvider struct {
k8sClient *kubernetes.Clientset
RestConfig *rest.Config
UniqueCorrelationId uuid.UUID // unique value injected in the user-agent, to differentiate Stratus Red Team executions
}
var (
kubeConfigPath string
kubeConfigPathWasResolved bool
)
func NewK8sProvider(uuid uuid.UUID) *K8sProvider {
kubeconfig := GetKubeConfigPath()
// Will default to an in-cluster client config if kubeconfig path is not set
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
log.Fatalf("unable to build kube config: %v", err)
}
restConfig := config
restConfig.UserAgent = GetStratusUserAgentForUUID(uuid)
k8sClient, err := kubernetes.NewForConfig(restConfig)
if err != nil {
log.Fatalf("unable to create kube client: %v", err)
}
return &K8sProvider{
UniqueCorrelationId: uuid,
RestConfig: restConfig,
k8sClient: k8sClient,
}
}
// GetKubeConfigPath returns the path of the kubeconfig, with the following priority:
// 1. KUBECONFIG environment variable
// 2. $HOME/.kube/config
func GetKubeConfigPath() string {
if !kubeConfigPathWasResolved {
kubeConfigPath = getKubeConfigPath()
kubeConfigPathWasResolved = true // Note: we can't use an empty string since it's a possible return value of getKubeConfigPath
}
return kubeConfigPath
}
// unexported function with the main logic
func getKubeConfigPath() string {
// if KUBECONFIG is set, use it
if kubeConfigEnvPath := os.Getenv("KUBECONFIG"); kubeConfigEnvPath != "" {
return kubeConfigEnvPath
}
// Otherwise, use $HOME/.kube/config if it exists
if kubeConfigFilePath := filepath.Join(homedir.HomeDir(), KubeconfigDefaultPath); utils.FileExists(kubeConfigFilePath) {
return kubeConfigFilePath
}
// Otherwise, return an empty string
// This will cause `clientcmd.BuildConfigFromFlags` called in `GetClient` will try to use
// in-cluster auth
// c.f. https://pkg.go.dev/k8s.io/client-go/tools/clientcmd#BuildConfigFromFlags
return ""
}
// GetClient is used to authenticate with Kubernetes and build the client from a kubeconfig
func (m *K8sProvider) GetClient() *kubernetes.Clientset {
return m.k8sClient
}
func (m *K8sProvider) GetRestConfig() *rest.Config {
return m.RestConfig
}
func (m *K8sProvider) IsAuthenticated() bool {
// We assume if the current user can do 'kubectl list pods' in the default namespace, they are authenticated
// Note: we do not perform authorization checks
var self = authv1.SelfSubjectAccessReview{
Spec: authv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authv1.ResourceAttributes{
Verb: "list",
Resource: "pods",
},
},
}
auth, err := m.k8sClient.AuthorizationV1().SelfSubjectAccessReviews().Create(
context.Background(),
&self,
metav1.CreateOptions{},
)
return err == nil || auth.Status.Allowed
}