Skip to content

Commit

Permalink
Allow authentication with Kubernetes JWT token (#138)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonas Vinther <jrvinther@gmail.com>
  • Loading branch information
rdrgmnzs and jonasvinther committed Sep 20, 2023
1 parent 06aea40 commit a60bef4
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 14 deletions.
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,15 +302,23 @@ Usage:
medusa [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
decrypt Decrypt an encrypted Vault output file into plaintext in stdout
delete Recursively delete all secrets below the given path
encrypt Encrypt a Vault export file onto stdout or to an output file
export Export Vault secrets as yaml
help Help about any command
import Import a yaml file into a Vault instance
version Print the version number of Medusa
Flags:
-a, --address string Address of the Vault server
-h, --help help for medusa
-k, --insecure Allow insecure server connections when using SSL
-t, --token string Vault authentication token
Use "medusa [command] --help" for more information about a command.
-a, --address string Address of the Vault server
-h, --help help for medusa
-k, --insecure Allow insecure server connections when using SSL
--kubernetes Authenticate using the Kubernetes JWT token
-n, --namespace string Namespace within the Vault server (Enterprise only)
-r, --role string Vault role for Kubernetes JWT authentication
-t, --token string Vault authentication token
Use "medusa [command] --help" for more information about a command
```
20 changes: 20 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,24 @@ Created by Jonas Vinther & Henrik Høegh.`,
}
}

role, _ := cmd.Flags().GetString("role")
if viper.IsSet("VAULT_ROLE") && role == "" {
value := viper.Get("VAULT_ROLE").(string)
err := cmd.Flags().Set("role", value)
if err != nil {
return err
}
}

kubernetes, _ := cmd.Flags().GetBool("kubernetes")
if viper.IsSet("KUBERNETES") && kubernetes == false {
value := viper.GetBool("KUBERNETES")
err := cmd.Flags().Set("kubernetes", strconv.FormatBool(value))
if err != nil {
return err
}
}

