RemoteSshd.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using Renci.SshNet.TestTools.OpenSSH;
  2. namespace Renci.SshNet.IntegrationTests
  3. {
  4. internal class RemoteSshd
  5. {
  6. private readonly IConnectionInfoFactory _connectionInfoFactory;
  7. public RemoteSshd(IConnectionInfoFactory connectionInfoFactory)
  8. {
  9. _connectionInfoFactory = connectionInfoFactory;
  10. }
  11. public RemoteSshdConfig OpenConfig()
  12. {
  13. return new RemoteSshdConfig(this, _connectionInfoFactory);
  14. }
  15. public RemoteSshd Restart()
  16. {
  17. // Restart SSH daemon
  18. using (var client = new SshClient(_connectionInfoFactory.Create()))
  19. {
  20. client.Connect();
  21. // Kill all processes that start with 'sshd' and that run as root
  22. var stopCommand = client.CreateCommand("sudo pkill -9 -U 0 sshd.pam");
  23. var stopOutput = stopCommand.Execute();
  24. if (stopCommand.ExitStatus != 0)
  25. {
  26. throw new ApplicationException($"Stopping ssh service failed with exit code {stopCommand.ExitStatus}.\r\n{stopOutput}\r\n{stopCommand.Error}");
  27. }
  28. var resetFailedCommand = client.CreateCommand("sudo /usr/sbin/sshd.pam");
  29. var resetFailedOutput = resetFailedCommand.Execute();
  30. if (resetFailedCommand.ExitStatus != 0)
  31. {
  32. throw new ApplicationException($"Reset failures for ssh service failed with exit code {resetFailedCommand.ExitStatus}.\r\n{resetFailedOutput}\r\n{resetFailedCommand.Error}");
  33. }
  34. }
  35. return this;
  36. }
  37. }
  38. internal class RemoteSshdConfig
  39. {
  40. private const string SshdConfigFilePath = "/etc/ssh/sshd_config";
  41. private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true);
  42. private readonly RemoteSshd _remoteSshd;
  43. private readonly IConnectionInfoFactory _connectionInfoFactory;
  44. private readonly SshdConfig _config;
  45. public RemoteSshdConfig(RemoteSshd remoteSshd, IConnectionInfoFactory connectionInfoFactory)
  46. {
  47. _remoteSshd = remoteSshd;
  48. _connectionInfoFactory = connectionInfoFactory;
  49. using (var client = new ScpClient(_connectionInfoFactory.Create()))
  50. {
  51. client.Connect();
  52. using (var memoryStream = new MemoryStream())
  53. {
  54. client.Download(SshdConfigFilePath, memoryStream);
  55. memoryStream.Position = 0;
  56. _config = SshdConfig.LoadFrom(memoryStream, Encoding.UTF8);
  57. }
  58. }
  59. }
  60. /// <summary>
  61. /// Specifies whether challenge-response authentication is allowed.
  62. /// </summary>
  63. /// <param name="value"><see langword="true"/> to allow challenge-response authentication.</param>
  64. /// <returns>
  65. /// The current <see cref="RemoteSshdConfig"/> instance.
  66. /// </returns>
  67. public RemoteSshdConfig WithChallengeResponseAuthentication(bool? value)
  68. {
  69. _config.ChallengeResponseAuthentication = value;
  70. return this;
  71. }
  72. /// <summary>
  73. /// Specifies whether to allow keyboard-interactive authentication.
  74. /// </summary>
  75. /// <param name="value"><see langword="true"/> to allow keyboard-interactive authentication.</param>
  76. /// <returns>
  77. /// The current <see cref="RemoteSshdConfig"/> instance.
  78. /// </returns>
  79. public RemoteSshdConfig WithKeyboardInteractiveAuthentication(bool value)
  80. {
  81. _config.KeyboardInteractiveAuthentication = value;
  82. return this;
  83. }
  84. /// <summary>
  85. /// Specifies whether <c>sshd</c> should print /etc/motd when a user logs in interactively.
  86. /// </summary>
  87. /// <param name="value"><see langword="true"/> if <c>sshd</c> should print /etc/motd when a user logs in interactively.</param>
  88. /// <returns>
  89. /// The current <see cref="RemoteSshdConfig"/> instance.
  90. /// </returns>
  91. public RemoteSshdConfig PrintMotd(bool? value = true)
  92. {
  93. _config.PrintMotd = value;
  94. return this;
  95. }
  96. /// <summary>
  97. /// Specifies whether TCP forwarding is permitted.
  98. /// </summary>
  99. /// <param name="value"><see langword="true"/> to allow TCP forwarding.</param>
  100. /// <returns>
  101. /// The current <see cref="RemoteSshdConfig"/> instance.
  102. /// </returns>
  103. public RemoteSshdConfig AllowTcpForwarding(bool? value = true)
  104. {
  105. _config.AllowTcpForwarding = value;
  106. return this;
  107. }
  108. public RemoteSshdConfig WithAuthenticationMethods(string user, string authenticationMethods)
  109. {
  110. var sshNetMatch = _config.Matches.FirstOrDefault(m => m.Users.Contains(user));
  111. if (sshNetMatch == null)
  112. {
  113. sshNetMatch = new Match(new[] { user }, new string[0]);
  114. _config.Matches.Add(sshNetMatch);
  115. }
  116. sshNetMatch.AuthenticationMethods = authenticationMethods;
  117. return this;
  118. }
  119. public RemoteSshdConfig ClearCiphers()
  120. {
  121. _config.Ciphers.Clear();
  122. return this;
  123. }
  124. public RemoteSshdConfig AddCipher(Cipher cipher)
  125. {
  126. _config.Ciphers.Add(cipher);
  127. return this;
  128. }
  129. public RemoteSshdConfig ClearKeyExchangeAlgorithms()
  130. {
  131. _config.KeyExchangeAlgorithms.Clear();
  132. return this;
  133. }
  134. public RemoteSshdConfig AddKeyExchangeAlgorithm(KeyExchangeAlgorithm keyExchangeAlgorithm)
  135. {
  136. _config.KeyExchangeAlgorithms.Add(keyExchangeAlgorithm);
  137. return this;
  138. }
  139. public RemoteSshdConfig ClearPublicKeyAcceptedAlgorithms()
  140. {
  141. _config.PublicKeyAcceptedAlgorithms.Clear();
  142. return this;
  143. }
  144. public RemoteSshdConfig AddPublicKeyAcceptedAlgorithm(PublicKeyAlgorithm publicKeyAlgorithm)
  145. {
  146. _config.PublicKeyAcceptedAlgorithms.Add(publicKeyAlgorithm);
  147. return this;
  148. }
  149. public RemoteSshdConfig ClearMessageAuthenticationCodeAlgorithms()
  150. {
  151. _config.MessageAuthenticationCodeAlgorithms.Clear();
  152. return this;
  153. }
  154. public RemoteSshdConfig AddMessageAuthenticationCodeAlgorithm(MessageAuthenticationCodeAlgorithm messageAuthenticationCodeAlgorithm)
  155. {
  156. _config.MessageAuthenticationCodeAlgorithms.Add(messageAuthenticationCodeAlgorithm);
  157. return this;
  158. }
  159. public RemoteSshdConfig ClearHostKeyAlgorithms()
  160. {
  161. _config.HostKeyAlgorithms.Clear();
  162. return this;
  163. }
  164. public RemoteSshdConfig AddHostKeyAlgorithm(HostKeyAlgorithm hostKeyAlgorithm)
  165. {
  166. _config.HostKeyAlgorithms.Add(hostKeyAlgorithm);
  167. return this;
  168. }
  169. public RemoteSshdConfig ClearSubsystems()
  170. {
  171. _config.Subsystems.Clear();
  172. return this;
  173. }
  174. public RemoteSshdConfig AddSubsystem(Subsystem subsystem)
  175. {
  176. _config.Subsystems.Add(subsystem);
  177. return this;
  178. }
  179. public RemoteSshdConfig WithLogLevel(LogLevel logLevel)
  180. {
  181. _config.LogLevel = logLevel;
  182. return this;
  183. }
  184. public RemoteSshdConfig WithUsePAM(bool usePAM)
  185. {
  186. _config.UsePAM = usePAM;
  187. return this;
  188. }
  189. public RemoteSshdConfig ClearHostKeyFiles()
  190. {
  191. _config.HostKeyFiles.Clear();
  192. return this;
  193. }
  194. public RemoteSshdConfig AddHostKeyFile(string hostKeyFile)
  195. {
  196. _config.HostKeyFiles.Add(hostKeyFile);
  197. return this;
  198. }
  199. public RemoteSshd Update()
  200. {
  201. using (var client = new ScpClient(_connectionInfoFactory.Create()))
  202. {
  203. client.Connect();
  204. using (var memoryStream = new MemoryStream())
  205. using (var sw = new StreamWriter(memoryStream, Utf8NoBom))
  206. {
  207. sw.NewLine = "\n";
  208. _config.SaveTo(sw);
  209. sw.Flush();
  210. memoryStream.Position = 0;
  211. client.Upload(memoryStream, SshdConfigFilePath);
  212. }
  213. }
  214. return _remoteSshd;
  215. }
  216. }
  217. }