1
16
17 package create
18
19 import (
20 "context"
21 "crypto/tls"
22 "fmt"
23 "os"
24
25 "github.com/spf13/cobra"
26 corev1 "k8s.io/api/core/v1"
27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 "k8s.io/apimachinery/pkg/runtime"
29 "k8s.io/cli-runtime/pkg/genericclioptions"
30 "k8s.io/cli-runtime/pkg/genericiooptions"
31 corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
32 cmdutil "k8s.io/kubectl/pkg/cmd/util"
33 "k8s.io/kubectl/pkg/scheme"
34 "k8s.io/kubectl/pkg/util"
35 "k8s.io/kubectl/pkg/util/hash"
36 "k8s.io/kubectl/pkg/util/i18n"
37 "k8s.io/kubectl/pkg/util/templates"
38 )
39
40 var (
41 secretForTLSLong = templates.LongDesc(i18n.T(`
42 Create a TLS secret from the given public/private key pair.
43
44 The public/private key pair must exist beforehand. The public key certificate must be .PEM encoded and match
45 the given private key.`))
46
47 secretForTLSExample = templates.Examples(i18n.T(`
48 # Create a new TLS secret named tls-secret with the given key pair
49 kubectl create secret tls tls-secret --cert=path/to/tls.crt --key=path/to/tls.key`))
50 )
51
52
53 type CreateSecretTLSOptions struct {
54
55 PrintFlags *genericclioptions.PrintFlags
56 PrintObj func(obj runtime.Object) error
57
58
59 Name string
60
61 Key string
62
63 Cert string
64
65 AppendHash bool
66
67 FieldManager string
68 CreateAnnotation bool
69 Namespace string
70 EnforceNamespace bool
71
72 Client corev1client.CoreV1Interface
73 DryRunStrategy cmdutil.DryRunStrategy
74 ValidationDirective string
75
76 genericiooptions.IOStreams
77 }
78
79
80 func NewSecretTLSOptions(ioStrems genericiooptions.IOStreams) *CreateSecretTLSOptions {
81 return &CreateSecretTLSOptions{
82 PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
83 IOStreams: ioStrems,
84 }
85 }
86
87
88 func NewCmdCreateSecretTLS(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
89 o := NewSecretTLSOptions(ioStreams)
90
91 cmd := &cobra.Command{
92 Use: "tls NAME --cert=path/to/cert/file --key=path/to/key/file [--dry-run=server|client|none]",
93 DisableFlagsInUseLine: true,
94 Short: i18n.T("Create a TLS secret"),
95 Long: secretForTLSLong,
96 Example: secretForTLSExample,
97 Run: func(cmd *cobra.Command, args []string) {
98 cmdutil.CheckErr(o.Complete(f, cmd, args))
99 cmdutil.CheckErr(o.Validate())
100 cmdutil.CheckErr(o.Run())
101 },
102 }
103
104 o.PrintFlags.AddFlags(cmd)
105
106 cmdutil.AddApplyAnnotationFlags(cmd)
107 cmdutil.AddValidateFlags(cmd)
108 cmdutil.AddDryRunFlag(cmd)
109
110 cmd.Flags().StringVar(&o.Cert, "cert", o.Cert, i18n.T("Path to PEM encoded public key certificate."))
111 cmd.Flags().StringVar(&o.Key, "key", o.Key, i18n.T("Path to private key associated with given certificate."))
112 cmd.Flags().BoolVar(&o.AppendHash, "append-hash", o.AppendHash, "Append a hash of the secret to its name.")
113
114 cmdutil.AddFieldManagerFlagVar(cmd, &o.FieldManager, "kubectl-create")
115
116 return cmd
117 }
118
119
120 func (o *CreateSecretTLSOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
121 var err error
122 o.Name, err = NameFromCommandArgs(cmd, args)
123 if err != nil {
124 return err
125 }
126
127 restConfig, err := f.ToRESTConfig()
128 if err != nil {
129 return err
130 }
131
132 o.Client, err = corev1client.NewForConfig(restConfig)
133 if err != nil {
134 return err
135 }
136
137 o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
138
139 o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
140 if err != nil {
141 return err
142 }
143
144 o.Namespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
145 if err != nil {
146 return err
147 }
148
149 cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
150 printer, err := o.PrintFlags.ToPrinter()
151 if err != nil {
152 return err
153 }
154
155 o.PrintObj = func(obj runtime.Object) error {
156 return printer.PrintObj(obj, o.Out)
157 }
158
159 o.ValidationDirective, err = cmdutil.GetValidationDirective(cmd)
160 if err != nil {
161 return err
162 }
163
164 return nil
165 }
166
167
168 func (o *CreateSecretTLSOptions) Validate() error {
169
170
171
172 if len(o.Key) == 0 || len(o.Cert) == 0 {
173 return fmt.Errorf("key and cert must be specified")
174 }
175 return nil
176 }
177
178
179
180 func (o *CreateSecretTLSOptions) Run() error {
181 secretTLS, err := o.createSecretTLS()
182 if err != nil {
183 return err
184 }
185 err = util.CreateOrUpdateAnnotation(o.CreateAnnotation, secretTLS, scheme.DefaultJSONEncoder())
186 if err != nil {
187 return err
188 }
189 if o.DryRunStrategy != cmdutil.DryRunClient {
190 createOptions := metav1.CreateOptions{}
191 if o.FieldManager != "" {
192 createOptions.FieldManager = o.FieldManager
193 }
194 createOptions.FieldValidation = o.ValidationDirective
195 if o.DryRunStrategy == cmdutil.DryRunServer {
196 createOptions.DryRun = []string{metav1.DryRunAll}
197 }
198 secretTLS, err = o.Client.Secrets(o.Namespace).Create(context.TODO(), secretTLS, createOptions)
199 if err != nil {
200 return fmt.Errorf("failed to create secret %v", err)
201 }
202 }
203 return o.PrintObj(secretTLS)
204 }
205
206
207
208 func (o *CreateSecretTLSOptions) createSecretTLS() (*corev1.Secret, error) {
209 namespace := ""
210 if o.EnforceNamespace {
211 namespace = o.Namespace
212 }
213 tlsCert, err := readFile(o.Cert)
214 if err != nil {
215 return nil, err
216 }
217 tlsKey, err := readFile(o.Key)
218 if err != nil {
219 return nil, err
220 }
221 if _, err := tls.X509KeyPair(tlsCert, tlsKey); err != nil {
222 return nil, err
223 }
224
225
226
227
228 secretTLS := newSecretObj(o.Name, namespace, corev1.SecretTypeTLS)
229 secretTLS.Data[corev1.TLSCertKey] = []byte(tlsCert)
230 secretTLS.Data[corev1.TLSPrivateKeyKey] = []byte(tlsKey)
231 if o.AppendHash {
232 hash, err := hash.SecretHash(secretTLS)
233 if err != nil {
234 return nil, err
235 }
236 secretTLS.Name = fmt.Sprintf("%s-%s", secretTLS.Name, hash)
237 }
238
239 return secretTLS, nil
240 }
241
242
243 func readFile(file string) ([]byte, error) {
244 b, err := os.ReadFile(file)
245 if err != nil {
246 return []byte{}, fmt.Errorf("Cannot read file %v, %v", file, err)
247 }
248 return b, nil
249 }
250
View as plain text