Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6fc07e1
feat: Add SSH key generation functionality using Ed25519
medyagh Dec 17, 2025
85850e2
feat: Update SSH client to prioritize Ed25519 keys for authentication
medyagh Dec 17, 2025
ef32b9b
feat: Update SSH key generation to use new sshkeys package
medyagh Dec 17, 2025
757c233
feat: Refactor SSH key generation to utilize sshkeys package
medyagh Dec 17, 2025
d637227
feat: Update krunkit driver to use Ed25519 SSH keys for improved secu…
medyagh Dec 17, 2025
962dcc1
feat: Update QEMU driver to use Ed25519 SSH keys for improved security
medyagh Dec 17, 2025
4c5416d
feat: Update vfkit driver to use Ed25519 SSH keys for improved security
medyagh Dec 17, 2025
e767aed
feat: Update service tunnel to use Ed25519 SSH keys for improved secu…
medyagh Dec 17, 2025
1ad1deb
feat: Update ssh-keyscan to prioritize Ed25519 keys for enhanced secu…
medyagh Dec 17, 2025
f6b178e
feat: Update ssh-key command to prioritize Ed25519 keys for improved …
medyagh Dec 17, 2025
847ed24
feat: Update tunnel command to prioritize Ed25519 SSH keys for improv…
medyagh Dec 17, 2025
5b527a9
Update Prow tester docker_deployer to generate Ed25519 SSH keys for e…
medyagh Dec 17, 2025
422578f
feat: Update SSH key path in vboxConfig to use Ed25519 for improved s…
medyagh Dec 17, 2025
5fb3007
feat: Update guestIssues regex to support Ed25519 SSH key errors
medyagh Dec 17, 2025
0116d58
ISO: Remove temporary support for ssh-rsa algorithm in sshd_config
medyagh Dec 17, 2025
7a1af9b
feat: Update Dockerfile to enable key-based SSH authentication and di…
medyagh Dec 17, 2025
8a1eece
feat: Add go-acl dependency for improved access control
medyagh Dec 17, 2025
aca40f2
feat: Update SSH key path in accessing.md to use Ed25519 for improved…
medyagh Dec 17, 2025
d8910ae
fix: Rename ssh constant to sshBinary for consistency in util.go
medyagh Dec 17, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cmd/minikube/cmd/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import (
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/tunnel/kic"
pkgnetwork "k8s.io/minikube/pkg/network"
"k8s.io/minikube/pkg/util/sshkeys"
)

const defaultServiceFormatTemplate = "http://{{.IP}}:{{.Port}}"
Expand Down Expand Up @@ -210,7 +211,10 @@ func startKicServiceTunnel(services service.URLs, configName, driverName string)
exit.Error(reason.DrvPortForward, "error getting ssh port", err)
}
sshPort := strconv.Itoa(port)
sshKey := filepath.Join(localpath.MiniPath(), "machines", configName, "id_rsa")
machineDir := filepath.Join(localpath.MiniPath(), "machines", configName)
primary := filepath.Join(machineDir, sshkeys.Ed25519KeyName)
fallback := filepath.Join(machineDir, sshkeys.RSAKeyName)
sshKey := sshkeys.ResolveKeyPath(primary, fallback)

serviceTunnel := kic.NewServiceTunnel(sshPort, sshKey, clientset.CoreV1(), serviceURLMode)
urls, err := serviceTunnel.Start(svc.Name, namespace)
Expand Down
7 changes: 6 additions & 1 deletion cmd/minikube/cmd/ssh-host.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/spf13/cobra"
"k8s.io/client-go/util/homedir"
Expand Down Expand Up @@ -69,9 +70,13 @@ func appendKnownHelper(nodeName string, appendKnown bool) {
}
}

scanArgs := []string{"-t", "rsa"}
scanArgs := []string{"-t", "ed25519"}

keys, err := machine.RunSSHHostCommand(co.API, *co.Config, *n, "ssh-keyscan", scanArgs)
if err != nil || strings.TrimSpace(keys) == "" {
scanArgs = []string{"-t", "rsa"}
keys, err = machine.RunSSHHostCommand(co.API, *co.Config, *n, "ssh-keyscan", scanArgs)
}
if err != nil {
// This is typically due to a non-zero exit code, so no need for flourish.
out.ErrLn("ssh-keyscan: %v", err)
Expand Down
6 changes: 5 additions & 1 deletion cmd/minikube/cmd/ssh-key.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"k8s.io/minikube/pkg/minikube/node"
"k8s.io/minikube/pkg/minikube/out"
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/util/sshkeys"
)

