Skip to content

Commit a60bef4

Browse files
Allow authentication with Kubernetes JWT token (#138)
Co-authored-by: Jonas Vinther <jrvinther@gmail.com>
1 parent 06aea40 commit a60bef4

File tree

9 files changed

+80
-14
lines changed

9 files changed

+80
-14
lines changed

README.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,15 +302,23 @@ Usage:
302302
medusa [command]
303303
304304
Available Commands:
305+
completion Generate the autocompletion script for the specified shell
306+
decrypt Decrypt an encrypted Vault output file into plaintext in stdout
307+
delete Recursively delete all secrets below the given path
308+
encrypt Encrypt a Vault export file onto stdout or to an output file
305309
export Export Vault secrets as yaml
306310
help Help about any command
307311
import Import a yaml file into a Vault instance
312+
version Print the version number of Medusa
308313
309314
Flags:
310-
-a, --address string Address of the Vault server
311-
-h, --help help for medusa
312-
-k, --insecure Allow insecure server connections when using SSL
313-
-t, --token string Vault authentication token
314-
315-
Use "medusa [command] --help" for more information about a command.
315+
-a, --address string Address of the Vault server
316+
-h, --help help for medusa
317+
-k, --insecure Allow insecure server connections when using SSL
318+
--kubernetes Authenticate using the Kubernetes JWT token
319+
-n, --namespace string Namespace within the Vault server (Enterprise only)
320+
-r, --role string Vault role for Kubernetes JWT authentication
321+
-t, --token string Vault authentication token
322+
323+
Use "medusa [command] --help" for more information about a command
316324
```

cmd/cmd.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@ Created by Jonas Vinther & Henrik Høegh.`,
3232
}
3333
}
3434

