aboutsummaryrefslogtreecommitdiff
path: root/cmd/silentct-mac
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/silentct-mac')
-rw-r--r--cmd/silentct-mac/examples.help2man6
-rw-r--r--cmd/silentct-mac/main.go121
-rw-r--r--cmd/silentct-mac/name.help2man2
-rw-r--r--cmd/silentct-mac/see-also.help2man2
4 files changed, 131 insertions, 0 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/silentct-mac/main.go b/cmd/silentct-mac/main.go
new file mode 100644
index 0000000..2add812
--- /dev/null
+++ b/cmd/silentct-mac/main.go
@@ -0,0 +1,121 @@
+package main
+
+import (
+ "errors"
+ "flag"
+ "fmt"
+ "os"
+ "strings"
+
+ "rgdd.se/silent-ct/internal/flagopt"
+ "rgdd.se/silent-ct/internal/ioutil"
+ "rgdd.se/silent-ct/internal/logger"
+ "rgdd.se/silent-ct/pkg/crtutil"
+ "rgdd.se/silent-ct/pkg/policy"
+ "rgdd.se/silent-ct/pkg/submission"
+)
+
+const usage = `
+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: silentct-mac [Options] -n NAME -s SECRET CRT-FILE [CRT-FILE ...]
+
+Options:
+ -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)
+`
+
+type config struct {
+ // Options
+ name string
+ output string
+ secret string
+ verbosity string
+
+ // Extracted
+ log *logger.Logger
+ files []string
+}
+
+func configure(cmd string, args []string) (cfg config, err error) {
+ fs := flag.NewFlagSet(cmd, flag.ContinueOnError)
+ fs.Usage = func() {}
+ flagopt.StringOpt(fs, &cfg.name, "name", "n", "")
+ 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
+ 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
+ cfg.files = fs.Args()
+ if len(cfg.files) == 0 {
+ return cfg, fmt.Errorf("at least one certificate chain file is required")
+ }
+
+ return cfg, err
+}
+
+func main() {
+ cfg, err := configure(os.Args[0], os.Args[1:])
+ if err != nil {
+ if errors.Is(err, flag.ErrHelp) {
+ fmt.Fprintf(os.Stdout, "%s", usage[1:])
+ os.Exit(0)
+ }
+ if !strings.Contains(err.Error(), "flag provided but not defined") {
+ fmt.Fprintf(os.Stdout, "%v\n", err)
+ }
+ os.Exit(1)
+ }
+
+ var chains [][]byte
+ for i, path := range cfg.files {
+ b, err := ioutil.ReadData(path)
+ if err != nil {
+ cfg.log.Dief("file %d: %v\n", i, err)
+ }
+ if _, err := crtutil.CertificateChainFromPEM(b); err != nil {
+ cfg.log.Dief("file %d: %v\n", i, err)
+ }
+
+ chains = append(chains, b)
+ }
+
+ node, err := policy.NewNode(cfg.name, cfg.secret, "http://www.example.org/unused", nil)
+ if err != nil {
+ cfg.log.Dief("api: %v\n", err)
+ }
+ s, err := submission.New(node, chains)
+ if err != nil {
+ cfg.log.Dief("api: %v\n", err)
+ }
+
+ fp := os.Stdout
+ if cfg.output != "" {
+ if fp, err = os.OpenFile(cfg.output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
+ cfg.log.Dief("output: %v\n", err)
+ }
+ }
+
+ fmt.Fprintf(fp, "%s", string(s))
+}
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)