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  }