Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1c27a44
ssh module
wiedemam-VU May 26, 2020
8ecdcb6
Merge branch 'master' of github.com:mwiede/docker-java
wiedemam-VU May 26, 2020
a1eaac0
added ssh module for testing
wiedemam-VU May 27, 2020
fed83d2
setup ssh
wiedemam-VU May 28, 2020
1b184df
setup docker
wiedemam-VU May 28, 2020
8c3b2d0
try-and-error
wiedemam-VU May 28, 2020
d4e08af
x permissions
wiedemam-VU May 28, 2020
795ebd2
ssh config
wiedemam-VU May 28, 2020
8ed2bf0
adding tmate
wiedemam-VU May 28, 2020
c9e6d8b
sshd already there
wiedemam-VU May 28, 2020
a595815
fixing swarm tests
wiedemam-VU May 29, 2020
8131a63
extracted client factory
wiedemam-VU May 29, 2020
f8911b6
using DOCKER_HOST setting by default
wiedemam-VU May 29, 2020
effaa50
rm tmate
wiedemam-VU May 29, 2020
3d8eed1
Merge remote-tracking branch 'upstream/master'
wiedemam-VU Jun 24, 2020
94c8da8
Merge branch 'ssh-ci'
wiedemam-VU Jun 24, 2020
b683ca1
merged ssh-ci
wiedemam-VU Jun 24, 2020
f81f7d6
negate condition
wiedemam-VU Jun 24, 2020
28862fb
running all ssh test and publish results
wiedemam-VU Jun 24, 2020
f443455
syntax
wiedemam-VU Jun 24, 2020
39ddef0
ignore test failures
wiedemam-VU Jun 24, 2020
48099d2
reverted testFailureIgnore, added failsafe plugin
wiedemam-VU Jun 25, 2020
d556b30
Refactoring and fix for Attch-Stdin Test
wiedemam-VU Jul 20, 2020
0c3c9af
fixing Attch-Stdin Test
wiedemam-VU Jul 20, 2020
ed0ec9c
Merge remote-tracking branch 'upstream/master'
wiedemam-VU Jul 20, 2020
56550fe
increased test timeout
wiedemam-VU Jul 21, 2020
dde5a88
revert Import reordering
wiedemam-VU Jul 21, 2020
e8db67e
revert Import reordering
wiedemam-VU Jul 21, 2020
fde8c8b
Merge branch 'master' of github.com:mwiede/docker-java
wiedemam-VU Jul 21, 2020
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
22 changes: 22 additions & 0 deletions .ci/setup_ssh_config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash

set -exu

mkdir -p ~/.ssh
cd ~/.ssh
ssh-keygen -q -t rsa -N "" -f jsch
cat jsch.pub >> authorized_keys

cat <<EOT >> config
Host junit-host
HostName localhost
StrictHostKeyChecking no
IdentityFile ~/.ssh/jsch
PreferredAuthentications publickey
EOT

chmod go-w $HOME $HOME/.ssh
chmod 600 $HOME/.ssh/authorized_keys

ssh -q junit-host exit

11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
- { name: "default" }
- { name: "over TCP", dockerHost: "tcp://127.0.0.1:2375" }
- { name: "Docker 18.06.3", dockerVersion: "18.06.3~ce~3-0~ubuntu" }
- { name: "ssh-default" , clientDockerHost: "ssh://junit-host"}
- { name: "ssh-19.03.9" , dockerVersion: "5:19.03.12~3-0~ubuntu-bionic", clientDockerHost: "ssh://junit-host"}

steps:
- uses: actions/checkout@v2
Expand All @@ -26,10 +28,19 @@ jobs:
DOCKER_VERSION: ${{matrix.dockerVersion}}
DOCKER_HOST: ${{matrix.dockerHost}}
run: .ci/setup_docker.sh
- name: Create ssh config
run: .ci/setup_ssh_config.sh
if: startsWith(matrix.name,'ssh')
- name: Build with Maven (SSH)
env:
DOCKER_HOST: ${{matrix.clientDockerHost}}
run: ./mvnw --no-transfer-progress -fae verify
if: startsWith(matrix.name,'ssh')
- name: Build with Maven
env:
DOCKER_HOST: ${{matrix.dockerHost}}
run: ./mvnw --no-transfer-progress verify
if: startsWith(matrix.name,'ssh')!=true
- name: Aggregate test reports with ciMate
if: always()
continue-on-error: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ public class DefaultDockerClientConfig implements Serializable, DockerClientConf

