mirror of
				https://github.com/SSLMate/certspotter.git
				synced 2025-07-03 10:47:17 +02:00 
			
		
		
		
	Execute scripts under $CERTSPOTTER_CONFIG_DIR/hooks.d, if it exists
This commit is contained in:
		
							parent
							
								
									d08ad53464
								
							
						
					
					
						commit
						fd0a2a4d44
					
				@ -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 {
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,7 @@ type Config struct {
 | 
			
		||||
	Verbose             bool
 | 
			
		||||
	SaveCerts           bool
 | 
			
		||||
	Script              string
 | 
			
		||||
	ScriptDir           string
 | 
			
		||||
	Email               []string
 | 
			
		||||
	Stdout              bool
 | 
			
		||||
	HealthCheckInterval time.Duration
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user