Skip to content

Commit 1c27a44

Browse files
committed
ssh module
1 parent e4eb048 commit 1c27a44

15 files changed

Lines changed: 1097 additions & 0 deletions

File tree

docker-java-core/src/main/java/com/github/dockerjava/core/DefaultDockerClientConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ public class DefaultDockerClientConfig implements Serializable, DockerClientConf
9999

100100
private URI checkDockerHostScheme(URI dockerHost) {
101101
switch (dockerHost.getScheme()) {
102+
case "ssh":
102103
case "tcp":
103104
case "unix":
104105
case "npipe":
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# docker-java-transport-ssh
2+
3+
Docker client implementation which uses [jsch](http://www.jcraft.com/jsch/) library, a java ssh implementation, to connect to the remote
4+
docker host via ssh.
5+
6+
While native docker cli supports ssh connections since Host docker version 18.09 [<sup>1</sup>](#1), with different options we can also make
7+
it work for older versions. This library opens the ssh connection and then forwards the docker daemon socket to make it available to the http client.
8+
9+
The ssh connection configuration relies on basic [ssh config file](https://www.ssh.com/ssh/config/) in ~/.ssh/config.
10+
11+
## dockerd configurations
12+
13+
On the remote host, one can connect to the docker daemon in several ways:
14+
15+
* `docker system dial-stdio`
16+
* `unix:///var/run/docker.sock` (default on linux) https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-socket-option
17+
* `npipe:////./pipe/docker_engine` (default on Windows) https://docs.docker.com/docker-for-windows/faqs/#how-do-i-connect-to-the-remote-docker-engine-api
18+
* `unix:///var/run/docker.sock` (default on macos) https://docs.docker.com/docker-for-mac/faqs/#how-do-i-connect-to-the-remote-docker-engine-api
19+
* tcp 2375
20+
* tcp with TLS
21+
22+
## limitations
23+
24+
__jsch__
25+
26+
Since jsch libary from jcraft does not support socket forwarding, a [fork of jsch](https://github.com/mwiede/jsch) is used.
27+
28+
__windows__
29+
30+
Since forwarding socket of windows host is not supported, there is the workaround of starting socat to forward the docker socket to a local tcp port.
31+
32+
Compare OpenSSH tickets:
33+
* https://github.com/PowerShell/Win32-OpenSSH/issues/435
34+
* https://github.com/PowerShell/openssh-portable/pull/433
35+
36+
## connection variants:
37+
38+
By setting flags in [SSHDockerConfig](src\main\java\com\github\dockerjava\jsch\SSHDockerConfig.java), one can control how the connection is made.
39+
40+
* docker system dial-stdio (default)
41+
* direct-streamlocal
42+
* direct-tcpip
43+
* socat
44+
45+
## references
46+
47+
<a class="anchor" id="1">[1]</a> docker ssh support https://github.com/docker/cli/pull/1014
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
alternative Java ssh implementations:
2+
* https://github.com/apache/mina-sshd
3+
* https://github.com/hierynomus/sshj
4+
* https://github.com/jcabi/jcabi-ssh

docker-java-transport-ssh/pom.xml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>docker-java-parent</artifactId>
7+
<groupId>com.github.docker-java</groupId>
8+
<version>3.2.2-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>docker-java-transport-ssh</artifactId>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>${project.groupId}</groupId>
17+
<artifactId>docker-java-core</artifactId>
18+
<version>${project.version}</version>
19+
</dependency>
20+
21+
<dependency>
22+
<groupId>com.squareup.okhttp3</groupId>
23+
<artifactId>okhttp</artifactId>
24+
<version>3.14.4</version>
25+
</dependency>
26+
27+
<dependency>
28+
<groupId>com.github.mwiede</groupId>
29+
<artifactId>jsch</artifactId>
30+
<version>0.1.56</version>
31+
</dependency>
32+
33+
<!-- testing -->
34+
<dependency>
35+
<groupId>org.junit.jupiter</groupId>
36+
<artifactId>junit-jupiter</artifactId>
37+
<version>5.6.2</version>
38+
<scope>test</scope>
39+
</dependency>
40+
41+
<dependency>
42+
<groupId>org.slf4j</groupId>
43+
<artifactId>slf4j-simple</artifactId>
44+
<version>1.7.30</version>
45+
<scope>test</scope>
46+
</dependency>
47+
48+
</dependencies>
49+
50+
<profiles>
51+
<profile>
52+
<id>dummy-file-creation</id>
53+
<activation>
54+
<os>
55+
<family>windows</family>
56+
</os>
57+
</activation>
58+
<build>
59+
<plugins>
60+
<plugin>
61+
<groupId>org.apache.maven.plugins</groupId>
62+
<artifactId>maven-antrun-plugin</artifactId>
63+
<executions>
64+
<execution>
65+
<phase>test-compile</phase>
66+
<configuration>
67+
<tasks>
68+
<exec executable="cmd"
69+
failonerror="true">
70+
<arg value="/c"/>
71+
<arg value="${project.basedir}/src/test/resources/createDummyFile.bat"/>
72+
<arg value="${project.build.testOutputDirectory}/dummy.txt"/>
73+
</exec>
74+
</tasks>
75+
</configuration>
76+
<goals>
77+
<goal>run</goal>
78+
</goals>
79+
</execution>
80+
</executions>
81+
</plugin>
82+
</plugins>
83+
</build>
84+
</profile>
85+
</profiles>
86+
87+
88+
</project>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.github.dockerjava.jsch;
2+
3+
import org.slf4j.LoggerFactory;
4+
5+
public class JschLogger implements com.jcraft.jsch.Logger {
6+
7+
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(JschLogger.class);
8+
9+
@Override
10+
public boolean isEnabled(int level) {
11+
switch (level) {
12+
case DEBUG:
13+
return LOGGER.isDebugEnabled() || LOGGER.isTraceEnabled();
14+
case INFO:
15+
return LOGGER.isDebugEnabled();
16+
case WARN:
17+
return LOGGER.isWarnEnabled();
18+
case ERROR:
19+
case FATAL:
20+
return LOGGER.isErrorEnabled();
21+
default:
22+
throw new IllegalArgumentException("Unknown log level: " + level);
23+
}
24+
}
25+
26+
@Override
27+
public void log(int level, String message) {
28+
switch (level) {
29+
case DEBUG:
30+
LOGGER.debug(message);
31+
break;
32+
case INFO:
33+
LOGGER.info(message);
34+
break;
35+
case WARN:
36+
LOGGER.warn(message);
37+
break;
38+
case ERROR:
39+
LOGGER.error(message);
40+
break;
41+
case FATAL:
42+
LOGGER.error("FATAL: {}", message);
43+
break;
44+
default:
45+
throw new IllegalArgumentException("Unknown log level: " + level);
46+
}
47+
}
48+
49+
50+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.github.dockerjava.jsch;
2+
3+
import com.jcraft.jsch.Session;
4+
import okhttp3.Interceptor;
5+
6+
import java.io.File;
7+
8+
public class SSHDockerConfig {
9+
10+
static final String VAR_RUN_DOCKER_SOCK = "/var/run/docker.sock";
11+
12+
private String socketPath = VAR_RUN_DOCKER_SOCK;
13+
private Session session;
14+
private File identityFile;
15+
private Interceptor interceptor;
16+
private boolean useSocat;
17+
private boolean useTcp;
18+
private boolean useSocket;
19+
private Integer tcpPort;
20+
21+
public Integer getTcpPort() {
22+
return tcpPort;
23+
}
24+
25+
public void setTcpPort(Integer tcpPort) {
26+
this.tcpPort = tcpPort;
27+
}
28+
29+
30+
public String getSocketPath() {
31+
return socketPath;
32+
}
33+
34+
public void setSocketPath(String socketPath) {
35+
this.socketPath = socketPath;
36+
}
37+
38+
public Session getSession() {
39+
return session;
40+
}
41+
42+
public void setSession(Session session) {
43+
this.session = session;
44+
}
45+
46+
public File getIdentityFile() {
47+
return identityFile;
48+
}
49+
50+
public void setIdentityFile(File identityFile) {
51+
this.identityFile = identityFile;
52+
}
53+
54+
public Interceptor getInterceptor() {
55+
return interceptor;
56+
}
57+
58+
public void setInterceptor(Interceptor interceptor) {
59+
this.interceptor = interceptor;
60+
}
61+
62+
public boolean isUseSocat() {
63+
return useSocat;
64+
}
65+
66+
public void setUseSocat(boolean useSocat) {
67+
this.useSocat = useSocat;
68+
}
69+
70+
public void setUseTcp(boolean useTcp) {
71+
this.useTcp = useTcp;
72+
}
73+
74+
public boolean isUseTcp() {
75+
return useTcp;
76+
}
77+
78+
public boolean isUseSocket() {
79+
return useSocket;
80+
}
81+
82+
public void setUseSocket(boolean useSocket) {
83+
this.useSocket = useSocket;
84+
}
85+
}

0 commit comments

Comments
 (0)