mirror of https://github.com/k3s-io/k3s
200 lines
5.3 KiB
Go
200 lines
5.3 KiB
Go
// Package cli provides the template for adding new cfssl commands
|
|
package cli
|
|
|
|
/*
|
|
cfssl is the command line tool to issue/sign/bundle client certificate. It's
|
|
also a tool to start a HTTP server to handle web requests for signing, bundling
|
|
and verification.
|
|
|
|
Usage:
|
|
cfssl command [-flags] arguments
|
|
|
|
The commands are defined in the cli subpackages and include
|
|
|
|
bundle create a certificate bundle
|
|
sign signs a certificate signing request (CSR)
|
|
serve starts a HTTP server handling sign and bundle requests
|
|
version prints the current cfssl version
|
|
genkey generates a key and an associated CSR
|
|
gencert generates a key and a signed certificate
|
|
gencsr generates a certificate request
|
|
selfsign generates a self-signed certificate
|
|
ocspsign signs an OCSP response
|
|
|
|
Use "cfssl [command] -help" to find out more about a command.
|
|
*/
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
|
|
"github.com/cloudflare/cfssl/config"
|
|
)
|
|
|
|
// Command holds the implementation details of a cfssl command.
|
|
type Command struct {
|
|
// The Usage Text
|
|
UsageText string
|
|
// Flags to look up in the global table
|
|
Flags []string
|
|
// Main runs the command, args are the arguments after flags
|
|
Main func(args []string, c Config) error
|
|
}
|
|
|
|
var cmdName string
|
|
|
|
// usage is the cfssl usage heading. It will be appended with names of defined commands in cmds
|
|
// to form the final usage message of cfssl.
|
|
const usage = `Usage:
|
|
Available commands:
|
|
`
|
|
|
|
// printDefaultValue is a helper function to print out a user friendly
|
|
// usage message of a flag. It's useful since we want to write customized
|
|
// usage message on selected subsets of the global flag set. It is
|
|
// borrowed from standard library source code. Since flag value type is
|
|
// not exported, default string flag values are printed without
|
|
// quotes. The only exception is the empty string, which is printed as "".
|
|
func printDefaultValue(f *flag.Flag) {
|
|
format := " -%s=%s: %s\n"
|
|
if f.DefValue == "" {
|
|
format = " -%s=%q: %s\n"
|
|
}
|
|
fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage)
|
|
}
|
|
|
|
// PopFirstArgument returns the first element and the rest of a string
|
|
// slice and return error if failed to do so. It is a helper function
|
|
// to parse non-flag arguments previously used in cfssl commands.
|
|
func PopFirstArgument(args []string) (string, []string, error) {
|
|
if len(args) < 1 {
|
|
return "", nil, errors.New("not enough arguments are supplied --- please refer to the usage")
|
|
}
|
|
return args[0], args[1:], nil
|
|
}
|
|
|
|
// Start is the entrance point of cfssl command line tools.
|
|
func Start(cmds map[string]*Command) error {
|
|
// cfsslFlagSet is the flag sets for cfssl.
|
|
var cfsslFlagSet = flag.NewFlagSet("cfssl", flag.ExitOnError)
|
|
var c Config
|
|
|
|
registerFlags(&c, cfsslFlagSet)
|
|
// Initial parse of command line arguments. By convention, only -h/-help is supported.
|
|
if flag.Usage == nil {
|
|
flag.Usage = func() {
|
|
fmt.Fprintf(os.Stderr, usage)
|
|
for name := range cmds {
|
|
fmt.Fprintf(os.Stderr, "\t%s\n", name)
|
|
}
|
|
fmt.Fprintf(os.Stderr, "Top-level flags:\n")
|
|
flag.PrintDefaults()
|
|
}
|
|
}
|
|
|
|
flag.Parse()
|
|
|
|
if flag.NArg() < 1 {
|
|
fmt.Fprintf(os.Stderr, "No command is given.\n")
|
|
flag.Usage()
|
|
return errors.New("no command was given")
|
|
}
|
|
|
|
// Clip out the command name and args for the command
|
|
cmdName = flag.Arg(0)
|
|
args := flag.Args()[1:]
|
|
cmd, found := cmds[cmdName]
|
|
if !found {
|
|
fmt.Fprintf(os.Stderr, "Command %s is not defined.\n", cmdName)
|
|
flag.Usage()
|
|
return errors.New("undefined command")
|
|
}
|
|
// always have flag 'loglevel' for each command
|
|
cmd.Flags = append(cmd.Flags, "loglevel")
|
|
// The usage of each individual command is re-written to mention
|
|
// flags defined and referenced only in that command.
|
|
cfsslFlagSet.Usage = func() {
|
|
fmt.Fprintf(os.Stderr, "\t%s", cmd.UsageText)
|
|
for _, name := range cmd.Flags {
|
|
if f := cfsslFlagSet.Lookup(name); f != nil {
|
|
printDefaultValue(f)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parse all flags and take the rest as argument lists for the command
|
|
cfsslFlagSet.Parse(args)
|
|
args = cfsslFlagSet.Args()
|
|
|
|
var err error
|
|
if c.ConfigFile != "" {
|
|
c.CFG, err = config.LoadFile(c.ConfigFile)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Failed to load config file: %v", err)
|
|
return errors.New("failed to load config file")
|
|
}
|
|
}
|
|
|
|
if err := cmd.Main(args, c); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ReadStdin reads from stdin if the file is "-"
|
|
func ReadStdin(filename string) ([]byte, error) {
|
|
if filename == "-" {
|
|
return ioutil.ReadAll(os.Stdin)
|
|
}
|
|
return ioutil.ReadFile(filename)
|
|
}
|
|
|
|
// PrintCert outputs a cert, key and csr to stdout
|
|
func PrintCert(key, csrBytes, cert []byte) {
|
|
out := map[string]string{}
|
|
if cert != nil {
|
|
out["cert"] = string(cert)
|
|
}
|
|
|
|
if key != nil {
|
|
out["key"] = string(key)
|
|
}
|
|
|
|
if csrBytes != nil {
|
|
out["csr"] = string(csrBytes)
|
|
}
|
|
|
|
jsonOut, err := json.Marshal(out)
|
|
if err != nil {
|
|
return
|
|
}
|
|
fmt.Printf("%s\n", jsonOut)
|
|
}
|
|
|
|
// PrintOCSPResponse outputs an OCSP response to stdout
|
|
// ocspResponse is base64 encoded
|
|
func PrintOCSPResponse(resp []byte) {
|
|
b64Resp := base64.StdEncoding.EncodeToString(resp)
|
|
|
|
out := map[string]string{"ocspResponse": b64Resp}
|
|
jsonOut, err := json.Marshal(out)
|
|
if err != nil {
|
|
return
|
|
}
|
|
fmt.Printf("%s\n", jsonOut)
|
|
}
|
|
|
|
// PrintCRL outputs the CRL to stdout
|
|
func PrintCRL(certList []byte) {
|
|
b64Resp := base64.StdEncoding.EncodeToString(certList)
|
|
|
|
fmt.Printf("%s\n", b64Resp)
|
|
}
|