mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
Store service credentials securely in the stores supported by docker: - https://github.com/docker/docker-credential-helpers#available-programs Introduces a top-level config property, "secretStore" and additional command line arguments to manage the stored secrets. The value of secretStore is used to find a helper command, `docker-credential-<secretStore>`. The docker project currently provides 4 store helpers: - "osxkeychain" (OS X only) - "secretservice" (Linux only) - "wincred" (Windows only) - "pass" (any OS supporting pass, which uses gpg2) Docker-for-desktop installs the credential helpers above, as well as "desktop" (docker-credential-desktop). Generic installation instructions for the helpers: - https://github.com/docker/docker-credential-helpers#installation Users could provide additional helpers, the only requirement is that the helper implements the credential store protocol: - https://github.com/docker/docker-credential-helpers#development The credential protocol is open, and new credential stores can be implemented by any CLI satisfying the protocol: - https://github.com/docker/docker-credential-helpers#development The modifications to existing modules is not tested due to lack of API keys, but demonstrates the unobtrusive changes required to use the secret store.
178 lines
4.8 KiB
Go
178 lines
4.8 KiB
Go
package flags
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/chzyer/readline"
|
|
goFlags "github.com/jessevdk/go-flags"
|
|
"github.com/olebedev/config"
|
|
"github.com/wtfutil/wtf/cfg"
|
|
"github.com/wtfutil/wtf/help"
|
|
)
|
|
|
|
// Flags is the container for command line flag data
|
|
type Flags struct {
|
|
Config string `short:"c" long:"config" optional:"yes" description:"Path to config file"`
|
|
Module string `short:"m" long:"module" optional:"yes" description:"Display info about a specific module, i.e.: 'wtfutil -m=todo'"`
|
|
Profile bool `short:"p" long:"profile" optional:"yes" description:"Profile application memory usage"`
|
|
Version bool `short:"v" long:"version" description:"Show version info"`
|
|
// Work-around go-flags misfeatures. If any sub-command is defined
|
|
// then `wtf` (no sub-commands, the common usage), is warned about.
|
|
Opt struct {
|
|
Cmd string `positional-arg-name:"command"`
|
|
Args []string `positional-arg-name:"args"`
|
|
} `positional-args:"yes"`
|
|
|
|
hasCustom bool
|
|
}
|
|
|
|
var EXTRA = `
|
|
Commands:
|
|
save-secret <service> [secret [username]]
|
|
service Service URL or name for secret.
|
|
secret Secret to be saved for the service.
|
|
username Username to associate with the service.
|
|
Save a secret into the secret store. Requires wtf.secretStore
|
|
to be configured. See individual modules for information on what
|
|
service, secret, and username means for their configuration. Not
|
|
all modules use secrets, and not all secrets require a username.
|
|
If secret or username is not provided, will prompt for them.
|
|
`
|
|
|
|
// NewFlags creates an instance of Flags
|
|
func NewFlags() *Flags {
|
|
flags := Flags{}
|
|
return &flags
|
|
}
|
|
|
|
/* -------------------- Exported Functions -------------------- */
|
|
|
|
// ConfigFilePath returns the path to the currently-loaded config file
|
|
func (flags *Flags) ConfigFilePath() string {
|
|
return flags.Config
|
|
}
|
|
|
|
// RenderIf displays special-case information based on the flags passed
|
|
// in, if any flags were passed in
|
|
func (flags *Flags) RenderIf(version, date string, config *config.Config) {
|
|
if flags.HasModule() {
|
|
help.Display(flags.Module, config)
|
|
os.Exit(0)
|
|
}
|
|
|
|
if flags.HasVersion() {
|
|
fmt.Println(fmt.Sprintf("%s (%s)", version, date))
|
|
os.Exit(0)
|
|
}
|
|
|
|
if flags.Opt.Cmd == "" {
|
|
return
|
|
}
|
|
|
|
switch cmd := flags.Opt.Cmd; cmd {
|
|
case "save-secret":
|
|
var service, secret, username string
|
|
args := flags.Opt.Args
|
|
|
|
if len(args) < 1 || args[0] == "" {
|
|
fmt.Fprintf(os.Stderr, "save-secret: service required, see `%s --help`\n", os.Args[0])
|
|
os.Exit(1)
|
|
}
|
|
|
|
service = args[0]
|
|
|
|
if len(args) > 1 {
|
|
secret = args[1]
|
|
} else {
|
|
b, err := readline.Password("Secret (required): ")
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
secret = string(b)
|
|
}
|
|
secret = strings.TrimSpace(secret)
|
|
|
|
if secret == "" {
|
|
fmt.Fprintf(os.Stderr, "save-secret: secret required, see `%s --help`\n", os.Args[0])
|
|
os.Exit(1)
|
|
}
|
|
|
|
if len(args) > 2 {
|
|
username = args[2]
|
|
} else {
|
|
fmt.Printf("Username (optional): ")
|
|
reader := bufio.NewReader(os.Stdin)
|
|
var err error
|
|
username, err = reader.ReadString('\n')
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
username = strings.TrimSpace(username)
|
|
|
|
err := cfg.StoreSecret(config, &cfg.Secret{
|
|
Service: service,
|
|
Secret: secret,
|
|
Username: username,
|
|
})
|
|
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Saving secret for service %q: %s\n", service, err.Error())
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Printf("Saved secret for service %q (username %q)\n", service, username)
|
|
os.Exit(0)
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "Command `%s` is not supported, try `%s --help`\n", cmd, os.Args[0])
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// HasCustomConfig returns TRUE if a config path was passed in, FALSE if one was not
|
|
func (flags *Flags) HasCustomConfig() bool {
|
|
return flags.hasCustom
|
|
}
|
|
|
|
// HasModule returns TRUE if a module name was passed in, FALSE if one was not
|
|
func (flags *Flags) HasModule() bool {
|
|
return len(flags.Module) > 0
|
|
}
|
|
|
|
// HasVersion returns TRUE if the version flag was passed in, FALSE if it was not
|
|
func (flags *Flags) HasVersion() bool {
|
|
return flags.Version
|
|
}
|
|
|
|
// Parse parses the incoming flags
|
|
func (flags *Flags) Parse() {
|
|
parser := goFlags.NewParser(flags, goFlags.Default)
|
|
if _, err := parser.Parse(); err != nil {
|
|
if flagsErr, ok := err.(*goFlags.Error); ok && flagsErr.Type == goFlags.ErrHelp {
|
|
fmt.Println(EXTRA)
|
|
os.Exit(0)
|
|
}
|
|
}
|
|
|
|
// If we have a custom config, then we're done parsing parameters, we don't need to
|
|
// generate the default value
|
|
flags.hasCustom = (len(flags.Config) > 0)
|
|
if flags.hasCustom {
|
|
return
|
|
}
|
|
|
|
// If no config file is explicitly passed in as a param then set the flag to the default config file
|
|
configDir, err := cfg.WtfConfigDir()
|
|
if err != nil {
|
|
fmt.Printf("Error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
flags.Config = filepath.Join(configDir, "config.yml")
|
|
}
|