3030using Microsoft . Rest ;
3131using Microsoft . WindowsAzure . Commands . Common ;
3232using Microsoft . WindowsAzure . Commands . Utilities . Common ;
33+ using System . Runtime . InteropServices ;
3334
3435namespace Microsoft . Azure . Commands . Aks
3536{
@@ -311,6 +312,61 @@ private void PreValidate()
311312 }
312313 }
313314
315+ // From OpenSSH 9.4 onwards, the default key type is ed25519, which is not supported in AKS.
316+ // To ensure backward compatibility, we need to check the OpenSSH version installed and adjust the parameters accordingly.
317+ static Version GetOpenSSHVersion ( )
318+ {
319+ using ( Process process = new Process ( ) )
320+ {
321+ try
322+ {
323+ // Using runtime information to determine the path to ssh.exe based on OS type.
324+ bool isWindows = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) ;
325+ string defaultWindowsSshPath = @"C:\Windows\System32\OpenSSH\ssh.exe" ;
326+ process . StartInfo . FileName = isWindows && File . Exists ( defaultWindowsSshPath ) ? defaultWindowsSshPath : "ssh" ;
327+ process . StartInfo . Arguments = "-V" ;
328+ process . StartInfo . UseShellExecute = false ;
329+ process . StartInfo . RedirectStandardInput = true ;
330+ process . StartInfo . RedirectStandardError = true ;
331+ process . StartInfo . RedirectStandardOutput = true ;
332+ process . StartInfo . CreateNoWindow = true ;
333+ process . Start ( ) ;
334+
335+ string standardOutput = process . StandardOutput . ReadToEnd ( ) + process . StandardError . ReadToEnd ( ) ;
336+
337+ process . WaitForExit ( ) ;
338+ // OpenSSH version output follows formats like:
339+ // Windows: "OpenSSH_for_Windows_8.6p1, LibreSSL 3.4.3"
340+ // Windows: "OpenSSH_for_Windows_9.5p1, LibreSSL 3.8.2"
341+ // Linux/macOS: "OpenSSH_9.5p1, OpenSSL 3.0.13 30 Jan 2024"
342+ // We match the common "OpenSSH_" prefix and make "for_Windows_" optional so this works across platforms.
343+ var regMatch = System . Text . RegularExpressions . Regex . Match ( standardOutput , @"OpenSSH_(?:for_Windows_)?(\d+)\.(\d+)" ) ;
344+
345+ // We don't really care about the patch version, so only return major and minor version.
346+ return regMatch . Success ? new Version ( int . Parse ( regMatch . Groups [ 1 ] . Value ) , int . Parse ( regMatch . Groups [ 2 ] . Value ) ) : null ;
347+ }
348+ // We're not expecting ssh to be missing, but just in case, we catch any exception and return null.
349+ catch
350+ {
351+ return null ;
352+ }
353+ finally //Ensure process is properly disposed of and exited
354+ {
355+ if ( ! process . HasExited )
356+ {
357+ try
358+ {
359+ process . Kill ( ) ;
360+ }
361+ catch
362+ {
363+ // Ignore exceptions from Kill if process already exited
364+ }
365+ }
366+ }
367+ }
368+ }
369+
314370 private string GenerateSshKeyValue ( )
315371 {
316372 String generateSshKeyFolder = Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . UserProfile ) , ".ssh" ) ;
@@ -329,8 +385,17 @@ private string GenerateSshKeyValue()
329385 {
330386 try
331387 {
388+ // Validate if OpenSSH version is 9.4 or above to add -t rsa argument
389+ // Which has been defaulted to ed25519 from OpenSSH 9.4 onwards
390+ // https://github.com/openssh/openssh-portable/blob/master/ssh-keygen.c#L70
391+ var openSshVersion = GetOpenSSHVersion ( ) ;
392+ // If we cannot determine the OpenSSH version, we assume it's below 9.4 to maintain compatibility.
393+ // If openSshVersion isn't null and is >= 9.4, we add the -t rsa argument, otherwise we skip it
394+ var keyTypeArgument = openSshVersion != null && openSshVersion >= new Version ( 9 , 4 ) ? "-t rsa " : "" ;
332395 process . StartInfo . FileName = "ssh-keygen" ;
333- process . StartInfo . Arguments = String . Format ( "-f \" {0}\" " , generateSshKeyPath ) ;
396+ // if keyTypeArgument is empty, we skip it to maintain compatibility with older OpenSSH versions
397+ // Otherwise, we explicitly set the key type to rsa
398+ process . StartInfo . Arguments = String . Format ( "{0}-f \" {1}\" " , keyTypeArgument , generateSshKeyPath ) ;
334399 process . StartInfo . UseShellExecute = false ;
335400 process . StartInfo . RedirectStandardInput = true ;
336401 process . StartInfo . RedirectStandardError = true ;
0 commit comments