encryption

package
v1.0.0-rc3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 30, 2024 License: MPL-2.0 Imports: 20 Imported by: 0

README

OpenTofu State and Plan encryption

[!WARNING] This file is not an end-user documentation, it is intended for developers. Please follow the user documentation on the OpenTofu website unless you want to work on the encryption code.

This folder contains the code for state and plan encryption. For a quick example on how to use this package, please take a look at the example_test.go file.

Structure

The current folder contains the top level API. It requires a registry for holding the available key providers and encryption methods, which is located in the registry folder. The key providers are located in the keyprovider folder, while the encryption methods are located in the method folder. You can also find the configuration struct and its related functions in the config folder.

Further reading

For a detailed design document on state encryption, please read this document.

Documentation

Overview

Example

This example demonstrates how to use the encryption package to encrypt and decrypt data.

package main

import (
	"fmt"

	"github.com/terramate-io/hcl/v2"
	"github.com/terramate-io/opentofulib/internal/configs"
	"github.com/terramate-io/opentofulib/internal/encryption"
	"github.com/terramate-io/opentofulib/internal/encryption/config"
	"github.com/terramate-io/opentofulib/internal/encryption/keyprovider/static"
	"github.com/terramate-io/opentofulib/internal/encryption/method/aesgcm"
	"github.com/terramate-io/opentofulib/internal/encryption/registry/lockingencryptionregistry"
)

var (
	ConfigA = `
state {
	enforced = true
}
`
	ConfigB = `
key_provider "static" "basic" {
	key = "6f6f706830656f67686f6834616872756f3751756165686565796f6f72653169"
}
method "aes_gcm" "example" {
	keys = key_provider.static.basic
}
state {
	method = method.aes_gcm.example
}
`
)

// This example demonstrates how to use the encryption package to encrypt and decrypt data.
func main() {
	// Construct a new registry
	// the registry is where we store the key providers and methods
	reg := lockingencryptionregistry.New()
	if err := reg.RegisterKeyProvider(static.New()); err != nil {
		panic(err)
	}
	if err := reg.RegisterMethod(aesgcm.New()); err != nil {
		panic(err)
	}

	// Load the 2 different configurations
	cfgA, diags := config.LoadConfigFromString("Test Source A", ConfigA)
	handleDiags(diags)

	cfgB, diags := config.LoadConfigFromString("Test Source B", ConfigB)
	handleDiags(diags)

	// Merge the configurations
	cfg := config.MergeConfigs(cfgA, cfgB)

	// Construct static evaluator to pass additional values into encryption configuration.
	staticEval := configs.NewStaticEvaluator(nil, configs.RootModuleCallForTesting())

	// Construct the encryption object
	enc, diags := encryption.New(reg, cfg, staticEval)
	handleDiags(diags)

	sfe := enc.State()

	// Encrypt the data, for this example we will be using the string `{"serial": 42, "lineage": "magic"}`,
	// but in a real world scenario this would be the plan file.
	sourceData := []byte(`{"serial": 42, "lineage": "magic"}`)
	encrypted, err := sfe.EncryptState(sourceData)
	if err != nil {
		panic(err)
	}

	if string(encrypted) == `{"serial": 42, "lineage": "magic"}` {
		panic("The data has not been encrypted!")
	}

	// Decrypt
	decryptedState, err := sfe.DecryptState(encrypted)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%s\n", decryptedState)
}

func handleDiags(diags hcl.Diagnostics) {
	for _, d := range diags {
		println(d.Error())
	}
	if diags.HasErrors() {
		panic(diags.Error())
	}
}
Output:

{"serial": 42, "lineage": "magic"}

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultRegistry = lockingencryptionregistry.New()

Functions

func IsEncryptionPayload

func IsEncryptionPayload(data []byte) (bool, error)

Types

type Encryption

type Encryption interface {
	// State produces a StateEncryption overlay for encrypting and decrypting state files for local storage.
	State() StateEncryption

	// Plan produces a PlanEncryption overlay for encrypting and decrypting plan files.
	Plan() PlanEncryption

	// RemoteState produces a StateEncryption for reading remote states using the terraform_remote_state data
	// source.
	RemoteState(string) StateEncryption
}

