Skip to content

Commit

Permalink
Merge pull request #51 from ngtuna/mongo-pw
Browse files Browse the repository at this point in the history
generate and inject random base64 mongo pw
  • Loading branch information
ngtuna authored Nov 28, 2017
2 parents 77505a6 + 14ba411 commit d45ef5e
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 26 deletions.
23 changes: 23 additions & 0 deletions cmd/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,13 @@ package cmd

import (
"fmt"

"github.com/ksonnet/kubecfg/pkg/kubecfg"
"github.com/ksonnet/kubecfg/utils"
"github.com/spf13/cobra"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)

var downCmd = &cobra.Command{
Expand Down Expand Up @@ -51,6 +55,12 @@ var downCmd = &cobra.Command{
return fmt.Errorf("kubernetes with RBAC enabled (v1.7+) is required to run Kubeapps")
}

//delete mongodb secret
err = deleteSecretObject(c, MongoDB_Secret, Kubeapps_NS)
if err != nil {
return err
}

manifest, err := fsGetFile("/kubeapps-objs.yaml")
if err != nil {
return fmt.Errorf("can't read kubeapps manifest: %v", err)
Expand All @@ -72,3 +82,16 @@ func init() {
RootCmd.AddCommand(downCmd)
downCmd.Flags().Int64("grace-period", -1, "Number of seconds given to resources to terminate gracefully. A negative value is ignored.")
}

func deleteSecretObject(c kubecfg.DeleteCmd, name, ns string) error {
gvk := schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
rc, err := clientForGroupVersionKind(c.ClientPool, c.Discovery, gvk, ns)
if err != nil {
return fmt.Errorf("can't delete secret object %s due to: %v", name, err)
}
err = rc.Delete(name, &metav1.DeleteOptions{})
if err != nil && !k8sErrors.IsNotFound(err) {
return fmt.Errorf("can't delete secret object %s due to: %v", name, err)
}
return nil
}
31 changes: 31 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package cmd

import (
"bufio"
"crypto/rand"
"encoding/base64"
"errors"
"io"
"os"
Expand Down Expand Up @@ -152,3 +154,32 @@ func getHome() (string, error) {

return "", errors.New("Can't get home directory")
}

// generateRandomBytes returns securely generated random bytes.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func generateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return nil, err
}

return b, nil
}

// generateEncodedRandomPassword returns a standard base64 encoded
// securely generated random string.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func generateEncodedRandomPassword(s int) (string, error) {
b, err := generateRandomBytes(s)
if err != nil {
return "", err
}
pw := base64.StdEncoding.EncodeToString(b)
return base64.StdEncoding.EncodeToString([]byte(pw)), nil
}
29 changes: 29 additions & 0 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package cmd

import (
"bytes"
"encoding/base64"
"testing"
)

Expand Down Expand Up @@ -58,3 +60,30 @@ metadata:
t.Error("Expected parse fail, got success")
}
}

func TestGenerateEncodedRandomPassword(t *testing.T) {
got, err := generateEncodedRandomPassword(12)
if err != nil {
t.Error(err)
}
_, err = base64.StdEncoding.DecodeString(got)
if err != nil {
t.Error(err)
}
}

func TestGenerateRandomBytes(t *testing.T) {
got1, err := generateRandomBytes(12)
if err != nil {
t.Error(err)
}

got2, err := generateRandomBytes(12)
if err != nil {
t.Error(err)
}

if bytes.Equal(got1, got2) {
t.Errorf("expect get two different byte slices, got the same")
}
}
73 changes: 72 additions & 1 deletion cmd/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,18 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/pkg/api/v1"
"k8s.io/client-go/pkg/apis/apps/v1beta1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
)

const (
GcTag = "bitnami/kubeapps"
KubeappsNS = "kubeapps"
KubelessNS = "kubeless"
SystemNS = "kube-system"
Kubeapps_NS = "kubeapps"
MongoDB_Secret = "mongodb"
)

var upCmd = &cobra.Command{
Expand Down Expand Up @@ -128,6 +133,28 @@ List of components that kubeapps up installs:
c.SkipGc = false
}

// mongodb secret
// FIXME (tuna): if the mongodb secret exists then do nothing,
// otherwise, add it (with new generated rand pw) to the objs list then do full update.
if prevsecret, exist, err := mongoSecretExists(c, MongoDB_Secret, Kubeapps_NS); err != nil {
return err
} else if !exist {
pwFields := []string{"mongodb-password", "mongodb-root-password"}
pw := make(map[string]string)
for _, p := range pwFields {
s, err := generateEncodedRandomPassword(12)
if err != nil {
return fmt.Errorf("error reading random data for secret %s: %v", MongoDB_Secret, err)
}
pw[p] = s
}
secret := buildSecretObject(pw, MongoDB_Secret, Kubeapps_NS)
objs = append(objs, secret)
} else if exist {
// add prevsecret to the list so it won't be GC-ed
objs = append(objs, prevsecret)
}

