From 6571722e661ceeef0d4023fe69105e1b45e13882 Mon Sep 17 00:00:00 2001 From: Alex H Date: Tue, 19 Jul 2016 15:54:45 +0100 Subject: [PATCH] Added a WrappedTokenUnsealer to allow users to unseal Gatekeeper with a token created and stored with Vault response wrapping, by providing the temp token which wraps the stored token. --- README.md | 2 ++ gatekeeper.go | 27 +++++++++++++++++-------- html.go | 10 +++++++++ routes.go | 6 ++++++ unsealer.go | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7884260..5510e38 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ VGM also supports the client environment variables used by vault such as, `VAULT `CUBBY_PATH` | `-cubby-path` - Path to key in cubbyhole. By default this is `/vault-token`. +`WRAPPED_TOKEN_AUTH` | `-wrapped-token-auth` - Temporary vault authorization token that has a wrapped permanent vault token. + `APP_ID` | `-auth-appid` - Use the `app-id` authorization method with this app id. `USER_ID_METHOD` | `-auth-userid-method` - With the `app-id` authorization method, this argument decides how VGM should generate the user id. Valid values are `mac` and `file`. diff --git a/gatekeeper.go b/gatekeeper.go index 488988c..9e54760 100644 --- a/gatekeeper.go +++ b/gatekeeper.go @@ -35,14 +35,15 @@ var config struct { CaPath string GkPolicies string } - SelfRecreate bool - ListenAddress string - TlsCert string - TlsKey string - Mesos string - MaxTaskLife time.Duration - AppIdAuth AppIdUnsealer - CubbyAuth CubbyUnsealer + SelfRecreate bool + ListenAddress string + TlsCert string + TlsKey string + Mesos string + MaxTaskLife time.Duration + AppIdAuth AppIdUnsealer + CubbyAuth CubbyUnsealer + WrappedTokenAuth WrappedTokenUnsealer } var state struct { @@ -88,6 +89,8 @@ func init() { flag.StringVar(&config.CubbyAuth.TempToken, "cubby-token", defaultEnvVar("CUBBY_TOKEN", ""), "Temporary vault authorization token that has a cubbyhole secret in CUBBY_PATH that contains the permanent vault token.") flag.StringVar(&config.CubbyAuth.Path, "cubby-path", defaultEnvVar("CUBBY_PATH", "/vault-token"), "Path to key in cubbyhole. By default this is /vault-token.") + flag.StringVar(&config.WrappedTokenAuth.TempToken, "wrapped-token-auth", defaultEnvVar("WRAPPED_TOKEN_AUTH", ""), "Temporary vault authorization token that has a wrapped permanent vault token.") + flag.StringVar(&config.AppIdAuth.AppId, "auth-appid", defaultEnvVar("APP_ID", ""), "Vault App Id for authenication. (Overrides the APP_ID environment variable if set.)") flag.StringVar(&config.AppIdAuth.UserIdMethod, "auth-userid-method", defaultEnvVar("USER_ID_METHOD", ""), "Vault User Id authenication method (either 'mac' or 'file'). (Overrides the USER_ID_METHOD environment variable if set.)") flag.StringVar(&config.AppIdAuth.UserIdInterface, "auth-userid-interface", defaultEnvVar("USER_ID_INTERFACE", ""), "Network interface for 'mac' user id authenication method. (Overrides the USER_ID_INTERFACE environment variable if set.)") @@ -342,6 +345,14 @@ func main() { os.Exit(1) } log.Println("Unseal successful with token provided by Cubbyhole.") + } else if config.WrappedTokenAuth.TempToken != "" { + log.Println("Attempting to unseal with Wrapped Token...") + if err := unseal(config.WrappedTokenAuth); err != nil { + log.Println("Failed to unseal using Wrapped Token. Please make sure the Wrapped Token auth is correctly setup.") + log.Println("Error:", err) + os.Exit(1) + } + log.Println("Unseal successful with Wrapped Token.") } else if config.AppIdAuth.AppId != "" { log.Println("Attempting to unseal with provided APP ID credentials, using user_id from '" + config.AppIdAuth.UserIdMethod + "'...") if err := unseal(config.AppIdAuth); err != nil { diff --git a/html.go b/html.go index 5bba89f..65b47eb 100644 --- a/html.go +++ b/html.go @@ -37,6 +37,9 @@ const htmlTemplateVal = ` .active-form.active-cubby .visible-cubby { display: block; } + .active-form.active-wrapped-token .visible-wrapped-token { + display: block; + } .status-unsealed { display: {{.StatusUnsealed}}; } @@ -85,6 +88,7 @@ const htmlTemplateVal = ` + @@ -150,6 +154,12 @@ const htmlTemplateVal = ` +
+
+ + +
+
diff --git a/routes.go b/routes.go index 3521eab..73af8c1 100644 --- a/routes.go +++ b/routes.go @@ -91,6 +91,8 @@ func Unseal(c *gin.Context) { case "cubby": request.Token = c.Request.FormValue("cubby_token") request.CubbyPath = c.Request.FormValue("cubby_path") + case "wrapped-token": + request.Token = c.Request.FormValue("wrapped_token") default: c.JSON(400, struct { Status string `json:"status"` @@ -140,6 +142,10 @@ func Unseal(c *gin.Context) { TempToken: request.Token, Path: request.CubbyPath, } + case "wrapped-token": + unsealer = WrappedTokenUnsealer{ + TempToken: request.Token, + } default: c.JSON(400, struct { Status string `json:"status"` diff --git a/unsealer.go b/unsealer.go index 2792c23..2ddc8bc 100644 --- a/unsealer.go +++ b/unsealer.go @@ -270,3 +270,59 @@ func (t CubbyUnsealer) Token() (string, error) { func (t CubbyUnsealer) Name() string { return "cubby" } + +type WrappedTokenUnsealer struct { + TempToken string +} + +var errInvalidWrappedToken = errors.New("Invalid wrapped token.") + +func (t WrappedTokenUnsealer) Token() (string, error) { + resp, err := VaultRequest{ + goreq.Request{ + Uri: vaultPath("/v1/cubbyhole/response", ""), + MaxRedirects: 10, + RedirectHeaders: true, + }.WithHeader("X-Vault-Token", t.TempToken), + }.Do() + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + var e vaultError + e.Code = resp.StatusCode + + if err := resp.Body.FromJsonTo(&e); err != nil { + e.Errors = []string{"communication error."} + return "", e + + } + + return "", e + } + + vaultWrappedResp := struct { + Data struct { + WrappedSecret struct { + Token string `json:"token"` + } `json:"response"` + } `json:"data"` + }{} + + if err := resp.Body.FromJsonTo(&vaultWrappedResp); err != nil { + return "", err + + } + + if vaultWrappedResp.Data.WrappedSecret.Token == "" { + return "", errInvalidWrappedToken + } + + return TokenUnsealer{vaultWrappedResp.Data.WrappedSecret.Token}.Token() +} + +func (t WrappedTokenUnsealer) Name() string { + return "wrapped-token" +} \ No newline at end of file