cli.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. // Package cli provides the template for adding new cfssl commands
  2. package cli
  3. /*
  4. cfssl is the command line tool to issue/sign/bundle client certificate. It's
  5. also a tool to start a HTTP server to handle web requests for signing, bundling
  6. and verification.
  7. Usage:
  8. cfssl command [-flags] arguments
  9. The commands are defined in the cli subpackages and include
  10. bundle create a certificate bundle
  11. sign signs a certificate signing request (CSR)
  12. serve starts a HTTP server handling sign and bundle requests
  13. version prints the current cfssl version
  14. genkey generates a key and an associated CSR
  15. gencert generates a key and a signed certificate
  16. gencsr generates a certificate request
  17. selfsign generates a self-signed certificate
  18. ocspsign signs an OCSP response
  19. Use "cfssl [command] -help" to find out more about a command.
  20. */
  21. import (
  22. "encoding/base64"
  23. "encoding/json"
  24. "errors"
  25. "flag"
  26. "fmt"
  27. "io"
  28. "os"
  29. "github.com/cloudflare/cfssl/config"
  30. )
  31. // Command holds the implementation details of a cfssl command.
  32. type Command struct {
  33. // The Usage Text
  34. UsageText string
  35. // Flags to look up in the global table
  36. Flags []string
  37. // Main runs the command, args are the arguments after flags
  38. Main func(args []string, c Config) error
  39. }
  40. var cmdName string
  41. // usage is the cfssl usage heading. It will be appended with names of defined commands in cmds
  42. // to form the final usage message of cfssl.
  43. const usage = `Usage:
  44. Available commands:
  45. `
  46. // printDefaultValue is a helper function to print out a user friendly
  47. // usage message of a flag. It's useful since we want to write customized
  48. // usage message on selected subsets of the global flag set. It is
  49. // borrowed from standard library source code. Since flag value type is
  50. // not exported, default string flag values are printed without
  51. // quotes. The only exception is the empty string, which is printed as "".
  52. func printDefaultValue(f *flag.Flag) {
  53. format := " -%s=%s: %s\n"
  54. if f.DefValue == "" {
  55. format = " -%s=%q: %s\n"
  56. }
  57. fmt.Fprintf(os.Stderr, format, f.Name, f.DefValue, f.Usage)
  58. }
  59. // PopFirstArgument returns the first element and the rest of a string
  60. // slice and return error if failed to do so. It is a helper function
  61. // to parse non-flag arguments previously used in cfssl commands.
  62. func PopFirstArgument(args []string) (string, []string, error) {
  63. if len(args) < 1 {
  64. return "", nil, errors.New("not enough arguments are supplied --- please refer to the usage")
  65. }
  66. return args[0], args[1:], nil
  67. }
  68. // Start is the entrance point of cfssl command line tools.
  69. func Start(cmds map[string]*Command) error {
  70. // cfsslFlagSet is the flag sets for cfssl.
  71. var cfsslFlagSet = flag.NewFlagSet("cfssl", flag.ExitOnError)
  72. var c Config
  73. registerFlags(&c, cfsslFlagSet)
  74. // Initial parse of command line arguments. By convention, only -h/-help is supported.
  75. if flag.Usage == nil {
  76. flag.Usage = func() {
  77. fmt.Fprintf(os.Stderr, usage)
  78. for name := range cmds {
  79. fmt.Fprintf(os.Stderr, "\t%s\n", name)
  80. }
  81. fmt.Fprintf(os.Stderr, "Top-level flags:\n")
  82. flag.PrintDefaults()
  83. }
  84. }
  85. flag.Parse()
  86. if flag.NArg() < 1 {
  87. fmt.Fprintf(os.Stderr, "No command is given.\n")
  88. flag.Usage()
  89. return errors.New("no command was given")
  90. }
  91. // Clip out the command name and args for the command
  92. cmdName = flag.Arg(0)
  93. args := flag.Args()[1:]
  94. cmd, found := cmds[cmdName]
  95. if !found {
  96. fmt.Fprintf(os.Stderr, "Command %s is not defined.\n", cmdName)
  97. flag.Usage()
  98. return errors.New("undefined command")
  99. }
  100. // always have flag 'loglevel' for each command
  101. cmd.Flags = append(cmd.Flags, "loglevel")
  102. // The usage of each individual command is re-written to mention
  103. // flags defined and referenced only in that command.
  104. cfsslFlagSet.Usage = func() {
  105. fmt.Fprintf(os.Stderr, "\t%s", cmd.UsageText)
  106. for _, name := range cmd.Flags {
  107. if f := cfsslFlagSet.Lookup(name); f != nil {
  108. printDefaultValue(f)
  109. }
  110. }
  111. }
  112. // Parse all flags and take the rest as argument lists for the command
  113. cfsslFlagSet.Parse(args)
  114. args = cfsslFlagSet.Args()
  115. var err error
  116. if c.ConfigFile != "" {
  117. c.CFG, err = config.LoadFile(c.ConfigFile)
  118. if err != nil {
  119. fmt.Fprintf(os.Stderr, "Failed to load config file: %v", err)
  120. return errors.New("failed to load config file")
  121. }
  122. }
  123. if err := cmd.Main(args, c); err != nil {
  124. fmt.Fprintln(os.Stderr, err)
  125. return err
  126. }
  127. return nil
  128. }
  129. // ReadStdin reads from stdin if the file is "-"
  130. func ReadStdin(filename string) ([]byte, error) {
  131. if filename == "-" {
  132. return io.ReadAll(os.Stdin)
  133. }
  134. return os.ReadFile(filename)
  135. }
  136. // PrintCert outputs a cert, key and csr to stdout
  137. func PrintCert(key, csrBytes, cert []byte) {
  138. out := map[string]string{}
  139. if cert != nil {
  140. out["cert"] = string(cert)
  141. }
  142. if key != nil {
  143. out["key"] = string(key)
  144. }
  145. if csrBytes != nil {
  146. out["csr"] = string(csrBytes)
  147. }
  148. jsonOut, err := json.Marshal(out)
  149. if err != nil {
  150. return
  151. }
  152. fmt.Printf("%s\n", jsonOut)
  153. }
  154. // PrintOCSPResponse outputs an OCSP response to stdout
  155. // ocspResponse is base64 encoded
  156. func PrintOCSPResponse(resp []byte) {
  157. b64Resp := base64.StdEncoding.EncodeToString(resp)
  158. out := map[string]string{"ocspResponse": b64Resp}
  159. jsonOut, err := json.Marshal(out)
  160. if err != nil {
  161. return
  162. }
  163. fmt.Printf("%s\n", jsonOut)
  164. }
  165. // PrintCRL outputs the CRL to stdout
  166. func PrintCRL(certList []byte) {
  167. b64Resp := base64.StdEncoding.EncodeToString(certList)
  168. fmt.Printf("%s\n", b64Resp)
  169. }