Encryption contains the methods for obtaining a StateEncryption or PlanEncryption correctly configured for a specific purpose. If no encryption configuration is present, it should return a pass through method that doesn't do anything.

func Disabled

func Disabled() Encryption

func New

func New(reg registry.Registry, cfg *config.EncryptionConfig, staticEval *configs.StaticEvaluator) (Encryption, hcl.Diagnostics)

New creates a new Encryption provider from the given configuration and registry.

type PlanEncryption

type PlanEncryption interface {
	// EncryptPlan encrypts a plan file and returns the encrypted form.
	//
	// When implementing this function:
	//
	// Plan files are opaque values. You may expect a valid plan file on the input, but you can produce binary data
	// that is not necessarily a valid plan file. If no encryption is configured, this function should pass through
	// any data it receives without modification, even if the plan file is invalid.
	//
	// When using this function:
	//
	// Make sure that you pass a valid plan file as an input. Failing to provide a valid plan file may result in an
	// error. However, output values may not be valid plan files and you should not pass the encrypted plan file to any
	// additional functions that normally work with plan files.
	EncryptPlan([]byte) ([]byte, error)

	// DecryptPlan decrypts an encrypted plan file.
	//
	// When implementing this function:
	//
	// If the user has configured no encryption, pass through any input unmodified regardless if the input is a valid
	// plan file. If the user configured encryption, decrypt the plan file and return the decrypted plan file as a
	// binary without further evaluating its validity.
	//
	// When using this function:
	//
	// Pass a potentially encrypted plan file as an input, and you will receive the decrypted plan file or an error as
	// a result.
	DecryptPlan([]byte) ([]byte, error)
}

PlanEncryption describes the methods that you can use for encrypting a plan file. Plan files are opaque values with no standardized format, so the encrypted form should be treated equally an opaque value.

func PlanEncryptionDisabled

func PlanEncryptionDisabled() PlanEncryption

type StateEncryption

type StateEncryption interface {
	// DecryptState decrypts a potentially encrypted state file and returns a valid JSON-serialized state file.
	//
	// When implementing this function:
	//
	// If the user configured no encryption, also return the input as-is regardless if the state file is valid. If the
	// user configured encryption unserialize the input as JSON and check for the presence of the field specified in the
	// StateEncryptionMarkerField. If the field is not present, return the input as-is and return a warning that an
	// unexpected unencrypted state file was encountered. Otherwise, decrypt the state file and return the decrypted
	// state file as serialized JSON. If the state file cannot be decrypted, return an error in the diagnostics.
	//
	// When using this function:
	//
	// After reading the state file from its source (local file, remote backend, etc.), pass in the state file to this
	// function. Do not attempt to determine if the state file is encrypted as this function will take care of any
	// and all encryption-related matters. After the function returns, use the returned byte array as a normal state
	// file.
	DecryptState([]byte) ([]byte, error)

	// EncryptState encrypts a state file and returns the encrypted form.
	//
	// When implementing this function:
	//
	// The file should take a JSON-serialized state file as an input and encrypt it according to the configuration.
	// The encrypted form should also return a JSON which contains, at least, the key specified in
	// StateEncryptionMarkerField to identify the state file as encrypted. This is necessary because some backends
	// expect a state file to always be a JSON file.
	//
	// If the user configured no encryption, this function should be a no-op and return the input unchanged. If the
	// input is not a valid state file, this function should return an error in the diagnostics return.
	//
	// When using this function:
	//
	// Pass in a valid JSON-serialized state file as an input and store the output. Note that you should not pass the
	// output to any additional functions that require a valid state file as it may not contain the fields typically
	// present in a state file.
	EncryptState([]byte) ([]byte, error)
}

StateEncryption describes the interface for encrypting state files.

func StateEncryptionDisabled

func StateEncryptionDisabled() StateEncryption

Directories

Path Synopsis
pbkdf2
Package pbkdf2 contains a key provider that takes a passphrase and emits a PBKDF2 hash of the configured length.
Package pbkdf2 contains a key provider that takes a passphrase and emits a PBKDF2 hash of the configured length.
static
Package static contains a key provider that emits a static key.
Package static contains a key provider that emits a static key.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL