Skip to content

Commit e32bdca

Browse files
committed
SSHCredentials implementation
1 parent b946bb4 commit e32bdca

File tree

5 files changed

+55
-7
lines changed

5 files changed

+55
-7
lines changed

src/Docker.DotNet.SSH/Docker.DotNet.SSH.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@
88
<ItemGroup>
99
<ProjectReference Include="..\Docker.DotNet\Docker.DotNet.csproj" />
1010
</ItemGroup>
11+
<ItemGroup>
12+
<PackageReference Include="TrapTech.SSH.NET" Version="2020.0.3" />
13+
</ItemGroup>
1114
</Project>

src/Docker.DotNet.SSH/SSHCredentials.cs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,41 @@
11
using System.IO;
22
using System.Net.Http;
33
using System.Threading;
4+
using System.Threading.Tasks;
45
using Microsoft.Net.Http.Client;
6+
using Renci.SshNet;
57

68
namespace Docker.DotNet.SSH
79
{
810
public class SSHCredentials : Credentials
911
{
12+
private PrivateKeyFile _privateKey;
13+
14+
/// <summary>
15+
/// Creates SSH credentials to handle connecting over SSH.
16+
/// </summary>
17+
/// <param name="privateKey">The private key contents</param>
18+
public SSHCredentials(string privateKey) : this(privateKey, null) { }
19+
20+
/// <summary>
21+
/// Creates SSH credentials to handle connecting over SSH.
22+
/// </summary>
23+
/// <param name="privateKey">The private key contents</param>
24+
/// <param name="password">Password for the private key</param>
25+
public SSHCredentials(string privateKey, string password)
26+
{
27+
var stream = new MemoryStream();
28+
using (var writer = new StreamWriter(stream))
29+
{
30+
writer.Write(privateKey);
31+
writer.Flush();
32+
33+
stream.Position = 0;
34+
// This needs to be created *before* the StreamWriter (which closes the stream) is disposed
35+
_privateKey = new PrivateKeyFile(stream, password);
36+
}
37+
}
38+
1039
public override HttpMessageHandler GetHandler(HttpMessageHandler innerHandler)
1140
{
1241
return innerHandler;
@@ -22,11 +51,20 @@ public override bool IsTlsCredentials()
2251
return false;
2352
}
2453

25-
public override ManagedHandler.StreamOpener GetStreamOpener()
54+
public override ManagedHandler.StreamOpener GetSshStreamOpener(string user)
2655
{
27-
// TODO
28-
return async (string host, int port, CancellationToken cancellationToken) => {
29-
return File.OpenRead("/dev/null");
56+
return (string host, int port, CancellationToken cancellationToken) => {
57+
var authMethod = new PrivateKeyAuthenticationMethod(user, _privateKey);
58+
var connectionInfo = new ConnectionInfo(host, port, user, authMethod);
59+
var client = new SshClient(connectionInfo);
60+
client.Connect();
61+
62+
var cmd = client.CreateCommand("docker system dial-stdio");
63+
cmd.BeginExecute();
64+
65+
var result = new JoinedReadWriteStream(cmd.OutputStream, cmd.InputStream);
66+
67+
return Task.FromResult((Stream) result);
3068
};
3169
}
3270
}

src/Docker.DotNet/Credentials.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public abstract class Credentials : IDisposable
1212

1313
public abstract HttpMessageHandler GetHandler(HttpMessageHandler innerHandler);
1414

15-
public virtual ManagedHandler.StreamOpener GetStreamOpener()
15+
public virtual ManagedHandler.StreamOpener GetSshStreamOpener(string username)
1616
{
1717
return null;
1818
}

src/Docker.DotNet/DockerClient.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,15 @@ await sock.ConnectAsync(new Microsoft.Net.Http.Client.UnixDomainSocketEndPoint(p
118118
{
119119
throw new ArgumentException("ssh:// protocol can only be used with SSHCredentials");
120120
};
121-
handler = new ManagedHandler(Configuration.Credentials.GetStreamOpener());
121+
122+
var username = uri.UserInfo;
123+
if(username.Contains(":"))
124+
{
125+
throw new ArgumentException("ssh:// protocol only supports authentication with private keys");
126+
};
127+
128+
handler = new ManagedHandler(Configuration.Credentials.GetSshStreamOpener(username));
122129
uri = new UriBuilder("http", uri.Host, uri.IsDefaultPort ? 22 : uri.Port).Uri;
123-
Console.WriteLine(uri.ToString());
124130
break;
125131

126132
default:

src/Docker.DotNet/Microsoft.Net.Http.Client/HttpConnection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ private async Task<List<string>> ReadResponseLinesAsync(CancellationToken cancel
106106
private HttpResponseMessage CreateResponseMessage(List<string> responseLines)
107107
{
108108
string responseLine = responseLines.First();
109+
109110
// HTTP/1.1 200 OK
110111
string[] responseLineParts = responseLine.Split(new[] { ' ' }, 3);
111112
// TODO: Verify HTTP/1.0 or 1.1.

0 commit comments

Comments
 (0)