github.com/projectcontour/contour@v1.28.2/cmd/contour/certgen.go (about) 1 // Copyright Project Contour Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package main 15 16 import ( 17 "fmt" 18 "os" 19 "path/filepath" 20 "strconv" 21 22 "github.com/alecthomas/kingpin/v2" 23 "github.com/projectcontour/contour/internal/certgen" 24 "github.com/projectcontour/contour/internal/k8s" 25 "github.com/projectcontour/contour/pkg/certs" 26 "github.com/sirupsen/logrus" 27 corev1 "k8s.io/api/core/v1" 28 utilerrors "k8s.io/apimachinery/pkg/util/errors" 29 "k8s.io/client-go/kubernetes" 30 ) 31 32 // registercertgen registers the certgen subcommand and flags 33 // with the Application provided. 34 func registerCertGen(app *kingpin.Application) (*kingpin.CmdClause, *certgenConfig) { 35 var certgenConfig certgenConfig 36 certgenApp := app.Command("certgen", "Generate new TLS certs for bootstrapping gRPC over TLS.") 37 certgenApp.Arg("outputdir", "Directory to write output files into (default \"certs\").").Default("certs").StringVar(&certgenConfig.OutputDir) 38 39 // NOTE: --certificate-lifetime can be used to accept Duration string once certificate rotation is supported. 40 certgenApp.Flag("certificate-lifetime", "Generated certificate lifetime (in days).").Default(strconv.Itoa(certs.DefaultCertificateLifetime)).UintVar(&certgenConfig.Lifetime) 41 certgenApp.Flag("incluster", "Use in cluster configuration.").BoolVar(&certgenConfig.InCluster) 42 certgenApp.Flag("kube", "Apply the generated certs directly to the current Kubernetes cluster.").BoolVar(&certgenConfig.OutputKube) 43 certgenApp.Flag("kubeconfig", "Path to kubeconfig (if not in running inside a cluster).").Default(filepath.Join(os.Getenv("HOME"), ".kube", "config")).StringVar(&certgenConfig.KubeConfig) 44 certgenApp.Flag("namespace", "Kubernetes namespace, used for Kube objects.").Default(certs.DefaultNamespace).Envar("CONTOUR_NAMESPACE").StringVar(&certgenConfig.Namespace) 45 certgenApp.Flag("overwrite", "Overwrite existing files or Secrets.").BoolVar(&certgenConfig.Overwrite) 46 certgenApp.Flag("pem", "Render the generated certs as individual PEM files to the current directory.").BoolVar(&certgenConfig.OutputPEM) 47 certgenApp.Flag("secrets-format", "Specify how to format the generated Kubernetes Secrets.").Default("legacy").StringVar(&certgenConfig.Format) 48 certgenApp.Flag("secrets-name-suffix", "Specify a suffix to be appended to the generated Kubernetes secrets' names.").StringVar(&certgenConfig.NameSuffix) 49 certgenApp.Flag("yaml", "Render the generated certs as Kubernetes Secrets in YAML form to the current directory.").BoolVar(&certgenConfig.OutputYAML) 50 51 return certgenApp, &certgenConfig 52 } 53 54 // certgenConfig holds the configuration for the certificate generation process. 55 type certgenConfig struct { 56 // KubeConfig is the path to the Kubeconfig file if we're not running in a cluster 57 KubeConfig string 58 59 // Incluster means that we should assume we are running in a Kubernetes cluster and work accordingly. 60 InCluster bool 61 62 // Namespace is the namespace to put any generated config into for YAML or Kube outputs. 63 Namespace string 64 65 // OutputDir stores the directory where any requested files will be output. 66 OutputDir string 67 68 // OutputKube means that the certs generated will be output into a Kubernetes cluster as secrets. 69 OutputKube bool 70 71 // OutputYAML means that the certs generated will be output into Kubernetes secrets as YAML in the current directory. 72 OutputYAML bool 73 74 // OutputPEM means that the certs generated will be output as PEM files in the current directory. 75 OutputPEM bool 76 77 // Lifetime is the number of days for which certificates will be valid. 78 Lifetime uint 79 80 // Overwrite allows certgen to overwrite any existing files or Kubernetes Secrets. 81 Overwrite bool 82 83 // Format specifies how to format the Kubernetes Secrets (must be "legacy" or "compat"). 84 Format string 85 86 // NameSuffix specifies the suffix to use for the generated Kubernetes secrets' names. 87 NameSuffix string 88 } 89 90 // OutputCerts outputs the certs in certs as directed by config. 91 func OutputCerts(config *certgenConfig, kubeclient *kubernetes.Clientset, certs *certs.Certificates) error { 92 var secrets []*corev1.Secret 93 var errs []error 94 95 force := certgen.NoOverwrite 96 if config.Overwrite { 97 force = certgen.Overwrite 98 } 99 100 if config.OutputYAML || config.OutputKube { 101 switch config.Format { 102 case "legacy": 103 secrets, errs = certgen.AsLegacySecrets(config.Namespace, config.NameSuffix, certs) 104 case "compact": 105 secrets, errs = certgen.AsSecrets(config.Namespace, config.NameSuffix, certs) 106 default: 107 return fmt.Errorf("unsupported Secrets format %q", config.Format) 108 } 109 110 if len(errs) > 0 { 111 return utilerrors.NewAggregate(errs) 112 } 113 } 114 115 if config.OutputPEM { 116 fmt.Printf("Writing certificates to PEM files in %s/\n", config.OutputDir) 117 if err := certgen.WriteCertsPEM(config.OutputDir, certs, force); err != nil { 118 return fmt.Errorf("failed to write certificates to %q: %w", config.OutputDir, err) 119 } 120 } 121 122 if config.OutputYAML { 123 fmt.Printf("Writing %q format Secrets to YAML files in %s/\n", config.Format, config.OutputDir) 124 if err := certgen.WriteSecretsYAML(config.OutputDir, secrets, force); err != nil { 125 return fmt.Errorf("failed to write Secrets to %q: %w", config.OutputDir, err) 126 } 127 } 128 129 if config.OutputKube { 130 fmt.Printf("Writing %q format Secrets to namespace %q\n", config.Format, config.Namespace) 131 if err := certgen.WriteSecretsKube(kubeclient, secrets, force); err != nil { 132 return fmt.Errorf("failed to write certificates to %q: %w", config.Namespace, err) 133 } 134 } 135 return nil 136 } 137 138 func doCertgen(config *certgenConfig, log logrus.FieldLogger) { 139 generatedCerts, err := certs.GenerateCerts( 140 &certs.Configuration{ 141 Lifetime: config.Lifetime, 142 Namespace: config.Namespace, 143 }) 144 if err != nil { 145 log.WithError(err).Fatal("failed to generate certificates") 146 } 147 148 coreClient, err := k8s.NewCoreClient(config.KubeConfig, config.InCluster) 149 if err != nil { 150 log.WithError(err).Fatalf("failed to create Kubernetes client") 151 } 152 153 if oerr := OutputCerts(config, coreClient, generatedCerts); oerr != nil { 154 log.WithError(oerr).Fatalf("failed output certificates") 155 } 156 }