Skip to content

Commit

Permalink
Seal function
Browse files Browse the repository at this point in the history
  • Loading branch information
dzsak committed Nov 22, 2023
1 parent 100d951 commit 45877e2
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 19 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ require (
github.com/alecthomas/colour v0.1.0 // indirect
github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/bitnami-labs/sealed-secrets v0.13.1 // indirect
github.com/containerd/console v1.0.3 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/camelcase v1.0.0 // indirect
Expand All @@ -120,6 +121,8 @@ require (
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/tools v0.13.0 // indirect
k8s.io/code-generator v0.27.4 // indirect
k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect
lukechampine.com/uint128 v1.2.0 // indirect
modernc.org/cc/v3 v3.40.0 // indirect
modernc.org/ccgo/v3 v3.16.13 // indirect
Expand Down
128 changes: 128 additions & 0 deletions go.sum

Large diffs are not rendered by default.

27 changes: 22 additions & 5 deletions pkg/agent/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ func (e *KubeEnv) Services(repo string) ([]*api.Stack, error) {
}

stacks = append(stacks, &api.Stack{
Repo: service.ObjectMeta.GetAnnotations()[AnnotationGitRepository],
Osca: getOpenServiceCatalogAnnotations(service),
Service: &api.Service{Name: service.Name, Namespace: service.Namespace},
Deployment: deployment,
Ingresses: ingresses,
Repo: service.ObjectMeta.GetAnnotations()[AnnotationGitRepository],
Certificate: fetchCertificate(e),
Osca: getOpenServiceCatalogAnnotations(service),
Service: &api.Service{Name: service.Name, Namespace: service.Namespace},
Deployment: deployment,
Ingresses: ingresses,
})
}

Expand All @@ -117,6 +118,22 @@ func getOpenServiceCatalogAnnotations(svc v1.Service) *api.Osca {
}
}

func fetchCertificate(kubeEnv *KubeEnv) []byte {
service, err := kubeEnv.Client.CoreV1().Services("infrastructure").Get(context.Background(), "sealed-secrets-controller", metav1.GetOptions{})
if err != nil {
logrus.Errorf("could not get sealed secret service: %s", err)
return nil
}

cert, err := kubeEnv.Client.CoreV1().Services("infrastructure").ProxyGet("http", "sealed-secrets-controller", service.Spec.Ports[0].Name, "/v1/cert.pem", nil).DoRaw(context.Background())
if err != nil {
logrus.Errorf("could not get cert: %s", err)
return nil
}

return cert
}

