aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorRasmus Dahlberg <rgdd@glasklarteknik.se>2024-05-16 12:48:22 +0200
committerRasmus Dahlberg <rgdd@glasklarteknik.se>2024-05-16 17:26:38 +0200
commitaba0f17953c9947bb51e78ed581f4e66b7012518 (patch)
tree6fbbbfe369224ca17088e429e49082f6ce9d5f5a /cmd
parent6567c8f4ec3eefb855c6ef630a53b0fb8d8bf1e9 (diff)
Add man pages and installer Makefile
Includes renaming of the tools, part one of trying to simplify terminology and letting go of "node" and "moon". Improving the terminology was suggested by Martin H a while back, thank you.
Diffstat (limited to 'cmd')
-rw-r--r--cmd/silentct-mac/examples.help2man6
-rw-r--r--cmd/silentct-mac/main.go (renamed from cmd/silent-ctnode/main.go)42
-rw-r--r--cmd/silentct-mac/name.help2man2
-rw-r--r--cmd/silentct-mac/see-also.help2man2
-rw-r--r--cmd/silentct-mon/examples.help2man38
-rw-r--r--cmd/silentct-mon/main.go (renamed from cmd/silent-ctmoon/main.go)60
-rw-r--r--cmd/silentct-mon/name.help2man2
-rw-r--r--cmd/silentct-mon/see-also.help2man2
8 files changed, 99 insertions, 55 deletions
diff --git a/cmd/silentct-mac/examples.help2man b/cmd/silentct-mac/examples.help2man
new file mode 100644
index 0000000..f7bbff5
--- /dev/null
+++ b/cmd/silentct-mac/examples.help2man
@@ -0,0 +1,6 @@
+[EXAMPLES]
+Allowlist the current certificate in a Let's Encrypt deployment:
+
+.B $ silentct-mac -n example.org -s sikritpassword -o /var/www/example.org/silent-ct/allowlist /etc/letsencrypt/live/example.org/fullchain.pem
+
+You may run the above as part of your crontab or certbot renewal configuration.
diff --git a/cmd/silent-ctnode/main.go b/cmd/silentct-mac/main.go
index 99f4437..2add812 100644
--- a/cmd/silent-ctnode/main.go
+++ b/cmd/silentct-mac/main.go
@@ -16,32 +16,26 @@ import (
)
const usage = `
-A utility that generates a submission of one or more certificate chains.
-The generated submission is protected by a message authentication code.
+silentct-mac is a utility that helps allowlist legitimately issued certificates
+while monitoring Certificate Transparency logs. One or more certificate chains
+are bundled with a message authentication code, such that the silentct-mon tool
+can fetch them over an insecure channel or from untrusted intermediary storage.
-Usage:
-
- silent-ctnode --help
- silent-ctnode [Options] -n NAME -s SECRET FILE [FILE ...]
+Usage: silentct-mac [Options] -n NAME -s SECRET CRT-FILE [CRT-FILE ...]
Options:
-
- -h, --help: Output usage message and exit
+ -n, --name Name of the system that allowlists certificates
+ -o, --output Filename to write allowlisted certificates to (default: stdout)
+ -s, --secret Shared secret between the allowlisting system and its monitor
-v, --verbosity Leveled logging output (default: NOTICE)
-
- -n, --name: Name of the node generating the submission
- -s, --secret: Shared secret between the node and its monitor
- -o, --output: File to write submission to (default: stdout)
-
-Each trailing FILE argument must contain a single certificate chain.
`
type config struct {
// Options
- verbosity string
name string
- secret string
output string
+ secret string
+ verbosity string
// Extracted
log *logger.Logger
@@ -51,25 +45,25 @@ type config struct {
func configure(cmd string, args []string) (cfg config, err error) {
fs := flag.NewFlagSet(cmd, flag.ContinueOnError)
fs.Usage = func() {}
- flagopt.StringOpt(fs, &cfg.verbosity, "verbosity", "v", logger.LevelNotice.String())
flagopt.StringOpt(fs, &cfg.name, "name", "n", "")
- flagopt.StringOpt(fs, &cfg.secret, "secret", "s", "")
flagopt.StringOpt(fs, &cfg.output, "output", "o", "")
+ flagopt.StringOpt(fs, &cfg.secret, "secret", "s", "")
+ flagopt.StringOpt(fs, &cfg.verbosity, "verbosity", "v", logger.LevelNotice.String())
if err = fs.Parse(args); err != nil {
return cfg, err
}
// Options
- lv, err := logger.NewLevel(cfg.verbosity)
- if err != nil {
- return cfg, fmt.Errorf("invalid verbosity: %v", err)
- }
if cfg.name == "" {
return cfg, fmt.Errorf("node name is required")
}
if cfg.secret == "" {
return cfg, fmt.Errorf("node secret is required")
}
+ lv, err := logger.NewLevel(cfg.verbosity)
+ if err != nil {
+ return cfg, fmt.Errorf("invalid verbosity: %v", err)
+ }
cfg.log = logger.New(logger.Config{Level: lv, File: os.Stderr})
// Arguments
@@ -85,11 +79,11 @@ func main() {
cfg, err := configure(os.Args[0], os.Args[1:])
if err != nil {
if errors.Is(err, flag.ErrHelp) {
- fmt.Fprintf(os.Stderr, "%s", usage[1:])
+ fmt.Fprintf(os.Stdout, "%s", usage[1:])
os.Exit(0)
}
if !strings.Contains(err.Error(), "flag provided but not defined") {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ fmt.Fprintf(os.Stdout, "%v\n", err)
}
os.Exit(1)
}
diff --git a/cmd/silentct-mac/name.help2man b/cmd/silentct-mac/name.help2man
new file mode 100644
index 0000000..d7f6a28
--- /dev/null
+++ b/cmd/silentct-mac/name.help2man
@@ -0,0 +1,2 @@
+[NAME]
+silentct-mac - allowlist certificates with message authentication codes
diff --git a/cmd/silentct-mac/see-also.help2man b/cmd/silentct-mac/see-also.help2man
new file mode 100644
index 0000000..02987dc
--- /dev/null
+++ b/cmd/silentct-mac/see-also.help2man
@@ -0,0 +1,2 @@
+[SEE ALSO]
+.BR silentct-mon (1)
diff --git a/cmd/silentct-mon/examples.help2man b/cmd/silentct-mon/examples.help2man
new file mode 100644
index 0000000..7a1e8fc
--- /dev/null
+++ b/cmd/silentct-mon/examples.help2man
@@ -0,0 +1,38 @@
+[EXAMPLES]
+
+A basic configuration is shown below.
+
+ {
+ "monitor": [
+ {
+ "bootstrap_at": "2024-05-16T00:00:00Z",
+ "wildcard": "example.org",
+ "excludes": [
+ "test"
+ ]
+ }
+ ],
+ "nodes": [
+ {
+ "name": "example.org",
+ "secret": "sikritpassword",
+ "url": "https://www.example.org/silent-ct/allowlist",
+ "issues": [
+ "example.org",
+ "www.example.org"
+ ]
+ }
+ ]
+ }
+
+Bootstrap a new monitor in a non-existent directory:
+
+.B $ silentct-mon -b -d ~/.local/lib/silent-ct -f ~/.config/silent-ct/config.json
+
+Run the monitor continuously:
+
+.B $ silentct-mon -d ~/.local/lib/silent-ct -f ~/.config/silent-ct/config.json
+
+Use
+.B -v DEBUG
+to see what's happening underneath the hood.
diff --git a/cmd/silent-ctmoon/main.go b/cmd/silentct-mon/main.go
index c651e68..2d070fb 100644
--- a/cmd/silent-ctmoon/main.go
+++ b/cmd/silentct-mon/main.go
@@ -23,28 +23,27 @@ import (
)
const usage = `
-A utility that follows relevant Certificate Transparency logs to
-discover certificates that may be mis-issued. To be silent, any
-legitimately issued certificates are pulled from trusted nodes.
+silentct-mon is a tool that monitors Certificate Transparency logs. The tool
+can operate silently, which means there need not be any output unless a
+certificate is possibly mis-issued. This requires use of the silentct-mac
+utility on the trusted systems that legitimately request certificates.
-Usage:
+The same list of Certificate Transparency logs as Google Chrome is used. This
+list can be overridden in the silentct-mon configuration file.
- silent-ctmoon --help
- silent-ctmoon [Opts] -d DIRECTORY -f POLICY_FILE
+Usage: silentct-mon [Options] -d DIRECTORY -f POLICY-FILE
Options:
- -h, --help: Output usage message and exit
- -v, --verbosity: Leveled logging output (default: NOTICE)
-
- -b, --bootstrap: Initializate a new state directory (Default: false)
- -c, --contact: A string that helps log operators know who you are (Default: "")
- -d, --directory: Path to a directory where all state will be stored
- -e, --please-exit Toggle to only run until up-to-date (Default: false)
- -f, --policy-file: Path to the monitor's policy file in JSON format
+ -b, --bootstrap Initializes a new state directory (Default: false)
+ -c, --contact A string that helps log operators know who you are (Default: "")
+ -d, --directory Path to a directory where all state will be stored
+ -w, --num-workers Number of parallel workers to fetch each log with (Default: 1)
-o, --output-file File that all output will be written to (Default: stdout)
- -p, --pull-interval: How often nodes are pulled for certificates (Default: 15m)
- -w, --num-workers: Number of parallel workers to fetch each log with (Default: 1)
+ -e, --please-exit Toggle to only run until up-to-date (Default: false)
+ -f, --policy-file Path to the monitor's policy file in JSON format
+ -p, --pull-interval How often nodes are pulled for certificates (Default: 15m)
+ -v, --verbosity Leveled logging output (default: NOTICE)
`
type config struct {
@@ -67,29 +66,25 @@ type config struct {
func configure(cmd string, args []string) (cfg config, err error) {
fs := flag.NewFlagSet(cmd, flag.ContinueOnError)
fs.Usage = func() {}
- flagopt.StringOpt(fs, &cfg.verbosity, "verbosity", "v", logger.LevelNotice.String())
flagopt.BoolOpt(fs, &cfg.bootstrap, "bootstrap", "b", false)
flagopt.StringOpt(fs, &cfg.contact, "contact", "c", "")
flagopt.StringOpt(fs, &cfg.directory, "directory", "d", "")
+ flagopt.UintOpt(fs, &cfg.numWorkers, "num-workers", "w", 1)
+ flagopt.StringOpt(fs, &cfg.outputFile, "output-file", "o", "")
flagopt.BoolOpt(fs, &cfg.pleaseExit, "please-exit", "e", false)
flagopt.StringOpt(fs, &cfg.policyFile, "policy-file", "f", "")
- flagopt.StringOpt(fs, &cfg.outputFile, "output-file", "o", "")
flagopt.DurationOpt(fs, &cfg.pullInterval, "pull-interval", "p", 15*time.Minute)
- flagopt.UintOpt(fs, &cfg.numWorkers, "num-workers", "w", 1)
+ flagopt.StringOpt(fs, &cfg.verbosity, "verbosity", "v", logger.LevelNotice.String())
if err = fs.Parse(args); err != nil {
return cfg, err
}
// Options
- lv, err := logger.NewLevel(cfg.verbosity)
- if err != nil {
- return cfg, fmt.Errorf("invalid verbosity: %v", err)
- }
if cfg.directory == "" {
return cfg, fmt.Errorf("directory is a required option")
}
- if cfg.policyFile == "" {
- return cfg, fmt.Errorf("policy file is a required option")
+ if cfg.numWorkers == 0 || cfg.numWorkers >= 4 {
+ return cfg, fmt.Errorf("number of workers must be in [1, 4)")
}
output := os.Stdout
if cfg.outputFile != "" {
@@ -97,17 +92,20 @@ func configure(cmd string, args []string) (cfg config, err error) {
return cfg, fmt.Errorf("failed to open output file: %v", err)
}
}
- if cfg.numWorkers == 0 || cfg.numWorkers > 4 {
- return cfg, fmt.Errorf("number of workers must be in [1, 4]")
+ if cfg.policyFile == "" {
+ return cfg, fmt.Errorf("policy file is a required option")
}
-
- cfg.log = logger.New(logger.Config{Level: lv, File: output})
if err := ioutil.ReadJSON(cfg.policyFile, &cfg.policy); err != nil {
return cfg, err
}
if len(cfg.policy.Monitor) == 0 {
return cfg, fmt.Errorf("policy: need at least one wildcard to monitor")
}
+ lv, err := logger.NewLevel(cfg.verbosity)
+ if err != nil {
+ return cfg, fmt.Errorf("invalid verbosity: %v", err)
+ }
+ cfg.log = logger.New(logger.Config{Level: lv, File: output})
// Arguments
if len(fs.Args()) != 0 {
@@ -121,11 +119,11 @@ func main() {
cfg, err := configure(os.Args[0], os.Args[1:])
if err != nil {
if errors.Is(err, flag.ErrHelp) {
- fmt.Fprintf(os.Stderr, "%s", usage[1:])
+ fmt.Fprintf(os.Stdout, "%s", usage[1:])
os.Exit(0)
}
if !strings.Contains(err.Error(), "flag provided but not defined") {
- fmt.Fprintf(os.Stderr, "%v\n", err)
+ fmt.Fprintf(os.Stdout, "%v\n", err)
}
os.Exit(1)
}
diff --git a/cmd/silentct-mon/name.help2man b/cmd/silentct-mon/name.help2man
new file mode 100644
index 0000000..a4edcc1
--- /dev/null
+++ b/cmd/silentct-mon/name.help2man
@@ -0,0 +1,2 @@
+[NAME]
+silentct-mon - monitor Certificate Transparency logs
diff --git a/cmd/silentct-mon/see-also.help2man b/cmd/silentct-mon/see-also.help2man
new file mode 100644
index 0000000..d4b9782
--- /dev/null
+++ b/cmd/silentct-mon/see-also.help2man
@@ -0,0 +1,2 @@
+[SEE ALSO]
+.BR silentct-mac (1)