// sshKeyCmd represents the sshKey command
Expand All @@ -43,7 +44,10 @@ var sshKeyCmd = &cobra.Command{
exit.Error(reason.GuestNodeRetrieve, "retrieving node", err)
}

out.Ln(filepath.Join(localpath.MiniPath(), "machines", config.MachineName(*cc, *n), "id_rsa"))
machineDir := filepath.Join(localpath.MiniPath(), "machines", config.MachineName(*cc, *n))
primary := filepath.Join(machineDir, sshkeys.Ed25519KeyName)
fallback := filepath.Join(machineDir, sshkeys.RSAKeyName)
out.Ln(sshkeys.ResolveKeyPath(primary, fallback))
},
}

Expand Down
6 changes: 5 additions & 1 deletion cmd/minikube/cmd/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
"k8s.io/minikube/pkg/minikube/tunnel"
"k8s.io/minikube/pkg/minikube/tunnel/kic"
pkgnetwork "k8s.io/minikube/pkg/network"
"k8s.io/minikube/pkg/util/sshkeys"
)

var cleanup bool
Expand Down Expand Up @@ -104,7 +105,10 @@ var tunnelCmd = &cobra.Command{
exit.Error(reason.DrvPortForward, "error getting ssh port", err)
}
sshPort := strconv.Itoa(port)
sshKey := filepath.Join(localpath.MiniPath(), "machines", cname, "id_rsa")
machineDir := filepath.Join(localpath.MiniPath(), "machines", cname)
primary := filepath.Join(machineDir, sshkeys.Ed25519KeyName)
fallback := filepath.Join(machineDir, sshkeys.RSAKeyName)
sshKey := sshkeys.ResolveKeyPath(primary, fallback)

outputTunnelStarted()
kicSSHTunnel := kic.NewSSHTunnel(ctx, sshPort, sshKey, bindAddress, clientset.CoreV1(), clientset.NetworkingV1())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,3 @@ Subsystem sftp /usr/libexec/sftp-server
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server

# Temporarily accept ssh-rsa algorithm for openssh >= 8.8,
# until most ssh clients could deprecate ssh-rsa.
HostkeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,3 @@ Subsystem sftp /usr/libexec/sftp-server
# AllowTcpForwarding no
# PermitTTY no
# ForceCommand cvs server

# Temporarily accept ssh-rsa algorithm for openssh >= 8.8,
# until most ssh clients could deprecate ssh-rsa.
HostkeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsa
6 changes: 1 addition & 5 deletions deploy/kicbase/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,8 @@ RUN clean-install \
openssh-server \
dnsutils

# Add support for rsa1 in sshd
# modern debian-based OSs dont support rsa1 by default, so we need to enable it to support older ssh clients
# TODO: remove after https://github.com/kubernetes/minikube/issues/21543 is solved
# Ensure key-based SSH auth is enabled and password auth disabled
RUN cat <<EOF >> /etc/ssh/sshd_config
PubkeyAcceptedAlgorithms +ssh-rsa
HostkeyAlgorithms +ssh-rsa
PubkeyAuthentication yes
PasswordAuthentication no
EOF
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
github.com/google/uuid v1.6.0
github.com/hashicorp/go-getter v1.8.0
github.com/hashicorp/go-retryablehttp v0.7.8
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95
github.com/hooklift/iso9660 v1.0.0
github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0
github.com/johanneswuerbach/nfsexports v0.0.0-20200318065542-c48c3734757f
Expand Down Expand Up @@ -167,7 +168,6 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect
github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 // indirect
github.com/huandu/xstrings v1.3.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
Expand Down
96 changes: 51 additions & 45 deletions hack/prow/minitest/deployer/docker_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,22 @@ package deployer

import (
"context"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"os"
"path/filepath"
"strconv"

"github.com/docker/go-connections/nat"
"github.com/google/uuid"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/client"
"github.com/phayes/freeport"
gossh "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh"
"k8s.io/klog/v2"
)

