From 14f9b7e1d22feacaaad1e0ead9734737c996fa0b Mon Sep 17 00:00:00 2001 From: Nick Hall Date: Thu, 13 Jul 2017 13:35:11 +0100 Subject: [PATCH 1/2] initial GSSAPI feature request --- api/client.go | 14 +++++++++++++- api/logical.go | 20 +++++++++++--------- cli/commands.go | 3 +++ http/logical.go | 20 ++++++++++++++++++++ logical/response.go | 5 +++++ vault/router.go | 10 ++++++++-- vendor/vendor.json | 6 ++++++ 7 files changed, 66 insertions(+), 12 deletions(-) diff --git a/api/client.go b/api/client.go index df01083dddf1..2070ab0b55eb 100644 --- a/api/client.go +++ b/api/client.go @@ -3,6 +3,7 @@ package api import ( "crypto/tls" "fmt" + "net" "net/http" "net/url" "os" @@ -336,12 +337,23 @@ func (c *Client) Clone() (*Client, error) { // configured for this client. This is an advanced method and generally // doesn't need to be called externally. func (c *Client) NewRequest(method, requestPath string) *Request { + // if SRV records exist (see https://tools.ietf.org/html/draft-andrews-http-srv-02), lookup the SRV + // record and take the highest match; this is not designed for high-availability, just discovery + var host string = c.addr.Host + if c.addr.Port() == "" { + // Internet Draft specifies that the SRV record is ignored if a port is given + _, addrs, err := net.LookupSRV("http", "tcp", c.addr.Hostname()) + if err == nil { + host = fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port) + } + } + req := &Request{ Method: method, URL: &url.URL{ User: c.addr.User, Scheme: c.addr.Scheme, - Host: c.addr.Host, + Host: host, Path: path.Join(c.addr.Path, requestPath), }, ClientToken: c.token, diff --git a/api/logical.go b/api/logical.go index 0d5e7d495aaa..1fd8dc84c3a9 100644 --- a/api/logical.go +++ b/api/logical.go @@ -79,25 +79,27 @@ func (c *Logical) List(path string) (*Secret, error) { return ParseSecret(resp.Body) } -func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, error) { +func (c *Logical) WriteRaw(path string, data map[string]interface{}) (*Response, *Secret, error) { r := c.c.NewRequest("PUT", "/v1/"+path) if err := r.SetJSONBody(data); err != nil { - return nil, err + return nil, nil, err } + var secret *Secret = nil resp, err := c.c.RawRequest(r) if resp != nil { defer resp.Body.Close() - } - if err != nil { - return nil, err + if err == nil && resp.StatusCode == 200 { + secret, err = ParseSecret(resp.Body) + } } - if resp.StatusCode == 200 { - return ParseSecret(resp.Body) - } + return resp, secret, err +} - return nil, nil +func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, error) { + _, secret, err := c.WriteRaw(path, data) + return secret, err } func (c *Logical) Delete(path string) (*Secret, error) { diff --git a/cli/commands.go b/cli/commands.go index 3fab33956c93..f8ea404b0aaf 100644 --- a/cli/commands.go +++ b/cli/commands.go @@ -13,6 +13,7 @@ import ( credAws "github.com/hashicorp/vault/builtin/credential/aws" credCert "github.com/hashicorp/vault/builtin/credential/cert" credGitHub "github.com/hashicorp/vault/builtin/credential/github" + credKerberos "github.com/hashicorp/vault/builtin/credential/kerberos" credLdap "github.com/hashicorp/vault/builtin/credential/ldap" credOkta "github.com/hashicorp/vault/builtin/credential/okta" credRadius "github.com/hashicorp/vault/builtin/credential/radius" @@ -79,6 +80,7 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory { "ldap": credLdap.Factory, "okta": credOkta.Factory, "radius": credRadius.Factory, + "kerberos": credKerberos.Factory, }, LogicalBackends: map[string]logical.Factory{ "aws": aws.Factory, @@ -123,6 +125,7 @@ func Commands(metaPtr *meta.Meta) map[string]cli.CommandFactory { "cert": &credCert.CLIHandler{}, "aws": &credAws.CLIHandler{}, "radius": &credUserpass.CLIHandler{DefaultMount: "radius"}, + "kerberos": &credKerberos.CLIHandler{DefaultMount: "kerberos"}, }, }, nil }, diff --git a/http/logical.go b/http/logical.go index 1de2ef2456f7..4cb04ed5712d 100644 --- a/http/logical.go +++ b/http/logical.go @@ -223,6 +223,26 @@ func respondRaw(w http.ResponseWriter, r *http.Request, resp *logical.Response) } } + // get any returned headers + headersRaw, ok := resp.Data[logical.HTTPHeaders] + if !ok { + retErr(w, "no headers given") + return + } else { + headers, ok := headersRaw.(map[string]string) + if !ok { + retErr(w, "returned headers are not a map") + return + } else { + for header, value := range headers { + w.Header().Set(header, value) + } + + // some headers set now, so response is no longer empty + nonEmpty = false + } + } + if nonEmpty { // Get the body bodyRaw, ok := resp.Data[logical.HTTPRawBody] diff --git a/logical/response.go b/logical/response.go index 6ee452b6865b..a298dcf15c68 100644 --- a/logical/response.go +++ b/logical/response.go @@ -23,6 +23,11 @@ const ( // This can only be specified for non-secrets, and should should be similarly // avoided like the HTTPContentType. The value must be an integer. HTTPStatusCode = "http_status_code" + + // HTTPHeaders is a mechanism to support returning HTTP headers with the response. + // This can only be specified for non-secrets, and should should be similarly + // avoided like the HTTPContentType. The value must be a map of string to string. + HTTPHeaders = "http_headers" ) // Response is a struct that stores the response of a request. diff --git a/vault/router.go b/vault/router.go index ec86ceeb3634..84c2a880c197 100644 --- a/vault/router.go +++ b/vault/router.go @@ -347,9 +347,15 @@ func (r *Router) routeCommon(req *logical.Request, existenceCheck bool) (*logica originalClientTokenRemainingUses := req.ClientTokenRemainingUses req.ClientTokenRemainingUses = 0 - // Cache the headers and hide them from backends + // Cache the headers and hide all but a small, standard set of them from backends headers := req.Headers - req.Headers = nil + for header := range headers { + switch header { + case "Authorization": + default: + delete(req.Headers, header) + } + } // Cache the wrap info of the request var wrapInfo *logical.RequestWrapInfo diff --git a/vendor/vendor.json b/vendor/vendor.json index c01039b89025..9c13882acfc1 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -186,6 +186,12 @@ "revision": "85b1699d505667d13f8ac4478c1debbf85d6c5de", "revisionTime": "2017-06-08T22:14:41Z" }, + { + "checksumSHA1": "m1aIIVtZXprQLTHexJ7e0fBZ/fc=", + "path": "github.com/apcera/gssapi", + "revision": "5fb4217df13b8e6878046fe1e5c10e560e1b86dc", + "revisionTime": "2016-10-10T21:59:02Z" + }, { "checksumSHA1": "M+ZeYktTT2wak9ZvQ0OZBbIHAGo=", "path": "github.com/armon/go-metrics", From 98b3e35076a0a9a296ad5f4fe434d52d2a6a1128 Mon Sep 17 00:00:00 2001 From: Nick Hall Date: Wed, 19 Jul 2017 13:27:45 +0100 Subject: [PATCH 2/2] remove SRV record patch --- api/client.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/api/client.go b/api/client.go index 2070ab0b55eb..df01083dddf1 100644 --- a/api/client.go +++ b/api/client.go @@ -3,7 +3,6 @@ package api import ( "crypto/tls" "fmt" - "net" "net/http" "net/url" "os" @@ -337,23 +336,12 @@ func (c *Client) Clone() (*Client, error) { // configured for this client. This is an advanced method and generally // doesn't need to be called externally. func (c *Client) NewRequest(method, requestPath string) *Request { - // if SRV records exist (see https://tools.ietf.org/html/draft-andrews-http-srv-02), lookup the SRV - // record and take the highest match; this is not designed for high-availability, just discovery - var host string = c.addr.Host - if c.addr.Port() == "" { - // Internet Draft specifies that the SRV record is ignored if a port is given - _, addrs, err := net.LookupSRV("http", "tcp", c.addr.Hostname()) - if err == nil { - host = fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port) - } - } - req := &Request{ Method: method, URL: &url.URL{ User: c.addr.User, Scheme: c.addr.Scheme, - Host: host, + Host: c.addr.Host, Path: path.Join(c.addr.Path, requestPath), }, ClientToken: c.token,