mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 01:00:16 -05:00
Signed-off-by: AbstractionFactory <179820029+abstractionfactory@users.noreply.github.com> Signed-off-by: ollevche <ollevche@gmail.com> Co-authored-by: Oleksandr Levchenkov <ollevche@gmail.com>
This commit is contained in:
committed by
GitHub
parent
ec20752054
commit
5a6d2d3e98
@@ -0,0 +1 @@
|
||||
{"magic":"OpenTofu-External-Key-Provider","version":1}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"external_data": {
|
||||
"key1": "value1",
|
||||
"key2": "value2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"key": {
|
||||
"encryption_key": "newly generated base64-encoded encryption key",
|
||||
"decryption_key": "base64-encoded decryption key, if input meta was present, omitted otherwise"
|
||||
},
|
||||
"meta": {
|
||||
"external_data": {
|
||||
"key1": "value1",
|
||||
"key2": "value2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Header is the initial greeting the key provider sends out.
|
||||
type Header struct {
|
||||
// Magic must always be OpenTofu-External-Keyprovider
|
||||
Magic string `json:"magic"`
|
||||
// Version must be 1.
|
||||
Version int `json:"version"`
|
||||
}
|
||||
|
||||
// Metadata describes both the input and the output metadata.
|
||||
type Metadata struct {
|
||||
ExternalData map[string]any `json:"external_data"`
|
||||
}
|
||||
|
||||
// Input describes the input data structure. This is nil on input if no existing
|
||||
// data needs to be decrypted.
|
||||
type Input *Metadata
|
||||
|
||||
// Output describes the output data written to stdout.
|
||||
type Output struct {
|
||||
Key struct {
|
||||
// EncryptionKey must always be provided.
|
||||
EncryptionKey []byte `json:"encryption_key,omitempty"`
|
||||
// DecryptionKey must be provided when the input metadata is present.
|
||||
DecryptionKey []byte `json:"decryption_key,omitempty"`
|
||||
} `json:"key"`
|
||||
// Meta contains the metadata to store alongside the encrypted data. You can
|
||||
// store data here you need to reconstruct the decryption key later.
|
||||
Meta Metadata `json:"meta"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Write logs to stderr
|
||||
log.Default().SetOutput(os.Stderr)
|
||||
|
||||
// Write the header:
|
||||
header := Header{
|
||||
"OpenTofu-External-Key-Provider",
|
||||
1,
|
||||
}
|
||||
marshalledHeader, err := json.Marshal(header)
|
||||
if err != nil {
|
||||
log.Fatalf("%v", err)
|
||||
}
|
||||
_, _ = os.Stdout.Write(append(marshalledHeader, []byte("\n")...))
|
||||
|
||||
// Read the input
|
||||
input, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read stdin: %v", err)
|
||||
}
|
||||
var inMeta Input
|
||||
if err := json.Unmarshal(input, &inMeta); err != nil {
|
||||
log.Fatalf("Failed to parse stdin: %v", err)
|
||||
}
|
||||
|
||||
// TODO produce the encryption key
|
||||
if inMeta != nil {
|
||||
// TODO produce decryption key
|
||||
}
|
||||
|
||||
output := Output{
|
||||
// TODO: produce output
|
||||
}
|
||||
outputData, err := json.Marshal(output)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to encode output: %v", err)
|
||||
}
|
||||
_, _ = os.Stdout.Write(outputData)
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import base64
|
||||
import json
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Write the header:
|
||||
sys.stdout.write((json.dumps(
|
||||
{"magic": "OpenTofu-External-Key-Provider", "version": 1}) + "\n"
|
||||
))
|
||||
|
||||
# Read the input:
|
||||
inputData = sys.stdin.read()
|
||||
data = json.loads(inputData)
|
||||
|
||||
# Construct the key:
|
||||
key = b''
|
||||
for i in range(1, 17):
|
||||
key += chr(i).encode('ascii')
|
||||
|
||||
# Output the keys:
|
||||
if data is None:
|
||||
# No input metadata was passed, we shouldn't output a decryption key.
|
||||
# If needed, we can produce an output metadata here, which will be
|
||||
# stored alongside the encrypted data.
|
||||
outputMeta = {"external_data":{}}
|
||||
sys.stdout.write(json.dumps({
|
||||
"keys": {
|
||||
"encryption_key": base64.b64encode(key).decode('ascii')
|
||||
},
|
||||
"meta": outputMeta
|
||||
}))
|
||||
else:
|
||||
# We had some input metadata, output a decryption key. In a real-life
|
||||
# scenario we would use the metadata for something like pbdkf2.
|
||||
inputMeta = data["external_data"]
|
||||
# Do something with the input metadata if needed and produce the output
|
||||
# metadata:
|
||||
outputMeta = {"external_data":{}}
|
||||
sys.stdout.write(json.dumps({
|
||||
"keys": {
|
||||
"encryption_key": base64.b64encode(key).decode('ascii'),
|
||||
"decryption_key": base64.b64encode(key).decode('ascii')
|
||||
},
|
||||
"meta": outputMeta
|
||||
}))
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Output the header as a single line:
|
||||
echo '{"magic":"OpenTofu-External-Key-Provider","version":1}'
|
||||
|
||||
# Read the input metadata.
|
||||
INPUT=$(echo -n $(cat))
|
||||
|
||||
if [ "${INPUT}" = "null" ]; then
|
||||
# We don't have metadata and shouldn't output a decryption key.
|
||||
cat << EOF
|
||||
{
|
||||
"keys":{
|
||||
"encryption_key":"AQIDBAUGBwgJCgsMDQ4PEA=="
|
||||
},
|
||||
"meta":{
|
||||
"external_data":{}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
else
|
||||
|
||||
# We have metadata and should output a decryption key. In our simplified case
|
||||
# it is the same as the encryption key.
|
||||
cat << EOF
|
||||
{
|
||||
"keys":{
|
||||
"encryption_key":"AQIDBAUGBwgJCgsMDQ4PEA==",
|
||||
"decryption_key":"AQIDBAUGBwgJCgsMDQ4PEA=="
|
||||
},
|
||||
"meta":{
|
||||
"external_data":{}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
fi
|
||||
@@ -0,0 +1,7 @@
|
||||
terraform {
|
||||
encryption {
|
||||
key_provider "externalcommand" "foo" {
|
||||
command = ["./some_program", "some_parameter"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@ terraform {
|
||||
# Specify a long / complex passphrase (min. 16 characters)
|
||||
passphrase = "correct-horse-battery-staple"
|
||||
|
||||
# Alternatively, receive the passphrase from another key provider:
|
||||
chain = key_provider.other.provider
|
||||
|
||||
# Adjust the key length to the encryption method (default: 32)
|
||||
key_length = 32
|
||||
|
||||
|
||||
Reference in New Issue
Block a user