Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit generate root requests and responses. #8301

Merged
merged 1 commit into from
Feb 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 67 additions & 2 deletions http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,10 @@ func Handler(props *vault.HandlerProperties) http.Handler {
mux.Handle("/v1/sys/unseal", handleSysUnseal(core))
mux.Handle("/v1/sys/leader", handleSysLeader(core))
mux.Handle("/v1/sys/health", handleSysHealth(core))
mux.Handle("/v1/sys/generate-root/attempt", handleRequestForwarding(core, handleSysGenerateRootAttempt(core, vault.GenerateStandardRootTokenStrategy)))
mux.Handle("/v1/sys/generate-root/update", handleRequestForwarding(core, handleSysGenerateRootUpdate(core, vault.GenerateStandardRootTokenStrategy)))
mux.Handle("/v1/sys/generate-root/attempt", handleRequestForwarding(core,
handleAuditNonLogical(core, handleSysGenerateRootAttempt(core, vault.GenerateStandardRootTokenStrategy))))
mux.Handle("/v1/sys/generate-root/update", handleRequestForwarding(core,
handleAuditNonLogical(core, handleSysGenerateRootUpdate(core, vault.GenerateStandardRootTokenStrategy))))
mux.Handle("/v1/sys/rekey/init", handleRequestForwarding(core, handleSysRekeyInit(core, false)))
mux.Handle("/v1/sys/rekey/update", handleRequestForwarding(core, handleSysRekeyUpdate(core, false)))
mux.Handle("/v1/sys/rekey/verify", handleRequestForwarding(core, handleSysRekeyVerify(core, false)))
Expand Down Expand Up @@ -181,6 +183,69 @@ func Handler(props *vault.HandlerProperties) http.Handler {
return printablePathCheckHandler
}

type copyResponseWriter struct {
wrapped http.ResponseWriter
statusCode int
body *bytes.Buffer
}

// newCopyResponseWriter returns an initialized newCopyResponseWriter
func newCopyResponseWriter(wrapped http.ResponseWriter) *copyResponseWriter {
w := &copyResponseWriter{
wrapped: wrapped,
body: new(bytes.Buffer),
statusCode: 200,
}
return w
}

func (w *copyResponseWriter) Header() http.Header {
return w.wrapped.Header()
}

func (w *copyResponseWriter) Write(buf []byte) (int, error) {
w.body.Write(buf)
return w.wrapped.Write(buf)
}

func (w *copyResponseWriter) WriteHeader(code int) {
w.statusCode = code
w.wrapped.WriteHeader(code)
}

func handleAuditNonLogical(core *vault.Core, h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origBody := new(bytes.Buffer)
reader := ioutil.NopCloser(io.TeeReader(r.Body, origBody))
r.Body = reader
req, _, status, err := buildLogicalRequestNoAuth(core.PerfStandby(), w, r)
if err != nil || status != 0 {
respondError(w, status, err)
return
}
if origBody != nil {
r.Body = ioutil.NopCloser(origBody)
}
input := &logical.LogInput{
Request: req,
}

core.AuditLogger().AuditRequest(r.Context(), input)
cw := newCopyResponseWriter(w)
h.ServeHTTP(cw, r)
data := make(map[string]interface{})
err = jsonutil.DecodeJSON(cw.body.Bytes(), &data)
if err != nil {
// best effort, ignore
}
httpResp := &logical.HTTPResponse{Data: data, Headers: cw.Header()}
input.Response = logical.HTTPResponseToLogicalResponse(httpResp)
core.AuditLogger().AuditResponse(r.Context(), input)
return
})

}

// wrapGenericHandler wraps the handler with an extra layer of handler where
// tasks that should be commonly handled for all the requests and/or responses
// are performed.
Expand Down
61 changes: 55 additions & 6 deletions http/sys_generate_root_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package http

import (
"context"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"net"
"net/http"
"reflect"
"testing"

"github.com/go-test/deep"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/helper/pgpkeys"
"github.com/hashicorp/vault/helper/xor"
"github.com/hashicorp/vault/sdk/logical"
"github.com/hashicorp/vault/vault"
)

Expand Down Expand Up @@ -196,16 +200,58 @@ func TestSysGenerateRootAttempt_Cancel(t *testing.T) {
}
}

