Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use memguard for secrets #286

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
422 changes: 422 additions & 0 deletions NOTICE

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions cmd/config-gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
"strings"

"github.com/Seagate/cloudfuse/common"
"github.com/awnumar/memguard"
"github.com/spf13/cobra"
)

Expand All @@ -40,7 +41,7 @@ type configGenOptions struct {
outputConfigPath string
containerName string
tempDirPath string
passphrase string
passphrase []byte
}

var opts configGenOptions
Expand Down Expand Up @@ -112,6 +113,8 @@ var generateConfig = &cobra.Command{
var templateConfig []byte
var err error

encryptedPassphrase = memguard.NewEnclave(opts.passphrase)

templateConfig, err = os.ReadFile(opts.configFilePath)
if err != nil {
return fmt.Errorf("failed to read file [%s]", err.Error())
Expand All @@ -134,7 +137,7 @@ var generateConfig = &cobra.Command{
}
}

cipherText, err := common.EncryptData([]byte(newConfig), opts.passphrase)
cipherText, err := common.EncryptData([]byte(newConfig), encryptedPassphrase)
if err != nil {
return err
}
Expand All @@ -160,6 +163,6 @@ func init() {
generateConfig.Flags().StringVar(&opts.configFilePath, "config-file", "", "Input config file.")
generateConfig.Flags().StringVar(&opts.outputConfigPath, "output-file", "", "Output config file path.")
generateConfig.Flags().StringVar(&opts.tempDirPath, "temp-path", "", "Temporary file path.")
generateConfig.Flags().StringVar(&opts.passphrase, "passphrase", "",
generateConfig.Flags().BytesBase64Var(&opts.passphrase, "passphrase", nil,
"Key to be used for encryption / decryption. Key length shall be 16 (AES-128), 24 (AES-192), or 32 (AES-256) bytes in length.")
}
9 changes: 6 additions & 3 deletions cmd/config-gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ package cmd

import (
"bytes"
"encoding/base64"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -124,6 +125,7 @@ func (suite *genConfigTestSuite) TestGenConfig() {
defer suite.cleanupTest()
confFile, _ := os.CreateTemp("", "conf*.yaml")
outFile := "config_encrypted.aes"
passphrase := base64.StdEncoding.EncodeToString([]byte("12312312312312312312312312312312"))

defer os.Remove(confFile.Name())
defer os.Remove(outFile)
Expand All @@ -133,7 +135,7 @@ func (suite *genConfigTestSuite) TestGenConfig() {

confFile.Close()

_, err = executeCommandGen(rootCmd, "gen-config", fmt.Sprintf("--config-file=%s", confFile.Name()), "--passphrase=12312312312312312312312312312312", fmt.Sprintf("--output-file=%s", outFile), "--temp-path=/tmp")
_, err = executeCommandGen(rootCmd, "gen-config", fmt.Sprintf("--config-file=%s", confFile.Name()), fmt.Sprintf("--passphrase=%s", passphrase), fmt.Sprintf("--output-file=%s", outFile), "--temp-path=/tmp")
suite.assert.NoError(err)

// Out file should exist
Expand All @@ -144,6 +146,7 @@ func (suite *genConfigTestSuite) TestGenConfigGet() {
defer suite.cleanupTest()
confFile, _ := os.CreateTemp("", "conf*.yaml")
outFile := "config_encrypted.aes"
passphrase := base64.StdEncoding.EncodeToString([]byte("12312312312312312312312312312312"))
Ferelith-maker marked this conversation as resolved.
Show resolved Hide resolved

defer os.Remove(confFile.Name())
defer os.Remove(outFile)
Expand All @@ -153,14 +156,14 @@ func (suite *genConfigTestSuite) TestGenConfigGet() {

confFile.Close()

_, err = executeCommandGen(rootCmd, "gen-config", fmt.Sprintf("--config-file=%s", confFile.Name()), "--passphrase=12312312312312312312312312312312", fmt.Sprintf("--output-file=%s", outFile), "--temp-path=/tmp")
_, err = executeCommandGen(rootCmd, "gen-config", fmt.Sprintf("--config-file=%s", confFile.Name()), fmt.Sprintf("--passphrase=%s", passphrase), fmt.Sprintf("--output-file=%s", outFile), "--temp-path=/tmp")
suite.assert.NoError(err)

// Out file should exist
suite.assert.FileExists(outFile)

// Gen-config should correctly set the temp path for the file_cache
path, err := executeCommandGen(rootCmd, "secure", "get", fmt.Sprintf("--config-file=%s", outFile), "--passphrase=12312312312312312312312312312312", "--key=file_cache.path")
path, err := executeCommandGen(rootCmd, "secure", "get", fmt.Sprintf("--config-file=%s", outFile), fmt.Sprintf("--passphrase=%s", passphrase), "--key=file_cache.path")
suite.assert.NoError(err)
suite.assert.Equal("Fetching scalar configuration\nfile_cache.path = /tmp\n", path)
}
26 changes: 17 additions & 9 deletions cmd/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package cmd
import (
"bytes"
"context"
"encoding/base64"
"errors"
"fmt"
"io/fs"
Expand All @@ -46,6 +47,7 @@ import (
"github.com/Seagate/cloudfuse/common/config"
"github.com/Seagate/cloudfuse/common/log"
"github.com/Seagate/cloudfuse/internal"
"github.com/awnumar/memguard"

"github.com/sevlyar/go-daemon"
"github.com/spf13/cobra"
Expand All @@ -72,7 +74,7 @@ type mountOptions struct {
DefaultWorkingDir string `config:"default-working-dir"`
CPUProfile string `config:"cpu-profile"`
MemProfile string `config:"mem-profile"`
PassPhrase string `config:"passphrase"`
PassPhrase []byte `config:"passphrase"`
SecureConfig bool `config:"secure-config"`
DynamicProfiler bool `config:"dynamic-profile"`
ProfilerPort int `config:"profiler-port"`
Expand Down Expand Up @@ -225,26 +227,32 @@ func parseConfig() error {
filepath.Ext(options.ConfigFile) == SecureConfigExtension {

// Validate config is to be secured on write or not
if options.PassPhrase == "" {
options.PassPhrase = os.Getenv(SecureConfigEnvName)
}
if options.PassPhrase == nil || string(options.PassPhrase) == "" {
options.PassPhrase = []byte(os.Getenv(SecureConfigEnvName))
if options.PassPhrase == nil || string(options.PassPhrase) == "" {
return errors.New("no passphrase provided to decrypt the config file.\n Either use --passphrase cli option or store passphrase in CLOUDFUSE_SECURE_CONFIG_PASSPHRASE environment variable")
}

if options.PassPhrase == "" {
return fmt.Errorf("no passphrase provided to decrypt the config file.\n Either use --passphrase cli option or store passphrase in CLOUDFUSE_SECURE_CONFIG_PASSPHRASE environment variable")
_, err := base64.StdEncoding.DecodeString(string(options.PassPhrase))
if err != nil {
return fmt.Errorf("passphrase is not valid base64 encoded [%s]", err.Error())
}
}

encryptedPassphrase = memguard.NewEnclave(options.PassPhrase)

cipherText, err := os.ReadFile(options.ConfigFile)
if err != nil {
return fmt.Errorf("failed to read encrypted config file %s [%s]", options.ConfigFile, err.Error())
}

plainText, err := common.DecryptData(cipherText, options.PassPhrase)
plainText, err := common.DecryptData(cipherText, encryptedPassphrase)
if err != nil {
return fmt.Errorf("failed to decrypt config file %s [%s]", options.ConfigFile, err.Error())
}

config.SetConfigFile(options.ConfigFile)
config.SetSecureConfigOptions(options.PassPhrase)
config.SetSecureConfigOptions(encryptedPassphrase)
err = config.ReadFromConfigBuffer(plainText)
if err != nil {
return fmt.Errorf("invalid decrypted config file [%s]", err.Error())
Expand Down Expand Up @@ -681,7 +689,7 @@ func init() {
mountCmd.PersistentFlags().BoolVar(&options.SecureConfig, "secure-config", false,
"Encrypt auto generated config file for each container")

mountCmd.PersistentFlags().StringVar(&options.PassPhrase, "passphrase", "",
mountCmd.PersistentFlags().BytesBase64Var(&options.PassPhrase, "passphrase", []byte(""),
"Base64 encoded key to decrypt config file. Can also be specified by env-variable CLOUDFUSE_SECURE_CONFIG_PASSPHRASE.\n Decoded key length shall be 16 (AES-128), 24 (AES-192), or 32 (AES-256) bytes in length.")

mountCmd.PersistentFlags().String("log-type", "syslog", "Type of logger to be used by the system. Set to syslog by default. Allowed values are silent|syslog|base.")
Expand Down
26 changes: 20 additions & 6 deletions cmd/mount_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ package cmd
import (
"bytes"
"context"
"encoding/base64"
"errors"
"fmt"
"os"
"os/exec"
Expand All @@ -38,6 +40,7 @@ import (
"github.com/Seagate/cloudfuse/common"
"github.com/Seagate/cloudfuse/common/config"
"github.com/Seagate/cloudfuse/common/log"
"github.com/awnumar/memguard"

"github.com/Seagate/cloudfuse/component/azstorage"
"github.com/Seagate/cloudfuse/component/s3storage"
Expand Down Expand Up @@ -155,12 +158,23 @@ func processCommand() error {
}

// Validate config is to be secured on write or not
if options.PassPhrase == "" {
options.PassPhrase = os.Getenv(SecureConfigEnvName)
}
if options.SecureConfig ||
filepath.Ext(options.ConfigFile) == SecureConfigExtension {

// Validate config is to be secured on write or not
if options.PassPhrase == nil || string(options.PassPhrase) == "" {
options.PassPhrase = []byte(os.Getenv(SecureConfigEnvName))
if options.PassPhrase == nil || string(options.PassPhrase) == "" {
return errors.New("no passphrase provided to decrypt the config file.\n Either use --passphrase cli option or store passphrase in CLOUDFUSE_SECURE_CONFIG_PASSPHRASE environment variable")
}

_, err := base64.StdEncoding.DecodeString(string(options.PassPhrase))
if err != nil {
return fmt.Errorf("passphrase is not valid base64 encoded [%s]", err.Error())
}
}

if options.SecureConfig && options.PassPhrase == "" {
return fmt.Errorf("key not provided to decrypt config file")
encryptedPassphrase = memguard.NewEnclave(options.PassPhrase)
}

var containerList []string
Expand Down Expand Up @@ -395,7 +409,7 @@ func writeConfigFile(contConfigFile string) error {
return fmt.Errorf("failed to marshall yaml content")
}

cipherText, err := common.EncryptData(confStream, opts.passphrase)
cipherText, err := common.EncryptData(confStream, encryptedPassphrase)
if err != nil {
return fmt.Errorf("failed to encrypt yaml content [%s]", err.Error())
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/mount_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func createDaemon(pipeline *internal.Pipeline, ctx context.Context, pidFileName

// Use WinFSP to mount and if successful, add instance to persistent mount list
func createMountInstance() error {
err := winservice.StartMount(options.MountPath, options.ConfigFile, options.PassPhrase)
err := winservice.StartMount(options.MountPath, options.ConfigFile, encryptedPassphrase)
if err != nil {
return err
}
Expand Down
27 changes: 15 additions & 12 deletions cmd/secure.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,15 @@ import (
"path/filepath"

"github.com/Seagate/cloudfuse/common"
"github.com/awnumar/memguard"

"github.com/spf13/cobra"
)

type secureOptions struct {
Operation string
ConfigFile string
PassPhrase string
PassPhrase []byte
OutputFile string
Key string
Value string
Expand All @@ -50,6 +51,7 @@ const SecureConfigEnvName string = "CLOUDFUSE_SECURE_CONFIG_PASSPHRASE"
const SecureConfigExtension string = ".aes"

var secOpts secureOptions
var encryptedPassphrase *memguard.Enclave

// Section defining all the command that we have in secure feature
var secureCmd = &cobra.Command{
Expand Down Expand Up @@ -116,15 +118,20 @@ var decryptCmd = &cobra.Command{
//--------------- command section ends

func validateOptions() error {
if secOpts.PassPhrase == "" {
secOpts.PassPhrase = os.Getenv(SecureConfigEnvName)
if secOpts.PassPhrase == nil || string(secOpts.PassPhrase) == "" {
secOpts.PassPhrase = []byte(os.Getenv(SecureConfigEnvName))
if secOpts.PassPhrase == nil || string(secOpts.PassPhrase) == "" {
return errors.New("provide the passphrase as a cli parameter or configure the CLOUDFUSE_SECURE_CONFIG_PASSPHRASE environment variable")
}
}

_, err := base64.StdEncoding.DecodeString(secOpts.PassPhrase)
_, err := base64.StdEncoding.DecodeString(string(secOpts.PassPhrase))
if err != nil {
return fmt.Errorf("failed to base64 decode passphrase [%s]", err.Error())
return fmt.Errorf("passphrase is not valid base64 encoded [%s]", err.Error())
}

encryptedPassphrase = memguard.NewEnclave(secOpts.PassPhrase)

if secOpts.ConfigFile == "" {
return errors.New("config file not provided, check usage")
}
Expand All @@ -133,10 +140,6 @@ func validateOptions() error {
return errors.New("config file does not exist")
}

if secOpts.PassPhrase == "" {
return errors.New("provide the passphrase as a cli parameter or configure the CLOUDFUSE_SECURE_CONFIG_PASSPHRASE environment variable")
}

return nil
}

Expand All @@ -147,7 +150,7 @@ func encryptConfigFile(saveConfig bool) ([]byte, error) {
return nil, err
}

cipherText, err := common.EncryptData(plaintext, secOpts.PassPhrase)
cipherText, err := common.EncryptData(plaintext, encryptedPassphrase)
if err != nil {
return nil, err
}
Expand All @@ -173,7 +176,7 @@ func decryptConfigFile(saveConfig bool) ([]byte, error) {
return nil, err
}

plainText, err := common.DecryptData(cipherText, secOpts.PassPhrase)
plainText, err := common.DecryptData(cipherText, encryptedPassphrase)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -231,7 +234,7 @@ func init() {
secureCmd.PersistentFlags().StringVar(&secOpts.ConfigFile, "config-file", "",
"Configuration file to be encrypted / decrypted")

secureCmd.PersistentFlags().StringVar(&secOpts.PassPhrase, "passphrase", "",
secureCmd.PersistentFlags().BytesBase64Var(&secOpts.PassPhrase, "passphrase", []byte(""),
"Base64 encoded key to decrypt config file. Can also be specified by env-variable CLOUDFUSE_SECURE_CONFIG_PASSPHRASE.\n Decoded key length shall be 16 (AES-128), 24 (AES-192), or 32 (AES-256) bytes in length.")

secureCmd.PersistentFlags().StringVar(&secOpts.OutputFile, "output-file", "",
Expand Down
2 changes: 1 addition & 1 deletion cmd/secure_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ var setKeyCmd = &cobra.Command{
return fmt.Errorf("failed to marshal config [%s]", err.Error())
}

cipherText, err := common.EncryptData(confStream, secOpts.PassPhrase)
cipherText, err := common.EncryptData(confStream, encryptedPassphrase)
if err != nil {
return fmt.Errorf("failed to encrypt config [%s]", err.Error())
}
Expand Down
Loading
Loading