2019-01-12 04:58:27 +00:00
/ *
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 .
* /
package certificates
import (
2020-03-26 21:07:15 +00:00
"context"
2019-01-12 04:58:27 +00:00
"fmt"
"io"
"github.com/spf13/cobra"
2020-08-10 17:43:49 +00:00
certificatesv1 "k8s.io/api/certificates/v1"
2019-01-12 04:58:27 +00:00
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
2020-08-10 17:43:49 +00:00
corev1 "k8s.io/api/core/v1"
2019-01-12 04:58:27 +00:00
"k8s.io/apimachinery/pkg/api/errors"
2020-08-10 17:43:49 +00:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2019-01-12 04:58:27 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020-08-10 17:43:49 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2019-01-12 04:58:27 +00:00
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"
2019-04-07 17:07:55 +00:00
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
2020-08-10 17:43:49 +00:00
clientset "k8s.io/client-go/kubernetes"
2019-09-27 21:51:53 +00:00
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
2019-01-12 04:58:27 +00:00
)
func NewCmdCertificate ( f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
cmd := & cobra . Command {
Use : "certificate SUBCOMMAND" ,
DisableFlagsInUseLine : true ,
Short : i18n . T ( "Modify certificate resources." ) ,
2020-12-01 01:06:26 +00:00
Long : i18n . T ( "Modify certificate resources." ) ,
2019-01-12 04:58:27 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
cmd . Help ( )
} ,
}
cmd . AddCommand ( NewCmdCertificateApprove ( f , ioStreams ) )
cmd . AddCommand ( NewCmdCertificateDeny ( f , ioStreams ) )
return cmd
}
type CertificateOptions struct {
resource . FilenameOptions
PrintFlags * genericclioptions . PrintFlags
PrintObj printers . ResourcePrinterFunc
csrNames [ ] string
outputStyle string
2020-08-10 17:43:49 +00:00
clientSet clientset . Interface
2019-01-12 04:58:27 +00:00
builder * resource . Builder
genericclioptions . IOStreams
}
2019-04-07 17:07:55 +00:00
// NewCertificateOptions creates the options for certificate
2019-12-12 01:27:03 +00:00
func NewCertificateOptions ( ioStreams genericclioptions . IOStreams , operation string ) * CertificateOptions {
2019-04-07 17:07:55 +00:00
return & CertificateOptions {
2019-12-12 01:27:03 +00:00
PrintFlags : genericclioptions . NewPrintFlags ( operation ) . WithTypeSetter ( scheme . Scheme ) ,
2019-04-07 17:07:55 +00:00
IOStreams : ioStreams ,
}
}
2019-01-12 04:58:27 +00:00
func ( o * CertificateOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string ) error {
o . csrNames = args
o . outputStyle = cmdutil . GetFlagString ( cmd , "output" )
printer , err := o . PrintFlags . ToPrinter ( )
if err != nil {
return err
}
o . PrintObj = func ( obj runtime . Object , out io . Writer ) error {
return printer . PrintObj ( obj , out )
}
o . builder = f . NewBuilder ( )
2020-08-10 17:43:49 +00:00
o . clientSet , err = f . KubernetesClientSet ( )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
return nil
}
func ( o * CertificateOptions ) Validate ( ) error {
2019-08-30 18:33:25 +00:00
if len ( o . csrNames ) < 1 && cmdutil . IsFilenameSliceEmpty ( o . Filenames , o . Kustomize ) {
2019-01-12 04:58:27 +00:00
return fmt . Errorf ( "one or more CSRs must be specified as <name> or -f <filename>" )
}
return nil
}
func NewCmdCertificateApprove ( f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
2019-12-12 01:27:03 +00:00
o := NewCertificateOptions ( ioStreams , "approved" )
2019-04-07 17:07:55 +00:00
2019-01-12 04:58:27 +00:00
cmd := & cobra . Command {
Use : "approve (-f FILENAME | NAME)" ,
DisableFlagsInUseLine : true ,
Short : i18n . T ( "Approve a certificate signing request" ) ,
2020-12-01 01:06:26 +00:00
Long : templates . LongDesc ( i18n . T ( `
2019-01-12 04:58:27 +00:00
Approve a certificate signing request .
kubectl certificate approve allows a cluster admin to approve a certificate
signing request ( CSR ) . This action tells a certificate signing controller to
issue a certificate to the requestor with the attributes requested in the CSR .
SECURITY NOTICE : Depending on the requested attributes , the issued certificate
can potentially grant a requester access to cluster resources or to authenticate
as a requested identity . Before approving a CSR , ensure you understand what the
signed certificate can do .
2020-12-01 01:06:26 +00:00
` ) ) ,
2019-01-12 04:58:27 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2019-04-07 17:07:55 +00:00
cmdutil . CheckErr ( o . Complete ( f , cmd , args ) )
cmdutil . CheckErr ( o . Validate ( ) )
cmdutil . CheckErr ( o . RunCertificateApprove ( cmdutil . GetFlagBool ( cmd , "force" ) ) )
2019-01-12 04:58:27 +00:00
} ,
}
2019-04-07 17:07:55 +00:00
o . PrintFlags . AddFlags ( cmd )
2019-01-12 04:58:27 +00:00
cmd . Flags ( ) . Bool ( "force" , false , "Update the CSR even if it is already approved." )
2019-04-07 17:07:55 +00:00
cmdutil . AddFilenameOptionFlags ( cmd , & o . FilenameOptions , "identifying the resource to update" )
2019-01-12 04:58:27 +00:00
return cmd
}
func ( o * CertificateOptions ) RunCertificateApprove ( force bool ) error {
2020-08-10 17:43:49 +00:00
return o . modifyCertificateCondition (
o . builder ,
o . clientSet ,
force ,
addConditionIfNeeded ( string ( certificatesv1 . CertificateDenied ) , string ( certificatesv1 . CertificateApproved ) , "KubectlApprove" , "This CSR was approved by kubectl certificate approve." ) ,
)
2019-01-12 04:58:27 +00:00
}
func NewCmdCertificateDeny ( f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
2019-12-12 01:27:03 +00:00
o := NewCertificateOptions ( ioStreams , "denied" )
2019-04-07 17:07:55 +00:00
2019-01-12 04:58:27 +00:00
cmd := & cobra . Command {
Use : "deny (-f FILENAME | NAME)" ,
DisableFlagsInUseLine : true ,
Short : i18n . T ( "Deny a certificate signing request" ) ,
2020-12-01 01:06:26 +00:00
Long : templates . LongDesc ( i18n . T ( `
2019-01-12 04:58:27 +00:00
Deny a certificate signing request .
kubectl certificate deny allows a cluster admin to deny a certificate
signing request ( CSR ) . This action tells a certificate signing controller to
not to issue a certificate to the requestor .
2020-12-01 01:06:26 +00:00
` ) ) ,
2019-01-12 04:58:27 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2019-04-07 17:07:55 +00:00
cmdutil . CheckErr ( o . Complete ( f , cmd , args ) )
cmdutil . CheckErr ( o . Validate ( ) )
cmdutil . CheckErr ( o . RunCertificateDeny ( cmdutil . GetFlagBool ( cmd , "force" ) ) )
2019-01-12 04:58:27 +00:00
} ,
}
2019-04-07 17:07:55 +00:00
o . PrintFlags . AddFlags ( cmd )
2019-01-12 04:58:27 +00:00
cmd . Flags ( ) . Bool ( "force" , false , "Update the CSR even if it is already denied." )
2019-04-07 17:07:55 +00:00
cmdutil . AddFilenameOptionFlags ( cmd , & o . FilenameOptions , "identifying the resource to update" )
2019-01-12 04:58:27 +00:00
return cmd
}
func ( o * CertificateOptions ) RunCertificateDeny ( force bool ) error {
2020-08-10 17:43:49 +00:00
return o . modifyCertificateCondition (
o . builder ,
o . clientSet ,
force ,
addConditionIfNeeded ( string ( certificatesv1 . CertificateApproved ) , string ( certificatesv1 . CertificateDenied ) , "KubectlDeny" , "This CSR was denied by kubectl certificate deny." ) ,
)
2019-01-12 04:58:27 +00:00
}
2020-08-10 17:43:49 +00:00
func ( o * CertificateOptions ) modifyCertificateCondition ( builder * resource . Builder , clientSet clientset . Interface , force bool , modify func ( csr runtime . Object ) ( runtime . Object , bool , error ) ) error {
2019-01-12 04:58:27 +00:00
var found int
r := builder .
2020-08-10 17:43:49 +00:00
Unstructured ( ) .
2019-01-12 04:58:27 +00:00
ContinueOnError ( ) .
2019-04-07 17:07:55 +00:00
FilenameParam ( false , & o . FilenameOptions ) .
2020-08-10 17:43:49 +00:00
ResourceNames ( "certificatesigningrequests" , o . csrNames ... ) .
2019-01-12 04:58:27 +00:00
RequireObject ( true ) .
Flatten ( ) .
Latest ( ) .
Do ( )
err := r . Visit ( func ( info * resource . Info , err error ) error {
if err != nil {
return err
}
for i := 0 ; ; i ++ {
2020-08-10 17:43:49 +00:00
obj , ok := info . Object . ( * unstructured . Unstructured )
2020-06-23 22:12:14 +00:00
if ! ok {
2020-08-10 17:43:49 +00:00
return fmt . Errorf ( "expected *unstructured.Unstructured, got %T" , obj )
}
if want , got := certificatesv1 . Kind ( "CertificateSigningRequest" ) , obj . GetObjectKind ( ) . GroupVersionKind ( ) . GroupKind ( ) ; want != got {
return fmt . Errorf ( "can only handle %s objects, got %s" , want . String ( ) , got . String ( ) )
}
var csr runtime . Object
// get a typed object
// first try v1
csr , err = clientSet . CertificatesV1 ( ) . CertificateSigningRequests ( ) . Get ( context . TODO ( ) , obj . GetName ( ) , metav1 . GetOptions { } )
if apierrors . IsNotFound ( err ) {
// fall back to v1beta1
csr , err = clientSet . CertificatesV1beta1 ( ) . CertificateSigningRequests ( ) . Get ( context . TODO ( ) , obj . GetName ( ) , metav1 . GetOptions { } )
}
if apierrors . IsNotFound ( err ) {
return fmt . Errorf ( "could not find v1 or v1beta1 version of %s: %v" , obj . GetName ( ) , err )
}
if err != nil {
return err
}
modifiedCSR , hasCondition , err := modify ( csr )
if err != nil {
return err
2020-06-23 22:12:14 +00:00
}
2019-01-12 04:58:27 +00:00
if ! hasCondition || force {
2020-08-10 17:43:49 +00:00
switch modifiedCSR := modifiedCSR . ( type ) {
case * certificatesv1 . CertificateSigningRequest :
_ , err = clientSet . CertificatesV1 ( ) . CertificateSigningRequests ( ) . UpdateApproval ( context . TODO ( ) , modifiedCSR . Name , modifiedCSR , metav1 . UpdateOptions { } )
case * certificatesv1beta1 . CertificateSigningRequest :
_ , err = clientSet . CertificatesV1beta1 ( ) . CertificateSigningRequests ( ) . UpdateApproval ( context . TODO ( ) , modifiedCSR , metav1 . UpdateOptions { } )
default :
return fmt . Errorf ( "can only handle certificates.k8s.io CertificateSigningRequest objects, got %T" , modifiedCSR )
}
2019-01-12 04:58:27 +00:00
if errors . IsConflict ( err ) && i < 10 {
if err := info . Get ( ) ; err != nil {
return err
}
continue
}
if err != nil {
return err
}
}
break
}
found ++
2019-04-07 17:07:55 +00:00
return o . PrintObj ( info . Object , o . Out )
2019-01-12 04:58:27 +00:00
} )
2020-08-10 17:43:49 +00:00
if found == 0 && err == nil {
2019-04-07 17:07:55 +00:00
fmt . Fprintf ( o . Out , "No resources found\n" )
2019-01-12 04:58:27 +00:00
}
return err
}
2020-08-10 17:43:49 +00:00
func addConditionIfNeeded ( mustNotHaveConditionType , conditionType , reason , message string ) func ( runtime . Object ) ( runtime . Object , bool , error ) {
return func ( csr runtime . Object ) ( runtime . Object , bool , error ) {
switch csr := csr . ( type ) {
case * certificatesv1 . CertificateSigningRequest :
var alreadyHasCondition bool
for _ , c := range csr . Status . Conditions {
if string ( c . Type ) == mustNotHaveConditionType {
return nil , false , fmt . Errorf ( "certificate signing request %q is already %s" , csr . Name , c . Type )
}
if string ( c . Type ) == conditionType {
alreadyHasCondition = true
}
}
if alreadyHasCondition {
return csr , true , nil
}
csr . Status . Conditions = append ( csr . Status . Conditions , certificatesv1 . CertificateSigningRequestCondition {
Type : certificatesv1 . RequestConditionType ( conditionType ) ,
Status : corev1 . ConditionTrue ,
Reason : reason ,
Message : message ,
LastUpdateTime : metav1 . Now ( ) ,
} )
return csr , false , nil
case * certificatesv1beta1 . CertificateSigningRequest :
var alreadyHasCondition bool
for _ , c := range csr . Status . Conditions {
if string ( c . Type ) == mustNotHaveConditionType {
return nil , false , fmt . Errorf ( "certificate signing request %q is already %s" , csr . Name , c . Type )
}
if string ( c . Type ) == conditionType {
alreadyHasCondition = true
}
}
if alreadyHasCondition {
return csr , true , nil
}
csr . Status . Conditions = append ( csr . Status . Conditions , certificatesv1beta1 . CertificateSigningRequestCondition {
Type : certificatesv1beta1 . RequestConditionType ( conditionType ) ,
Status : corev1 . ConditionTrue ,
Reason : reason ,
Message : message ,
LastUpdateTime : metav1 . Now ( ) ,
} )
return csr , false , nil
default :
return csr , false , nil
}
}
}