Expand Down Expand Up @@ -252,54 +252,20 @@ func (m *MiniTestDockerDeployer) sshSetUp() error {
m.sshTempDir = sshTempDir
klog.Info("Created temp dir for ssh keys: ", sshTempDir)

// create private key file
sshPrivateKeyFile, err := os.CreateTemp(sshTempDir, "id_rsa")
if err != nil {
return fmt.Errorf("failed to create temp file for private key: %v", err)
}
if sshPrivateKeyFile.Chmod(0600) != nil {
return fmt.Errorf("failed to chmod private key file: %v", err)
keyPath := filepath.Join(sshTempDir, "id_ed25519")
pubKeyPath := keyPath + ".pub"
if err := generateEd25519KeyPair(keyPath, pubKeyPath); err != nil {
return fmt.Errorf("failed to generate ssh key pair: %v", err)
}
m.sshPrivateKeyFile = sshPrivateKeyFile.Name()
m.sshPrivateKeyFile = keyPath
m.sshPublicKeyFile = pubKeyPath
klog.Info("Created temp file for private key: ", m.sshPrivateKeyFile)

// create public key file
sshPublicKeyFile, err := os.CreateTemp(sshTempDir, "id_rsa.pub")
if err != nil {
return fmt.Errorf("failed to create temp file for public key: %v", err)
}
if sshPublicKeyFile.Chmod(0644) != nil {
return fmt.Errorf("failed to chmod public key file: %v", err)
}
m.sshPublicKeyFile = sshPublicKeyFile.Name()
klog.Info("Created temp file for public key: ", m.sshPublicKeyFile)

// generate private key and convert to PEM format
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
pubBytes, err := os.ReadFile(m.sshPublicKeyFile)
if err != nil {
return fmt.Errorf("failed to generate private key: %v", err)
}
privDER := x509.MarshalPKCS1PrivateKey(privateKey)
privBlock := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privDER,
}

if err := pem.Encode(sshPrivateKeyFile, privBlock); err != nil {
sshPrivateKeyFile.Close()
return fmt.Errorf("failed to write private key to file: %v", err)
return fmt.Errorf("failed to read public key file: %v", err)
}

pub, err := gossh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return fmt.Errorf("failed to generate public key: %v", err)
}
pubBytes := gossh.MarshalAuthorizedKey(pub)

if _, err := sshPublicKeyFile.Write(pubBytes); err != nil {
return fmt.Errorf("failed to write public key to file: %v", err)
}
sshPublicKeyFile.Close()
m.sshPublicKeyContent = string(pubBytes)
klog.Infof("Generated ssh public key:%s ", m.sshPublicKeyContent)
return nil
Expand All @@ -312,3 +278,43 @@ func (m *MiniTestDockerDeployer) sshAdditionalArgs() []string {
func (m *MiniTestDockerDeployer) scpAdditionalArgs() []string {
return []string{"-i", m.sshPrivateKeyFile, "-P", m.sshPort}
}

func generateEd25519KeyPair(privateKeyPath, publicKeyPath string) error {
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return fmt.Errorf("generate ed25519 key: %w", err)
}

block, err := ssh.MarshalPrivateKey(privateKey, "")
if err != nil {
return fmt.Errorf("marshal private key: %w", err)
}
if err := writeKeyFile(privateKeyPath, pem.EncodeToMemory(block), 0600); err != nil {
return fmt.Errorf("write private key: %w", err)
}

sshPublicKey, err := ssh.NewPublicKey(publicKey)
if err != nil {
return fmt.Errorf("convert public key: %w", err)
}
if err := writeKeyFile(publicKeyPath, ssh.MarshalAuthorizedKey(sshPublicKey), 0644); err != nil {
return fmt.Errorf("write public key: %w", err)
}

return nil
}

func writeKeyFile(path string, data []byte, mode os.FileMode) error {
f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, mode)
if err != nil {
return err
}
if _, err := f.Write(data); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
return os.Chmod(path, mode)
}
4 changes: 2 additions & 2 deletions hack/prow/minitest/deployer/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"k8s.io/klog/v2"
)

const ssh = "ssh"
const sshBinary = "ssh"
const rsync = "rsync"

