You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

44 lines
1.2 KiB

package portservice
import (
"crypto/ed25519"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"os"
"path/filepath"
"golang.org/x/crypto/ssh"
"tera-clone/services/tera-api/internal/storage/atomicfile"
)
func LoadOrCreateSSHSigner(path string) (ssh.Signer, error) {
if path == "" || !filepath.IsAbs(path) {
return nil, errors.New("SSH host key path must be absolute")
}
if payload, err := os.ReadFile(path); err == nil {
return ssh.ParsePrivateKey(payload)
} else if !os.IsNotExist(err) {
return nil, fmt.Errorf("read SSH host key: %w", err)
}
if err := os.MkdirAll(filepath.Dir(path), 0o750); err != nil {
return nil, fmt.Errorf("prepare SSH host key directory: %w", err)
}
_, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, fmt.Errorf("generate SSH host key: %w", err)
}
encoded, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
return nil, fmt.Errorf("encode SSH host key: %w", err)
}
payload := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: encoded})
if err := atomicfile.Write(path, payload, 0o600); err != nil {
return nil, fmt.Errorf("persist SSH host key: %w", err)
}
return ssh.ParsePrivateKey(payload)
}