github.com/jcarley/cli@v0.0.0-20180201210820-966d90434c30/commands/certs/contract.go (about) 1 package certs 2 3 import ( 4 "github.com/Sirupsen/logrus" 5 "github.com/daticahealth/cli/commands/services" 6 "github.com/daticahealth/cli/commands/ssl" 7 "github.com/daticahealth/cli/config" 8 "github.com/daticahealth/cli/lib/auth" 9 "github.com/daticahealth/cli/lib/prompts" 10 "github.com/daticahealth/cli/models" 11 "github.com/jault3/mow.cli" 12 ) 13 14 // Cmd is the contract between the user and the CLI. This specifies the command 15 // name, arguments, and required/optional arguments and flags for the command. 16 var Cmd = models.Command{ 17 Name: "certs", 18 ShortHelp: "Manage your SSL certificates and domains", 19 LongHelp: "The <code>certs</code> command gives access to certificate and private key management for public facing services. The certs command cannot be run directly but has subcommands.", 20 CmdFunc: func(settings *models.Settings) func(cmd *cli.Cmd) { 21 return func(cmd *cli.Cmd) { 22 cmd.CommandLong(CreateSubCmd.Name, CreateSubCmd.ShortHelp, CreateSubCmd.LongHelp, CreateSubCmd.CmdFunc(settings)) 23 cmd.CommandLong(ListSubCmd.Name, ListSubCmd.ShortHelp, ListSubCmd.LongHelp, ListSubCmd.CmdFunc(settings)) 24 cmd.CommandLong(RmSubCmd.Name, RmSubCmd.ShortHelp, RmSubCmd.LongHelp, RmSubCmd.CmdFunc(settings)) 25 cmd.CommandLong(UpdateSubCmd.Name, UpdateSubCmd.ShortHelp, UpdateSubCmd.LongHelp, UpdateSubCmd.CmdFunc(settings)) 26 } 27 }, 28 } 29 30 var CreateSubCmd = models.Command{ 31 Name: "create", 32 ShortHelp: "Create a new domain with an SSL certificate and private key or create a Let's Encrypt certificate", 33 LongHelp: "<code>certs create</code> allows you to upload an SSL certificate and private key which can be used to secure your public facing code service. " + 34 "Alternatively, you may opt to create a Let's Encrypt certificate. When creating a Let's Encrypt certificate, you only need to provide the certificate name along with the \"-l\" flag. " + 35 "Let's Encrypt certificates are issued asynchronously and may not be available immediately. Use the certs list command to check on the issuance status. " + 36 "Once issued, Let's Encrypt certificates automatically renew before expiring. " + 37 "Cert creation can be done at any time, even after environment provisioning, but must be done before creating a site. " + 38 "When uploading a custom cert, the CLI will check to ensure the certificate and private key match. If you are using a self signed cert, pass in the <code>-s</code> flag and the hostname check will be skipped. " + 39 "Datica requires that your certificate file include your own certificate, intermediate certificates, and the root certificate in that order. " + 40 "If you only include your certificate, the CLI will attempt to resolve this and fetch intermediate and root certificates for you. " + 41 "It is advised that you create a full chain before running this command as the <code>-r</code> flag is accomplished on a \"best effort\" basis.\n\n" + 42 "Here are a few sample commands\n\n" + 43 "<pre>\ndatica -E \"<your_env_name>\" certs create wildcard_mysitecom ~/path/to/cert.pem ~/path/to/priv.key\n" + 44 "datica -E \"<your_env_name>\" certs create my.site.com --lets-encrypt\n</pre>", 45 CmdFunc: func(settings *models.Settings) func(cmd *cli.Cmd) { 46 return func(subCmd *cli.Cmd) { 47 name := subCmd.StringArg("NAME", "", "The name of this SSL certificate plus private key pair") 48 pubKeyPath := subCmd.StringArg("PUBLIC_KEY_PATH", "", "The path to a public key file in PEM format") 49 privKeyPath := subCmd.StringArg("PRIVATE_KEY_PATH", "", "The path to an unencrypted private key file in PEM format") 50 downStream := subCmd.StringOpt("down-stream", "service_proxy", "The down-stream service the cert belongs to.") 51 selfSigned := subCmd.BoolOpt("s self-signed", false, "Whether or not the given SSL certificate and private key are self signed") 52 resolve := subCmd.BoolOpt("r resolve", true, "Whether or not to attempt to automatically resolve incomplete SSL certificate issues") 53 letsEncrypt := subCmd.BoolOpt("l lets-encrypt", false, "Whether or not this is a Let's Encrypt certificate") 54 subCmd.Action = func() { 55 if _, err := auth.New(settings, prompts.New()).Signin(); err != nil { 56 logrus.Fatal(err.Error()) 57 } 58 if err := config.CheckRequiredAssociation(settings); err != nil { 59 logrus.Fatal(err.Error()) 60 } 61 err := CmdCreate(*name, *pubKeyPath, *privKeyPath, *downStream, *selfSigned, *resolve, *letsEncrypt, New(settings), services.New(settings), ssl.New(settings)) 62 if err != nil { 63 logrus.Fatal(err.Error()) 64 } 65 } 66 subCmd.Spec = "NAME ((PUBLIC_KEY_PATH PRIVATE_KEY_PATH [-s] [-r]) | -l) [--down-stream]" 67 } 68 }, 69 } 70 71 var ListSubCmd = models.Command{ 72 Name: "list", 73 ShortHelp: "List all existing domains that have SSL certificate and private key pairs", 74 LongHelp: "<code>certs list</code> lists all of the available certs you have created on your environment. " + 75 "The displayed names are the names that should be used as the <code>CERT_NAME</code> parameter in the sites create command. " + 76 "If any certs are Let's Encrypt certs, the issuance status will also be shown. " + 77 "Here is a sample command\n\n" + 78 "<pre>\ndatica -E \"<your_env_name>\" certs list\n</pre>", 79 CmdFunc: func(settings *models.Settings) func(cmd *cli.Cmd) { 80 return func(subCmd *cli.Cmd) { 81 downStream := subCmd.StringOpt("down-stream", "service_proxy", "The down-stream service to list certs for.") 82 subCmd.Action = func() { 83 if _, err := auth.New(settings, prompts.New()).Signin(); err != nil { 84 logrus.Fatal(err.Error()) 85 } 86 if err := config.CheckRequiredAssociation(settings); err != nil { 87 logrus.Fatal(err.Error()) 88 } 89 err := CmdList(New(settings), services.New(settings), *downStream) 90 if err != nil { 91 logrus.Fatal(err.Error()) 92 } 93 } 94 subCmd.Spec = "[--down-stream]" 95 } 96 }, 97 } 98 99 var RmSubCmd = models.Command{ 100 Name: "rm", 101 ShortHelp: "Remove an existing domain and its associated SSL certificate and private key pair", 102 LongHelp: "<code>certs rm</code> allows you to delete old certificate and private key pairs. Only certs that are not in use by a site can be deleted. Here is a sample command\n\n" + 103 "<pre>\ndatica -E \"<your_env_name>\" certs rm mywebsite.com\n</pre>", 104 CmdFunc: func(settings *models.Settings) func(cmd *cli.Cmd) { 105 return func(subCmd *cli.Cmd) { 106 name := subCmd.StringArg("NAME", "", "The name of the certificate to remove") 107 downStream := subCmd.StringOpt("down-stream", "service_proxy", "The down-stream service the cert belongs to.") 108 subCmd.Action = func() { 109 if _, err := auth.New(settings, prompts.New()).Signin(); err != nil { 110 logrus.Fatal(err.Error()) 111 } 112 if err := config.CheckRequiredAssociation(settings); err != nil { 113 logrus.Fatal(err.Error()) 114 } 115 err := CmdRm(*name, New(settings), services.New(settings), *downStream) 116 if err != nil { 117 logrus.Fatal(err.Error()) 118 } 119 } 120 subCmd.Spec = "NAME [--down-stream]" 121 } 122 }, 123 } 124 125 var UpdateSubCmd = models.Command{ 126 Name: "update", 127 ShortHelp: "Update the SSL certificate and private key pair for an existing domain", 128 LongHelp: "<code>certs update</code> works nearly identical to the certs create command. " + 129 "All rules regarding self signed certs and certificate resolution from the <code>certs create</code> command apply to the <code>certs update</code> command. " + 130 "Let's Encrypt certs cannot be updated since they are automatically renewed before expiring. " + 131 "This is useful for when your certificates have expired and you need to upload new ones. Update your certs and then redeploy your service_proxy. Here is a sample command\n\n" + 132 "<pre>\ndatica -E \"<your_env_name>\" certs update mywebsite.com ~/path/to/new/cert.pem ~/path/to/new/priv.key\n</pre>", 133 CmdFunc: func(settings *models.Settings) func(cmd *cli.Cmd) { 134 return func(subCmd *cli.Cmd) { 135 name := subCmd.StringArg("NAME", "", "The name of this SSL certificate and private key pair") 136 pubKeyPath := subCmd.StringArg("PUBLIC_KEY_PATH", "", "The path to a public key file in PEM format") 137 privKeyPath := subCmd.StringArg("PRIVATE_KEY_PATH", "", "The path to an unencrypted private key file in PEM format") 138 downStream := subCmd.StringOpt("down-stream", "service_proxy", "The down-stream service the cert belongs to.") 139 selfSigned := subCmd.BoolOpt("s self-signed", false, "Whether or not the given SSL certificate and private key are self signed") 140 resolve := subCmd.BoolOpt("r resolve", true, "Whether or not to attempt to automatically resolve incomplete SSL certificate issues") 141 subCmd.Action = func() { 142 if _, err := auth.New(settings, prompts.New()).Signin(); err != nil { 143 logrus.Fatal(err.Error()) 144 } 145 if err := config.CheckRequiredAssociation(settings); err != nil { 146 logrus.Fatal(err.Error()) 147 } 148 err := CmdUpdate(*name, *pubKeyPath, *privKeyPath, *downStream, *selfSigned, *resolve, New(settings), services.New(settings), ssl.New(settings)) 149 if err != nil { 150 logrus.Fatal(err.Error()) 151 } 152 } 153 subCmd.Spec = "NAME PUBLIC_KEY_PATH PRIVATE_KEY_PATH [-s] [-r] [--down-stream]" 154 } 155 }, 156 } 157 158 // ICerts 159 type ICerts interface { 160 Create(name, pubKey, privKey, svcID string) error 161 CreateLetsEncrypt(name, svcID string) error 162 Update(name, pubKey, privKey, svcID string) error 163 List(svcID string) (*[]models.Cert, error) 164 Rm(name, svcID string) error 165 } 166 167 // SCerts is a concrete implementation of ICerts 168 type SCerts struct { 169 Settings *models.Settings 170 } 171 172 // New returns an instance of ICerts 173 func New(settings *models.Settings) ICerts { 174 return &SCerts{ 175 Settings: settings, 176 } 177 }