err = c.Run(objs, wd)
if err != nil {
return fmt.Errorf("can't install kubeapps components: %v", err)
Expand Down Expand Up @@ -181,7 +208,6 @@ func printOutput(w io.Writer, c *kubernetes.Clientset) error {
if err != nil {
return err
}

err = printPod(w, c, nss)
if err != nil {
return err
Expand All @@ -192,6 +218,51 @@ func printOutput(w io.Writer, c *kubernetes.Clientset) error {
return nil
}

func mongoSecretExists(c kubecfg.ApplyCmd, name, ns string) (*unstructured.Unstructured, bool, error) {
gvk := schema.GroupVersionKind{Version: "v1", Kind: "Secret"}
rc, err := clientForGroupVersionKind(c.ClientPool, c.Discovery, gvk, ns)
if err != nil {
return nil, false, err
}
pwFields := []string{"mongodb-password", "mongodb-root-password"}
prevSec, err := rc.Get(name)

if k8sErrors.IsNotFound(err) {
return nil, false, nil
}

if err != nil {
return nil, true, err
}

if prevSec.Object["data"] == nil {
return nil, true, fmt.Errorf("secret %s already exists but it doesn't contain any expected key", name)
}

prevPw := prevSec.Object["data"].(map[string]interface{})
for _, p := range pwFields {
if prevPw[p] == nil {
return nil, true, fmt.Errorf("secret %s already exists but it doesn't contain the expected key %s", name, p)
}
}

return prevSec, true, nil
}

func buildSecretObject(pw map[string]string, name, ns string) *unstructured.Unstructured {
return &unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Secret",
"apiVersion": "v1",
"metadata": map[string]interface{}{
"name": name,
"namespace": ns,
},
"data": pw,
},
}
}

func printPod(w io.Writer, c kubernetes.Interface, nss []string) error {
table := uitable.New()
table.MaxColWidth = 50
Expand Down
26 changes: 26 additions & 0 deletions cmd/up_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,29 @@ func TestPrintOutput(t *testing.T) {
t.Errorf("statefulset %s shouldn't be listed", po2.Name)
}
}

func TestBuildSecret(t *testing.T) {
pw := map[string]string{
"foo": "bar",
"bar": "baz",
}
name := "foo"
ns := "my-ns"

sr := buildSecretObject(pw, name, ns)
if sr.Object["kind"] != "Secret" {
t.Errorf("expect kind = secret, got %v", sr.Object["kind"])
}
if sr.Object["data"] == nil {
t.Errorf("data can't be nil")
}

meta := sr.Object["metadata"].(map[string]interface{})
if meta["name"].(string) != name || meta["namespace"].(string) != ns {
t.Errorf("wrong metadata")
}
data := sr.Object["data"].(map[string]string)
if data["foo"] != "bar" || data["bar"] != "baz" {
t.Errorf("wrong data")
}
}
14 changes: 0 additions & 14 deletions scripts/sync-manifest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,3 @@ KUBECFG_JPATH=$MANIFEST_REPO/lib:$MANIFEST_REPO/vendor/kubecfg/lib:$MANIFEST_REP
export KUBECFG_JPATH

kubecfg show $MANIFEST_REPO/kubeapps.jsonnet > static/kubeapps-objs.yaml

cat >> static/kubeapps-objs.yaml <<EOF
---
apiVersion: v1
data:
mongodb-password: MjNneWZ3ZWZoZzkyOA==
mongodb-root-password: MjNneWZ3ZWZoZzkyOA==
kind: Secret
metadata:
annotations: {}
name: mongodb
namespace: kubeapps
type: Opaque
EOF
11 changes: 0 additions & 11 deletions static/kubeapps-objs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1393,14 +1393,3 @@ spec:
servicePort: 3000
path: /kubeless
tls: []
---
apiVersion: v1
data:
mongodb-password: MjNneWZ3ZWZoZzkyOA==
mongodb-root-password: MjNneWZ3ZWZoZzkyOA==
kind: Secret
metadata:
annotations: {}
name: mongodb
namespace: kubeapps
type: Opaque

0 comments on commit d45ef5e

Please sign in to comment.