Add test for listener reloading, and update website docs.
This commit is contained in:
parent
b3218d26d6
commit
0e3764832a
|
@ -2,8 +2,6 @@ package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
auditFile "github.com/hashicorp/vault/builtin/audit/file"
|
auditFile "github.com/hashicorp/vault/builtin/audit/file"
|
||||||
auditSyslog "github.com/hashicorp/vault/builtin/audit/syslog"
|
auditSyslog "github.com/hashicorp/vault/builtin/audit/syslog"
|
||||||
|
@ -79,8 +77,8 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
||||||
"mysql": mysql.Factory,
|
"mysql": mysql.Factory,
|
||||||
"ssh": ssh.Factory,
|
"ssh": ssh.Factory,
|
||||||
},
|
},
|
||||||
ShutdownCh: makeShutdownCh(),
|
ShutdownCh: command.MakeShutdownCh(),
|
||||||
SighupCh: makeSighupCh(),
|
SighupCh: command.MakeSighupCh(),
|
||||||
ReloadFuncs: map[string][]server.ReloadFunc{},
|
ReloadFuncs: map[string][]server.ReloadFunc{},
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
|
@ -311,37 +309,3 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeShutdownCh returns a channel that can be used for shutdown
|
|
||||||
// notifications for commands. This channel will send a message for every
|
|
||||||
// interrupt or SIGTERM received.
|
|
||||||
func makeShutdownCh() <-chan struct{} {
|
|
||||||
resultCh := make(chan struct{})
|
|
||||||
|
|
||||||
signalCh := make(chan os.Signal, 4)
|
|
||||||
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
<-signalCh
|
|
||||||
resultCh <- struct{}{}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return resultCh
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeSighupCh returns a channel that can be used for SIGHUP
|
|
||||||
// reloading. This channel will send a message for every
|
|
||||||
// SIGHUP received.
|
|
||||||
func makeSighupCh() <-chan struct{} {
|
|
||||||
resultCh := make(chan struct{})
|
|
||||||
|
|
||||||
signalCh := make(chan os.Signal, 4)
|
|
||||||
signal.Notify(signalCh, os.Interrupt, syscall.SIGHUP)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
<-signalCh
|
|
||||||
resultCh <- struct{}{}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return resultCh
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,10 +8,12 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/armon/go-metrics"
|
"github.com/armon/go-metrics"
|
||||||
|
@ -35,8 +37,8 @@ type ServerCommand struct {
|
||||||
CredentialBackends map[string]logical.Factory
|
CredentialBackends map[string]logical.Factory
|
||||||
LogicalBackends map[string]logical.Factory
|
LogicalBackends map[string]logical.Factory
|
||||||
|
|
||||||
ShutdownCh <-chan struct{}
|
ShutdownCh chan struct{}
|
||||||
SighupCh <-chan struct{}
|
SighupCh chan struct{}
|
||||||
|
|
||||||
Meta
|
Meta
|
||||||
|
|
||||||
|
@ -637,3 +639,37 @@ General Options:
|
||||||
`
|
`
|
||||||
return strings.TrimSpace(helpText)
|
return strings.TrimSpace(helpText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeShutdownCh returns a channel that can be used for shutdown
|
||||||
|
// notifications for commands. This channel will send a message for every
|
||||||
|
// interrupt or SIGTERM received.
|
||||||
|
func MakeShutdownCh() chan struct{} {
|
||||||
|
resultCh := make(chan struct{})
|
||||||
|
|
||||||
|
signalCh := make(chan os.Signal, 4)
|
||||||
|
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
<-signalCh
|
||||||
|
resultCh <- struct{}{}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return resultCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeSighupCh returns a channel that can be used for SIGHUP
|
||||||
|
// reloading. This channel will send a message for every
|
||||||
|
// SIGHUP received.
|
||||||
|
func MakeSighupCh() chan struct{} {
|
||||||
|
resultCh := make(chan struct{})
|
||||||
|
|
||||||
|
signalCh := make(chan os.Signal, 4)
|
||||||
|
signal.Notify(signalCh, os.Interrupt, syscall.SIGHUP)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
<-signalCh
|
||||||
|
resultCh <- struct{}{}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return resultCh
|
||||||
|
}
|
||||||
|
|
|
@ -41,11 +41,7 @@ func TestTCPListener_tls(t *testing.T) {
|
||||||
defer os.RemoveAll(td)
|
defer os.RemoveAll(td)
|
||||||
|
|
||||||
// Setup initial certs
|
// Setup initial certs
|
||||||
inBytes, _ := ioutil.ReadFile(wd + "reload_foo.pem")
|
inBytes, _ := ioutil.ReadFile(wd + "reload_ca.pem")
|
||||||
ioutil.WriteFile(td+"reload_curr.pem", inBytes, 0777)
|
|
||||||
inBytes, _ = ioutil.ReadFile(wd + "reload_foo.key")
|
|
||||||
ioutil.WriteFile(td+"reload_curr.key", inBytes, 0777)
|
|
||||||
inBytes, _ = ioutil.ReadFile(wd + "reload_ca.pem")
|
|
||||||
certPool := x509.NewCertPool()
|
certPool := x509.NewCertPool()
|
||||||
ok := certPool.AppendCertsFromPEM(inBytes)
|
ok := certPool.AppendCertsFromPEM(inBytes)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -54,8 +50,8 @@ func TestTCPListener_tls(t *testing.T) {
|
||||||
|
|
||||||
ln, _, _, err := tcpListenerFactory(map[string]string{
|
ln, _, _, err := tcpListenerFactory(map[string]string{
|
||||||
"address": "127.0.0.1:0",
|
"address": "127.0.0.1:0",
|
||||||
"tls_cert_file": td + "reload_curr.pem",
|
"tls_cert_file": wd + "reload_foo.pem",
|
||||||
"tls_key_file": td + "reload_curr.key",
|
"tls_key_file": wd + "reload_foo.key",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
|
@ -75,22 +71,4 @@ func TestTCPListener_tls(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testListenerImpl(t, ln, connFn, "foo.example.com")
|
testListenerImpl(t, ln, connFn, "foo.example.com")
|
||||||
/*
|
|
||||||
inBytes, _ = ioutil.ReadFile(wd + "reload_bar.pem")
|
|
||||||
ioutil.WriteFile(td+"reload_curr.pem", inBytes, 0777)
|
|
||||||
inBytes, _ = ioutil.ReadFile(wd + "reload_bar.key")
|
|
||||||
ioutil.WriteFile(td+"reload_curr.key", inBytes, 0777)
|
|
||||||
|
|
||||||
req := logical.TestRequest(t, logical.UpdateOperation, "sys/reload")
|
|
||||||
req.ClientToken = root
|
|
||||||
resp, err := core.HandleRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("err: %s", err)
|
|
||||||
}
|
|
||||||
if resp != nil {
|
|
||||||
t.Fatal("expected nil response")
|
|
||||||
}
|
|
||||||
|
|
||||||
testListenerImpl(t, ln, connFn, "bar.example.com")
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,18 @@
|
||||||
package command
|
package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/vault/command/server"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,6 +45,20 @@ ha_backend "consul" {
|
||||||
ha_backend "file" {
|
ha_backend "file" {
|
||||||
path = "/dev/null"
|
path = "/dev/null"
|
||||||
}
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
reloadhcl = `
|
||||||
|
backend "file" {
|
||||||
|
path = "/dev/null"
|
||||||
|
}
|
||||||
|
|
||||||
|
disable_mlock = true
|
||||||
|
|
||||||
|
listener "tcp" {
|
||||||
|
address = "127.0.0.1:8203"
|
||||||
|
tls_cert_file = "TMPDIR/reload_FILE.pem"
|
||||||
|
tls_key_file = "TMPDIR/reload_FILE.key"
|
||||||
|
}
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -121,3 +142,111 @@ func TestServer_BadSeparateHA(t *testing.T) {
|
||||||
t.Fatalf("bad: should have gotten an error on a bad HA config")
|
t.Fatalf("bad: should have gotten an error on a bad HA config")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestServer_ReloadListener(t *testing.T) {
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
wd += "/server/test-fixtures/reload/"
|
||||||
|
|
||||||
|
td, err := ioutil.TempDir("", fmt.Sprintf("vault-test-%d", rand.New(rand.NewSource(time.Now().Unix())).Int63))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(td)
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
|
||||||
|
// Setup initial certs
|
||||||
|
inBytes, _ := ioutil.ReadFile(wd + "reload_foo.pem")
|
||||||
|
ioutil.WriteFile(td+"/reload_foo.pem", inBytes, 0777)
|
||||||
|
inBytes, _ = ioutil.ReadFile(wd + "reload_foo.key")
|
||||||
|
ioutil.WriteFile(td+"/reload_foo.key", inBytes, 0777)
|
||||||
|
inBytes, _ = ioutil.ReadFile(wd + "reload_bar.pem")
|
||||||
|
ioutil.WriteFile(td+"/reload_bar.pem", inBytes, 0777)
|
||||||
|
inBytes, _ = ioutil.ReadFile(wd + "reload_bar.key")
|
||||||
|
ioutil.WriteFile(td+"/reload_bar.key", inBytes, 0777)
|
||||||
|
|
||||||
|
relhcl := strings.Replace(strings.Replace(reloadhcl, "TMPDIR", td, -1), "FILE", "foo", -1)
|
||||||
|
ioutil.WriteFile(td+"/reload.hcl", []byte(relhcl), 0777)
|
||||||
|
|
||||||
|
inBytes, _ = ioutil.ReadFile(wd + "reload_ca.pem")
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
ok := certPool.AppendCertsFromPEM(inBytes)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("not ok when appending CA cert")
|
||||||
|
}
|
||||||
|
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &ServerCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
ShutdownCh: MakeShutdownCh(),
|
||||||
|
SighupCh: MakeSighupCh(),
|
||||||
|
ReloadFuncs: map[string][]server.ReloadFunc{},
|
||||||
|
}
|
||||||
|
|
||||||
|
finished := false
|
||||||
|
finishedMutex := sync.Mutex{}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
args := []string{"-config", td + "/reload.hcl"}
|
||||||
|
go func() {
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Error("got a non-zero exit status")
|
||||||
|
}
|
||||||
|
finishedMutex.Lock()
|
||||||
|
finished = true
|
||||||
|
finishedMutex.Unlock()
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
checkFinished := func() {
|
||||||
|
finishedMutex.Lock()
|
||||||
|
if finished {
|
||||||
|
t.Fatal(fmt.Sprintf("finished early; relhcl was\n%s\nstdout was\n%s\nstderr was\n%s\n", relhcl, ui.OutputWriter.String(), ui.ErrorWriter.String()))
|
||||||
|
}
|
||||||
|
finishedMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
testCertificateName := func(cn string) error {
|
||||||
|
conn, err := tls.Dial("tcp", "127.0.0.1:8203", &tls.Config{
|
||||||
|
RootCAs: certPool,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
if err = conn.Handshake(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
servName := conn.ConnectionState().PeerCertificates[0].Subject.CommonName
|
||||||
|
if servName != cn {
|
||||||
|
return fmt.Errorf("expected %s, got %s", cn, servName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
checkFinished()
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
checkFinished()
|
||||||
|
|
||||||
|
if err := testCertificateName("foo.example.com"); err != nil {
|
||||||
|
t.Fatalf("certificate name didn't check out: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
relhcl = strings.Replace(strings.Replace(reloadhcl, "TMPDIR", td, -1), "FILE", "bar", -1)
|
||||||
|
ioutil.WriteFile(td+"/reload.hcl", []byte(relhcl), 0777)
|
||||||
|
|
||||||
|
c.SighupCh <- struct{}{}
|
||||||
|
checkFinished()
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
checkFinished()
|
||||||
|
|
||||||
|
if err := testCertificateName("bar.example.com"); err != nil {
|
||||||
|
t.Fatalf("certificate name didn't check out: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ShutdownCh <- struct{}{}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ telemetry {
|
||||||
After the configuration is written, use the `-config` flag with `vault server`
|
After the configuration is written, use the `-config` flag with `vault server`
|
||||||
to specify where the configuration is.
|
to specify where the configuration is.
|
||||||
|
|
||||||
|
Starting with 0.5.2, limited configuration options can be changed on-the-fly by
|
||||||
|
sending a SIGHUP to the server process. These are denoted below.
|
||||||
|
|
||||||
## Reference
|
## Reference
|
||||||
|
|
||||||
* `backend` (required) - Configures the storage backend where Vault data
|
* `backend` (required) - Configures the storage backend where Vault data
|
||||||
|
@ -93,10 +96,10 @@ The supported options are:
|
||||||
by default that TLS will be used.
|
by default that TLS will be used.
|
||||||
|
|
||||||
* `tls_cert_file` (required unless disabled) - The path to the certificate
|
* `tls_cert_file` (required unless disabled) - The path to the certificate
|
||||||
for TLS.
|
for TLS. This is reloaded via SIGHUP.
|
||||||
|
|
||||||
* `tls_key_file` (required unless disabled) - The path to the private key
|
* `tls_key_file` (required unless disabled) - The path to the private key
|
||||||
for the certificate.
|
for the certificate. This is reloaded via SIGHUP.
|
||||||
|
|
||||||
* `tls_min_version` (optional) - **(Vault > 0.2)** If provided, specifies
|
* `tls_min_version` (optional) - **(Vault > 0.2)** If provided, specifies
|
||||||
the minimum supported version of TLS. Accepted values are "tls10", "tls11"
|
the minimum supported version of TLS. Accepted values are "tls10", "tls11"
|
||||||
|
|
Loading…
Reference in New Issue