Execute scripts under $CERTSPOTTER_CONFIG_DIR/hooks.d, if it exists

This commit is contained in:
Andrew Ayer 2023-02-20 10:02:48 -05:00
parent d08ad53464
commit fd0a2a4d44
3 changed files with 50 additions and 5 deletions

View File

@ -99,6 +99,9 @@ func defaultWatchListPathIfExists() string {
return ""
}
}
func defaultScriptDir() string {
return filepath.Join(defaultConfigDir(), "hooks.d")
}
func readWatchListFile(filename string) (monitor.WatchList, error) {
file, err := os.Open(filename)
@ -162,11 +165,6 @@ func main() {
os.Exit(2)
}
if len(flags.email) == 0 && len(flags.script) == 0 && flags.stdout == false {
fmt.Fprintf(os.Stderr, "%s: at least one of -email, -script, or -stdout must be specified (see -help for details)\n", programName)
os.Exit(2)
}
config := &monitor.Config{
LogListSource: flags.logs,
StateDir: flags.stateDir,
@ -174,11 +172,22 @@ func main() {
StartAtEnd: flags.startAtEnd,
Verbose: flags.verbose,
Script: flags.script,
ScriptDir: defaultScriptDir(),
Email: flags.email,
Stdout: flags.stdout,
HealthCheckInterval: flags.healthcheck,
}
if len(config.Email) == 0 && config.Script == "" && !fileExists(config.ScriptDir) && config.Stdout == false {
fmt.Fprintf(os.Stderr, "%s: no notification methods were specified\n", programName)
fmt.Fprintf(os.Stderr, "Please specify at least one of the following notification methods:\n")
fmt.Fprintf(os.Stderr, " - Place one or more executable scripts in the %s directory\n", config.ScriptDir)
fmt.Fprintf(os.Stderr, " - Specify an email address using the -email flag\n")
fmt.Fprintf(os.Stderr, " - Specify the path to an executable script using the -script flag\n")
fmt.Fprintf(os.Stderr, " - Specify the -stdout flag\n")
os.Exit(2)
}
if flags.watchlist == "-" {
watchlist, err := monitor.ReadWatchList(os.Stdin)
if err != nil {

View File

@ -21,6 +21,7 @@ type Config struct {
Verbose bool
SaveCerts bool
Script string
ScriptDir string
Email []string
Stdout bool
HealthCheckInterval time.Duration

View File

@ -12,9 +12,12 @@ package monitor
import (
"bytes"
"context"
"errors"
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
)
@ -44,6 +47,12 @@ func notify(ctx context.Context, config *Config, notif notification) error {
}
}
if config.ScriptDir != "" {
if err := execScriptDir(ctx, config.ScriptDir, notif); err != nil {
return err
}
}
return nil
}
@ -104,6 +113,32 @@ func execScript(ctx context.Context, scriptName string, notif notification) erro
}
}
func execScriptDir(ctx context.Context, dirPath string, notif notification) error {
dirents, err := os.ReadDir(dirPath)
if errors.Is(err, fs.ErrNotExist) {
return nil
} else if err != nil {
return fmt.Errorf("error executing scripts in directory %q: %w", dirPath, err)
}
for _, dirent := range dirents {
if strings.HasPrefix(dirent.Name(), ".") {
continue
}
scriptPath := filepath.Join(dirPath, dirent.Name())
info, err := os.Stat(scriptPath)
if errors.Is(err, fs.ErrNotExist) {
continue
} else if err != nil {
return fmt.Errorf("error executing %q in directory %q: %w", dirent.Name(), dirPath, err)
} else if info.Mode().IsRegular() && isExecutable(info.Mode()) {
if err := execScript(ctx, scriptPath, notif); err != nil {
return err
}
}
}
return nil
}
func isExecutable(mode os.FileMode) bool {
return mode&0111 != 0
}