1
16
17 package auth
18
19 import (
20 "context"
21 "fmt"
22 "io"
23
24 "github.com/spf13/cobra"
25 authenticationv1 "k8s.io/api/authentication/v1"
26 authenticationv1alpha1 "k8s.io/api/authentication/v1alpha1"
27 authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
28 "k8s.io/apimachinery/pkg/api/errors"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/runtime"
31 "k8s.io/apimachinery/pkg/util/sets"
32 "k8s.io/cli-runtime/pkg/genericclioptions"
33 "k8s.io/cli-runtime/pkg/genericiooptions"
34 "k8s.io/cli-runtime/pkg/printers"
35 authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1"
36 authenticationv1alpha1client "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1"
37 authenticationv1beta1client "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
38 cmdutil "k8s.io/kubectl/pkg/cmd/util"
39 "k8s.io/kubectl/pkg/scheme"
40 "k8s.io/kubectl/pkg/util/templates"
41 )
42
43
44
45
46 type WhoAmIFlags struct {
47 RESTClientGetter genericclioptions.RESTClientGetter
48 PrintFlags *genericclioptions.PrintFlags
49
50 genericiooptions.IOStreams
51 }
52
53
54 func NewWhoAmIFlags(restClientGetter genericclioptions.RESTClientGetter, streams genericiooptions.IOStreams) *WhoAmIFlags {
55 return &WhoAmIFlags{
56 RESTClientGetter: restClientGetter,
57 PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme),
58 IOStreams: streams,
59 }
60 }
61
62
63 func (flags *WhoAmIFlags) AddFlags(cmd *cobra.Command) {
64 flags.PrintFlags.AddFlags(cmd)
65 }
66
67
68 func (flags *WhoAmIFlags) ToOptions(ctx context.Context, args []string) (*WhoAmIOptions, error) {
69 w := &WhoAmIOptions{
70 ctx: ctx,
71 IOStreams: flags.IOStreams,
72 }
73
74 clientConfig, err := flags.RESTClientGetter.ToRESTConfig()
75 if err != nil {
76 return nil, err
77 }
78
79 w.authV1alpha1Client, err = authenticationv1alpha1client.NewForConfig(clientConfig)
80 if err != nil {
81 return nil, err
82 }
83
84 w.authV1beta1Client, err = authenticationv1beta1client.NewForConfig(clientConfig)
85 if err != nil {
86 return nil, err
87 }
88
89 w.authV1Client, err = authenticationv1client.NewForConfig(clientConfig)
90 if err != nil {
91 return nil, err
92 }
93
94 if !flags.PrintFlags.OutputFlagSpecified() {
95 w.resourcePrinterFunc = printTableSelfSubjectAccessReview
96 } else {
97 printer, err := flags.PrintFlags.ToPrinter()
98 if err != nil {
99 return nil, err
100 }
101 w.resourcePrinterFunc = printer.PrintObj
102 }
103
104 return w, nil
105 }
106
107
108
109 type WhoAmIOptions struct {
110 authV1alpha1Client authenticationv1alpha1client.AuthenticationV1alpha1Interface
111 authV1beta1Client authenticationv1beta1client.AuthenticationV1beta1Interface
112 authV1Client authenticationv1client.AuthenticationV1Interface
113
114 ctx context.Context
115
116 resourcePrinterFunc printers.ResourcePrinterFunc
117
118 genericiooptions.IOStreams
119 }
120
121 var (
122 whoAmILong = templates.LongDesc(`
123 Experimental: Check who you are and your attributes (groups, extra).
124
125 This command is helpful to get yourself aware of the current user attributes,
126 especially when dynamic authentication, e.g., token webhook, auth proxy, or OIDC provider,
127 is enabled in the Kubernetes cluster.
128 `)
129
130 whoAmIExample = templates.Examples(`
131 # Get your subject attributes.
132 kubectl auth whoami
133
134 # Get your subject attributes in JSON format.
135 kubectl auth whoami -o json
136 `)
137 )
138
139
140 func NewCmdWhoAmI(restClientGetter genericclioptions.RESTClientGetter, streams genericiooptions.IOStreams) *cobra.Command {
141 flags := NewWhoAmIFlags(restClientGetter, streams)
142
143 cmd := &cobra.Command{
144 Use: "whoami",
145 DisableFlagsInUseLine: true,
146 Short: "Experimental: Check self subject attributes",
147 Long: whoAmILong,
148 Example: whoAmIExample,
149 Run: func(cmd *cobra.Command, args []string) {
150 o, err := flags.ToOptions(cmd.Context(), args)
151 cmdutil.CheckErr(err)
152 cmdutil.CheckErr(o.Run())
153 },
154 }
155
156 flags.AddFlags(cmd)
157 return cmd
158 }
159
160 var (
161 notEnabledErr = fmt.Errorf(
162 "the selfsubjectreviews API is not enabled in the cluster\n" +
163 "enable APISelfSubjectReview feature gate and authentication.k8s.io/v1alpha1 or authentication.k8s.io/v1beta1 API")
164
165 forbiddenErr = fmt.Errorf(
166 "the selfsubjectreviews API is not enabled in the cluster or you do not have permission to call it")
167 )
168
169
170 func (o WhoAmIOptions) Run() error {
171 var (
172 res runtime.Object
173 err error
174 )
175
176 res, err = o.authV1Client.
177 SelfSubjectReviews().
178 Create(context.TODO(), &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
179 if err != nil && errors.IsNotFound(err) {
180
181 res, err = o.authV1beta1Client.
182 SelfSubjectReviews().
183 Create(context.TODO(), &authenticationv1beta1.SelfSubjectReview{}, metav1.CreateOptions{})
184 if err != nil && errors.IsNotFound(err) {
185
186 res, err = o.authV1alpha1Client.
187 SelfSubjectReviews().
188 Create(context.TODO(), &authenticationv1alpha1.SelfSubjectReview{}, metav1.CreateOptions{})
189 }
190 }
191 if err != nil {
192 switch {
193 case errors.IsForbidden(err):
194 return forbiddenErr
195 case errors.IsNotFound(err):
196 return notEnabledErr
197 default:
198 return err
199 }
200 }
201 return o.resourcePrinterFunc(res, o.Out)
202 }
203
204 func getUserInfo(obj runtime.Object) (authenticationv1.UserInfo, error) {
205 switch obj.(type) {
206 case *authenticationv1alpha1.SelfSubjectReview:
207 return obj.(*authenticationv1alpha1.SelfSubjectReview).Status.UserInfo, nil
208 case *authenticationv1beta1.SelfSubjectReview:
209 return obj.(*authenticationv1beta1.SelfSubjectReview).Status.UserInfo, nil
210 case *authenticationv1.SelfSubjectReview:
211 return obj.(*authenticationv1.SelfSubjectReview).Status.UserInfo, nil
212 default:
213 return authenticationv1.UserInfo{}, fmt.Errorf("unexpected response type %T, expected SelfSubjectReview", obj)
214 }
215 }
216
217 func printTableSelfSubjectAccessReview(obj runtime.Object, out io.Writer) error {
218 ui, err := getUserInfo(obj)
219 if err != nil {
220 return err
221 }
222
223 w := printers.GetNewTabWriter(out)
224 defer w.Flush()
225
226 _, err = fmt.Fprintf(w, "ATTRIBUTE\tVALUE\n")
227 if err != nil {
228 return fmt.Errorf("cannot write a header: %w", err)
229 }
230
231 if ui.Username != "" {
232 _, err := fmt.Fprintf(w, "Username\t%s\n", ui.Username)
233 if err != nil {
234 return fmt.Errorf("cannot write a username: %w", err)
235 }
236 }
237
238 if ui.UID != "" {
239 _, err := fmt.Fprintf(w, "UID\t%s\n", ui.UID)
240 if err != nil {
241 return fmt.Errorf("cannot write a uid: %w", err)
242 }
243 }
244
245 if len(ui.Groups) > 0 {
246 _, err := fmt.Fprintf(w, "Groups\t%v\n", ui.Groups)
247 if err != nil {
248 return fmt.Errorf("cannot write groups: %w", err)
249 }
250 }
251
252 if len(ui.Extra) > 0 {
253 for _, k := range sets.StringKeySet(ui.Extra).List() {
254 v := ui.Extra[k]
255 _, err := fmt.Fprintf(w, "Extra: %s\t%v\n", k, v)
256 if err != nil {
257 return fmt.Errorf("cannot write an extra: %w", err)
258 }
259 }
260
261 }
262 return nil
263 }
264
View as plain text