-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add SPIFFE Federation Examples (#1162)
* remove static federation config * add bundle enpoints back * config update * wip * wip * wip * wip * update image urls * manifest changes * images * key rotation * lastworking * update. * add kubectl * add statefulset. * more * wip * chmod * a * sed se * export secrets * update server code * update deployment * update client and server * add error logging * minor updates * add error details * more logs * change * printing error * fix encryption * update client * client check * algo update * cleanup * last changes. * edge store * updated logic. * recent changes * a * add env variable for custom spiffeid prefix. * add env var. * asdfasd * update dpeloyment. * changes * minor * up * a * bugfix * changes
- Loading branch information
Showing
32 changed files
with
4,081 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
examples/workshop_spiffe_federation/apps/control-plane-server/crypto.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package main | ||
|
||
import ( | ||
"crypto" | ||
"crypto/aes" | ||
"crypto/cipher" | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/sha256" | ||
"crypto/x509" | ||
"encoding/pem" | ||
"fmt" | ||
"io" | ||
) | ||
|
||
func signData(data []byte, privateKey *rsa.PrivateKey) ([]byte, error) { | ||
hash := sha256.Sum256(data) | ||
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hash[:]) | ||
if err != nil { | ||
return nil, fmt.Errorf("error signing data: %v", err) | ||
} | ||
|
||
return signature, nil | ||
} | ||
|
||
func parsePublicKey(publicKeyPEM string) (*rsa.PublicKey, error) { | ||
block, _ := pem.Decode([]byte(publicKeyPEM)) | ||
if block == nil { | ||
return nil, fmt.Errorf("failed to parse PEM block containing the public key") | ||
} | ||
|
||
pub, err := x509.ParsePKIXPublicKey(block.Bytes) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rsaPub, ok := pub.(*rsa.PublicKey) | ||
if !ok { | ||
return nil, fmt.Errorf("not an RSA public key") | ||
} | ||
|
||
return rsaPub, nil | ||
} | ||
|
||
func generateAESKey() ([]byte, error) { | ||
key := make([]byte, 32) // AES-256 | ||
_, err := rand.Read(key) | ||
return key, err | ||
} | ||
|
||
func encryptAES(plaintext []byte, key []byte) ([]byte, error) { | ||
block, err := aes.NewCipher(key) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
gcm, err := cipher.NewGCM(block) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
nonce := make([]byte, gcm.NonceSize()) | ||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil { | ||
return nil, err | ||
} | ||
|
||
return gcm.Seal(nonce, nonce, plaintext, nil), nil | ||
} | ||
|
||
func generateKeyPair() (*rsa.PrivateKey, *rsa.PublicKey, error) { | ||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
return privateKey, &privateKey.PublicKey, nil | ||
} |
33 changes: 33 additions & 0 deletions
33
examples/workshop_spiffe_federation/apps/control-plane-server/entity.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package main | ||
|
||
type Endpoint struct { | ||
Name string `json:"name"` | ||
BundleEndpointURL string `json:"bundleEndpointUrl"` | ||
TrustDomain string `json:"trustDomain"` | ||
EndpointSPIFFEID string `json:"endpointSPIFFEID"` | ||
} | ||
|
||
type Endpoints struct { | ||
Endpoints map[string]Endpoint `json:""` | ||
FederateWith []string `json:"federateWith"` | ||
} | ||
|
||
type Secret struct { | ||
Name string `json:"name"` | ||
Value []string `json:"value"` | ||
Created string `json:"created"` | ||
Updated string `json:"updated"` | ||
NotBefore string `json:"notBefore"` | ||
ExpiresAfter string `json:"expiresAfter"` | ||
} | ||
|
||
type EncryptedResponse struct { | ||
EncryptedAESKey string `json:"encryptedAESKey"` | ||
EncryptedData string `json:"encryptedData"` | ||
Signature string `json:"signature"` | ||
} | ||
|
||
type Secrets struct { | ||
Secrets []Secret `json:"secrets"` | ||
Algorithm string `json:"algorithm"` | ||
} |
36 changes: 36 additions & 0 deletions
36
examples/workshop_spiffe_federation/apps/control-plane-server/io.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"os" | ||
) | ||
|
||
func loadEndpoints(filename string) Endpoints { | ||
data, err := os.ReadFile(filename) | ||
if err != nil { | ||
panic("Error reading endpoints file: " + err.Error()) | ||
} | ||
|
||
var endpoints Endpoints | ||
err = json.Unmarshal(data, &endpoints) | ||
if err != nil { | ||
panic("Error parsing endpoints JSON: " + err.Error()) | ||
} | ||
|
||
return endpoints | ||
} | ||
|
||
func loadSecrets(filename string) Secrets { | ||
data, err := os.ReadFile(filename) | ||
if err != nil { | ||
panic("Error reading secrets file: " + err.Error()) | ||
} | ||
|
||
var secrets Secrets | ||
err = json.Unmarshal(data, &secrets) | ||
if err != nil { | ||
panic("Error parsing secrets JSON: " + err.Error()) | ||
} | ||
|
||
return secrets | ||
} |
128 changes: 128 additions & 0 deletions
128
examples/workshop_spiffe_federation/apps/control-plane-server/network.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
package main | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/rsa" | ||
"crypto/sha256" | ||
"crypto/x509" | ||
"encoding/base64" | ||
"encoding/json" | ||
"encoding/pem" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"strings" | ||
) | ||
|
||
func handleRequest(w http.ResponseWriter, r *http.Request, secrets Secrets) { | ||
// Extract SPIFFE ID from the client certificate | ||
if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 { | ||
http.Error(w, "No client certificate provided", http.StatusUnauthorized) | ||
return | ||
} | ||
|
||
spiffeID := r.TLS.PeerCertificates[0].URIs[0].String() | ||
fmt.Printf("Received request from SPIFFE ID: %s\n", spiffeID) | ||
|
||
// Extract trust domain from SPIFFE ID | ||
parts := strings.Split(spiffeID, "/") | ||
if len(parts) < 3 { | ||
fmt.Println("Invalid SPIFFE ID") | ||
http.Error(w, "Invalid SPIFFE ID", http.StatusBadRequest) | ||
return | ||
} | ||
trustDomain := parts[2] | ||
|
||
// Read the client's public key from the request body | ||
body, err := io.ReadAll(r.Body) | ||
if err != nil { | ||
fmt.Println("error in request body") | ||
http.Error(w, "Error reading request body", http.StatusBadRequest) | ||
return | ||
} | ||
clientPublicKey, err := parsePublicKey(string(body)) | ||
if err != nil { | ||
fmt.Println("error in parsing public key") | ||
http.Error(w, "Invalid client public key", http.StatusBadRequest) | ||
return | ||
} | ||
|
||
// Generate a new keypair for this response | ||
privateKey, publicKey, err := generateKeyPair() | ||
if err != nil { | ||
http.Error(w, "Error generating keypair", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Find the corresponding secret | ||
secretName := fmt.Sprintf("vsecm-relay:%s", trustDomain) | ||
var secretValue []string | ||
for _, secret := range secrets.Secrets { | ||
if secret.Name == secretName { | ||
secretValue = secret.Value | ||
break | ||
} | ||
} | ||
|
||
if secretValue == nil { | ||
http.Error(w, "No secret found for the given SPIFFE ID", http.StatusNotFound) | ||
return | ||
} | ||
|
||
// Generate AES key | ||
aesKey, err := generateAESKey() | ||
if err != nil { | ||
http.Error(w, "Error generating AES key", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Encrypt the AES key with the client's public key | ||
encryptedAESKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, clientPublicKey, aesKey, nil) | ||
if err != nil { | ||
http.Error(w, "Error encrypting AES key", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Encrypt the secret with AES | ||
plaintext, err := json.Marshal(secretValue) | ||
if err != nil { | ||
http.Error(w, "Error marshaling secret", http.StatusInternalServerError) | ||
return | ||
} | ||
encryptedData, err := encryptAES(plaintext, aesKey) | ||
if err != nil { | ||
http.Error(w, "Error encrypting data", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Sign the encrypted data | ||
signature, err := signData(encryptedData, privateKey) | ||
if err != nil { | ||
http.Error(w, "Error signing data", http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Prepare the response | ||
response := EncryptedResponse{ | ||
EncryptedAESKey: base64.StdEncoding.EncodeToString(encryptedAESKey), | ||
EncryptedData: base64.StdEncoding.EncodeToString(encryptedData), | ||
Signature: base64.StdEncoding.EncodeToString(signature), | ||
} | ||
|
||
publicKeyBytes, err := x509.MarshalPKIXPublicKey(publicKey) | ||
if err != nil { | ||
http.Error(w, "Error encoding public key", http.StatusInternalServerError) | ||
return | ||
} | ||
publicKeyPEM := pem.EncodeToMemory(&pem.Block{ | ||
Type: "PUBLIC KEY", | ||
Bytes: publicKeyBytes, | ||
}) | ||
|
||
// Add the public key to the response headers | ||
w.Header().Set("X-Public-Key", base64.StdEncoding.EncodeToString(publicKeyPEM)) | ||
|
||
// Send the response | ||
w.Header().Set("Content-Type", "application/json") | ||
_ = json.NewEncoder(w).Encode(response) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.