diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c2e2318..30742aa 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,7 +7,7 @@ on: jobs: - slv: + release: runs-on: ubuntu-latest steps: - name: Setting SLV Version diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 1dcfb3a..da1035d 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -4,7 +4,7 @@ builds: main: ./cli/main env: - CGO_ENABLED=0 - ldflags: "-X savesecrets.org/slv.Version={{.Version}}" + ldflags: "-X savesecrets.org/slv.Version={{.Version}} -X savesecrets.org/slv.BuildDate={{.Date}} -X savesecrets.org/slv.Commit={{.Commit}}" targets: - darwin_amd64 - darwin_arm64 diff --git a/README.md b/README.md index 7f702ba..22127aa 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,79 @@ Secure Local Vault - SLV (a.k.a Secrets Launch Vehicle 🔐🚀) is a tool to manage secrets locally in a secure manner. It is designed to be used by developers to manage secrets along with their code so that they can be shared with other developers and services in a secure manner. SLV is designed based on the following **key principles** - - Anyone can add or update secrets, however will not be allowed to read them unless they have access to the vault - - An environment should have a single key that will give access to all necessary secrets irrespective of the number of vaults shared with it + - Anyone can add or update secrets, however will not be able to read them unless they have access to the vault + - An environment should have a single identity that will give access to all necessary secrets from any vault shared with it - ## How to install - SLV can be installed using brew using the following command +## Installation +Download the latest SLV binary from the [releases](https://github.com/savesecrets/slv/releases/latest) page and add it to your path. + +SLV can also be installed with brew using the following command ```zsh brew install savesecrets/tap/slv ``` -Alternatively, you can download the SLV binary from the [releases](https://github.com/savesecrets/slv/releases/latest) page and add it to your path. \ No newline at end of file + +## Usage + +#### Create a new profile +```sh +$ slv profile new -n amagi + +Created profile: amagi +``` + +#### Create a new environment +```sh +$ slv env new -n alice -e alice@example.com --add + +ID (Public Key): SLV_EPK_AEAUKAELRTIL2YIXNP7NYTYQMHUX77BWK2LXSKXN4GHSUECDNEJ7XFECLE +Name: alice +Email: alice@example.com +Tags: [] + +Env Data: SLV_EDS_AF4JYNGKIFVYGMAYQDQ774U5MUDRSBDSTK5G7UZFBPMYYW5ECRETKSBAKFISVFOS75PKJ5HY6I7FPEHHSN3S3MY3KAUPSX4DSI2QSJQVJOIP7KUCY522DBJEUJLPLT3XLZUUFUT7CZZV2MRNLY77HMWC5RO6AF6RD6MHDBAIQQERMKAY55NAWELAGDHD766NLZGJRPD5NHD3BP3BKXN3J26FZ3V4GK6TF5AA7RYI4Q6K5LVTOPVINTQNHVIIBWZ5AAAP775I7Q3QS + +Secret Key: SLV_ESK_AEAEKAHBIONE3QIIWFXFRNJPE6A6AYL527QW4OF4HWWFDOE5E4XR5LO2WI +``` + +#### Create a vault +- To create a vault and share it with the environment `alice`, use the following command +```sh +$ slv vault new -v test.slv -s alice + +Created vault: test.slv +``` +- To create a K8s compatible vault, use the following command +```sh +$ slv vault new -v test.slv.yaml -s alice --k8s production + +Created vault: test.slv.yaml +``` + +#### Add secrets to the vault +```sh +$ slv secret put -v test.slv -n db_password -s "super_secret_pwd" + +Added secret: db_password to vault: test.slv +``` + +#### Get secrets from the vault +Set the environment variable `SLV_ENV_SECRET_KEY` to the secret key generated in the previous step +```sh +$ export SLV_ENV_SECRET_KEY=SLV_ESK_AEAEKAHBIONE3QIIWFXFRNJPE6A6AYL527QW4OF4HWWFDOE5E4XR5LO2WI +$ slv secret get -v test.slv -n db_password + +super_secret_pwd +``` + +#### Share the vault with other environments +Ensure that the current environment has access to the vault in order to share it with other environments +```sh +$ slv vault share -v test.slv -s bob + +Shared vault: test.slv +``` +Once shared, the other environments can access the vault using their respective secret keys + +## Integrations +Some of the integrations that SLV supports currently are: +- [Kubernetes Operator](/operator/README.md) \ No newline at end of file diff --git a/cli/internal/commands/commons.go b/cli/internal/commands/commons.go index d144af3..e309fad 100644 --- a/cli/internal/commands/commons.go +++ b/cli/internal/commands/commons.go @@ -3,10 +3,19 @@ package commands import ( "fmt" "os" + "syscall" "github.com/fatih/color" + "golang.org/x/term" ) +func getHiddenInputFromUser(prompt string) ([]byte, error) { + fmt.Print(prompt) + input, err := term.ReadPassword(int(syscall.Stdin)) + fmt.Println() + return input, err +} + func exitOnError(err error) { fmt.Fprintln(os.Stderr, color.RedString(err.Error())) erroredExit() diff --git a/cli/internal/commands/secret.go b/cli/internal/commands/secret.go index 8b0dc84..e4227d7 100644 --- a/cli/internal/commands/secret.go +++ b/cli/internal/commands/secret.go @@ -50,20 +50,35 @@ func secretPutCommand() *cobra.Command { Run: func(cmd *cobra.Command, args []string) { vaultFile := cmd.Flag(vaultFileFlag.name).Value.String() name := cmd.Flag(secretNameFlag.name).Value.String() - secret := cmd.Flag(secretValueFlag.name).Value.String() + secretStr := cmd.Flag(secretValueFlag.name).Value.String() vault, err := getVault(vaultFile) if err != nil { exitOnError(err) } forceUpdate, _ := cmd.Flags().GetBool(secretForceUpdateFlag.name) if !forceUpdate && vault.SecretExists(name) { - exitOnErrorWithMessage("secret already exists. please use the --" + secretForceUpdateFlag.name + " flag to overwrite it.") + fmt.Print("Secret already exists. Do you wish to overwrite it? (y/n): ") + var confirmation string + fmt.Scanln(&confirmation) + if confirmation != "y" { + fmt.Println(color.YellowString("Operation aborted")) + safeExit() + } + } + var secret []byte + if secretStr == "" { + secret, err = getHiddenInputFromUser("Enter the secret value for " + name + ": ") + if err != nil { + exitOnError(err) + } + } else { + secret = []byte(secretStr) } - err = vault.PutSecret(name, []byte(secret)) + err = vault.PutSecret(name, secret) if err != nil { exitOnError(err) } - fmt.Println("Added secret: ", color.GreenString(name), " to vault: ", color.GreenString(vaultFile)) + fmt.Println("Updated secret: ", color.GreenString(name), " to vault: ", color.GreenString(vaultFile)) safeExit() }, } @@ -73,7 +88,6 @@ func secretPutCommand() *cobra.Command { secretPutCmd.Flags().Bool(secretForceUpdateFlag.name, false, secretForceUpdateFlag.usage) secretPutCmd.MarkFlagRequired(vaultFileFlag.name) secretPutCmd.MarkFlagRequired(secretNameFlag.name) - secretPutCmd.MarkFlagRequired(secretValueFlag.name) return secretPutCmd } @@ -156,11 +170,11 @@ func secretExportCommand() *cobra.Command { secretOutputMap[name] = string(secret) } } - listFormat := cmd.Flag(secretListFormatFlag.name).Value.String() - if listFormat == "" { - listFormat = "table" + exportFormat := cmd.Flag(secretListFormatFlag.name).Value.String() + if exportFormat == "" { + exportFormat = "env" } - switch listFormat { + switch exportFormat { case "table": tw := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', 0) for key, value := range secretOutputMap { @@ -181,10 +195,12 @@ func secretExportCommand() *cobra.Command { fmt.Println(string(yamlData)) case "envars", "envar", "env": for key, value := range secretOutputMap { - fmt.Printf("%s=%s\n", key, value) + value = strings.ReplaceAll(value, "\\", "\\\\") + value = strings.ReplaceAll(value, "\"", "\\\"") + fmt.Printf("%s=\"%s\"\n", key, value) } default: - exitOnErrorWithMessage("invalid format: " + listFormat) + exitOnErrorWithMessage("invalid format: " + exportFormat) } safeExit() }, diff --git a/cli/internal/commands/version.go b/cli/internal/commands/version.go index 17dee3f..fa80aa9 100644 --- a/cli/internal/commands/version.go +++ b/cli/internal/commands/version.go @@ -2,6 +2,8 @@ package commands import ( "fmt" + "runtime" + "time" "github.com/spf13/cobra" "savesecrets.org/slv" @@ -9,7 +11,20 @@ import ( ) func showVersionInfo() { - fmt.Println(config.AppNameUpperCase, slv.Version) + buildAt := "unknown" + if builtAtTime, err := time.Parse(time.RFC3339, slv.BuildDate); err == nil { + builtAtLocalTime := builtAtTime.Local() + buildAt = builtAtLocalTime.Format("02 Jan 2006 03:04:05 PM MST") + } + fmt.Println(config.Art) + fmt.Println(config.AppNameUpperCase + ": " + config.Description) + fmt.Println("-------------------------------------------------") + fmt.Printf("Version : %s\n", slv.Version) + fmt.Printf("Built At : %s\n", buildAt) + fmt.Printf("Git Commit : %s\n", slv.Commit) + fmt.Printf("Web : %s\n", config.Website) + fmt.Printf("Platform : %s\n", runtime.GOOS+"/"+runtime.GOARCH) + fmt.Printf("Go Version : %s\n", runtime.Version()) } func versionCommand() *cobra.Command { diff --git a/const.go b/const.go index 1e39d0e..5c58157 100644 --- a/const.go +++ b/const.go @@ -6,13 +6,10 @@ import ( "savesecrets.org/slv/core/crypto" ) -const ( - // AppName = config.AppName - Prefix = "SLV" -) - var ( Version = "dev" + Commit = "none" + BuildDate = "" secretKey *crypto.SecretKey errEnvironmentAccessNotFound = errors.New("environment doesn't have access. please set the required environment variables") ) diff --git a/core/config/const.go b/core/config/const.go index c2bd8ff..e9bcf93 100644 --- a/core/config/const.go +++ b/core/config/const.go @@ -12,6 +12,15 @@ const ( AppNameLowerCase = "slv" AppNameUpperCase = "SLV" + Description = "Secure Local Vault | Secrets Launch Vehicle" + Website = "https://savesecrets.org/slv" + Art = ` + ____ _ __ __ + / ___|| |\ \ / / + \___ \| | \ \ / / + ___) | |__\ V / + |____/|_____\_/ + ` ) var ( diff --git a/docs/operator/samples/deploy.yaml b/docs/operator/samples/deploy.yaml new file mode 100644 index 0000000..3c0a418 --- /dev/null +++ b/docs/operator/samples/deploy.yaml @@ -0,0 +1,161 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: slvs.slv.savesecrets.org +spec: + group: slv.savesecrets.org + names: + kind: SLV + listKind: SLVList + plural: slvs + singular: slv + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: SLV is the Schema for the slvs API + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: SLVSpec defines the desired state of SLV + properties: + slvConfig: + properties: + hashLength: + format: int32 + type: integer + publicKey: + type: string + wrappedKeys: + items: + type: string + type: array + required: + - publicKey + - wrappedKeys + type: object + slvSecrets: + additionalProperties: + type: string + type: object + required: + - slvConfig + - slvSecrets + type: object + status: + description: SLVStatus defines the observed state of SLV + properties: + error: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} + +--- +apiVersion: v1 +kind: Namespace +metadata: + name: slv + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: slv-operator + namespace: slv +automountServiceAccountToken: true + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: slv-operator-clusterrole +rules: + + - apiGroups: ["slv.savesecrets.org"] + resources: ["slvs"] # plural of SLV CRD + verbs: + - "watch" + - "get" + - "list" + + - apiGroups: [""] + resources: ["secrets"] + verbs: + - "create" + - "list" + - "update" + - "delete" # Delete the secret when corresponding SLV CR is deleted + - "watch" # Watching secrets annotated by SLV being modified and reverse sync for reverting to original state. + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: slv-operator-rolebinding +subjects: +- kind: ServiceAccount + name: slv-operator + namespace: slv +roleRef: + kind: ClusterRole + name: slv-operator-clusterrole + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: slv-operator + namespace: slv + labels: + app: slv-operator +spec: + replicas: 1 + selector: + matchLabels: + app: slv-operator + template: + metadata: + labels: + app: slv-operator + spec: + serviceAccountName: slv-operator + containers: + - name: slv-operator + image: ghcr.io/savesecrets/slv/operator:latest # Use a specific version tag corresponding to the version of SLV used with the CR + resources: + limits: + cpu: "1" # 1 CPU should be fairly enough for SLV Controller + memory: "1Gi" # 1Gi is the minimum memory requirement for SLV Controller + env: + - name: SLV_ENV_SECRET_BINDING + value: "SLV_ESB_ENVSECRETBINDINGSTRINGGOESHERE" # Ensure the appropriate KMS role is attached to the K8s Service Account + - name: SLV_ENV_SECRET_KEY + valueFrom: # SLV Environment Secret Key taken from K8s secret + secretKeyRef: + name: slv # Name of K8 Secret + key: secretkey # Key within K8 Secret \ No newline at end of file diff --git a/docs/operator/samples/pets.slv.yaml b/docs/operator/samples/pets.slv.yaml new file mode 100644 index 0000000..55ad408 --- /dev/null +++ b/docs/operator/samples/pets.slv.yaml @@ -0,0 +1,21 @@ +apiVersion: slv.savesecrets.org/v1 +kind: SLV +metadata: + name: pets +spec: + slvSecrets: + supercat: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAATPB5MNWWE2E3ALMLGUOKBC6N2EXPPWWKPORH3U3QVTQEUDCXUUOISVUI5I5ESRNYTLHDTUFF57TLBG7NTKOG5WZFLQARNLXSEJWQSKEP4G75PPKK2A4TNPCK7IRYU3U67ERFMW2LN6LSRB6Q + cat1: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAA3UTBQQ36X6IHCLCMOUTSECWG7LBVIRT73NNWCHANMPYPFFQQPJBHDRU7ONADRGY3ZCYVXUNNMH5JWO3DYMPODMDBLUA57ZCEVTSF4HN3V2W4QSMN5VPI7NULFIPQ62XZGFOKGKO4FQISKHGJ + cat2: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAAJ54ZG3CM64U6HMZDWHDN4Q7A7JKHSUXMFJQ2Q72GB6DIOUFIFEFB4A3H3SAPZ7KU3RAUOJY7C7ILB4U2RVLJSHTNFMAXBIYH42VLREDWGGGDS632FJAKHDRIT6HQEBEBVZXJPOALRFZBOGO4VGSR4 + dog1: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAAZERB5D7EH43DHEDL73MPWLPDGDZXNLY6I6LZZCDKWMMRDGMV346Q23NUFCEFMFZ7ZKFOCGMRSGD7BIA7GRFJ2CFGW4AVKCAFVLYH55K4TI565TTVUKOD7KLJ3RID3TXEABVTKNNH7IFLLUKI + dog2: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAA6EKSCXWMKU5EJBT76L5JUEUHY5Z2AZJSXU5GVW5TWKGTCAVFZJL6RDO5SH3Y6PYGDVTLXUIPRZGA75LFFAK6JTINYAAS5C2LH6WRZJP4BX4H63SCOVFGGIKO32CYPOZKWCDSSCZYSB6YV532N4ZQ + dog3: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAAX5PDJLYMJJFS5WSM7MUSGOUFZHPWVVGQYRFN2XAME2SJ2N4X2BPYHJLX5MPHIM4BI6VQCL54RYSJX4SIOWHEWRTXVMAYY6DZP4LB2V6Y2EQX2E7TEHH7JJZUQRUENNMVTIVIXVFES7V3HWA + dog4: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAA5KKTTNTGJVDAYCB53MPDIM2TTVFOZ277FM2FDBN2RFMSCABLNNTH7Q4UMWZXSCUELAHKUGP4C4ZOTUVMXCYW4ZH7EUA5VOK7A3AB4CPVICRJDNTJ2MIYWDZ2TH4NG2AKUO4YYMUZ3HCDKJPK + dog5: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAAZRAGCZI4RMMW2QIGXJ7AOY4LGOXLCES6Q2WO4JUYY5HEEACB5BDRK6O6GN3CREVF4522Z4ALK4UJXFV3EP44QG7XU4A5HBKEKFAB7R4GALRPNQ6362W4X5MXDE2AU6KPVOX3AOPP3EJ3VF4J3U + dog6: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAAVVR5FSDZ5TS2UAFORSNEMXSBEJ4WHRXYVP7YOGWSBOVGH7TO4ZGKZ6G6TA6BNK66VNWIGJK25622POY5TUT7P7KZJAA5PMREQVNU66WRGKDLOD2PWSTFQEAAEQVVWJO3L2YVUXLOTOE6KXAZOFDA + dog7: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAA6ZS2LSPURVVTZGLAIQX6KUF2HJ7OAC36XIGVJUSPFWQ5NWJFRZIY4QLLDF467B67ISEUNMTPHTMMDA2L5WZR2NDHLIAVTXNA765F5Z7AJXRPBJXCRGPUGM3U5QVLNZDUG2L24Q7ZUBT4Z3Q + dog8: SLV_VSS_AFLGLVMLEEACIAIBKYAL7QS4URIE3VNM4BDC7U5ULCUFXI7VSUGYU623JRBOINO2FTPACXAASXY3S325JRWOGC2LW2JTTZABWFOWHOT5WSY74ZJATOWT7IPY2JISZEGMNN7R7UGPAQCHT5KRVNT6MFENBBJB3DTC7UA55W5LSYSLBNF57YC2YYJZDQ3JJA2LJEF7OZJF2EEZO2LQ5J6OPBA + slvConfig: + publicKey: SLV_VPK_AEAVMAF7YJOKIUCN2WWOARRP2O2FRKC3UP2ZKDMKPNNUYQXEGXNCZXQBLQ + wrappedKeys: + - SLV_EWK_AFCWLVMKRUACIAIBIUAIXDGQXVQRO2763RHRAYPJP76DMVUXPEVO3YMPFIIEG2IT7OKIEWIA2QG6EVXOHHKNZVUG73LXSQ5OD2XW3QEXKN5FYWLVMT67E2RHN5NKCEQ2EY4CRN3M5LGVPTAWMEIE37NT7Y5QH6AKMMA6NNASASUMJ3KNEPHS5BOQ43ROZGLDFVWC53HOUI3H6L32D5VOTLZWDT6N62QBYU6AH6IRBNKSUV6TSFEU2ZE7VVXDZXQ2APYURJHRFOYM4 diff --git a/go.mod b/go.mod index b1e1edc..ea1b1cc 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,16 @@ module savesecrets.org/slv -go 1.21 +go 1.22 require ( dev.shib.me/xipher v0.8.3 - github.com/aws/aws-sdk-go v1.50.23 + github.com/aws/aws-sdk-go v1.50.26 github.com/fatih/color v1.16.0 github.com/go-logr/logr v1.4.1 github.com/onsi/ginkgo/v2 v2.15.0 github.com/onsi/gomega v1.31.1 github.com/spf13/cobra v1.8.0 - golang.org/x/crypto v0.19.0 + golang.org/x/crypto v0.20.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.29.2 k8s.io/apimachinery v0.29.2 @@ -41,7 +41,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.11.2 // indirect + github.com/emicklei/go-restful/v3 v3.11.3 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-git/go-git/v5 v5.11.0 @@ -56,7 +56,7 @@ require ( github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 // indirect + github.com/google/pprof v0.0.0-20240225044709-fd706174c886 // indirect github.com/google/uuid v1.6.0 // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -72,16 +72,16 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect - github.com/prometheus/common v0.47.0 // indirect + github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/oauth2 v0.17.0 // indirect golang.org/x/sys v0.17.0 // indirect - golang.org/x/term v0.17.0 // indirect + golang.org/x/term v0.17.0 golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.18.0 // indirect @@ -93,7 +93,7 @@ require ( k8s.io/apiextensions-apiserver v0.29.2 // indirect k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.120.1 // indirect - k8s.io/kube-openapi v0.0.0-20240221221325-2ac9dc51f3f1 // indirect + k8s.io/kube-openapi v0.0.0-20240227032403-f107216b40e2 // indirect k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/go.sum b/go.sum index 896e274..5454900 100644 --- a/go.sum +++ b/go.sum @@ -11,8 +11,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go v1.50.23 h1:BB99ohyCmq6O7m5RvjN2yqTt57snL8OhDvfxEvM6ihs= -github.com/aws/aws-sdk-go v1.50.23/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.50.26 h1:tuv8+dje59DBK1Pj65tSCdD36oamBxKYJgbng4bFylc= +github.com/aws/aws-sdk-go v1.50.26/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= @@ -29,8 +29,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= -github.com/emicklei/go-restful/v3 v3.11.2 h1:1onLa9DcsMYO9P+CXaL0dStDqQ2EHHXLiz+BtnqkLAU= -github.com/emicklei/go-restful/v3 v3.11.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.3 h1:yagOQz/38xJmcNeZJtrUcKjkHRltIaIFXKWeG1SkWGE= +github.com/emicklei/go-restful/v3 v3.11.3/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= @@ -80,8 +80,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5 h1:E/LAvt58di64hlYjx7AsNS6C/ysHWYo+2qPCZKTQhRo= -github.com/google/pprof v0.0.0-20240207164012-fb44976bdcd5/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20240225044709-fd706174c886 h1:JSJUTZTQT1Gzb2ROdAKOY3HwzBYcclS2GgumhMfHqjw= +github.com/google/pprof v0.0.0-20240225044709-fd706174c886/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -137,8 +137,8 @@ github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+ github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos= github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8= -github.com/prometheus/common v0.47.0 h1:p5Cz0FNHo7SnWOmWmoRozVcjEp0bIVU8cV7OShpjL1k= -github.com/prometheus/common v0.47.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= @@ -178,10 +178,10 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5CTOAIPhgL4W8PNiIpVE= -golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= +golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -293,8 +293,8 @@ k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240221221325-2ac9dc51f3f1 h1:rtdnaWfP40MTKv7izH81gkWpZB45pZrwIxyZdPSn1mI= -k8s.io/kube-openapi v0.0.0-20240221221325-2ac9dc51f3f1/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw= +k8s.io/kube-openapi v0.0.0-20240227032403-f107216b40e2 h1:02WBxjyRwX4rJdl3XlWVjFbXT/kAKCsipoM8hQY3Dwo= +k8s.io/kube-openapi v0.0.0-20240227032403-f107216b40e2/go.mod h1:B7Huvd1LKZtTYmY+nC6rnmN8lyGYT9lifBcPD5epL6k= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= diff --git a/operator/README.md b/operator/README.md new file mode 100644 index 0000000..3ccf8f7 --- /dev/null +++ b/operator/README.md @@ -0,0 +1,35 @@ +# SLV Operator +SLV operator is a Kubernetes controller that helps in reconciling SLV vaults as kubernetes secrets into namespaces. This can be achieved by creating a vault with a `--k8s` flag. Doing so will create vaults that are custom resources managed by the SLV operator. + +### A working example +- Create a namespace and add SLV_ENV_SECRET_KEY as a secret (recommended to use Access Binding using KMS for cloud environments) +```sh +kubectl create ns slv +# Disclaimer: The below secret key is only for demonstration purposes. Please avoid using it in production. +kubectl create secret generic slv -n slv --from-literal=secretkey=SLV_ESK_AEAEKAHBIONE3QIIWFXFRNJPE6A6AYL527QW4OF4HWWFDOE5E4XR5LO2WI +``` +- Install the Kubernetes operator into your cluster +```sh +kubectl apply -f https://savesecrets.org/slv/operator/samples/deploy.yaml +``` +- Download this vault and keep it locally +```sh +curl -s https://savesecrets.org/slv/operator/samples/pets.slv.yaml > pets.slv.yaml +``` +- Apply the downloaded vault to the cluster +```sh +kubectl apply -f pets.slv.yaml +``` +- Try reading SLV controller reconciled secrets from the cluster +```sh +kubectl get secret pets -o jsonpath='{.data.supercat}' | base64 --decode +``` +- Add any secret using the following commands +```sh +slv secret put -v pets.slv.yaml -n hi -s "Hello World" +kubectl apply -f pets.slv.yaml +``` +- Try reading newly created secret from the cluster +```sh +kubectl get secret pets -o jsonpath='{.data.hi}' | base64 --decode +``` \ No newline at end of file