erste module drin.
zwischenstand: Architektur aktualisiert, TOFU implementiert,
This commit is contained in:
1 parent
9d5789c5d7
commit
c8df9d7651
41 files changed
+6455
-36
No files matched your search
@@ -0,0 +1,106 @@
|
||||
// Package auth authentifiziert Agent-Requests per Ed25519-Signatur (Spec
|
||||
// Abschnitt 3.2) – in jedem Tier, auch im Community-/Homelab-Betrieb.
|
||||
//
|
||||
// Der Agent signiert jeden Request mit seinem lokal erzeugten node.key
|
||||
// (Signer). Der Server prüft Signatur, Zeitfenster und Nonce, schlägt den
|
||||
// Knoten über den Public Key nach und liefert dessen Identität (Verifier).
|
||||
// Die Knoten-ID stammt damit immer aus dem geprüften Schlüssel, nie aus einer
|
||||
// Angabe des Clients.
|
||||
//
|
||||
// Beide Seiten verweigern Klartext: Der Signer signiert nur https-Requests,
|
||||
// der Verifier akzeptiert nur Requests, die über TLS angekommen sind.
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidKey: Schlüssel fehlt, ist kaputt oder kein Ed25519-Schlüssel.
|
||||
ErrInvalidKey = errors.New("auth: ungültiger Schlüssel")
|
||||
// ErrInsecureKeyFile: Die Schlüsseldatei ist für Gruppe oder andere lesbar.
|
||||
ErrInsecureKeyFile = errors.New("auth: Schlüsseldatei hat zu offene Rechte (erwartet 0600)")
|
||||
)
|
||||
|
||||
const pemTypePrivateKey = "PRIVATE KEY"
|
||||
|
||||
// GenerateKey erzeugt ein neues Ed25519-Schlüsselpaar.
|
||||
func GenerateKey() (ed25519.PrivateKey, error) {
|
||||
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
return priv, err
|
||||
}
|
||||
|
||||
// WritePrivateKey speichert key als PKCS#8-PEM mit Rechten 0600. Eine
|
||||
// bestehende Datei wird nie überschrieben (os.ErrExist) – ein versehentlich
|
||||
// ersetzter Schlüssel würde den Knoten aussperren.
|
||||
func WritePrivateKey(path string, key ed25519.PrivateKey) error {
|
||||
der, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %v", ErrInvalidKey, err)
|
||||
}
|
||||
f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0o600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pem.Encode(f, &pem.Block{Type: pemTypePrivateKey, Bytes: der}); err != nil {
|
||||
f.Close()
|
||||
os.Remove(path)
|
||||
return err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
os.Remove(path)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadPrivateKey liest einen mit WritePrivateKey gespeicherten Schlüssel.
|
||||
// Dateien, die Gruppe oder andere lesen dürfen, werden abgelehnt.
|
||||
func ReadPrivateKey(path string) (ed25519.PrivateKey, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Mode().Perm()&0o077 != 0 {
|
||||
return nil, fmt.Errorf("%w: %s hat %o", ErrInsecureKeyFile, path, info.Mode().Perm())
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, _ := pem.Decode(data)
|
||||
if block == nil || block.Type != pemTypePrivateKey {
|
||||
return nil, fmt.Errorf("%w: kein %q-PEM-Block in %s", ErrInvalidKey, pemTypePrivateKey, path)
|
||||
}
|
||||
parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", ErrInvalidKey, err)
|
||||
}
|
||||
key, ok := parsed.(ed25519.PrivateKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %T statt Ed25519", ErrInvalidKey, parsed)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// EncodePublicKey kodiert einen Public Key so, wie er in Headern und im
|
||||
// Store (nodes.public_key) steht: Base64 (Standard-Alphabet).
|
||||
func EncodePublicKey(pub ed25519.PublicKey) string {
|
||||
return base64.StdEncoding.EncodeToString(pub)
|
||||
}
|
||||
|
||||
// DecodePublicKey ist die Umkehrung von EncodePublicKey.
|
||||
func DecodePublicKey(s string) (ed25519.PublicKey, error) {
|
||||
b, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil || len(b) != ed25519.PublicKeySize {
|
||||
return nil, fmt.Errorf("%w: Public Key nicht lesbar", ErrInvalidKey)
|
||||
}
|
||||
return ed25519.PublicKey(b), nil
|
||||
}
|
||||
Reference in new issue
Block a user