Skip to content

Commit

Permalink
Merge pull request #62 from ministryofjustice/decode-k8s-secrets
Browse files Browse the repository at this point in the history
Add feature - decode k8s secrets
  • Loading branch information
digitalronin authored Dec 4, 2020
2 parents 9ca0a8d + 0f55998 commit 259635a
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 3 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/sonar-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ on:

jobs:
run-scan:
name: Sonarqube Scan
name: Sonarqube Scan
runs-on: ubuntu-latest

steps:
- name: Setup sonarqube
uses: warchant/setup-sonar-scanner@v1
uses: warchant/setup-sonar-scanner@v3

- name: Checkout Code
uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions pkg/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ func AddCommands(topLevel *cobra.Command) {
addKubecfgCmd(topLevel)
addVersion(topLevel)
addEnvironmentCmd(topLevel)
addDecodeSecret(topLevel)
}
31 changes: 31 additions & 0 deletions pkg/commands/decodeSecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package commands

import (
"github.com/MakeNowJust/heredoc"
decodeSecret "github.com/ministryofjustice/cloud-platform-cli/pkg/decodeSecret"
"github.com/spf13/cobra"
)

func addDecodeSecret(topLevel *cobra.Command) {
opts := &decodeSecret.DecodeSecretOptions{}

cmd := &cobra.Command{
Use: "decode-secret",
Short: `Decode a kubernetes secret`,
Example: heredoc.Doc(`
$ cloud-platform decode-secret -n mynamespace -s mysecret
`),
PreRun: upgradeIfNotLatest,
RunE: func(cmd *cobra.Command, args []string) error {
return decodeSecret.DecodeSecret(opts)
},
}

cmd.Flags().StringVarP(&opts.Secret, "secret", "s", "", "Secret name")
cmd.MarkFlagRequired("secret")

cmd.Flags().StringVarP(&opts.Namespace, "namespace", "n", "", "Namespace name")
cmd.MarkFlagRequired("namespace")

topLevel.AddCommand(cmd)
}
2 changes: 1 addition & 1 deletion pkg/commands/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
)

// This MUST match the number of the latest release on github
var Version = "1.6.4"
var Version = "1.6.5"

const owner = "ministryofjustice"
const repoName = "cloud-platform-cli"
Expand Down
98 changes: 98 additions & 0 deletions pkg/decodeSecret/decodeSecret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package decodeSecret

import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"os/exec"
)

type secretDecoder struct{}

type DecodeSecretOptions struct {
Secret string
Namespace string
}

func DecodeSecret(opts *DecodeSecretOptions) error {
jsn := retrieveSecret(opts.Namespace, opts.Secret)

sd := secretDecoder{}

err, str := sd.processJson(jsn)
if err != nil {
return err
}

fmt.Printf(str)
return nil
}

func retrieveSecret(namespace, secret string) string {
cmd := exec.Command("kubectl", "--namespace", namespace, "get", "secret", secret, "-o", "json")

var out bytes.Buffer
cmd.Stdout = &out

err := cmd.Run()
if err != nil {
fmt.Println("Error: ", err)
return ""
}

return out.String()
}

func (sd *secretDecoder) processJson(jsn string) (error, string) {
if jsn == "" {
return errors.New("failed to retrieve secret from namespace"), ""
}

var result map[string]interface{}
json.Unmarshal([]byte(jsn), &result)

data := result["data"].(map[string]interface{})

err := decodeKeys(data)
if err != nil {
return err, ""
}

err, str := formatJson(result)
if err != nil {
return err, ""
}

return nil, str
}

func decodeKeys(data map[string]interface{}) error {
for k, v := range data {
switch v.(type) {
case string:
data[k] = base64decode(v)
default:
return fmt.Errorf("Expected key %s of secret to be a string, but it wasn't\n", k)
}
}
return nil
}

func base64decode(i interface{}) string {
str, e := base64.StdEncoding.DecodeString(i.(string))
if e != nil {
return "ERROR: base64 decode failed"
}
return fmt.Sprintf("%s", str)
}

func formatJson(result map[string]interface{}) (error, string) {
str, err := json.MarshalIndent(result, "", " ")
if err != nil {
return err, ""
}

return nil, fmt.Sprintf("%s\n", str)
}
45 changes: 45 additions & 0 deletions pkg/decodeSecret/decodeSecret_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package decodeSecret

import "testing"

func TestDecodeSecret(t *testing.T) {
jsn := `{ "data": { "key1": "d2liYmxl", "key2": "d29iYmxl" } }`

expected := `{
"data": {
"key1": "wibble",
"key2": "wobble"
}
}
`
sd := secretDecoder{}
err, actual := sd.processJson(jsn)
if err != nil || actual != expected {
t.Errorf("Expected:\n%s\nGot:\n%s\n", expected, actual)
}
}

func TestBadBase64(t *testing.T) {
jsn := `{ "data": { "key1": "1", "key2": "2" } }`

expected := `{
"data": {
"key1": "ERROR: base64 decode failed",
"key2": "ERROR: base64 decode failed"
}
}
`
sd := secretDecoder{}
err, actual := sd.processJson(jsn)
if err != nil || actual != expected {
t.Errorf("Expected:\n%s\nGot:\n%s\n", expected, actual)
}
}

func TestNoSuchSecret(t *testing.T) {
sd := secretDecoder{}
err, _ := sd.processJson("")
if err == nil {
t.Errorf("Expected an error")
}
}

0 comments on commit 259635a

Please sign in to comment.