insecure, _ := cmd.Flags().GetBool("insecure")
if viper.IsSet("VAULT_SKIP_VERIFY") && insecure == false {
value := viper.GetBool("VAULT_SKIP_VERIFY")
Expand Down Expand Up @@ -62,6 +80,8 @@ func Execute() error {
func init() {
rootCmd.PersistentFlags().StringP("address", "a", "", "Address of the Vault server")
rootCmd.PersistentFlags().StringP("token", "t", "", "Vault authentication token")
rootCmd.PersistentFlags().StringP("role", "r", "", "Vault role for Kubernetes JWT authentication")
rootCmd.PersistentFlags().BoolP("kubernetes", "", false, "Authenticate using the Kubernetes JWT token")
rootCmd.PersistentFlags().BoolP("insecure", "k", false, "Allow insecure server connections when using SSL")
rootCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace within the Vault server (Enterprise only)")

Expand Down
4 changes: 3 additions & 1 deletion cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ var deleteCmd = &cobra.Command{
vaultAddr, _ := cmd.Flags().GetString("address")
vaultToken, _ := cmd.Flags().GetString("token")
insecure, _ := cmd.Flags().GetBool("insecure")
vaultRole, _ := cmd.Flags().GetString("role")
kubernetes, _ := cmd.Flags().GetBool("kubernetes")
namespace, _ := cmd.Flags().GetString("namespace")
engineType, _ := cmd.Flags().GetString("engine-type")
isApproved, _ := cmd.Flags().GetBool("auto-approve")

// Setup Vault client
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace)
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace, vaultRole, kubernetes)
engine, path, err := client.MountpathSplitPrefix(path)
if err != nil {
fmt.Println(err)
Expand Down
5 changes: 4 additions & 1 deletion cmd/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"errors"
"fmt"

"github.com/jonasvinther/medusa/pkg/encrypt"
"github.com/jonasvinther/medusa/pkg/vaultengine"

Expand All @@ -27,14 +28,16 @@ var exportCmd = &cobra.Command{
path := args[0]
vaultAddr, _ := cmd.Flags().GetString("address")
vaultToken, _ := cmd.Flags().GetString("token")
vaultRole, _ := cmd.Flags().GetString("role")
kubernetes, _ := cmd.Flags().GetBool("kubernetes")
insecure, _ := cmd.Flags().GetBool("insecure")
namespace, _ := cmd.Flags().GetString("namespace")
engineType, _ := cmd.Flags().GetString("engine-type")
doEncrypt, _ := cmd.Flags().GetBool("encrypt")
exportFormat, _ := cmd.Flags().GetString("format")
output, _ := cmd.Flags().GetString("output")

client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace)
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace, vaultRole, kubernetes)
engine, path, err := client.MountpathSplitPrefix(path)
if err != nil {
fmt.Println(err)
Expand Down
4 changes: 3 additions & 1 deletion cmd/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ var importCmd = &cobra.Command{
vaultAddr, _ := cmd.Flags().GetString("address")
vaultToken, _ := cmd.Flags().GetString("token")
insecure, _ := cmd.Flags().GetBool("insecure")
vaultRole, _ := cmd.Flags().GetString("role")
kubernetes, _ := cmd.Flags().GetBool("kubernetes")
namespace, _ := cmd.Flags().GetString("namespace")
engineType, _ := cmd.Flags().GetString("engine-type")
doDecrypt, _ := cmd.Flags().GetBool("decrypt")
privateKey, _ := cmd.Flags().GetString("private-key")

client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace)
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace, vaultRole, kubernetes)
engine, prefix, err := client.MountpathSplitPrefix(path)
if err != nil {
fmt.Println(err)
Expand Down
7 changes: 7 additions & 0 deletions docs/examples/kubernetes/cronjob/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ medusa-1615982100-5tfl8 0/1 Completed 0 60s
medusa-1615982160-4b527 0/1 Completed 0 9s
```

### Using Kubernetes authentication
If you are using the kubernetes authentication method in Vault, it is also possible to use the kubernetes provided JWT token inside a Pod and auth role in order to authenticate.

```yaml
command: ["./medusa", "export", "$(VAULT_PATH)", "--kubernetes", "--role=default", "-o", "/backup/backup.vault"]
```

### Further customization
This only serves as an example as to how you could use `Medusa` to take a backup of Vault from a given location.

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.13

require (
github.com/hashicorp/vault/api v1.10.0
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0
github.com/manifoldco/promptui v0.9.0
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.16.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,8 @@ github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ=
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 h1:CXO0fD7M3iCGovP/UApeHhPcH4paDFKcu7AjEXi94rI=
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0/go.mod h1:afrElBIO9Q4sHFVuVWgNevG4uAs1bT2AZFA9aEiI608=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
Expand Down
31 changes: 26 additions & 5 deletions pkg/vaultengine/vaultclient.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package vaultengine

import (
"context"
"errors"
"strings"

vault "github.com/hashicorp/vault/api"
auth "github.com/hashicorp/vault/api/auth/kubernetes"
)

// Client describes the arguments that is needed to to establish a connecting to a Vault instance
Expand All @@ -14,17 +16,22 @@ type Client struct {
namespace string
engine string
engineType string
role string
kubernetes bool
insecure bool
vc *vault.Client
}

// NewClient creates a instance of the VaultClient struct
func NewClient(addr, token string, insecure bool, namespace string) *Client {
func NewClient(addr, token string, insecure bool, namespace string, role string, kubernetes bool) *Client {
client := &Client{
token: token,
addr: addr,
insecure: insecure,
namespace: namespace}
token: token,
addr: addr,
insecure: insecure,
namespace: namespace,
role: role,
kubernetes: kubernetes,
}

client.newVaultClient()

Expand Down Expand Up @@ -99,5 +106,19 @@ func (client *Client) newVaultClient() error {
client.vc.SetToken(client.token)
}

// Authenticate using Kubernetes JWT if kubernetes flag is set
if client.kubernetes {
kubernetesAuth, err := auth.NewKubernetesAuth(client.role)
if err != nil {
return err
}

authInfo, err := vc.Auth().Login(context.Background(), kubernetesAuth)
if err != nil {
return err
}
client.vc.SetToken(authInfo.Auth.ClientToken)
}

return nil
}

0 comments on commit a60bef4

Please sign in to comment.