private URI checkDockerHostScheme(URI dockerHost) {
switch (dockerHost.getScheme()) {
case "ssh":
case "tcp":
case "unix":
case "npipe":
Expand Down
47 changes: 47 additions & 0 deletions docker-java-transport-ssh/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# docker-java-transport-ssh

Docker client implementation which uses [jsch](http://www.jcraft.com/jsch/) library, a java ssh implementation, to connect to the remote
docker host via ssh.

While native docker cli supports ssh connections since Host docker version 18.09 [<sup>1</sup>](#1), with different options we can also make
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.

The ssh connection configuration relies on basic [ssh config file](https://www.ssh.com/ssh/config/) in ~/.ssh/config.

## dockerd configurations

On the remote host, one can connect to the docker daemon in several ways:

* `docker system dial-stdio`
* `unix:///var/run/docker.sock` (default on linux) https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-socket-option
* `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
* `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
* tcp 2375
* tcp with TLS

## limitations

__jsch__

Since jsch libary from jcraft does not support socket forwarding, a [fork of jsch](https://github.com/mwiede/jsch) is used.

__windows__

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.

Compare OpenSSH tickets:
* https://github.com/PowerShell/Win32-OpenSSH/issues/435
* https://github.com/PowerShell/openssh-portable/pull/433

## connection variants:

By setting flags in [SSHDockerConfig](src\main\java\com\github\dockerjava\jsch\SSHDockerConfig.java), one can control how the connection is made.

* docker system dial-stdio (default)
* direct-streamlocal
* direct-tcpip
* socat

## references

<a class="anchor" id="1">[1]</a> docker ssh support https://github.com/docker/cli/pull/1014
4 changes: 4 additions & 0 deletions docker-java-transport-ssh/alternativeSSHImplementations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
alternative Java ssh implementations:
* https://github.com/apache/mina-sshd
* https://github.com/hierynomus/sshj
* https://github.com/jcabi/jcabi-ssh
68 changes: 68 additions & 0 deletions docker-java-transport-ssh/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>docker-java-parent</artifactId>
<groupId>com.github.docker-java</groupId>
<version>3.2.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>docker-java-transport-ssh</artifactId>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>docker-java-core</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.14.4</version>
</dependency>

<dependency>
<groupId>com.github.mwiede</groupId>
<artifactId>jsch</artifactId>
<version>0.1.59</version>
</dependency>

<!-- testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-failsafe-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.github.dockerjava.jsch;

import com.github.dockerjava.transport.DockerHttpClient;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.connection.Exchange;
import okhttp3.internal.http.RealInterceptorChain;
import okhttp3.internal.ws.RealWebSocket;
import okio.BufferedSink;

import java.io.IOException;
import java.io.InputStream;

class HijackingInterceptor implements Interceptor {

@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (!response.isSuccessful()) {
return response;
}

DockerHttpClient.Request originalRequest = request.tag(DockerHttpClient.Request.class);

if (originalRequest == null) {
// WTF?
return response;
}

InputStream stdin = originalRequest.hijackedInput();

if (stdin == null) {
return response;
}

chain.call().timeout().clearTimeout().clearDeadline();

Exchange exchange = ((RealInterceptorChain) chain).exchange();
RealWebSocket.Streams streams = exchange.newWebSocketStreams();
Thread thread = new Thread(() -> {
try (BufferedSink sink = streams.sink) {
while (sink.isOpen()) {
int aByte = stdin.read();
if (aByte < 0) {
break;
}
sink.writeByte(aByte);
sink.flush();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
});
thread.setName("jsch-hijack-streaming-" + System.identityHashCode(request));
thread.setDaemon(true);
thread.start();
return response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.github.dockerjava.jsch;

import com.jcraft.jsch.Session;

import javax.net.SocketFactory;
import java.net.InetAddress;
import java.net.Socket;

public class JSchSocketFactory extends SocketFactory {

private final Session session;
private final JschDockerConfig config;

JSchSocketFactory(Session session, JschDockerConfig config) {
this.session = session;
this.config = config;
}

@Override
public Socket createSocket() {
return new JschSocket(session, config);
}

@Override
public Socket createSocket(String s, int i) {
throw new UnsupportedOperationException();
}

@Override
public Socket createSocket(String s, int i, InetAddress inetAddress, int i1) {
throw new UnsupportedOperationException();
}

@Override
public Socket createSocket(InetAddress inetAddress, int i) {
throw new UnsupportedOperationException();
}

@Override
public Socket createSocket(InetAddress inetAddress, int i, InetAddress inetAddress1, int i1) {
throw new UnsupportedOperationException();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.github.dockerjava.jsch;

import com.jcraft.jsch.Session;
import okhttp3.Interceptor;

import java.io.File;
import java.util.Hashtable;

class JschDockerConfig {

static final String VAR_RUN_DOCKER_SOCK = "/var/run/docker.sock";

private String socketPath = VAR_RUN_DOCKER_SOCK;
private Session session;
private File identityFile;
private Interceptor interceptor;
private boolean useSocat;
private boolean useTcp;
private boolean useSocket;
private Integer tcpPort;
private Hashtable jschConfig;
private String socatFlags;

public Integer getTcpPort() {
return tcpPort;
}

public void setTcpPort(Integer tcpPort) {
this.tcpPort = tcpPort;
}

public String getSocketPath() {
return socketPath;
}

public void setSocketPath(String socketPath) {
this.socketPath = socketPath;
}

public Session getSession() {
return session;
}

public void setSession(Session session) {
this.session = session;
}

public File getIdentityFile() {
return identityFile;
}

public void setIdentityFile(File identityFile) {
this.identityFile = identityFile;
}

public Interceptor getInterceptor() {
return interceptor;
}

public void setInterceptor(Interceptor interceptor) {
this.interceptor = interceptor;
}

public boolean isUseSocat() {
return useSocat;
}

public void setUseSocat(boolean useSocat) {
this.useSocat = useSocat;
}

public void setUseTcp(boolean useTcp) {
this.useTcp = useTcp;
}

public boolean isUseTcp() {
return useTcp;
}

public boolean isUseSocket() {
return useSocket;
}

public void setUseSocket(boolean useSocket) {
this.useSocket = useSocket;
}

public void setJschConfig(Hashtable jschConfig) {
this.jschConfig = jschConfig;
}

public Hashtable getJschConfig() {
return jschConfig;
}

public String getSocatFlags() {
return socatFlags != null ? socatFlags : "";
}

public void setSocatFlags(String socatFlags) {
this.socatFlags = socatFlags;
}
}
Loading