Skip to content

Commit

Permalink
#181 Connection secrets (+public key) in base64 (#310)
Browse files Browse the repository at this point in the history
Co-authored-by: Innokentii Min <innokentii_min@epam.com>
  • Loading branch information
keshamin and Innokentii Min committed Aug 25, 2020
1 parent da4f3e3 commit 2a80a51
Show file tree
Hide file tree
Showing 31 changed files with 782 additions and 221 deletions.
7 changes: 6 additions & 1 deletion packages/operator/controllers/pull_connections.go
Expand Up @@ -64,13 +64,18 @@ func (r *ModelDeploymentReconciler) reconcileDeploymentPullConnection(

log = log.WithValues(odahuflow.ConnectionIDLogPrefix, mdConnID)

mdConn, err := r.connRepo.GetDecryptedConnection(mdConnID)
mdConn, err := r.connRepo.GetConnection(mdConnID)
if err != nil {
log.Error(err, "Cannot retrieve connection")

return err
}

// Since connRepo here is actually an HTTP client, it returns connection with base64-encoded secrets
if err := mdConn.DecodeBase64Fields(); err != nil {
return err
}

switch mdConn.Spec.Type {
case connection.DockerType:
return r.reconcileDockerDeploymentSecret(log, md, mdConn, DockerSecret{
Expand Down
54 changes: 50 additions & 4 deletions packages/operator/pkg/apis/connection/connection.go
Expand Up @@ -16,7 +16,11 @@

package connection

import "github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
import (
"encoding/base64"
"github.com/odahu/odahu-flow/packages/operator/api/v1alpha1"
"go.uber.org/multierr"
)

const (
S3Type = v1alpha1.ConnectionType("s3")
Expand Down Expand Up @@ -67,7 +71,49 @@ func (c *Connection) DeleteSensitiveData() *Connection {
return c
}

// The wrapper around DeleteSensitiveData, but it doesn't mutate the original object and returns a copy
func (c Connection) DeleteSensitiveDataImmutable() *Connection {
return c.DeleteSensitiveData()
// Decodes sensitive data from base64
func (c *Connection) DecodeBase64Fields() error {
var err error

decoded, decodeErr := base64.StdEncoding.DecodeString(c.Spec.Password)
if decodeErr != nil {
err = multierr.Append(err, decodeErr)
}
c.Spec.Password = string(decoded)

decoded, decodeErr = base64.StdEncoding.DecodeString(c.Spec.KeySecret)
if decodeErr != nil {
err = multierr.Append(err, decodeErr)
}
c.Spec.KeySecret = string(decoded)

decoded, decodeErr = base64.StdEncoding.DecodeString(c.Spec.KeyID)
if decodeErr != nil {
err = multierr.Append(err, decodeErr)
}
c.Spec.KeyID = string(decoded)

decoded, decodeErr = base64.StdEncoding.DecodeString(c.Spec.PublicKey)
if decodeErr != nil {
err = multierr.Append(err, decodeErr)
}
c.Spec.PublicKey = string(decoded)

return err
}

// Encodes sensitive data and public key to base64
func (c *Connection) EncodeBase64Fields() {
if c.Spec.Password != DecryptedDataMask {
c.Spec.Password = base64.StdEncoding.EncodeToString([]byte(c.Spec.Password))
}
if c.Spec.KeySecret != DecryptedDataMask {
c.Spec.KeySecret = base64.StdEncoding.EncodeToString([]byte(c.Spec.KeySecret))
}
if c.Spec.KeyID != DecryptedDataMask {
c.Spec.KeyID = base64.StdEncoding.EncodeToString([]byte(c.Spec.KeyID))
}
if c.Spec.PublicKey != DecryptedDataMask {
c.Spec.PublicKey = base64.StdEncoding.EncodeToString([]byte(c.Spec.PublicKey))
}
}
19 changes: 18 additions & 1 deletion packages/operator/pkg/errors/errors.go
Expand Up @@ -16,7 +16,10 @@

package errors

import "fmt"
import (
"fmt"
"strings"
)

type NotFoundError struct {
Entity string
Expand Down Expand Up @@ -65,3 +68,17 @@ func IsForbiddenError(err error) bool {
_, ok := err.(ForbiddenError)
return ok
}

type InvalidEntityError struct {
Entity string
ValidationErrors []error
}

func (iee InvalidEntityError) Error() string {
errorStrings := make([]string, 0, len(iee.ValidationErrors))
for _, err := range iee.ValidationErrors {
errorStrings = append(errorStrings, err.Error())
}

return fmt.Sprintf("entity %q is invalid; errors: %s", iee.Entity, strings.Join(errorStrings, ", "))
}
14 changes: 12 additions & 2 deletions packages/operator/pkg/packager/edi.go
Expand Up @@ -33,22 +33,32 @@ func (p *Packager) getPackaging() (*packaging.K8sPackager, error) {

targets := make([]packaging.PackagerTarget, 0, len(modelPackaging.Spec.Targets))
for _, target := range modelPackaging.Spec.Targets {
conn, err := p.connRepo.GetDecryptedConnection(target.ConnectionName)
conn, err := p.connRepo.GetConnection(target.ConnectionName)
if err != nil {
return nil, err
}

// Since connRepo here is actually an HTTP client, it returns connection with some fields base64-encoded
if err := conn.DecodeBase64Fields(); err != nil {
return nil, err
}

targets = append(targets, packaging.PackagerTarget{
Name: target.Name,
Connection: *conn,
})
}

modelHolder, err := p.connRepo.GetDecryptedConnection(modelPackaging.Spec.OutputConnection)
modelHolder, err := p.connRepo.GetConnection(modelPackaging.Spec.OutputConnection)
if err != nil {
return nil, err
}

// Since connRepo here is actually an HTTP client, it returns connection with some fields base64-encoded
if err := modelHolder.DecodeBase64Fields(); err != nil {
return nil, err
}

return &packaging.K8sPackager{
ModelHolder: modelHolder,
ModelPackaging: modelPackaging,
Expand Down
9 changes: 8 additions & 1 deletion packages/operator/pkg/rclone/gcs.go
Expand Up @@ -22,9 +22,12 @@ import (
_ "github.com/rclone/rclone/backend/googlecloudstorage" // s3 specific handlers
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config"
"io/ioutil"
"net/url"
)

const serviceAccountJSONPath = "gcs_service_account.json"

func createGcsConfig(configName string, conn *v1alpha1.ConnectionSpec) (*FileDescription, error) {
_, err := fs.Find("googlecloudstorage")
if err != nil {
Expand All @@ -39,7 +42,11 @@ func createGcsConfig(configName string, conn *v1alpha1.ConnectionSpec) (*FileDes
}

if len(conn.KeySecret) != 0 {
options["service_account_credentials"] = conn.KeySecret
if err = ioutil.WriteFile(serviceAccountJSONPath, []byte(conn.KeySecret), 0600); err != nil {
log.Error(err, "Failed to write service account JSON-file")
return nil, err
}
options["service_account_file"] = serviceAccountJSONPath
}

if err := config.CreateRemote(configName, "googlecloudstorage", options, true, false); err != nil {
Expand Down
19 changes: 3 additions & 16 deletions packages/operator/pkg/repository/connection/http/connection.go
Expand Up @@ -56,24 +56,13 @@ func wrapConnLogger(id string) logr.Logger {
return log.WithValues("conn_id", id)
}

func (hcr *httpConnectionRepository) GetDecryptedConnection(id string) (*connection.Connection, error) {
connLogger := wrapConnLogger(id)

return hcr.getConnectionFromAPI(connLogger, &http.Request{
Method: http.MethodGet,
URL: &url.URL{
Path: strings.Replace("/connection/:id/decrypted", ":id", id, 1),
},
})
}

func (hcr *httpConnectionRepository) GetConnection(id string) (conn *connection.Connection, err error) {
connLogger := wrapConnLogger(id)

return hcr.getConnectionFromAPI(connLogger, &http.Request{
Method: http.MethodGet,
URL: &url.URL{
Path: strings.Replace("/connection/:id", ":id", id, 1),
Path: strings.Replace("/connection/:id/decrypted", ":id", id, 1),
},
})
}
Expand Down Expand Up @@ -126,12 +115,10 @@ func (hcr *httpConnectionRepository) DeleteConnection(id string) error {
panic("not implemented")
}

func (hcr *httpConnectionRepository) UpdateConnection(connection *connection.Connection) (
*connection.Connection, error) {
func (hcr *httpConnectionRepository) UpdateConnection(connection *connection.Connection) error {
panic("not implemented")
}

func (hcr *httpConnectionRepository) CreateConnection(connection *connection.Connection) (
*connection.Connection, error) {
func (hcr *httpConnectionRepository) CreateConnection(connection *connection.Connection) error {
panic("not implemented")
}
Expand Up @@ -120,15 +120,3 @@ func (s *Suite) TestConnectionNotFound() {
s.g.Expect(err).Should(HaveOccurred())
s.g.Expect(err.Error()).Should(ContainSubstring("not found"))
}

func (s *Suite) TestDecryptedConnectionGet() {
connResult, err := s.connHTTPClient.GetDecryptedConnection(connID)
s.g.Expect(err).ShouldNot(HaveOccurred())
s.g.Expect(newStubConn()).Should(Equal(connResult))
}

func (s *Suite) TestDecryptedConnectionGetNotFound() {
_, err := s.connHTTPClient.GetDecryptedConnection("conn-not-found")
s.g.Expect(err).Should(HaveOccurred())
s.g.Expect(err.Error()).Should(ContainSubstring("not found"))
}
Expand Up @@ -70,11 +70,7 @@ func (kc *k8sConnectionRepository) GetConnection(id string) (*connection.Connect
return nil, err
}

return connectionFromK8s.DeleteSensitiveData(), err
}

func (kc *k8sConnectionRepository) GetDecryptedConnection(id string) (*connection.Connection, error) {
return kc.getConnectionFromK8s(id)
return connectionFromK8s, err
}

func (kc *k8sConnectionRepository) getConnectionFromK8s(id string) (*connection.Connection, error) {
Expand Down Expand Up @@ -138,7 +134,7 @@ func (kc *k8sConnectionRepository) GetConnectionList(options ...conn_repository.
Spec: currentConn.Spec,
Status: currentConn.Status,
}
conns[i] = *conn.DeleteSensitiveData()
conns[i] = conn
}

return conns, nil
Expand All @@ -163,15 +159,15 @@ func (kc *k8sConnectionRepository) DeleteConnection(id string) error {
return nil
}

func (kc *k8sConnectionRepository) UpdateConnection(conn *connection.Connection) (*connection.Connection, error) {
func (kc *k8sConnectionRepository) UpdateConnection(conn *connection.Connection) error {
var k8sConn v1alpha1.Connection
if err := kc.k8sClient.Get(context.TODO(),
types.NamespacedName{Name: conn.ID, Namespace: kc.namespace},
&k8sConn,
); err != nil {
logC.Error(err, "Get conn from k8s", "id", conn.ID)

return nil, convertK8sErrToOdahuflowErr(err)
return convertK8sErrToOdahuflowErr(err)
}

// TODO: think about update, not replacing as for now
Expand All @@ -182,15 +178,15 @@ func (kc *k8sConnectionRepository) UpdateConnection(conn *connection.Connection)
if err := kc.k8sClient.Update(context.TODO(), &k8sConn); err != nil {
logC.Error(err, "Creation of the conn", "id", conn.ID)

return nil, convertK8sErrToOdahuflowErr(err)
return convertK8sErrToOdahuflowErr(err)
}

conn.Status = k8sConn.Status

return conn.DeleteSensitiveDataImmutable(), nil
return nil
}

func (kc *k8sConnectionRepository) CreateConnection(connection *connection.Connection) (*connection.Connection, error) {
func (kc *k8sConnectionRepository) CreateConnection(connection *connection.Connection) error {
conn := &v1alpha1.Connection{
ObjectMeta: metav1.ObjectMeta{
Name: connection.ID,
Expand All @@ -206,12 +202,12 @@ func (kc *k8sConnectionRepository) CreateConnection(connection *connection.Conne
if err := kc.k8sClient.Create(context.TODO(), conn); err != nil {
logC.Error(err, "ConnectionName creation error from k8s", "name", connection.ID)

return nil, convertK8sErrToOdahuflowErr(err)
return convertK8sErrToOdahuflowErr(err)
}

connection.Status = conn.Status

return connection.DeleteSensitiveDataImmutable(), nil
return nil
}

func convertK8sErrToOdahuflowErr(err error) error {
Expand Down
Expand Up @@ -40,10 +40,10 @@ func TestConnectionRepository(t *testing.T) {
},
}

_, err := c.CreateConnection(created)
err := c.CreateConnection(created)
g.Expect(err).NotTo(HaveOccurred())

fetched, err := c.GetDecryptedConnection(connID)
fetched, err := c.GetConnection(connID)
g.Expect(err).NotTo(HaveOccurred())
g.Expect(fetched.ID).To(Equal(created.ID))
g.Expect(fetched.Spec).To(Equal(created.Spec))
Expand All @@ -60,7 +60,7 @@ func TestConnectionRepository(t *testing.T) {
Type: newConnType,
},
}
_, err = c.UpdateConnection(updated)
err = c.UpdateConnection(updated)
g.Expect(err).NotTo(HaveOccurred())

fetched, err = c.GetConnection(connID)
Expand Down
5 changes: 2 additions & 3 deletions packages/operator/pkg/repository/connection/repository.go
Expand Up @@ -26,11 +26,10 @@ const (

type Repository interface {
GetConnection(id string) (*connection.Connection, error)
GetDecryptedConnection(id string) (*connection.Connection, error)
GetConnectionList(options ...ListOption) ([]connection.Connection, error)
DeleteConnection(id string) error
UpdateConnection(connection *connection.Connection) (*connection.Connection, error)
CreateConnection(connection *connection.Connection) (*connection.Connection, error)
UpdateConnection(connection *connection.Connection) error
CreateConnection(connection *connection.Connection) error
}

type Filter struct {
Expand Down

0 comments on commit 2a80a51

Please sign in to comment.