var gitRepositoryResource = schema.GroupVersionResource{
Group: "source.toolkit.fluxcd.io",
Version: "v1",
Expand Down
13 changes: 7 additions & 6 deletions pkg/dashboard/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,13 @@ type FluxStateUpdate struct {
}

type Stack struct {
Repo string `json:"repo"`
Env string `json:"env"`
Osca *Osca `json:"osca"`
Service *Service `json:"service"`
Deployment *Deployment `json:"deployment,omitempty"`
Ingresses []*Ingress `json:"ingresses,omitempty"`
Repo string `json:"repo"`
Env string `json:"env"`
Certificate []byte `json:"certificate,omitempty"`
Osca *Osca `json:"osca"`
Service *Service `json:"service"`
Deployment *Deployment `json:"deployment,omitempty"`
Ingresses []*Ingress `json:"ingresses,omitempty"`
}

type StackUpdate struct {
Expand Down
81 changes: 81 additions & 0 deletions pkg/dashboard/server/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ package server

import (
"context"
"crypto/rand"
"crypto/rsa"

"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"path/filepath"
"strings"
"time"

"github.com/bitnami-labs/sealed-secrets/pkg/crypto"
"github.com/gimlet-io/gimlet-cli/cmd/dashboard/config"
"github.com/gimlet-io/gimlet-cli/cmd/dashboard/dynamicconfig"
"github.com/gimlet-io/gimlet-cli/pkg/dashboard/alert"
Expand All @@ -27,6 +32,7 @@ import (
"github.com/go-git/go-git/v5"
"github.com/sirupsen/logrus"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/util/cert"
"sigs.k8s.io/yaml"
)

Expand Down Expand Up @@ -489,6 +495,81 @@ func application(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(appinfosString))
}

func seal(w http.ResponseWriter, r *http.Request) {
var secret string
err := json.NewDecoder(r.Body).Decode(&secret)
if err != nil {
logrus.Errorf("cannot decode secret: %s", err)
http.Error(w, http.StatusText(400), 400)
return
}

env := chi.URLParam(r, "env")
agentHub, _ := r.Context().Value("agentHub").(*streaming.AgentHub)
cert, err := extractCert(agentHub, env)
if err != nil {
logrus.Errorf("cannot extract certificate from agenthub: %s", err)
http.Error(w, http.StatusText(500), 500)
return
}

key, err := parseKey(cert)
if err != nil {
logrus.Errorf("cannot parse public key: %s", err)
http.Error(w, http.StatusText(500), 500)
return
}

sealedValue, err := sealValue(key, secret)
if err != nil {
logrus.Errorf("cannot seal item: %s", err)
http.Error(w, http.StatusText(500), 500)
return
}

w.WriteHeader(http.StatusOK)
w.Write([]byte(sealedValue))
}

func extractCert(agentHub *streaming.AgentHub, env string) ([]byte, error) {
for _, a := range agentHub.Agents {
for _, stack := range a.Stacks {
if stack.Env != env {
continue
}

if stack.Certificate != nil {
return stack.Certificate, nil
}
}
}
return nil, fmt.Errorf("not found")
}

func parseKey(data []byte) (*rsa.PublicKey, error) {
certs, err := cert.ParseCertsPEM(data)
if err != nil {
return nil, err
}

cert, ok := certs[0].PublicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("expected RSA public key but found %v", certs[0].PublicKey)
}

return cert, nil
}

func sealValue(pubKey *rsa.PublicKey, data string) (string, error) {
if data == "" {
return "", fmt.Errorf("empty secret")
}

clusterWide := []byte("")
result, err := crypto.HybridEncrypt(rand.Reader, pubKey, []byte(data), clusterWide)
return base64.StdEncoding.EncodeToString(result), err
}

func saveEnvToDB(w http.ResponseWriter, r *http.Request) {
var envNameToSave string
err := json.NewDecoder(r.Body).Decode(&envNameToSave)
Expand Down
1 change: 1 addition & 0 deletions pkg/dashboard/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ func userRoutes(r *chi.Mux, clientHub *streaming.ClientHub) {
r.Post(("/api/deleteEnvFromDB"), deleteEnvFromDB)
r.Post(("/api/environments"), saveInfrastructureComponents)
r.Post(("/api/bootstrapGitops"), bootstrapGitops)
r.Post(("/api/env/{env}/seal"), seal)

r.Get("/ws/", func(w http.ResponseWriter, r *http.Request) {
streaming.ServeWs(clientHub, w, r)
Expand Down
2 changes: 2 additions & 0 deletions web/dashboard/src/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ export default class GimletClient {

saveInfrastructureComponents = (env, infrastructureComponents) => this.post('/api/environments', JSON.stringify({ env, infrastructureComponents }));

seal = (env, secret) => this.post(`/api/env/${env}/seal`, JSON.stringify(secret));

get = async (path) => {
try {
const { data } = await axios.get(path, {
Expand Down
7 changes: 1 addition & 6 deletions web/dashboard/src/views/envConfig/envConfig.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,6 @@ class EnvConfig extends Component {
}
}

console.log(chart)

chart.uiSchema[4].uiSchema = {
...chart.uiSchema[4].uiSchema,
"#/properties/sealedSecrets": {
Expand All @@ -254,9 +252,6 @@ class EnvConfig extends Component {
},
}


console.log(chart)

return chart
}

Expand Down Expand Up @@ -488,7 +483,7 @@ class EnvConfig extends Component {

const customFields = {
imageWidget: ImageWidget,
sealedSecretWidget: SealedSecretWidget,
sealedSecretWidget: (props) => <SealedSecretWidget {...props} gimletClient={this.props.gimletClient} store={this.props.store} env={env} />,
}

if (!this.state.configFile) {
Expand Down
29 changes: 27 additions & 2 deletions web/dashboard/src/views/envConfig/sealedSecretWidget.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Component } from "react";
import {
ACTION_TYPE_POPUPWINDOWERROR,
ACTION_TYPE_POPUPWINDOWRESET
} from "../../redux/redux";

class SealedSecretWidget extends Component {
constructor(props) {
Expand Down Expand Up @@ -27,9 +31,30 @@ class SealedSecretWidget extends Component {
};
}

resetPopupWindowAfterThreeSeconds() {
const { store } = this.props;
setTimeout(() => {
store.dispatch({
type: ACTION_TYPE_POPUPWINDOWRESET
});
}, 3000);
};

seal() {
const { gimletClient, store, env } = this.props;
return () => {
this.props.onChange("toSeal: " + this.state.value)
gimletClient.seal(env, this.state.value)
.then(data => {
this.props.onChange(data)
}, () => {
store.dispatch({
type: ACTION_TYPE_POPUPWINDOWERROR, payload: {
header: "Error",
message: "Failed to seal."
}
});
this.resetPopupWindowAfterThreeSeconds()
});
};
}

Expand All @@ -45,7 +70,7 @@ class SealedSecretWidget extends Component {
{ !this.state.sealed &&
<>
<textarea rows="5" className="form-control" id="root_repository" required="" placeholder="" type="text" list="examples_root_repository" value={this.state.value} onChange={this.onChange()} />
<button className="m-2 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded h-12"
<button disabled={this.state.value === ""} className={(this.state.value === "" ? "bg-gray-500" : "bg-blue-500 hover:bg-blue-700") + " m-2 text-white font-bold py-2 px-4 rounded h-12"}
onClick={this.seal()}
>
Seal
Expand Down

0 comments on commit 45877e2

Please sign in to comment.