mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 02:57:59 +00:00
VAULT-8732: Add log-file to Vault Agent (#17841)
* Started work on adding log-file support to Agent * Allow log file to be picked up and appended * Use NewLogFile everywhere * Tried to pull out the config aggregation from Agent.Run Co-authored-by: Nick Cabatoff <ncabatoff@hashicorp.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -119,3 +119,4 @@ website/components/node_modules
|
|||||||
|
|
||||||
.buildcache/
|
.buildcache/
|
||||||
.releaser/
|
.releaser/
|
||||||
|
*.log
|
||||||
|
|||||||
3
changelog/17841.txt
Normal file
3
changelog/17841.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:feature
|
||||||
|
logging: Vault Agent supports logging to a specified file path via environment variable, CLI or config
|
||||||
|
```
|
||||||
207
command/agent.go
207
command/agent.go
@@ -41,11 +41,11 @@ import (
|
|||||||
"github.com/hashicorp/vault/command/agent/sink/inmem"
|
"github.com/hashicorp/vault/command/agent/sink/inmem"
|
||||||
"github.com/hashicorp/vault/command/agent/template"
|
"github.com/hashicorp/vault/command/agent/template"
|
||||||
"github.com/hashicorp/vault/command/agent/winsvc"
|
"github.com/hashicorp/vault/command/agent/winsvc"
|
||||||
|
"github.com/hashicorp/vault/helper/logging"
|
||||||
"github.com/hashicorp/vault/helper/metricsutil"
|
"github.com/hashicorp/vault/helper/metricsutil"
|
||||||
"github.com/hashicorp/vault/internalshared/configutil"
|
"github.com/hashicorp/vault/internalshared/configutil"
|
||||||
"github.com/hashicorp/vault/internalshared/listenerutil"
|
"github.com/hashicorp/vault/internalshared/listenerutil"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/logging"
|
|
||||||
"github.com/hashicorp/vault/sdk/helper/useragent"
|
"github.com/hashicorp/vault/sdk/helper/useragent"
|
||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/hashicorp/vault/sdk/version"
|
"github.com/hashicorp/vault/sdk/version"
|
||||||
@@ -61,6 +61,12 @@ var (
|
|||||||
_ cli.CommandAutocomplete = (*AgentCommand)(nil)
|
_ cli.CommandAutocomplete = (*AgentCommand)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// flagNameAgentExitAfterAuth is used as an Agent specific flag to indicate
|
||||||
|
// that agent should exit after a single successful auth
|
||||||
|
flagNameAgentExitAfterAuth = "exit-after-auth"
|
||||||
|
)
|
||||||
|
|
||||||
type AgentCommand struct {
|
type AgentCommand struct {
|
||||||
*BaseCommand
|
*BaseCommand
|
||||||
|
|
||||||
@@ -80,6 +86,7 @@ type AgentCommand struct {
|
|||||||
|
|
||||||
flagConfigs []string
|
flagConfigs []string
|
||||||
flagLogLevel string
|
flagLogLevel string
|
||||||
|
flagLogFile string
|
||||||
flagExitAfterAuth bool
|
flagExitAfterAuth bool
|
||||||
|
|
||||||
flagTestVerifyOnly bool
|
flagTestVerifyOnly bool
|
||||||
@@ -124,17 +131,24 @@ func (c *AgentCommand) Flags() *FlagSets {
|
|||||||
})
|
})
|
||||||
|
|
||||||
f.StringVar(&StringVar{
|
f.StringVar(&StringVar{
|
||||||
Name: "log-level",
|
Name: flagNameLogLevel,
|
||||||
Target: &c.flagLogLevel,
|
Target: &c.flagLogLevel,
|
||||||
Default: "info",
|
Default: "info",
|
||||||
EnvVar: "VAULT_LOG_LEVEL",
|
EnvVar: EnvVaultLogLevel,
|
||||||
Completion: complete.PredictSet("trace", "debug", "info", "warn", "error"),
|
Completion: complete.PredictSet("trace", "debug", "info", "warn", "error"),
|
||||||
Usage: "Log verbosity level. Supported values (in order of detail) are " +
|
Usage: "Log verbosity level. Supported values (in order of detail) are " +
|
||||||
"\"trace\", \"debug\", \"info\", \"warn\", and \"error\".",
|
"\"trace\", \"debug\", \"info\", \"warn\", and \"error\".",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
f.StringVar(&StringVar{
|
||||||
|
Name: flagNameLogFile,
|
||||||
|
Target: &c.flagLogFile,
|
||||||
|
EnvVar: EnvVaultLogFile,
|
||||||
|
Usage: "Path to the log file that Vault should use for logging",
|
||||||
|
})
|
||||||
|
|
||||||
f.BoolVar(&BoolVar{
|
f.BoolVar(&BoolVar{
|
||||||
Name: "exit-after-auth",
|
Name: flagNameAgentExitAfterAuth,
|
||||||
Target: &c.flagExitAfterAuth,
|
Target: &c.flagExitAfterAuth,
|
||||||
Default: false,
|
Default: false,
|
||||||
Usage: "If set to true, the agent will exit with code 0 after a single " +
|
Usage: "If set to true, the agent will exit with code 0 after a single " +
|
||||||
@@ -193,27 +207,6 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
if c.flagCombineLogs {
|
if c.flagCombineLogs {
|
||||||
c.logWriter = os.Stdout
|
c.logWriter = os.Stdout
|
||||||
}
|
}
|
||||||
var level log.Level
|
|
||||||
c.flagLogLevel = strings.ToLower(strings.TrimSpace(c.flagLogLevel))
|
|
||||||
switch c.flagLogLevel {
|
|
||||||
case "trace":
|
|
||||||
level = log.Trace
|
|
||||||
case "debug":
|
|
||||||
level = log.Debug
|
|
||||||
case "notice", "info", "":
|
|
||||||
level = log.Info
|
|
||||||
case "warn", "warning":
|
|
||||||
level = log.Warn
|
|
||||||
case "err", "error":
|
|
||||||
level = log.Error
|
|
||||||
default:
|
|
||||||
c.UI.Error(fmt.Sprintf("Unknown log level: %s", c.flagLogLevel))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.logger == nil {
|
|
||||||
c.logger = logging.NewVaultLoggerWithWriter(c.logWriter, level)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validation
|
// Validation
|
||||||
if len(c.flagConfigs) != 1 {
|
if len(c.flagConfigs) != 1 {
|
||||||
@@ -221,7 +214,7 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the configuration
|
// Load the configuration file
|
||||||
config, err := agentConfig.LoadConfig(c.flagConfigs[0])
|
config, err := agentConfig.LoadConfig(c.flagConfigs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.UI.Error(fmt.Sprintf("Error loading configuration from %s: %s", c.flagConfigs[0], err))
|
c.UI.Error(fmt.Sprintf("Error loading configuration from %s: %s", c.flagConfigs[0], err))
|
||||||
@@ -235,6 +228,7 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
"-config flag."))
|
"-config flag."))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.AutoAuth == nil && config.Cache == nil {
|
if config.AutoAuth == nil && config.Cache == nil {
|
||||||
c.UI.Error("No auto_auth or cache block found in config file")
|
c.UI.Error("No auto_auth or cache block found in config file")
|
||||||
return 1
|
return 1
|
||||||
@@ -243,62 +237,29 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
c.UI.Info("No auto_auth block found in config file, not starting automatic authentication feature")
|
c.UI.Info("No auto_auth block found in config file, not starting automatic authentication feature")
|
||||||
}
|
}
|
||||||
|
|
||||||
exitAfterAuth := config.ExitAfterAuth
|
config = c.aggregateConfig(f, config)
|
||||||
f.Visit(func(fl *flag.Flag) {
|
|
||||||
if fl.Name == "exit-after-auth" {
|
|
||||||
exitAfterAuth = c.flagExitAfterAuth
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
c.setStringFlag(f, config.Vault.Address, &StringVar{
|
// Build the logger using level, format and path
|
||||||
Name: flagNameAddress,
|
logLevel, err := logging.ParseLogLevel(config.LogLevel)
|
||||||
Target: &c.flagAddress,
|
if err != nil {
|
||||||
Default: "https://127.0.0.1:8200",
|
c.UI.Error(err.Error())
|
||||||
EnvVar: api.EnvVaultAddress,
|
return 1
|
||||||
})
|
}
|
||||||
config.Vault.Address = c.flagAddress
|
|
||||||
c.setStringFlag(f, config.Vault.CACert, &StringVar{
|
logFormat, err := logging.ParseLogFormat(config.LogFormat)
|
||||||
Name: flagNameCACert,
|
if err != nil {
|
||||||
Target: &c.flagCACert,
|
c.UI.Error(err.Error())
|
||||||
Default: "",
|
return 1
|
||||||
EnvVar: api.EnvVaultCACert,
|
}
|
||||||
})
|
|
||||||
config.Vault.CACert = c.flagCACert
|
logCfg := logging.NewLogConfig("agent", logLevel, logFormat, config.LogFile)
|
||||||
c.setStringFlag(f, config.Vault.CAPath, &StringVar{
|
l, err := logging.Setup(logCfg, c.logWriter)
|
||||||
Name: flagNameCAPath,
|
if err != nil {
|
||||||
Target: &c.flagCAPath,
|
c.UI.Error(err.Error())
|
||||||
Default: "",
|
return 1
|
||||||
EnvVar: api.EnvVaultCAPath,
|
}
|
||||||
})
|
|
||||||
config.Vault.CAPath = c.flagCAPath
|
c.logger = l
|
||||||
c.setStringFlag(f, config.Vault.ClientCert, &StringVar{
|
|
||||||
Name: flagNameClientCert,
|
|
||||||
Target: &c.flagClientCert,
|
|
||||||
Default: "",
|
|
||||||
EnvVar: api.EnvVaultClientCert,
|
|
||||||
})
|
|
||||||
config.Vault.ClientCert = c.flagClientCert
|
|
||||||
c.setStringFlag(f, config.Vault.ClientKey, &StringVar{
|
|
||||||
Name: flagNameClientKey,
|
|
||||||
Target: &c.flagClientKey,
|
|
||||||
Default: "",
|
|
||||||
EnvVar: api.EnvVaultClientKey,
|
|
||||||
})
|
|
||||||
config.Vault.ClientKey = c.flagClientKey
|
|
||||||
c.setBoolFlag(f, config.Vault.TLSSkipVerify, &BoolVar{
|
|
||||||
Name: flagNameTLSSkipVerify,
|
|
||||||
Target: &c.flagTLSSkipVerify,
|
|
||||||
Default: false,
|
|
||||||
EnvVar: api.EnvVaultSkipVerify,
|
|
||||||
})
|
|
||||||
config.Vault.TLSSkipVerify = c.flagTLSSkipVerify
|
|
||||||
c.setStringFlag(f, config.Vault.TLSServerName, &StringVar{
|
|
||||||
Name: flagTLSServerName,
|
|
||||||
Target: &c.flagTLSServerName,
|
|
||||||
Default: "",
|
|
||||||
EnvVar: api.EnvVaultTLSServerName,
|
|
||||||
})
|
|
||||||
config.Vault.TLSServerName = c.flagTLSServerName
|
|
||||||
|
|
||||||
infoKeys := make([]string, 0, 10)
|
infoKeys := make([]string, 0, 10)
|
||||||
info := make(map[string]string)
|
info := make(map[string]string)
|
||||||
@@ -855,16 +816,16 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
ss := sink.NewSinkServer(&sink.SinkServerConfig{
|
ss := sink.NewSinkServer(&sink.SinkServerConfig{
|
||||||
Logger: c.logger.Named("sink.server"),
|
Logger: c.logger.Named("sink.server"),
|
||||||
Client: ahClient,
|
Client: ahClient,
|
||||||
ExitAfterAuth: exitAfterAuth,
|
ExitAfterAuth: config.ExitAfterAuth,
|
||||||
})
|
})
|
||||||
|
|
||||||
ts := template.NewServer(&template.ServerConfig{
|
ts := template.NewServer(&template.ServerConfig{
|
||||||
Logger: c.logger.Named("template.server"),
|
Logger: c.logger.Named("template.server"),
|
||||||
LogLevel: level,
|
LogLevel: logLevel,
|
||||||
LogWriter: c.logWriter,
|
LogWriter: c.logWriter,
|
||||||
AgentConfig: config,
|
AgentConfig: config,
|
||||||
Namespace: templateNamespace,
|
Namespace: templateNamespace,
|
||||||
ExitAfterAuth: exitAfterAuth,
|
ExitAfterAuth: config.ExitAfterAuth,
|
||||||
})
|
})
|
||||||
|
|
||||||
g.Add(func() error {
|
g.Add(func() error {
|
||||||
@@ -963,6 +924,84 @@ func (c *AgentCommand) Run(args []string) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// aggregateConfig ensures that the config object accurately reflects the desired
|
||||||
|
// settings as configured by the user. It applies the relevant config setting based
|
||||||
|
// on the precedence (env var overrides file config, cli overrides env var).
|
||||||
|
// It mutates the config object supplied and returns the updated object.
|
||||||
|
func (c *AgentCommand) aggregateConfig(f *FlagSets, config *agentConfig.Config) *agentConfig.Config {
|
||||||
|
f.Visit(func(fl *flag.Flag) {
|
||||||
|
if fl.Name == flagNameAgentExitAfterAuth {
|
||||||
|
config.ExitAfterAuth = c.flagExitAfterAuth
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
c.setStringFlag(f, config.LogFile, &StringVar{
|
||||||
|
Name: flagNameLogFile,
|
||||||
|
EnvVar: EnvVaultLogFile,
|
||||||
|
Target: &c.flagLogFile,
|
||||||
|
})
|
||||||
|
config.LogFile = c.flagLogFile
|
||||||
|
|
||||||
|
c.setStringFlag(f, config.LogLevel, &StringVar{
|
||||||
|
Name: flagNameLogLevel,
|
||||||
|
EnvVar: EnvVaultLogLevel,
|
||||||
|
Target: &c.flagLogLevel,
|
||||||
|
})
|
||||||
|
config.LogLevel = c.flagLogLevel
|
||||||
|
|
||||||
|
c.setStringFlag(f, config.Vault.Address, &StringVar{
|
||||||
|
Name: flagNameAddress,
|
||||||
|
Target: &c.flagAddress,
|
||||||
|
Default: "https://127.0.0.1:8200",
|
||||||
|
EnvVar: api.EnvVaultAddress,
|
||||||
|
})
|
||||||
|
config.Vault.Address = c.flagAddress
|
||||||
|
c.setStringFlag(f, config.Vault.CACert, &StringVar{
|
||||||
|
Name: flagNameCACert,
|
||||||
|
Target: &c.flagCACert,
|
||||||
|
Default: "",
|
||||||
|
EnvVar: api.EnvVaultCACert,
|
||||||
|
})
|
||||||
|
config.Vault.CACert = c.flagCACert
|
||||||
|
c.setStringFlag(f, config.Vault.CAPath, &StringVar{
|
||||||
|
Name: flagNameCAPath,
|
||||||
|
Target: &c.flagCAPath,
|
||||||
|
Default: "",
|
||||||
|
EnvVar: api.EnvVaultCAPath,
|
||||||
|
})
|
||||||
|
config.Vault.CAPath = c.flagCAPath
|
||||||
|
c.setStringFlag(f, config.Vault.ClientCert, &StringVar{
|
||||||
|
Name: flagNameClientCert,
|
||||||
|
Target: &c.flagClientCert,
|
||||||
|
Default: "",
|
||||||
|
EnvVar: api.EnvVaultClientCert,
|
||||||
|
})
|
||||||
|
config.Vault.ClientCert = c.flagClientCert
|
||||||
|
c.setStringFlag(f, config.Vault.ClientKey, &StringVar{
|
||||||
|
Name: flagNameClientKey,
|
||||||
|
Target: &c.flagClientKey,
|
||||||
|
Default: "",
|
||||||
|
EnvVar: api.EnvVaultClientKey,
|
||||||
|
})
|
||||||
|
config.Vault.ClientKey = c.flagClientKey
|
||||||
|
c.setBoolFlag(f, config.Vault.TLSSkipVerify, &BoolVar{
|
||||||
|
Name: flagNameTLSSkipVerify,
|
||||||
|
Target: &c.flagTLSSkipVerify,
|
||||||
|
Default: false,
|
||||||
|
EnvVar: api.EnvVaultSkipVerify,
|
||||||
|
})
|
||||||
|
config.Vault.TLSSkipVerify = c.flagTLSSkipVerify
|
||||||
|
c.setStringFlag(f, config.Vault.TLSServerName, &StringVar{
|
||||||
|
Name: flagTLSServerName,
|
||||||
|
Target: &c.flagTLSServerName,
|
||||||
|
Default: "",
|
||||||
|
EnvVar: api.EnvVaultTLSServerName,
|
||||||
|
})
|
||||||
|
config.Vault.TLSServerName = c.flagTLSServerName
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
// verifyRequestHeader wraps an http.Handler inside a Handler that checks for
|
// verifyRequestHeader wraps an http.Handler inside a Handler that checks for
|
||||||
// the request header that is used for SSRF protection.
|
// the request header that is used for SSRF protection.
|
||||||
func verifyRequestHeader(handler http.Handler) http.Handler {
|
func verifyRequestHeader(handler http.Handler) http.Handler {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -38,6 +37,7 @@ type Config struct {
|
|||||||
DisableKeepAlivesCaching bool `hcl:"-"`
|
DisableKeepAlivesCaching bool `hcl:"-"`
|
||||||
DisableKeepAlivesTemplating bool `hcl:"-"`
|
DisableKeepAlivesTemplating bool `hcl:"-"`
|
||||||
DisableKeepAlivesAutoAuth bool `hcl:"-"`
|
DisableKeepAlivesAutoAuth bool `hcl:"-"`
|
||||||
|
LogFile string `hcl:"log_file"`
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -173,7 +173,7 @@ func LoadConfig(path string) (*Config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the file
|
// Read the file
|
||||||
d, err := ioutil.ReadFile(path)
|
d, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ func TestLoadConfigFile(t *testing.T) {
|
|||||||
NumRetries: 12,
|
NumRetries: 12,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
LogFile: "/var/log/vault/vault-agent.log",
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Prune()
|
config.Prune()
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pid_file = "./pidfile"
|
pid_file = "./pidfile"
|
||||||
|
log_file = "/var/log/vault/vault-agent.log"
|
||||||
|
|
||||||
auto_auth {
|
auto_auth {
|
||||||
method "aws" {
|
method "aws" {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pid_file = "./pidfile"
|
pid_file = "./pidfile"
|
||||||
|
log_file = "/var/log/vault/vault-agent.log"
|
||||||
|
|
||||||
auto_auth {
|
auto_auth {
|
||||||
method {
|
method {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/hashicorp/vault/api"
|
"github.com/hashicorp/vault/api"
|
||||||
credAppRole "github.com/hashicorp/vault/builtin/credential/approle"
|
credAppRole "github.com/hashicorp/vault/builtin/credential/approle"
|
||||||
"github.com/hashicorp/vault/command/agent"
|
"github.com/hashicorp/vault/command/agent"
|
||||||
|
agentConfig "github.com/hashicorp/vault/command/agent/config"
|
||||||
vaulthttp "github.com/hashicorp/vault/http"
|
vaulthttp "github.com/hashicorp/vault/http"
|
||||||
"github.com/hashicorp/vault/sdk/helper/consts"
|
"github.com/hashicorp/vault/sdk/helper/consts"
|
||||||
"github.com/hashicorp/vault/sdk/helper/logging"
|
"github.com/hashicorp/vault/sdk/helper/logging"
|
||||||
@@ -27,9 +28,26 @@ import (
|
|||||||
"github.com/hashicorp/vault/sdk/logical"
|
"github.com/hashicorp/vault/sdk/logical"
|
||||||
"github.com/hashicorp/vault/vault"
|
"github.com/hashicorp/vault/vault"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BasicHclConfig = `
|
||||||
|
log_file = "/foo/bar/juan.log"
|
||||||
|
vault {
|
||||||
|
address = "http://127.0.0.1:8200"
|
||||||
|
retry {
|
||||||
|
num_retries = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
listener "tcp" {
|
||||||
|
address = "127.0.0.1:8100"
|
||||||
|
tls_disable = true
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
func testAgentCommand(tb testing.TB, logger hclog.Logger) (*cli.MockUi, *AgentCommand) {
|
func testAgentCommand(tb testing.TB, logger hclog.Logger) (*cli.MockUi, *AgentCommand) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
@@ -1245,6 +1263,27 @@ func makeTempFile(t *testing.T, name, contents string) string {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func populateTempFile(t *testing.T, name, contents string) *os.File {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
file, err := os.CreateTemp(t.TempDir(), name)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = file.WriteString(contents)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.Close()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
|
||||||
// handler makes 500 errors happen for reads on /v1/secret.
|
// handler makes 500 errors happen for reads on /v1/secret.
|
||||||
// Definitely not thread-safe, do not use t.Parallel with this.
|
// Definitely not thread-safe, do not use t.Parallel with this.
|
||||||
type handler struct {
|
type handler struct {
|
||||||
@@ -2211,6 +2250,105 @@ cache {}
|
|||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAgent_LogFile_EnvVarOverridesConfig(t *testing.T) {
|
||||||
|
// Create basic config
|
||||||
|
configFile := populateTempFile(t, "agent-config.hcl", BasicHclConfig)
|
||||||
|
cfg, err := agentConfig.LoadConfig(configFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot load config to test update/merge", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check that the config value is the current value
|
||||||
|
assert.Equal(t, "/foo/bar/juan.log", cfg.LogFile)
|
||||||
|
|
||||||
|
// Make sure the env var is configured
|
||||||
|
oldEnvVarLogFile := os.Getenv(EnvVaultLogFile)
|
||||||
|
os.Setenv(EnvVaultLogFile, "/squiggle/logs.txt")
|
||||||
|
if oldEnvVarLogFile == "" {
|
||||||
|
defer os.Unsetenv(EnvVaultLogFile)
|
||||||
|
} else {
|
||||||
|
defer os.Setenv(EnvVaultLogFile, oldEnvVarLogFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the command and parse any flags
|
||||||
|
cmd := &AgentCommand{BaseCommand: &BaseCommand{}}
|
||||||
|
f := cmd.Flags()
|
||||||
|
err = f.Parse([]string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the config based on the inputs.
|
||||||
|
cfg = cmd.aggregateConfig(f, cfg)
|
||||||
|
|
||||||
|
assert.NotEqual(t, "/foo/bar/juan.log", cfg.LogFile)
|
||||||
|
assert.Equal(t, "/squiggle/logs.txt", cfg.LogFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgent_LogFile_CliOverridesEnvVar(t *testing.T) {
|
||||||
|
// Create basic config
|
||||||
|
configFile := populateTempFile(t, "agent-config.hcl", BasicHclConfig)
|
||||||
|
cfg, err := agentConfig.LoadConfig(configFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot load config to test update/merge", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check that the config value is the current value
|
||||||
|
assert.Equal(t, "/foo/bar/juan.log", cfg.LogFile)
|
||||||
|
|
||||||
|
// Make sure the env var is configured
|
||||||
|
oldEnvVarLogFile := os.Getenv(EnvVaultLogFile)
|
||||||
|
os.Setenv(EnvVaultLogFile, "/squiggle/logs.txt")
|
||||||
|
if oldEnvVarLogFile == "" {
|
||||||
|
defer os.Unsetenv(EnvVaultLogFile)
|
||||||
|
} else {
|
||||||
|
defer os.Setenv(EnvVaultLogFile, oldEnvVarLogFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the command and parse any flags
|
||||||
|
cmd := &AgentCommand{BaseCommand: &BaseCommand{}}
|
||||||
|
f := cmd.Flags()
|
||||||
|
// Simulate the flag being specified
|
||||||
|
err = f.Parse([]string{"-log-file=/foo/bar/test.log"})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the config based on the inputs.
|
||||||
|
cfg = cmd.aggregateConfig(f, cfg)
|
||||||
|
|
||||||
|
assert.NotEqual(t, "/foo/bar/juan.log", cfg.LogFile)
|
||||||
|
assert.NotEqual(t, "/squiggle/logs.txt", cfg.LogFile)
|
||||||
|
assert.Equal(t, "/foo/bar/test.log", cfg.LogFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAgent_LogFile_Config(t *testing.T) {
|
||||||
|
// Sanity check, remove any env var
|
||||||
|
os.Unsetenv(EnvVaultLogFile)
|
||||||
|
|
||||||
|
configFile := populateTempFile(t, "agent-config.hcl", BasicHclConfig)
|
||||||
|
|
||||||
|
cfg, err := agentConfig.LoadConfig(configFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Cannot load config to test update/merge", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check that the config value is the current value
|
||||||
|
assert.Equal(t, "/foo/bar/juan.log", cfg.LogFile, "sanity check on log config failed")
|
||||||
|
|
||||||
|
// Parse the cli flags (but we pass in an empty slice)
|
||||||
|
cmd := &AgentCommand{BaseCommand: &BaseCommand{}}
|
||||||
|
f := cmd.Flags()
|
||||||
|
err = f.Parse([]string{})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg = cmd.aggregateConfig(f, cfg)
|
||||||
|
|
||||||
|
assert.Equal(t, "/foo/bar/juan.log", cfg.LogFile, "actual config check")
|
||||||
|
}
|
||||||
|
|
||||||
// Get a randomly assigned port and then free it again before returning it.
|
// Get a randomly assigned port and then free it again before returning it.
|
||||||
// There is still a race when trying to use it, but should work better
|
// There is still a race when trying to use it, but should work better
|
||||||
// than a static port.
|
// than a static port.
|
||||||
|
|||||||
@@ -82,6 +82,11 @@ const (
|
|||||||
EnvVaultLicensePath = "VAULT_LICENSE_PATH"
|
EnvVaultLicensePath = "VAULT_LICENSE_PATH"
|
||||||
// EnvVaultDetailed is to output detailed information (e.g., ListResponseWithInfo).
|
// EnvVaultDetailed is to output detailed information (e.g., ListResponseWithInfo).
|
||||||
EnvVaultDetailed = `VAULT_DETAILED`
|
EnvVaultDetailed = `VAULT_DETAILED`
|
||||||
|
// EnvVaultLogFile is used to specify the path to the log file that Vault should use for logging
|
||||||
|
EnvVaultLogFile = "VAULT_LOG_FILE"
|
||||||
|
// EnvVaultLogLevel is used to specify the log level applied to logging
|
||||||
|
// Supported log levels: Trace, Debug, Error, Warn, Info
|
||||||
|
EnvVaultLogLevel = "VAULT_LOG_LEVEL"
|
||||||
|
|
||||||
// DisableSSCTokens is an env var used to disable index bearing
|
// DisableSSCTokens is an env var used to disable index bearing
|
||||||
// token functionality
|
// token functionality
|
||||||
@@ -136,6 +141,11 @@ const (
|
|||||||
flagNameUserLockoutDisable = "user-lockout-disable"
|
flagNameUserLockoutDisable = "user-lockout-disable"
|
||||||
// flagNameDisableRedirects is used to prevent the client from honoring a single redirect as a response to a request
|
// flagNameDisableRedirects is used to prevent the client from honoring a single redirect as a response to a request
|
||||||
flagNameDisableRedirects = "disable-redirects"
|
flagNameDisableRedirects = "disable-redirects"
|
||||||
|
// flagNameLogFile is used to specify the path to the log file that Vault should use for logging
|
||||||
|
flagNameLogFile = "log-file"
|
||||||
|
// flagNameLogLevel is used to specify the log level applied to logging
|
||||||
|
// Supported log levels: Trace, Debug, Error, Warn, Info
|
||||||
|
flagNameLogLevel = "log-level"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
56
helper/logging/logfile.go
Normal file
56
helper/logging/logfile.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogFile struct {
|
||||||
|
// Name of the log file
|
||||||
|
fileName string
|
||||||
|
|
||||||
|
// Path to the log file
|
||||||
|
logPath string
|
||||||
|
|
||||||
|
// fileInfo is the pointer to the current file being written to
|
||||||
|
fileInfo *os.File
|
||||||
|
|
||||||
|
// acquire is the mutex utilized to ensure we have no concurrency issues
|
||||||
|
acquire sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogFile(logPath string, fileName string) *LogFile {
|
||||||
|
return &LogFile{
|
||||||
|
fileName: strings.TrimSpace(fileName),
|
||||||
|
logPath: strings.TrimSpace(logPath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write is used to implement io.Writer
|
||||||
|
func (l *LogFile) Write(b []byte) (n int, err error) {
|
||||||
|
l.acquire.Lock()
|
||||||
|
defer l.acquire.Unlock()
|
||||||
|
// Create a new file if we have no file to write to
|
||||||
|
if l.fileInfo == nil {
|
||||||
|
if err := l.openNew(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.fileInfo.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LogFile) openNew() error {
|
||||||
|
newFilePath := filepath.Join(l.logPath, l.fileName)
|
||||||
|
|
||||||
|
// Try to open an existing file or create a new one if it doesn't exist.
|
||||||
|
filePointer, err := os.OpenFile(newFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o640)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
l.fileInfo = filePointer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
22
helper/logging/logfile_test.go
Normal file
22
helper/logging/logfile_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogFile_openNew(t *testing.T) {
|
||||||
|
logFile := NewLogFile(t.TempDir(), "vault-agent.log")
|
||||||
|
err := logFile.openNew()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
msg := "[INFO] Something"
|
||||||
|
_, err = logFile.Write([]byte(msg))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
content, err := os.ReadFile(logFile.fileInfo.Name())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, string(content), msg)
|
||||||
|
}
|
||||||
135
helper/logging/logger.go
Normal file
135
helper/logging/logger.go
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/hashicorp/go-hclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnspecifiedFormat LogFormat = iota
|
||||||
|
StandardFormat
|
||||||
|
JSONFormat
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogFormat int
|
||||||
|
|
||||||
|
// LogConfig should be used to supply configuration when creating a new Vault logger
|
||||||
|
type LogConfig struct {
|
||||||
|
name string
|
||||||
|
logLevel log.Level
|
||||||
|
logFormat LogFormat
|
||||||
|
logFilePath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogConfig(name string, logLevel log.Level, logFormat LogFormat, logFilePath string) LogConfig {
|
||||||
|
return LogConfig{
|
||||||
|
name: name,
|
||||||
|
logLevel: logLevel,
|
||||||
|
logFormat: logFormat,
|
||||||
|
logFilePath: strings.TrimSpace(logFilePath),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c LogConfig) IsFormatJson() bool {
|
||||||
|
return c.logFormat == JSONFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stringer implementation
|
||||||
|
func (lf LogFormat) String() string {
|
||||||
|
switch lf {
|
||||||
|
case UnspecifiedFormat:
|
||||||
|
return "unspecified"
|
||||||
|
case StandardFormat:
|
||||||
|
return "standard"
|
||||||
|
case JSONFormat:
|
||||||
|
return "json"
|
||||||
|
}
|
||||||
|
|
||||||
|
// unreachable
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
|
||||||
|
// noErrorWriter is a wrapper to suppress errors when writing to w.
|
||||||
|
type noErrorWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w noErrorWriter) Write(p []byte) (n int, err error) {
|
||||||
|
_, _ = w.w.Write(p)
|
||||||
|
// We purposely return n == len(p) as if write was successful
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup creates a new logger with the specified configuration and writer
|
||||||
|
func Setup(config LogConfig, w io.Writer) (log.InterceptLogger, error) {
|
||||||
|
// Validate the log level
|
||||||
|
if config.logLevel.String() == "unknown" {
|
||||||
|
return nil, fmt.Errorf("invalid log level: %v", config.logLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If out is os.Stdout and Vault is being run as a Windows Service, writes will
|
||||||
|
// fail silently, which may inadvertently prevent writes to other writers.
|
||||||
|
// noErrorWriter is used as a wrapper to suppress any errors when writing to out.
|
||||||
|
writers := []io.Writer{noErrorWriter{w: w}}
|
||||||
|
|
||||||
|
if config.logFilePath != "" {
|
||||||
|
dir, fileName := filepath.Split(config.logFilePath)
|
||||||
|
if fileName == "" {
|
||||||
|
fileName = "vault-agent.log"
|
||||||
|
}
|
||||||
|
logFile := NewLogFile(dir, fileName)
|
||||||
|
if err := logFile.openNew(); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to set up file logging: %w", err)
|
||||||
|
}
|
||||||
|
writers = append(writers, logFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := log.NewInterceptLogger(&log.LoggerOptions{
|
||||||
|
Name: config.name,
|
||||||
|
Level: config.logLevel,
|
||||||
|
Output: io.MultiWriter(writers...),
|
||||||
|
JSONFormat: config.IsFormatJson(),
|
||||||
|
})
|
||||||
|
return logger, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLogFormat parses the log format from the provided string.
|
||||||
|
func ParseLogFormat(format string) (LogFormat, error) {
|
||||||
|
switch strings.ToLower(strings.TrimSpace(format)) {
|
||||||
|
case "":
|
||||||
|
return UnspecifiedFormat, nil
|
||||||
|
case "standard":
|
||||||
|
return StandardFormat, nil
|
||||||
|
case "json":
|
||||||
|
return JSONFormat, nil
|
||||||
|
default:
|
||||||
|
return UnspecifiedFormat, fmt.Errorf("unknown log format: %s", format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseLogLevel(logLevel string) (log.Level, error) {
|
||||||
|
var result log.Level
|
||||||
|
logLevel = strings.ToLower(strings.TrimSpace(logLevel))
|
||||||
|
|
||||||
|
switch logLevel {
|
||||||
|
case "trace":
|
||||||
|
result = log.Trace
|
||||||
|
case "debug":
|
||||||
|
result = log.Debug
|
||||||
|
case "notice", "info", "":
|
||||||
|
result = log.Info
|
||||||
|
case "warn", "warning":
|
||||||
|
result = log.Warn
|
||||||
|
case "err", "error":
|
||||||
|
result = log.Error
|
||||||
|
default:
|
||||||
|
return -1, errors.New(fmt.Sprintf("unknown log level: %s", logLevel))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
128
helper/logging/logger_test.go
Normal file
128
helper/logging/logger_test.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package logging
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
log "github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogger_SetupBasic(t *testing.T) {
|
||||||
|
cfg := NewLogConfig("test-system", log.Info, StandardFormat, t.TempDir()+"test.log")
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupInvalidLogLevel(t *testing.T) {
|
||||||
|
cfg := NewLogConfig("test-system", 999, StandardFormat, t.TempDir()+"test.log")
|
||||||
|
|
||||||
|
_, err := Setup(cfg, nil)
|
||||||
|
assert.Containsf(t, err.Error(), "invalid log level", "expected error %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupLoggerErrorLevel(t *testing.T) {
|
||||||
|
cfg := NewLogConfig("test-system", log.Error, StandardFormat, t.TempDir()+"test.log")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, &buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, logger)
|
||||||
|
|
||||||
|
logger.Error("test error msg")
|
||||||
|
logger.Info("test info msg")
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
|
||||||
|
require.Contains(t, output, "[ERROR] test-system: test error msg")
|
||||||
|
require.NotContains(t, output, "[INFO] test-system: test info msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupLoggerDebugLevel(t *testing.T) {
|
||||||
|
cfg := NewLogConfig("test-system", log.Debug, StandardFormat, t.TempDir()+"test.log")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, &buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, logger)
|
||||||
|
|
||||||
|
logger.Info("test info msg")
|
||||||
|
logger.Debug("test debug msg")
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
|
||||||
|
require.Contains(t, output, "[INFO] test-system: test info msg")
|
||||||
|
require.Contains(t, output, "[DEBUG] test-system: test debug msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupLoggerWithName(t *testing.T) {
|
||||||
|
cfg := NewLogConfig("test-system", log.Debug, StandardFormat, t.TempDir()+"test.log")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, &buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, logger)
|
||||||
|
|
||||||
|
logger.Warn("test warn msg")
|
||||||
|
|
||||||
|
require.Contains(t, buf.String(), "[WARN] test-system: test warn msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupLoggerWithJSON(t *testing.T) {
|
||||||
|
cfg := NewLogConfig("test-system", log.Debug, JSONFormat, t.TempDir()+"test.log")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, &buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, logger)
|
||||||
|
|
||||||
|
logger.Warn("test warn msg")
|
||||||
|
|
||||||
|
var jsonOutput map[string]string
|
||||||
|
err = json.Unmarshal(buf.Bytes(), &jsonOutput)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Contains(t, jsonOutput, "@level")
|
||||||
|
require.Equal(t, jsonOutput["@level"], "warn")
|
||||||
|
require.Contains(t, jsonOutput, "@message")
|
||||||
|
require.Equal(t, jsonOutput["@message"], "test warn msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupLoggerWithValidLogPath(t *testing.T) {
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
cfg := NewLogConfig("test-system", log.Info, StandardFormat, tmpDir+"/")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, &buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupLoggerWithInValidLogPath(t *testing.T) {
|
||||||
|
cfg := NewLogConfig("test-system", log.Info, StandardFormat, "nonexistentdir/")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, &buf)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.True(t, errors.Is(err, os.ErrNotExist))
|
||||||
|
require.Nil(t, logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger_SetupLoggerWithInValidLogPathPermission(t *testing.T) {
|
||||||
|
tmpDir := "/tmp/" + t.Name()
|
||||||
|
|
||||||
|
os.Mkdir(tmpDir, 0o000)
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
cfg := NewLogConfig("test-system", log.Info, StandardFormat, tmpDir+"/")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
|
||||||
|
logger, err := Setup(cfg, &buf)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.True(t, errors.Is(err, os.ErrPermission))
|
||||||
|
require.Nil(t, logger)
|
||||||
|
}
|
||||||
@@ -127,6 +127,12 @@ See the [caching](/docs/agent/caching#api) page for details on the cache API.
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
### Command Options
|
||||||
|
|
||||||
|
- `-log-file` `(string: "")` - If specified, should contain the full file path to use for outputting log files from Vault.
|
||||||
|
|
||||||
|
### Configuration File Options
|
||||||
|
|
||||||
These are the currently-available general configuration option:
|
These are the currently-available general configuration option:
|
||||||
|
|
||||||
- `vault` <code>([vault][vault]: <optional\>)</code> - Specifies the remote Vault server the Agent connects to.
|
- `vault` <code>([vault][vault]: <optional\>)</code> - Specifies the remote Vault server the Agent connects to.
|
||||||
@@ -373,4 +379,4 @@ template {
|
|||||||
[listener]: /docs/agent#listener-stanza
|
[listener]: /docs/agent#listener-stanza
|
||||||
[listener_main]: /docs/configuration/listener/tcp
|
[listener_main]: /docs/configuration/listener/tcp
|
||||||
[winsvc]: /docs/agent/winsvc
|
[winsvc]: /docs/agent/winsvc
|
||||||
[telemetry]: /docs/configuration/telemetry
|
[telemetry]: /docs/configuration/telemetry
|
||||||
@@ -35,7 +35,7 @@ of Vault Agent as a service, using "Vault Agent" as the display name, and starti
|
|||||||
The `binPath` argument should include the fully qualified path to the Vault executable, as well as any arguments required.
|
The `binPath` argument should include the fully qualified path to the Vault executable, as well as any arguments required.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
PS C:\Windows\system32> sc.exe create VaultAgent binPath= "C:\vault\vault.exe agent -config=C:\vault\agent-config.hcl" displayName= "Vault Agent" start= auto
|
PS C:\Windows\system32> sc.exe create VaultAgent binPath="C:\vault\vault.exe agent -config=C:\vault\agent-config.hcl" displayName="Vault Agent" start=auto
|
||||||
[SC] CreateService SUCCESS
|
[SC] CreateService SUCCESS
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -331,6 +331,10 @@ precedence over [#VAULT_LICENSE_PATH](#vault_license_path) and
|
|||||||
[Enterprise, Server only] Specify a path to a license on disk to use for this node.
|
[Enterprise, Server only] Specify a path to a license on disk to use for this node.
|
||||||
This takes precedence over [license_path in config](/docs/configuration#license_path).
|
This takes precedence over [license_path in config](/docs/configuration#license_path).
|
||||||
|
|
||||||
|
### `VAULT_LOG_FILE`
|
||||||
|
|
||||||
|
(Agent only) If provided, specifies the full path to a log file Vault should use to write its logs.
|
||||||
|
|
||||||
### `VAULT_MAX_RETRIES`
|
### `VAULT_MAX_RETRIES`
|
||||||
|
|
||||||
Maximum number of retries when certain error codes are encountered. The default
|
Maximum number of retries when certain error codes are encountered. The default
|
||||||
@@ -434,4 +438,4 @@ list of available flags, run:
|
|||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ vault <subcommand> -h
|
$ vault <subcommand> -h
|
||||||
```
|
```
|
||||||
Reference in New Issue
Block a user