35+
role, _ := cmd.Flags().GetString("role")
36+
if viper.IsSet("VAULT_ROLE") && role == "" {
37+
value := viper.Get("VAULT_ROLE").(string)
38+
err := cmd.Flags().Set("role", value)
39+
if err != nil {
40+
return err
41+
}
42+
}
43+
44+
kubernetes, _ := cmd.Flags().GetBool("kubernetes")
45+
if viper.IsSet("KUBERNETES") && kubernetes == false {
46+
value := viper.GetBool("KUBERNETES")
47+
err := cmd.Flags().Set("kubernetes", strconv.FormatBool(value))
48+
if err != nil {
49+
return err
50+
}
51+
}
52+
3553
insecure, _ := cmd.Flags().GetBool("insecure")
3654
if viper.IsSet("VAULT_SKIP_VERIFY") && insecure == false {
3755
value := viper.GetBool("VAULT_SKIP_VERIFY")
@@ -62,6 +80,8 @@ func Execute() error {
6280
func init() {
6381
rootCmd.PersistentFlags().StringP("address", "a", "", "Address of the Vault server")
6482
rootCmd.PersistentFlags().StringP("token", "t", "", "Vault authentication token")
83+
rootCmd.PersistentFlags().StringP("role", "r", "", "Vault role for Kubernetes JWT authentication")
84+
rootCmd.PersistentFlags().BoolP("kubernetes", "", false, "Authenticate using the Kubernetes JWT token")
6585
rootCmd.PersistentFlags().BoolP("insecure", "k", false, "Allow insecure server connections when using SSL")
6686
rootCmd.PersistentFlags().StringP("namespace", "n", "", "Namespace within the Vault server (Enterprise only)")
6787

cmd/delete.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ var deleteCmd = &cobra.Command{
2525
vaultAddr, _ := cmd.Flags().GetString("address")
2626
vaultToken, _ := cmd.Flags().GetString("token")
2727
insecure, _ := cmd.Flags().GetBool("insecure")
28+
vaultRole, _ := cmd.Flags().GetString("role")
29+
kubernetes, _ := cmd.Flags().GetBool("kubernetes")
2830
namespace, _ := cmd.Flags().GetString("namespace")
2931
engineType, _ := cmd.Flags().GetString("engine-type")
3032
isApproved, _ := cmd.Flags().GetBool("auto-approve")
3133

3234
// Setup Vault client
33-
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace)
35+
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace, vaultRole, kubernetes)
3436
engine, path, err := client.MountpathSplitPrefix(path)
3537
if err != nil {
3638
fmt.Println(err)

cmd/export.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cmd
33
import (
44
"errors"
55
"fmt"
6+
67
"github.com/jonasvinther/medusa/pkg/encrypt"
78
"github.com/jonasvinther/medusa/pkg/vaultengine"
89

@@ -27,14 +28,16 @@ var exportCmd = &cobra.Command{
2728
path := args[0]
2829
vaultAddr, _ := cmd.Flags().GetString("address")
2930
vaultToken, _ := cmd.Flags().GetString("token")
31+
vaultRole, _ := cmd.Flags().GetString("role")
32+
kubernetes, _ := cmd.Flags().GetBool("kubernetes")
3033
insecure, _ := cmd.Flags().GetBool("insecure")
3134
namespace, _ := cmd.Flags().GetString("namespace")
3235
engineType, _ := cmd.Flags().GetString("engine-type")
3336
doEncrypt, _ := cmd.Flags().GetBool("encrypt")
3437
exportFormat, _ := cmd.Flags().GetString("format")
3538
output, _ := cmd.Flags().GetString("output")
3639

37-
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace)
40+
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace, vaultRole, kubernetes)
3841
engine, path, err := client.MountpathSplitPrefix(path)
3942
if err != nil {
4043
fmt.Println(err)

cmd/import.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,14 @@ var importCmd = &cobra.Command{
3131
vaultAddr, _ := cmd.Flags().GetString("address")
3232
vaultToken, _ := cmd.Flags().GetString("token")
3333
insecure, _ := cmd.Flags().GetBool("insecure")
34+
vaultRole, _ := cmd.Flags().GetString("role")
35+
kubernetes, _ := cmd.Flags().GetBool("kubernetes")
3436
namespace, _ := cmd.Flags().GetString("namespace")
3537
engineType, _ := cmd.Flags().GetString("engine-type")
3638
doDecrypt, _ := cmd.Flags().GetBool("decrypt")
3739
privateKey, _ := cmd.Flags().GetString("private-key")
3840

39-
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace)
41+
client := vaultengine.NewClient(vaultAddr, vaultToken, insecure, namespace, vaultRole, kubernetes)
4042
engine, prefix, err := client.MountpathSplitPrefix(path)
4143
if err != nil {
4244
fmt.Println(err)

docs/examples/kubernetes/cronjob/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,13 @@ medusa-1615982100-5tfl8 0/1 Completed 0 60s
131131
medusa-1615982160-4b527 0/1 Completed 0 9s
132132
```
133133
134+
### Using Kubernetes authentication
135+
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.
136+
137+
```yaml
138+
command: ["./medusa", "export", "$(VAULT_PATH)", "--kubernetes", "--role=default", "-o", "/backup/backup.vault"]
139+
```
140+
134141
### Further customization
135142
This only serves as an example as to how you could use `Medusa` to take a backup of Vault from a given location.
136143

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.13
44

55
require (
66
github.com/hashicorp/vault/api v1.10.0
7+
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0
78
github.com/manifoldco/promptui v0.9.0
89
github.com/spf13/cobra v1.7.0
910
github.com/spf13/viper v1.16.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,8 @@ github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4
871871
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
872872
github.com/hashicorp/vault/api v1.10.0 h1:/US7sIjWN6Imp4o/Rj1Ce2Nr5bki/AXi9vAW3p2tOJQ=
873873
github.com/hashicorp/vault/api v1.10.0/go.mod h1:jo5Y/ET+hNyz+JnKDt8XLAdKs+AM0G5W0Vp1IrFI8N8=
874+
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0 h1:CXO0fD7M3iCGovP/UApeHhPcH4paDFKcu7AjEXi94rI=
875+
github.com/hashicorp/vault/api/auth/kubernetes v0.5.0/go.mod h1:afrElBIO9Q4sHFVuVWgNevG4uAs1bT2AZFA9aEiI608=
874876
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
875877
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
876878
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=

pkg/vaultengine/vaultclient.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package vaultengine
22

33
import (
4+
"context"
45
"errors"
56
"strings"
67

78
vault "github.com/hashicorp/vault/api"
9+
auth "github.com/hashicorp/vault/api/auth/kubernetes"
810
)
911

1012
// Client describes the arguments that is needed to to establish a connecting to a Vault instance
@@ -14,17 +16,22 @@ type Client struct {
1416
namespace string
1517
engine string
1618
engineType string
19+
role string
20+
kubernetes bool
1721
insecure bool
1822
vc *vault.Client
1923
}
2024

2125
// NewClient creates a instance of the VaultClient struct
22-
func NewClient(addr, token string, insecure bool, namespace string) *Client {
26+
func NewClient(addr, token string, insecure bool, namespace string, role string, kubernetes bool) *Client {
2327
client := &Client{
24-
token: token,
25-
addr: addr,
26-
insecure: insecure,
27-
namespace: namespace}
28+
token: token,
29+
addr: addr,
30+
insecure: insecure,
31+
namespace: namespace,
32+
role: role,
33+
kubernetes: kubernetes,
34+
}
2835

2936
client.newVaultClient()
3037

@@ -99,5 +106,19 @@ func (client *Client) newVaultClient() error {
99106
client.vc.SetToken(client.token)
100107
}
101108

109+
// Authenticate using Kubernetes JWT if kubernetes flag is set
110+
if client.kubernetes {
111+
kubernetesAuth, err := auth.NewKubernetesAuth(client.role)
112+
if err != nil {
113+
return err
114+
}
115+
116+
authInfo, err := vc.Auth().Login(context.Background(), kubernetesAuth)
117+
if err != nil {
118+
return err
119+
}
120+
client.vc.SetToken(authInfo.Auth.ClientToken)
121+
}
122+
102123
return nil
103124
}

0 commit comments

Comments
 (0)