func executeLocalCommand(ctx context.Context, name string, args ...string) error {
Expand Down Expand Up @@ -66,7 +66,7 @@ func sshConnectionCheck(ctx context.Context, user string, addr string, sshArgume
}

func executeRsyncSSHCommand(ctx context.Context, sshArguments []string, src string, dst string, rsyncArgs []string) error {
sshArgs := []string{ssh, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"}
sshArgs := []string{sshBinary, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"}
sshArgs = append(sshArgs, sshArguments...)

allArgs := []string{"-e", strings.Join(sshArgs, " "), "-avz"}
Expand Down
4 changes: 2 additions & 2 deletions pkg/drivers/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ import (
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnflag"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/ssh"
"github.com/pkg/errors"

"k8s.io/klog/v2"
"k8s.io/minikube/pkg/minikube/run"
"k8s.io/minikube/pkg/util"
"k8s.io/minikube/pkg/util/sshkeys"
)

// LeasesPath is the path to dhcpd leases
Expand Down Expand Up @@ -157,7 +157,7 @@ func MakeDiskImage(d *drivers.BaseDriver, boot2dockerURL string, diskSize int) e

keyPath := d.GetSSHKeyPath()
klog.Infof("Creating ssh key: %s...", keyPath)
if err := ssh.GenerateSSHKey(keyPath); err != nil {
if err := sshkeys.GenerateSSHKey(keyPath); err != nil {
return errors.Wrap(err, "generate ssh key")
}

Expand Down
8 changes: 5 additions & 3 deletions pkg/drivers/kic/kic.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"time"

"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/libmachine/state"
"github.com/pkg/errors"
"k8s.io/klog/v2"
Expand All @@ -47,6 +46,7 @@ import (
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/minikube/sysinit"
"k8s.io/minikube/pkg/util/retry"
"k8s.io/minikube/pkg/util/sshkeys"
)

// Driver represents a kic driver https://minikube.sigs.k8s.io/docs/reference/drivers/docker
Expand Down Expand Up @@ -223,7 +223,7 @@ func (d *Driver) Create() error {
func (d *Driver) prepareSSH() error {
keyPath := d.GetSSHKeyPath()
klog.Infof("Creating ssh key for kic: %s...", keyPath)
if err := ssh.GenerateSSHKey(keyPath); err != nil {
if err := sshkeys.GenerateSSHKey(keyPath); err != nil {
return errors.Wrap(err, "generate ssh key")
}

Expand Down Expand Up @@ -324,7 +324,9 @@ func (d *Driver) GetSSHUsername() string {
// GetSSHKeyPath returns the ssh key path
func (d *Driver) GetSSHKeyPath() string {
if d.SSHKeyPath == "" {
d.SSHKeyPath = d.ResolveStorePath("id_rsa")
primary := d.ResolveStorePath(sshkeys.Ed25519KeyName)
fallback := d.ResolveStorePath(sshkeys.RSAKeyName)
d.SSHKeyPath = sshkeys.ResolveKeyPath(primary, fallback)
}
return d.SSHKeyPath
}
Expand Down
8 changes: 5 additions & 3 deletions pkg/drivers/krunkit/krunkit.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import (
"github.com/docker/machine/libmachine/drivers"
"github.com/docker/machine/libmachine/log"
"github.com/docker/machine/libmachine/mcnutils"
"github.com/docker/machine/libmachine/ssh"
"github.com/docker/machine/libmachine/state"
"github.com/pkg/errors"

Expand All @@ -51,6 +50,7 @@ import (
"k8s.io/minikube/pkg/minikube/reason"
"k8s.io/minikube/pkg/minikube/run"
"k8s.io/minikube/pkg/minikube/style"
"k8s.io/minikube/pkg/util/sshkeys"
)

const (
Expand Down Expand Up @@ -112,7 +112,9 @@ func (d *Driver) GetSSHHostname() (string, error) {
}

func (d *Driver) GetSSHKeyPath() string {
return d.ResolveStorePath("id_rsa")
primary := d.ResolveStorePath(sshkeys.Ed25519KeyName)
fallback := d.ResolveStorePath(sshkeys.RSAKeyName)
return sshkeys.ResolveKeyPath(primary, fallback)
}

func (d *Driver) GetSSHPort() (int, error) {
Expand Down Expand Up @@ -172,7 +174,7 @@ func (d *Driver) Create() error {
}

log.Info("Creating SSH key...")
if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
if err := sshkeys.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
return err
}

Expand Down
Loading
Loading