mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-05 05:26:25 +00:00
feat: migrated the operator code to v4
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func decodeBase64ToBytes(encodedString string) string {
|
||||
decoded, err := base64.StdEncoding.DecodeString(encodedString)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
return string(decoded)
|
||||
}
|
||||
|
||||
func encodeBase64(plainString string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(plainString))
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||
)
|
||||
|
||||
func jwkPublicKeyPem(jwkjson string) string {
|
||||
k, err := jwk.ParseKey([]byte(jwkjson))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err))
|
||||
}
|
||||
var rawkey any
|
||||
err = k.Raw(&rawkey)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err))
|
||||
}
|
||||
mpk, err := x509.MarshalPKIXPublicKey(rawkey)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err))
|
||||
}
|
||||
return pemEncode(mpk, "PUBLIC KEY")
|
||||
}
|
||||
|
||||
func jwkPrivateKeyPem(jwkjson string) string {
|
||||
k, err := jwk.ParseKey([]byte(jwkjson))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err))
|
||||
}
|
||||
var mpk []byte
|
||||
var pk any
|
||||
err = k.Raw(&pk)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err))
|
||||
}
|
||||
mpk, err = x509.MarshalPKCS8PrivateKey(pk)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err))
|
||||
}
|
||||
return pemEncode(mpk, "PRIVATE KEY")
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
errJunk = "error filtering pem: found junk"
|
||||
|
||||
certTypeLeaf = "leaf"
|
||||
certTypeIntermediate = "intermediate"
|
||||
certTypeRoot = "root"
|
||||
)
|
||||
|
||||
func filterPEM(pemType, input string) string {
|
||||
data := []byte(input)
|
||||
var blocks []byte
|
||||
var block *pem.Block
|
||||
var rest []byte
|
||||
for {
|
||||
block, rest = pem.Decode(data)
|
||||
data = rest
|
||||
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if !strings.EqualFold(block.Type, pemType) {
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := pem.Encode(&buf, block)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[filterPEM] Error: %v", err))
|
||||
}
|
||||
blocks = append(blocks, buf.Bytes()...)
|
||||
}
|
||||
|
||||
if len(blocks) == 0 && len(rest) != 0 {
|
||||
panic(fmt.Sprintf("[filterPEM] Error: %v", errJunk))
|
||||
}
|
||||
|
||||
return string(blocks)
|
||||
}
|
||||
|
||||
func filterCertChain(certType, input string) string {
|
||||
ordered := fetchX509CertChains([]byte(input))
|
||||
|
||||
switch certType {
|
||||
case certTypeLeaf:
|
||||
cert := ordered[0]
|
||||
if cert.AuthorityKeyId != nil && !bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) {
|
||||
return pemEncode(ordered[0].Raw, pemTypeCertificate)
|
||||
}
|
||||
case certTypeIntermediate:
|
||||
if len(ordered) < 2 {
|
||||
return ""
|
||||
}
|
||||
var pemData []byte
|
||||
for _, cert := range ordered[1:] {
|
||||
if isRootCertificate(cert) {
|
||||
break
|
||||
}
|
||||
b := &pem.Block{
|
||||
Type: pemTypeCertificate,
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
pemData = append(pemData, pem.EncodeToMemory(b)...)
|
||||
}
|
||||
return string(pemData)
|
||||
case certTypeRoot:
|
||||
cert := ordered[len(ordered)-1]
|
||||
if isRootCertificate(cert) {
|
||||
return pemEncode(cert.Raw, pemTypeCertificate)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func isRootCertificate(cert *x509.Certificate) bool {
|
||||
return cert.AuthorityKeyId == nil || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId)
|
||||
}
|
||||
|
||||
func pemEncode(thing []byte, kind string) string {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
err := pem.Encode(buf, &pem.Block{Type: kind, Bytes: thing})
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[pemEncode] Error: %v", err))
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
errNilCert = "certificate is nil"
|
||||
errFoundDisjunctCert = "found multiple leaf or disjunct certificates"
|
||||
errNoLeafFound = "no leaf certificate found"
|
||||
errChainCycle = "constructing chain resulted in cycle"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
cert *x509.Certificate
|
||||
parent *node
|
||||
isParent bool
|
||||
}
|
||||
|
||||
func fetchX509CertChains(data []byte) []*x509.Certificate {
|
||||
var newCertChain []*x509.Certificate
|
||||
nodes := pemToNodes(data)
|
||||
|
||||
// at the end of this computation, the output will be a single linked list
|
||||
// the tail of the list will be the root node (which has no parents)
|
||||
// the head of the list will be the leaf node (whose parent will be intermediate certs)
|
||||
// (head) leaf -> intermediates -> root (tail)
|
||||
for i := range nodes {
|
||||
for j := range nodes {
|
||||
// ignore same node to prevent generating a cycle
|
||||
if i == j {
|
||||
continue
|
||||
}
|
||||
// if ith node AuthorityKeyId is same as jth node SubjectKeyId, jth node was used
|
||||
// to sign the ith certificate
|
||||
if bytes.Equal(nodes[i].cert.AuthorityKeyId, nodes[j].cert.SubjectKeyId) {
|
||||
nodes[j].isParent = true
|
||||
nodes[i].parent = nodes[j]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var foundLeaf bool
|
||||
var leaf *node
|
||||
for i := range nodes {
|
||||
if !nodes[i].isParent {
|
||||
if foundLeaf {
|
||||
panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errFoundDisjunctCert))
|
||||
}
|
||||
// this is the leaf node as it's not a parent for any other node
|
||||
leaf = nodes[i]
|
||||
foundLeaf = true
|
||||
}
|
||||
}
|
||||
|
||||
if leaf == nil {
|
||||
panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errNoLeafFound))
|
||||
}
|
||||
|
||||
processedNodes := 0
|
||||
// iterate through the directed list and append the nodes to new cert chain
|
||||
for leaf != nil {
|
||||
processedNodes++
|
||||
// ensure we aren't stuck in a cyclic loop
|
||||
if processedNodes > len(nodes) {
|
||||
panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errChainCycle))
|
||||
}
|
||||
newCertChain = append(newCertChain, leaf.cert)
|
||||
leaf = leaf.parent
|
||||
}
|
||||
return newCertChain
|
||||
}
|
||||
|
||||
func fetchCertChains(data []byte) []byte {
|
||||
var pemData []byte
|
||||
newCertChain := fetchX509CertChains(data)
|
||||
|
||||
for _, cert := range newCertChain {
|
||||
b := &pem.Block{
|
||||
Type: pemTypeCertificate,
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
pemData = append(pemData, pem.EncodeToMemory(b)...)
|
||||
}
|
||||
return pemData
|
||||
}
|
||||
|
||||
func pemToNodes(data []byte) []*node {
|
||||
nodes := make([]*node, 0)
|
||||
for {
|
||||
// decode pem to der first
|
||||
block, rest := pem.Decode(data)
|
||||
data = rest
|
||||
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("[pemToNodes] Error: %v", err))
|
||||
}
|
||||
|
||||
if cert == nil {
|
||||
panic(fmt.Sprintf("[pemToNodes] Error: %v", errNilCert))
|
||||
}
|
||||
nodes = append(nodes, &node{
|
||||
cert: cert,
|
||||
parent: nil,
|
||||
isParent: false,
|
||||
})
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
gopkcs12 "software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
func pkcs12keyPass(pass, input string) string {
|
||||
privateKey, _, _, err := gopkcs12.DecodeChain([]byte(input), pass)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
marshalPrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := pem.Encode(&buf, &pem.Block{
|
||||
Type: pemTypeKey,
|
||||
Bytes: marshalPrivateKey,
|
||||
}); err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func parsePrivateKey(block []byte) any {
|
||||
if k, err := x509.ParsePKCS1PrivateKey(block); err == nil {
|
||||
return k
|
||||
}
|
||||
if k, err := x509.ParsePKCS8PrivateKey(block); err == nil {
|
||||
return k
|
||||
}
|
||||
if k, err := x509.ParseECPrivateKey(block); err == nil {
|
||||
return k
|
||||
}
|
||||
panic("Error: unable to parse private key")
|
||||
}
|
||||
|
||||
func pkcs12key(input string) string {
|
||||
return pkcs12keyPass("", input)
|
||||
}
|
||||
|
||||
func pkcs12certPass(pass, input string) string {
|
||||
_, certificate, caCerts, err := gopkcs12.DecodeChain([]byte(input), pass)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
var pemData []byte
|
||||
var buf bytes.Buffer
|
||||
if err := pem.Encode(&buf, &pem.Block{
|
||||
Type: pemTypeCertificate,
|
||||
Bytes: certificate.Raw,
|
||||
}); err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
pemData = append(pemData, buf.Bytes()...)
|
||||
|
||||
for _, ca := range caCerts {
|
||||
var buf bytes.Buffer
|
||||
if err := pem.Encode(&buf, &pem.Block{
|
||||
Type: pemTypeCertificate,
|
||||
Bytes: ca.Raw,
|
||||
}); err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
pemData = append(pemData, buf.Bytes()...)
|
||||
}
|
||||
|
||||
// try to order certificate chain. If it fails we return
|
||||
// the unordered raw pem data.
|
||||
// This fails if multiple leaf or disjunct certs are provided.
|
||||
ordered := fetchCertChains(pemData)
|
||||
|
||||
return string(ordered)
|
||||
}
|
||||
|
||||
func pkcs12cert(input string) string {
|
||||
return pkcs12certPass("", input)
|
||||
}
|
||||
|
||||
func pemToPkcs12(cert, key string) string {
|
||||
return pemToPkcs12Pass(cert, key, "")
|
||||
}
|
||||
|
||||
func pemToPkcs12Pass(cert, key, pass string) string {
|
||||
certPem, _ := pem.Decode([]byte(cert))
|
||||
|
||||
parsedCert, err := x509.ParseCertificate(certPem.Bytes)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
return certsToPkcs12(parsedCert, key, nil, pass)
|
||||
}
|
||||
|
||||
func fullPemToPkcs12(cert, key string) string {
|
||||
return fullPemToPkcs12Pass(cert, key, "")
|
||||
}
|
||||
|
||||
func fullPemToPkcs12Pass(cert, key, pass string) string {
|
||||
certPem, rest := pem.Decode([]byte(cert))
|
||||
|
||||
parsedCert, err := x509.ParseCertificate(certPem.Bytes)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
caCerts := make([]*x509.Certificate, 0)
|
||||
for len(rest) > 0 {
|
||||
caPem, restBytes := pem.Decode(rest)
|
||||
rest = restBytes
|
||||
|
||||
caCert, err := x509.ParseCertificate(caPem.Bytes)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
caCerts = append(caCerts, caCert)
|
||||
}
|
||||
|
||||
return certsToPkcs12(parsedCert, key, caCerts, pass)
|
||||
}
|
||||
|
||||
func certsToPkcs12(cert *x509.Certificate, key string, caCerts []*x509.Certificate, password string) string {
|
||||
keyPem, _ := pem.Decode([]byte(key))
|
||||
parsedKey := parsePrivateKey(keyPem.Bytes)
|
||||
|
||||
pfx, err := gopkcs12.Modern.Encode(parsedKey, cert, caCerts, password)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(pfx)
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
tpl "text/template"
|
||||
|
||||
"github.com/Masterminds/sprig/v3"
|
||||
)
|
||||
|
||||
var customInfisicalSecretTemplateFunctions = tpl.FuncMap{
|
||||
"pkcs12key": pkcs12key,
|
||||
"pkcs12keyPass": pkcs12keyPass,
|
||||
"pkcs12cert": pkcs12cert,
|
||||
"pkcs12certPass": pkcs12certPass,
|
||||
|
||||
"pemToPkcs12": pemToPkcs12,
|
||||
"pemToPkcs12Pass": pemToPkcs12Pass,
|
||||
"fullPemToPkcs12": fullPemToPkcs12,
|
||||
"fullPemToPkcs12Pass": fullPemToPkcs12Pass,
|
||||
|
||||
"filterPEM": filterPEM,
|
||||
"filterCertChain": filterCertChain,
|
||||
|
||||
"jwkPublicKeyPem": jwkPublicKeyPem,
|
||||
"jwkPrivateKeyPem": jwkPrivateKeyPem,
|
||||
|
||||
"toYaml": toYAML,
|
||||
"fromYaml": fromYAML,
|
||||
|
||||
"decodeBase64ToBytes": decodeBase64ToBytes,
|
||||
"encodeBase64": encodeBase64,
|
||||
}
|
||||
|
||||
const (
|
||||
errParse = "unable to parse template at key %s: %s"
|
||||
errExecute = "unable to execute template at key %s: %s"
|
||||
errDecodePKCS12WithPass = "unable to decode pkcs12 with password: %s"
|
||||
errDecodeCertWithPass = "unable to decode pkcs12 certificate with password: %s"
|
||||
errParsePrivKey = "unable to parse private key type"
|
||||
errUnmarshalJSON = "unable to unmarshal json: %s"
|
||||
errMarshalJSON = "unable to marshal json: %s"
|
||||
|
||||
pemTypeCertificate = "CERTIFICATE"
|
||||
pemTypeKey = "PRIVATE KEY"
|
||||
)
|
||||
|
||||
func InitializeTemplateFunctions() {
|
||||
templates := customInfisicalSecretTemplateFunctions
|
||||
|
||||
sprigFuncs := sprig.TxtFuncMap()
|
||||
// removed for security reasons
|
||||
delete(sprigFuncs, "env")
|
||||
delete(sprigFuncs, "expandenv")
|
||||
|
||||
for k, v := range sprigFuncs {
|
||||
// make sure we aren't overwriting any of our own functions
|
||||
_, exists := templates[k]
|
||||
if !exists {
|
||||
templates[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
customInfisicalSecretTemplateFunctions = templates
|
||||
}
|
||||
|
||||
func GetTemplateFunctions() tpl.FuncMap {
|
||||
return customInfisicalSecretTemplateFunctions
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func toYAML(v any) string {
|
||||
data, err := yaml.Marshal(v)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
|
||||
}
|
||||
return strings.TrimSuffix(string(data), "\n")
|
||||
}
|
||||
|
||||
// fromYAML converts a YAML document into a map[string]any.
|
||||
//
|
||||
// This is not a general-purpose YAML parser, and will not parse all valid
|
||||
// YAML documents.
|
||||
func fromYAML(str string) map[string]any {
|
||||
mapData := map[string]any{}
|
||||
|
||||
if err := yaml.Unmarshal([]byte(str), &mapData); err != nil {
|
||||
panic(fmt.Sprintf("Error: %v", err))
|
||||
}
|
||||
return mapData
|
||||
}
|
||||
Reference in New Issue
Block a user