Skip to content

Commit

Permalink
Merge pull request #1162 from weaveworks/1121-blob-cache-encryption
Browse files Browse the repository at this point in the history
feat: Implement BLOB encryption within the tf-runner
  • Loading branch information
yitsushi authored Dec 15, 2023
2 parents bb22396 + 2c9ed0f commit 12e53ed
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 182 deletions.
9 changes: 9 additions & 0 deletions charts/tf-controller/templates/runner-serviceaccount.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ metadata:
{{- with $.Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 2 }}
---
apiVersion: v1
kind: Secret
metadata:
name: tf-runner.cache-encryption
namespace: {{ . }}
annotations:
kubernetes.io/service-account.name: {{ include "tf-controller.runner.serviceAccountName" $ }}
type: kubernetes.io/service-account-token
{{- end }}
{{- end }}
{{- end }}
358 changes: 184 additions & 174 deletions runner/runner.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions runner/runner.proto
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ message WorkspaceReply {
message CreateWorkspaceBlobRequest {
string tfInstance = 1;
string workingDir = 2;
string namespace = 3;
}

message CreateWorkspaceBlobReply {
Expand Down
48 changes: 45 additions & 3 deletions runner/server_workspace_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,22 @@ package runner

import (
context "context"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"os"
"path/filepath"

"github.com/weaveworks/tf-controller/internal/storage"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
)

const EncryptionKeyLength = 32

// CreateWorkspaceBlob archives and compresses using tar and gzip the .terraform directory and returns the tarball as a byte array
func (r *TerraformRunnerServer) CreateWorkspaceBlob(ctx context.Context, req *CreateWorkspaceBlobRequest) (*CreateWorkspaceBlobReply, error) {
log := ctrl.LoggerFrom(ctx).WithName(loggerName)
Expand All @@ -20,6 +27,7 @@ func (r *TerraformRunnerServer) CreateWorkspaceBlob(ctx context.Context, req *Cr
return nil, err
}

log.Info("archiving workspace directory", "dir", req.WorkingDir)
archivePath, err := storage.ArchiveDir(filepath.Join(req.WorkingDir, ".terraform"))
if err != nil {
log.Error(err, "unable to archive .terraform directory")
Expand All @@ -33,14 +41,48 @@ func (r *TerraformRunnerServer) CreateWorkspaceBlob(ctx context.Context, req *Cr
return nil, err
}

secretName := "tf-runner.cache-encryption"
encryptionSecretKey := types.NamespacedName{Name: secretName, Namespace: req.Namespace}
var encryptionSecret v1.Secret

log.Info("fetching secret key", "key", encryptionSecretKey)
if err := r.Client.Get(ctx, encryptionSecretKey, &encryptionSecret); err != nil {
return nil, err
}

// 256 bit AES encryption with Galois Counter Mode.
log.Info("encrypting content")
token := encryptionSecret.Data["token"]
key := token[:EncryptionKeyLength]

aesCipher, err := aes.NewCipher(key)
if err != nil {
return nil, err
}

gcm, err := cipher.NewGCM(aesCipher)
if err != nil {
return nil, err
}

nonce := make([]byte, gcm.NonceSize())
_, err = rand.Read(nonce)
if err != nil {
return nil, err
}

out := gcm.Seal(nonce, nonce, blob, nil)

// SHA256 checksum so we can verify if the saved content is not corrupted.
log.Info("generating sha256 checksum")
sha := sha256.New()
if _, err := sha.Write(blob); err != nil {
return &CreateWorkspaceBlobReply{Blob: blob}, err
if _, err := sha.Write(out); err != nil {
return nil, err
}
sum := sha.Sum(nil)

return &CreateWorkspaceBlobReply{
Blob: blob,
Blob: out,
Sha256Checksum: sum,
}, nil
}
44 changes: 40 additions & 4 deletions runner/server_workspace_blob_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,74 @@ package runner_test

import (
"bytes"
"crypto/aes"
"crypto/cipher"
"os"
"path/filepath"
"testing"

"github.com/fluxcd/pkg/untar"
. "github.com/onsi/gomega"
"github.com/weaveworks/tf-controller/runner"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const encryptionToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJVM0xaLXN3OUJYRFNEejF3THl2X3VvSGxoOWlHdXhYNHdTdV9Vc2w4QjAifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJ0ZXJyYWZvcm0iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlY3JldC5uYW1lIjoidGYtcnVubmVyLWVuY3J5cHRpb24iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoidGYtcnVubmVyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYjJiMTc4MjMtMWI5Yi00YzEzLThhMDctNmE0OThmNjUwYjM3Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OnRlcnJhZm9ybTp0Zi1ydW5uZXIifQ.s2T3_Yd-PNF0dJO-7sP_yKbohCP-GTWrHPACUQs0nQrD3hMjZTXm-CgQdtzuKPN0fPHp_GJ8iDpWrqMRcZSqHKVSXscfCI7-QnGjqwSvt-8KBMGE7J29tFgFy6-K6uvP_kYAaA5i4bDWPXHytLmOHJj7GL_D4-0XXVB3EmCfzwREl19FdjZnmEf8lB4gJ7aOZQnW6FzJHcdzo3bUwh-S0zrjGkGsBbrBBu5hyhCKoyZP1ufn8X9NQfkdtC29rEYgI_6o2gbQrGZRdIujAVgh3HJaU2bodV4sGAdgqsMDHEeoyGzp4LSlSrR2kAYJJznF0bMFY18eojbNXnmoIpkMEQ"

func TestCreateWorkspaceBlob(t *testing.T) {
g := NewGomegaWithT(t)

tempDir := t.TempDir()

encKeySecret := &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "tf-runner.cache-encryption",
Namespace: "flux-system",
Annotations: map[string]string{
"kubernetes.io/service-account.name": "tf-runner",
},
},
Data: map[string][]byte{
"token": []byte(encryptionToken),
},
Type: v1.SecretTypeServiceAccountToken,
}
err := k8sClient.Create(ctx, encKeySecret)
g.Expect(err).To(BeNil())

terraformDir := filepath.Join(tempDir, ".terraform")
err := os.Mkdir(terraformDir, 0755)
err = os.Mkdir(terraformDir, 0755)
g.Expect(err).To(BeNil())

filePath := filepath.Join(terraformDir, "random.txt")
randomContent := []byte("random content")
err = os.WriteFile(filePath, randomContent, 0644)
g.Expect(err).To(BeNil())

resp, err := runnerClient.CreateWorkspaceBlob(ctx, &runner.CreateWorkspaceBlobRequest{TfInstance: "test", WorkingDir: tempDir})
resp, err := runnerClient.CreateWorkspaceBlob(ctx, &runner.CreateWorkspaceBlobRequest{TfInstance: "test", WorkingDir: tempDir, Namespace: "flux-system"})
g.Expect(err).To(BeNil())

g.Expect(resp.GetSha256Checksum()).ToNot(BeEmpty())

blobReader := bytes.NewReader(resp.Blob)
token := encKeySecret.Data["token"]
key := token[:runner.EncryptionKeyLength]

// Decrypting content.
aesCipher, err := aes.NewCipher(key)
g.Expect(err).To(BeNil())
gcm, err := cipher.NewGCM(aesCipher)
g.Expect(err).To(BeNil())
nonceSize := gcm.NonceSize()
nonce, ciphertext := resp.Blob[:nonceSize], resp.Blob[nonceSize:]
blob, err := gcm.Open(nil, nonce, ciphertext, nil)
g.Expect(err).To(BeNil())

blobReader := bytes.NewReader(blob)

outputTempDir := t.TempDir()
untar.Untar(blobReader, outputTempDir)
_, err = untar.Untar(blobReader, outputTempDir)
g.Expect(err).To(BeNil())

outputFilePath := filepath.Join(outputTempDir, ".terraform", "random.txt")
outputContent, err := os.ReadFile(outputFilePath)
Expand Down
8 changes: 7 additions & 1 deletion runner/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/fluxcd/pkg/runtime/logger"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
sourcev1b2 "github.com/fluxcd/source-controller/api/v1beta2"
infrav1 "github.com/weaveworks/tf-controller/api/v1alpha2"
Expand Down Expand Up @@ -63,7 +64,11 @@ var (
func TestMain(m *testing.M) {
var err error

ctrl.SetLogger(zap.New(zap.WriteTo(&logBuffer)))
if os.Getenv("WITH_STDOUT_LOGGER") != "" {
ctrl.SetLogger(logger.NewLogger(logger.Options{}))
} else {
ctrl.SetLogger(zap.New(zap.WriteTo(&logBuffer)))
}

ctx, cancel = context.WithCancel(context.TODO())
// "bootstrapping test environment"
Expand Down Expand Up @@ -126,6 +131,7 @@ func TestMain(m *testing.M) {

k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme,
Logger: ctrl.LoggerFrom(ctx).WithName("controller"),
})
if err != nil {
panic(err.Error())
Expand Down

0 comments on commit 12e53ed

Please sign in to comment.