Fixes #2337: External encryption method (#2367)

Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com>
This commit is contained in:
AbstractionFactory
2025-01-31 18:13:18 +01:00
committed by GitHub
parent 2a4d81042b
commit 60fdd359d5
26 changed files with 1065 additions and 116 deletions

View File

@@ -0,0 +1 @@
{"magic":"OpenTofu-External-Encryption-Method","version":1}

View File

@@ -0,0 +1,4 @@
{
"payload": "base64-encoded payload to encrypt or decrypt",
"key": "base64-encoded key for encryption or decryption (optional)"
}

View File

@@ -0,0 +1,84 @@
package main
import (
"encoding/json"
"io"
"log"
"os"
)
// Header is the initial line that needs to be written as JSON when the program starts.
type Header struct {
Magic string `json:"magic"`
Version int `json:"version"`
}
// Input is the input data received from OpenTofu in response to the header as JSON.
type Input struct {
// Key is the encryption or decryption key, if present.
Key []byte `json:"key,omitempty"`
// Payload is the data to encrypt/decrypt.
Payload []byte `json:"payload"`
}
// Output is the data structure that should be written to the output.
type Output struct {
// Payload is the payload that has been encrypted/decrypted by the external method.
Payload []byte `json:"payload"`
}
func main() {
// Write logs to stderr
log.Default().SetOutput(os.Stderr)
// Write header:
header := Header{
"OpenTofu-External-Encryption-Method",
1,
}
marshalledHeader, err := json.Marshal(header)
if err != nil {
log.Fatalf("%v", err)
}
_, err = os.Stdout.Write(append(marshalledHeader, []byte("\n")...))
if err != nil {
log.Fatalf("Failed to write output: %v", err)
}
// Read input:
input, err := io.ReadAll(os.Stdin)
if err != nil {
log.Fatalf("Failed to read stdin: %v", err)
}
var inputData Input
if err = json.Unmarshal(input, &inputData); err != nil {
log.Fatalf("Failed to parse stdin: %v", err)
}
// Encrypt/decrypt the input and produce the output here.
var outputPayload []byte
if len(os.Args) != 2 {
log.Fatalf("Expected --encrypt or --decrypt")
}
switch os.Args[1] {
case "--encrypt":
// Encrypt the payload
case "--decrypt":
// Decrypt the payload
default:
log.Fatalf("Expected --encrypt or --decrypt")
}
// Write output
output := Output{
Payload: outputPayload,
}
outputData, err := json.Marshal(output)
if err != nil {
log.Fatalf("Failed to stringify output: %v", err)
}
_, err = os.Stdout.Write(outputData)
if err != nil {
log.Fatalf("Failed to write output: %v", err)
}
}

View File

@@ -0,0 +1,40 @@
#!/usr/bin/python
import argparse
import base64
import json
import sys
if __name__ == "__main__":
# Make sure that this program isn't running interactively:
if sys.stdout.isatty():
sys.stderr.write("This is an OpenTofu encryption method and is not meant to be run interactively. "
"Please configure this program in your OpenTofu encryption block to use it.\n")
sys.exit(1)
parser = argparse.ArgumentParser(prog='My External Encryption Method')
parser.add_argument('--encrypt', action='store_true')
parser.add_argument('--decrypt', action='store_true')
args = parser.parse_args()
# Write the header:
sys.stdout.write((json.dumps({"magic": "OpenTofu-External-Encryption-Method", "version": 1}) + "\n"))
sys.stdout.flush()
# Read the input:
inputData = sys.stdin.read()
data = json.loads(inputData)
key = base64.b64decode(data["key"])
payload = base64.b64decode(data["payload"])
# Produce the output payload here.
if args.encrypt:
outputPayload = b''
elif args.decrypt:
outputPayload = b''
else:
raise "Expected --encrypt or --decrypt."
# Write the output:
sys.stdout.write(json.dumps({
"payload": base64.b64encode(outputPayload).decode('ascii'),
}))

View File

@@ -0,0 +1,3 @@
{
"payload": "base64-encoded encrypted or decrypted payload"
}

View File

@@ -0,0 +1,10 @@
terraform {
encryption {
key_provider "external" "foo" {
encrypt_command = ["./some_program", "--encrypt"]
decrypt_command = ["./some_program", "--decrypt"]
# Optional:
keys = key_provider.some.provider
}
}
}