160 lines
4.5 KiB
Go
160 lines
4.5 KiB
Go
//go:build windows
|
|
|
|
// Package bootstrap provides the shared building blocks of the container
|
|
// entrypoints: process environment handling, Zabbix configuration file
|
|
// updates, logging and the final hand-off to the service binary.
|
|
package bootstrap
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Environment is a mutable set of environment variables keyed by name.
|
|
// The entrypoint modifies it while preparing a service and passes the
|
|
// result to the final process.
|
|
type Environment map[string]string
|
|
|
|
// NewEnvironment parses "NAME=value" pairs as returned by os.Environ.
|
|
func NewEnvironment(values []string) Environment {
|
|
env := make(Environment, len(values))
|
|
for _, item := range values {
|
|
name, value, found := strings.Cut(item, "=")
|
|
if found {
|
|
env[name] = value
|
|
}
|
|
}
|
|
return env
|
|
}
|
|
|
|
// List returns the variables as sorted "NAME=value" pairs suitable for exec.
|
|
func (env Environment) List() []string {
|
|
names := make([]string, 0, len(env))
|
|
for name := range env {
|
|
names = append(names, name)
|
|
}
|
|
sort.Strings(names)
|
|
|
|
values := make([]string, 0, len(names))
|
|
for _, name := range names {
|
|
values = append(values, name+"="+env[name])
|
|
}
|
|
return values
|
|
}
|
|
|
|
// ValueOrDefault returns the value of name, or defaultValue when the
|
|
// variable is not present. An empty value counts as present; use
|
|
// ValueOrDefaultNonEmpty to treat it as missing.
|
|
func (env Environment) ValueOrDefault(name, defaultValue string) string {
|
|
value, found := env[name]
|
|
if !found {
|
|
return defaultValue
|
|
}
|
|
return value
|
|
}
|
|
|
|
// ValueOrDefaultNonEmpty returns the value of name, or defaultValue when
|
|
// the variable is missing or empty.
|
|
func (env Environment) ValueOrDefaultNonEmpty(name, defaultValue string) string {
|
|
if value := env[name]; value != "" {
|
|
return value
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
// RequiredHomeDirectory returns the Zabbix home directory,
|
|
// verifying that it exists.
|
|
func RequiredHomeDirectory(env Environment) (string, error) {
|
|
return requiredDirectory(env, "ZABBIX_USER_HOME_DIR")
|
|
}
|
|
|
|
// RequiredDirectories returns the Zabbix home and config directories
|
|
// (ZABBIX_USER_HOME_DIR and ZABBIX_CONF_DIR), verifying that both exist.
|
|
func RequiredDirectories(env Environment) (homeDir, configDir string, err error) {
|
|
homeDir, err = RequiredHomeDirectory(env)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
configDir, err = requiredDirectory(env, "ZABBIX_CONF_DIR")
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
return homeDir, configDir, nil
|
|
}
|
|
|
|
// requiredDirectory returns directory from env variable.
|
|
func requiredDirectory(env Environment, name string) (string, error) {
|
|
directory := env[name]
|
|
if directory == "" {
|
|
return "", fmt.Errorf("%s must be set", name)
|
|
}
|
|
|
|
info, err := os.Stat(directory)
|
|
if err != nil {
|
|
return "", fmt.Errorf("access %s directory %s: %w", name, directory, err)
|
|
}
|
|
if !info.IsDir() {
|
|
return "", fmt.Errorf("%s path %s is not a directory", name, directory)
|
|
}
|
|
|
|
return directory, nil
|
|
}
|
|
|
|
// ProcessFileFromEnvironment persists the value of variable into a file
|
|
// under directory and points the corresponding "<variable>FILE" variable at
|
|
// it. The plain variable is always removed so that secrets do not stay in
|
|
// the service environment.
|
|
func ProcessFileFromEnvironment(env Environment, directory, variable string) error {
|
|
fileVariable := variable + "FILE"
|
|
if value := env[variable]; value != "" {
|
|
path := filepath.Join(directory, fileVariable)
|
|
if err := os.WriteFile(path, []byte(value), 0o600); err != nil {
|
|
return fmt.Errorf("write %s: %w", path, err)
|
|
}
|
|
env[fileVariable] = path
|
|
}
|
|
delete(env, variable)
|
|
return nil
|
|
}
|
|
|
|
// ProcessTLSFiles moves TLS material from the listed variables into
|
|
// files under <home>/enc_internal, so that Zabbix reads certificates and
|
|
// keys from disk instead of the environment.
|
|
func ProcessTLSFiles(env Environment, homeDir string, variables ...string) error {
|
|
directory := filepath.Join(homeDir, "enc_internal")
|
|
for _, variable := range variables {
|
|
if err := ProcessFileFromEnvironment(env, directory, variable); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ClearPrivateEnv removes variables with the supplied prefixes that the
|
|
// service should not inherit. ZBX_CLEAR_ENV=false disables the cleanup.
|
|
func ClearPrivateEnv(env Environment, prefixes ...string) {
|
|
if env["ZBX_CLEAR_ENV"] == "false" {
|
|
return
|
|
}
|
|
for name := range env {
|
|
if hasAnyPrefix(name, prefixes) {
|
|
delete(env, name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func hasAnyPrefix(value string, prefixes []string) bool {
|
|
for _, prefix := range prefixes {
|
|
if strings.HasPrefix(value, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|