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
44 lines
1.2 KiB
|
2 months ago
|
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)
|
||
|
|
}
|