Skip to content

Commit

Permalink
Generate SSH key with go instead of exec
Browse files Browse the repository at this point in the history
  • Loading branch information
cartermckinnon committed Dec 16, 2023
1 parent 11c2dfe commit cc442cd
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 19 deletions.
100 changes: 81 additions & 19 deletions kubetest2/internal/deployers/eksapi/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,18 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path"

"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"

"golang.org/x/crypto/ssh"
"k8s.io/klog"
)

func generateSSHKey() error {
home, err := os.UserHomeDir()
if err != nil {
return err
}
privateKeyFile := path.Join(home, ".ssh", "id_rsa")
if _, err := os.Stat(privateKeyFile); errors.Is(err, os.ErrNotExist) {
klog.V(2).Infof("Generating SSH key: %s", privateKeyFile)
out, err := exec.Command("ssh-keygen", "-P", "''", "-t", "rsa", "-b", "2048", "-f", privateKeyFile).CombinedOutput()
if err != nil {
return err
}
fmt.Println(string(out))
} else if err != nil {
return err
}
return nil
}
const sshKeyBits = 2048

func loadSSHPublicKey() (string, error) {
home, err := os.UserHomeDir()
Expand All @@ -44,3 +32,77 @@ func loadSSHPublicKey() (string, error) {
}
return string(material), err
}

func generateSSHKey() error {
home, err := os.UserHomeDir()
if err != nil {
return err
}
privateKeyFile := path.Join(home, ".ssh", "id_rsa")
publicKeyFile := privateKeyFile + ".pub"
if err := generateSSHKeyToFile(privateKeyFile, publicKeyFile); err != nil {
return fmt.Errorf("failed to generate ssh key: %v", err)
}
return nil
}

func generateSSHKeyToFile(privateKeyPath string, publicKeyPath string) error {
if _, err := os.Stat(privateKeyPath); !errors.Is(err, os.ErrNotExist) {
return err
}
if _, err := os.Stat(publicKeyPath); !errors.Is(err, os.ErrNotExist) {
return err
}
klog.Infof("Generating SSH key: %s", privateKeyPath)
privateKey, err := generatePrivateKey(sshKeyBits)
if err != nil {
return err
}
publicKeyBytes, err := encodePublicKey(privateKey)
if err != nil {
return err
}
privateKeyBytes := encodePrivateKeyToPEM(privateKey)
keyDir := path.Dir(privateKeyPath)
if err := os.MkdirAll(keyDir, 0700); err != nil {
return fmt.Errorf("failed to create directory for SSH key: %v", err)
}
if err := os.WriteFile(privateKeyPath, privateKeyBytes, 0600); err != nil {
return fmt.Errorf("failed to write SSH private key to %s: %v", privateKeyPath, err)
}
if err := os.WriteFile(publicKeyPath, publicKeyBytes, 0600); err != nil {
return fmt.Errorf("failed to write SSH public key to %s: %v", publicKeyPath, err)
}
return nil
}

func generatePrivateKey(bitSize int) (*rsa.PrivateKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, bitSize)
if err != nil {
return nil, err
}
err = privateKey.Validate()
if err != nil {
return nil, err
}
return privateKey, nil
}

func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
privDER := x509.MarshalPKCS1PrivateKey(privateKey)
privBlock := pem.Block{
Type: "RSA PRIVATE KEY",
Headers: nil,
Bytes: privDER,
}
return pem.EncodeToMemory(&privBlock)
}

func encodePublicKey(privateKey *rsa.PrivateKey) ([]byte, error) {
publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, err
}
publicKeyBytes := ssh.MarshalAuthorizedKey(publicKey)
return publicKeyBytes, nil
}
15 changes: 15 additions & 0 deletions kubetest2/internal/deployers/eksapi/ssh_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package eksapi

import (
"path"
"testing"
)

func Test_generateSSHKey(t *testing.T) {
tmp := t.TempDir()
privateKeyPath := path.Join(tmp, ".ssh", "id_rsa")
publicKeyPath := privateKeyPath + ".pub"
if err := generateSSHKeyToFile(privateKeyPath, publicKeyPath); err != nil {
t.Fatal(err)
}
}

0 comments on commit cc442cd

Please sign in to comment.