func TestSysGenerateRoot_badKey(t *testing.T) {
core, _, token := vault.TestCoreUnsealed(t)
func enableNoopAudit(t *testing.T, token string, core *vault.Core) {
t.Helper()
auditReq := &logical.Request{
Operation: logical.UpdateOperation,
ClientToken: token,
Path: "sys/audit/noop",
Data: map[string]interface{}{
"type": "noop",
},
}
resp, err := core.HandleRequest(namespace.RootContext(context.Background()), auditReq)
if err != nil {
t.Fatal(err)
}

if resp.IsError() {
t.Fatal(err)
}
}

func testCoreUnsealedWithAudit(t *testing.T, records **[][]byte) (*vault.Core, [][]byte, string) {
conf := &vault.CoreConfig{
BuiltinRegistry: vault.NewMockBuiltinRegistry(),
}
vault.AddNoopAudit(conf, records)
core, keys, token := vault.TestCoreUnsealedWithConfig(t, conf)
return core, keys, token
}

func testServerWithAudit(t *testing.T, records **[][]byte) (net.Listener, string, string, [][]byte) {
core, keys, token := testCoreUnsealedWithAudit(t, records)
ln, addr := TestServer(t, core)
defer ln.Close()
TestServerAuth(t, addr, token)
enableNoopAudit(t, token, core)
return ln, addr, token, keys
}

func TestSysGenerateRoot_badKey(t *testing.T) {
var records *[][]byte
ln, addr, token, _ := testServerWithAudit(t, &records)
defer ln.Close()

resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/update", map[string]interface{}{
"key": "0123",
})
testResponseStatus(t, resp, 400)

if len(*records) < 3 {
// One record for enabling the noop audit device, two for generate root attempt
t.Fatalf("expected at least 3 audit records, got %d", len(*records))
}
t.Log(string((*records)[2]))
}

func TestSysGenerateRoot_ReAttemptUpdate(t *testing.T) {
Expand All @@ -228,10 +274,9 @@ func TestSysGenerateRoot_ReAttemptUpdate(t *testing.T) {
}

func TestSysGenerateRoot_Update_OTP(t *testing.T) {
core, keys, token := vault.TestCoreUnsealed(t)
ln, addr := TestServer(t, core)
var records *[][]byte
ln, addr, token, keys := testServerWithAudit(t, &records)
defer ln.Close()
TestServerAuth(t, addr, token)

resp := testHttpPut(t, token, addr+"/v1/sys/generate-root/attempt", map[string]interface{}{})
var rootGenerationStatus map[string]interface{}
Expand Down Expand Up @@ -317,6 +362,10 @@ func TestSysGenerateRoot_Update_OTP(t *testing.T) {
if !reflect.DeepEqual(actual["data"], expected) {
t.Fatalf("\nexpected: %#v\nactual: %#v", expected, actual["data"])
}

for _, r := range *records {
t.Log(string(r))
}
}

func TestSysGenerateRoot_Update_PGP(t *testing.T) {
Expand Down
17 changes: 17 additions & 0 deletions vault/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,23 @@ func defaultAuditTable() *MountTable {
return table
}

type AuditLogger interface {
AuditRequest(ctx context.Context, input *logical.LogInput) error
AuditResponse(ctx context.Context, input *logical.LogInput) error
}

type basicAuditor struct {
c *Core
}

func (b *basicAuditor) AuditRequest(ctx context.Context, input *logical.LogInput) error {
return b.c.auditBroker.LogRequest(ctx, input, b.c.auditedHeaders)
}

func (b *basicAuditor) AuditResponse(ctx context.Context, input *logical.LogInput) error {
return b.c.auditBroker.LogResponse(ctx, input, b.c.auditedHeaders)
}

type genericAuditor struct {
c *Core
mountType string
Expand Down
4 changes: 4 additions & 0 deletions vault/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -2251,3 +2251,7 @@ type BuiltinRegistry interface {
Get(name string, pluginType consts.PluginType) (func() (interface{}, error), bool)
Keys(pluginType consts.PluginType) []string
}

func (c *Core) AuditLogger() AuditLogger {
return &basicAuditor{c: c}
}
7 changes: 5 additions & 2 deletions vault/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ func (n *noopAudit) Salt(ctx context.Context) (*salt.Salt, error) {
return salt, nil
}

func AddNoopAudit(conf *CoreConfig) {
func AddNoopAudit(conf *CoreConfig, records **[][]byte) {
conf.AuditBackends = map[string]audit.Factory{
"noop": func(_ context.Context, config *audit.BackendConfig) (audit.Backend, error) {
view := &logical.InmemStorage{}
Expand All @@ -691,6 +691,9 @@ func AddNoopAudit(conf *CoreConfig) {
n.formatter.AuditFormatWriter = &audit.JSONFormatWriter{
SaltFunc: n.Salt,
}
if records != nil {
*records = &n.records
}
return n, nil
},
}
Expand Down Expand Up @@ -1437,7 +1440,7 @@ func NewTestCluster(t testing.T, base *CoreConfig, opts *TestClusterOptions) *Te

addAuditBackend := len(coreConfig.AuditBackends) == 0
if addAuditBackend {
AddNoopAudit(coreConfig)
AddNoopAudit(coreConfig, nil)
}

if coreConfig.Physical == nil && (opts == nil || opts.PhysicalFactory == nil) {
Expand Down