-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
252 lines (217 loc) · 7.69 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
package main
import (
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/Sirupsen/logrus"
"github.com/docker/notary/passphrase"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/version"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
configDir = ".notary/"
defaultServerURL = "https://notary-server:4443"
)
type usageTemplate struct {
Use string
Short string
Long string
}
type cobraRunE func(cmd *cobra.Command, args []string) error
func (u usageTemplate) ToCommand(run cobraRunE) *cobra.Command {
c := cobra.Command{
Use: u.Use,
Short: u.Short,
Long: u.Long,
}
if run != nil {
// newer versions of cobra support a run function that returns an error,
// but in the meantime, this should help ease the transition
c.RunE = run
}
return &c
}
func pathRelativeToCwd(path string) string {
if path == "" || filepath.IsAbs(path) {
return path
}
cwd, err := os.Getwd()
if err != nil {
return filepath.Clean(path)
}
return filepath.Clean(filepath.Join(cwd, path))
}
type notaryCommander struct {
// this needs to be set
getRetriever func() passphrase.Retriever
// these are for command line parsing - no need to set
debug bool
verbose bool
trustDir string
configFile string
remoteTrustServer string
tlsCAFile string
tlsCertFile string
tlsKeyFile string
}
func (n *notaryCommander) parseConfig() (*viper.Viper, error) {
n.setVerbosityLevel()
// Get home directory for current user
homeDir, err := homedir.Dir()
if err != nil {
return nil, fmt.Errorf("cannot get current user home directory: %v", err)
}
if homeDir == "" {
return nil, fmt.Errorf("cannot get current user home directory")
}
config := viper.New()
// By default our trust directory (where keys are stored) is in ~/.notary/
defaultTrustDir := filepath.Join(homeDir, filepath.Dir(configDir))
// If there was a commandline configFile set, we parse that.
// If there wasn't we attempt to find it on the default location ~/.notary/config.json
if n.configFile != "" {
config.SetConfigFile(n.configFile)
} else {
config.SetConfigFile(filepath.Join(defaultTrustDir, "config.json"))
}
// Setup the configuration details into viper
config.SetDefault("trust_dir", defaultTrustDir)
config.SetDefault("remote_server", map[string]string{"url": defaultServerURL})
// Find and read the config file
if err := config.ReadInConfig(); err != nil {
logrus.Debugf("Configuration file not found, using defaults")
// If we were passed in a configFile via command linen flags, bail if it doesn't exist,
// otherwise ignore it: we can use the defaults
if n.configFile != "" || !os.IsNotExist(err) {
return nil, fmt.Errorf("error opening config file: %v", err)
}
}
// At this point we either have the default value or the one set by the config.
// Either way, some command-line flags have precedence and overwrites the value
if n.trustDir != "" {
config.Set("trust_dir", pathRelativeToCwd(n.trustDir))
}
if n.tlsCAFile != "" {
config.Set("remote_server.root_ca", pathRelativeToCwd(n.tlsCAFile))
}
if n.tlsCertFile != "" {
config.Set("remote_server.tls_client_cert", pathRelativeToCwd(n.tlsCertFile))
}
if n.tlsKeyFile != "" {
config.Set("remote_server.tls_client_key", pathRelativeToCwd(n.tlsKeyFile))
}
if n.remoteTrustServer != "" {
config.Set("remote_server.url", n.remoteTrustServer)
}
// Expands all the possible ~/ that have been given, either through -d or config
// If there is no error, use it, if not, just attempt to use whatever the user gave us
expandedTrustDir, err := homedir.Expand(config.GetString("trust_dir"))
if err == nil {
config.Set("trust_dir", expandedTrustDir)
}
logrus.Debugf("Using the following trust directory: %s", config.GetString("trust_dir"))
return config, nil
}
func (n *notaryCommander) GetCommand() *cobra.Command {
notaryCmd := cobra.Command{
Use: "notary",
Short: "Notary allows the creation of trusted collections.",
Long: "Notary allows the creation and management of collections of signed targets, allowing the signing and validation of arbitrary content.",
SilenceUsage: true, // we don't want to print out usage for EVERY error
SilenceErrors: true, // we do our own error reporting with fatalf
Run: func(cmd *cobra.Command, args []string) { cmd.Usage() },
}
notaryCmd.SetOutput(os.Stdout)
notaryCmd.AddCommand(&cobra.Command{
Use: "version",
Short: "Print the version number of notary",
Long: "Print the version number of notary",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("notary\n Version: %s\n Git commit: %s\n", version.NotaryVersion, version.GitCommit)
},
})
notaryCmd.PersistentFlags().StringVarP(
&n.trustDir, "trustDir", "d", "", "Directory where the trust data is persisted to")
notaryCmd.PersistentFlags().StringVarP(
&n.configFile, "configFile", "c", "", "Path to the configuration file to use")
notaryCmd.PersistentFlags().BoolVarP(&n.verbose, "verbose", "v", false, "Verbose output")
notaryCmd.PersistentFlags().BoolVarP(&n.debug, "debug", "D", false, "Debug output")
notaryCmd.PersistentFlags().StringVarP(&n.remoteTrustServer, "server", "s", "", "Remote trust server location")
notaryCmd.PersistentFlags().StringVar(&n.tlsCAFile, "tlscacert", "", "Trust certs signed only by this CA")
notaryCmd.PersistentFlags().StringVar(&n.tlsCertFile, "tlscert", "", "Path to TLS certificate file")
notaryCmd.PersistentFlags().StringVar(&n.tlsKeyFile, "tlskey", "", "Path to TLS key file")
cmdKeyGenerator := &keyCommander{
configGetter: n.parseConfig,
getRetriever: n.getRetriever,
input: os.Stdin,
}
cmdDelegationGenerator := &delegationCommander{
configGetter: n.parseConfig,
retriever: n.getRetriever(),
}
cmdTufGenerator := &tufCommander{
configGetter: n.parseConfig,
retriever: n.getRetriever(),
}
notaryCmd.AddCommand(cmdKeyGenerator.GetCommand())
notaryCmd.AddCommand(cmdDelegationGenerator.GetCommand())
cmdTufGenerator.AddToCommand(¬aryCmd)
return ¬aryCmd
}
func main() {
notaryCommander := ¬aryCommander{getRetriever: getPassphraseRetriever}
notaryCmd := notaryCommander.GetCommand()
if err := notaryCmd.Execute(); err != nil {
notaryCmd.Println("")
fatalf(err.Error())
}
}
func fatalf(format string, args ...interface{}) {
fmt.Printf("* fatal: "+format+"\n", args...)
os.Exit(1)
}
func askConfirm(input io.Reader) bool {
var res string
if _, err := fmt.Fscanln(input, &res); err != nil {
return false
}
if strings.EqualFold(res, "y") || strings.EqualFold(res, "yes") {
return true
}
return false
}
func getPassphraseRetriever() passphrase.Retriever {
baseRetriever := passphrase.PromptRetriever()
env := map[string]string{
"root": os.Getenv("NOTARY_ROOT_PASSPHRASE"),
"targets": os.Getenv("NOTARY_TARGETS_PASSPHRASE"),
"snapshot": os.Getenv("NOTARY_SNAPSHOT_PASSPHRASE"),
"delegation": os.Getenv("NOTARY_DELEGATION_PASSPHRASE"),
}
return func(keyName string, alias string, createNew bool, numAttempts int) (string, bool, error) {
if v := env[alias]; v != "" {
return v, numAttempts > 1, nil
}
// For delegation roles, we can also try the "delegation" alias if it is specified
if v := env["delegation"]; v != "" && data.IsDelegation(alias) {
return v, numAttempts > 1, nil
}
return baseRetriever(keyName, alias, createNew, numAttempts)
}
}
// Set the logging level to fatal on default, or the most specific level the user specified (debug or error)
func (n *notaryCommander) setVerbosityLevel() {
if n.debug {
logrus.SetLevel(logrus.DebugLevel)
} else if n.verbose {
logrus.SetLevel(logrus.ErrorLevel)
} else {
logrus.SetLevel(logrus.FatalLevel)
}
logrus.SetOutput(os.Stderr)
}