From cbde98a2d639aead21b30fa2a2ddf77fd72c09bb Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Thu, 2 Apr 2015 11:12:13 -0700 Subject: [PATCH] vault: Support tainting router paths --- vault/router.go | 24 ++++++++++++++++++++++++ vault/router_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/vault/router.go b/vault/router.go index 23b1660b8e..34c19f2fd2 100644 --- a/vault/router.go +++ b/vault/router.go @@ -25,6 +25,7 @@ func NewRouter() *Router { // mountEntry is used to represent a mount point type mountEntry struct { + tainted bool backend logical.Backend view *BarrierView rootPaths *radix.Tree @@ -49,6 +50,7 @@ func (r *Router) Mount(backend logical.Backend, prefix string, view *BarrierView // Create a mount entry me := &mountEntry{ + tainted: false, backend: backend, view: view, rootPaths: pathsToRadix(paths.Root), @@ -83,6 +85,18 @@ func (r *Router) Remount(src, dst string) error { return nil } +// Taint is used to mark a path as tainted. This means only RollbackOperation +// RenewOperation requests are allowed to proceed +func (r *Router) Taint(path string) error { + r.l.Lock() + defer r.l.Unlock() + _, raw, ok := r.root.LongestPrefix(path) + if ok { + raw.(*mountEntry).tainted = true + } + return nil +} + // MatchingMount returns the mount prefix that would be used for a path func (r *Router) MatchingMount(path string) string { r.l.RLock() @@ -116,6 +130,16 @@ func (r *Router) Route(req *logical.Request) (*logical.Response, error) { } me := raw.(*mountEntry) + // If the path is tainted, we reject any operation except for + // Rollback and Revoke + if me.tainted { + switch req.Operation { + case logical.RevokeOperation, logical.RollbackOperation: + default: + return nil, fmt.Errorf("no handler for route '%s'", req.Path) + } + } + // Determine if this path is an unauthenticated path before we modify it loginPath := r.LoginPath(req.Path) diff --git a/vault/router_test.go b/vault/router_test.go index f61bfd55fe..5a8db07a8a 100644 --- a/vault/router_test.go +++ b/vault/router_test.go @@ -261,6 +261,45 @@ func TestRouter_LoginPath(t *testing.T) { } } +func TestRouter_Taint(t *testing.T) { + r := NewRouter() + _, barrier, _ := mockBarrier(t) + view := NewBarrierView(barrier, "logical/") + + n := &NoopBackend{} + err := r.Mount(n, "prod/aws/", view) + if err != nil { + t.Fatalf("err: %v", err) + } + + err = r.Taint("prod/aws/") + if err != nil { + t.Fatalf("err: %v", err) + } + + req := &logical.Request{ + Operation: logical.ReadOperation, + Path: "prod/aws/foo", + } + _, err = r.Route(req) + if err.Error() != "no handler for route 'prod/aws/foo'" { + t.Fatalf("err: %v", err) + } + + // Rollback and Revoke should work + req.Operation = logical.RollbackOperation + _, err = r.Route(req) + if err != nil { + t.Fatalf("err: %v", err) + } + + req.Operation = logical.RevokeOperation + _, err = r.Route(req) + if err != nil { + t.Fatalf("err: %v", err) + } +} + func TestPathsToRadix(t *testing.T) { // Provide real paths paths := []string{