Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use ssh port forwarding to expose host's ports to the containers #806

Merged
merged 7 commits into from
Aug 16, 2018

Conversation

bsideup
Copy link
Member

@bsideup bsideup commented Jul 28, 2018

// tell Testcontainers which host ports to expose to your containers
Testcontainers.exposeHostPorts(someHostPortNumber);

// later, just give your container this address to use to reach back to the host:
address = "http://host.testcontainers.internal:" + someHostPortNumber;

@bsideup bsideup requested review from rnorth and kiview July 28, 2018 15:46
@bsideup bsideup added this to the next milestone Jul 28, 2018
Copy link
Member

@rnorth rnorth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty awesome, nice work!

I assume we are OK regarding security - this will not be accessible on any real external (LAN) network, will it?

We can update the docs after releasing. I think this will warrant some major examples updates too :)

@@ -245,6 +248,13 @@ private void tryStart(Profiler profiler) {
applyConfiguration(createCommand);

containerId = createCommand.exec().getId();

PortForwardingContainer.INSTANCE.getNetwork().map(ContainerNetwork::getNetworkID).ifPresent(networkId -> {
if (!Arrays.asList(networkId, "none", "host").contains(createCommand.getNetworkMode())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah good, does this prevent the sshd container being connected to the host network?

Obviously it would be bad to let this thing be accessible on real networks 😬

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

host & none networks are not connectable, this is why I added it :)

We could also use a random password to improve the security

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If adding a random password isn't a massive change, let's do it. I can't think of a practical attack via this container, but we may as well just in case we've missed something.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to generate a random password

@bsideup bsideup removed this from the next milestone Jul 30, 2018
@bsideup bsideup added this to the next milestone Jul 31, 2018
Copy link
Member

@kiview kiview left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really love this feature, already thinking about how to refactor some of our tests at work, which are hardcoded interacting with the Linux host network.

import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public enum PortForwardingContainer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason to use an Enum here and not a Singleton? Maybe I'm missing something obvious, or do we use this approach elsewhere as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's the easiest way to achieve singleton in JVM, used by Spring and others.

https://dzone.com/articles/java-singletons-using-enum

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I didn't know this, nice I could learn something new 😄

@SneakyThrows
public void exposeHostPort(int port) {
if (exposedPorts.add(port)) {
getSshConnection().requestRemotePortForwarding("", port, "localhost", port);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method of the ssh lib is so cool 😎

@@ -85,6 +86,8 @@

public static final int CONTAINER_RUNNING_TIMEOUT_SEC = 30;

public static final String INTERNAL_HOST_HOSTNAME = "host.testcontainers.internal";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe better, to use host.testcontainers here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the .internal TLD is technically 'wrong' to use, I can't actually find an official TLD that is properly reserved and semantically correct.

Given that Docker is host.docker.internal, I feel it's OK to follow in their tracks and keep host.testcontainers.internal.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

K, makes sense to be similar to Docker.

@@ -245,6 +248,13 @@ private void tryStart(Profiler profiler) {
applyConfiguration(createCommand);

containerId = createCommand.exec().getId();

PortForwardingContainer.INSTANCE.getNetwork().map(ContainerNetwork::getNetworkID).ifPresent(networkId -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT about refactoring to private method for better readibility? Took me some time to get the high level view what's happening here (and I possibly even got it wrong):

[...]
String networkMode = createCommand.getNetworkMode();
connectToPortForwardingNetwork(networkMode);
[...]



private void connectToPortForwardingNetwork(String networkMode) {
        PortForwardingContainer.INSTANCE.getNetwork().map(ContainerNetwork::getNetworkID).ifPresent(networkId -> {
            if (!Arrays.asList(networkId, "none", "host").contains(networkMode)) {
                dockerClient.connectToNetworkCmd().withContainerId(containerId).withNetworkId(networkId).exec();
            }
        });
    }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 yes, I think that would help the readability.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kiview extracted

}

Optional<ContainerNetwork> getNetwork() {
return Optional.ofNullable(container)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have the same exact code multiple times in our code base, so we could add a utility method for this? (not in this PR).

Optional<ContainerNetwork> getFirstNetwork() in GenericContainer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this code doesn't have any logic in it (and just calls the getters), but exposing it as public API (in Testcontainers) makes me feel uneasy, I would rather not do that

@kiview
Copy link
Member

kiview commented Aug 3, 2018

@rnorth AFAIK the SSH server is only available to Docker networks, if I understood the implementation correctly?

@rnorth
Copy link
Member

rnorth commented Aug 5, 2018

@kiview yep, that makes perfect sense.

@bsideup bsideup modified the milestones: 1.8.3, next Aug 6, 2018
@bsideup bsideup merged commit dabe7da into master Aug 16, 2018
@bsideup bsideup deleted the expose_host_ports branch August 16, 2018 13:29
@rnorth
Copy link
Member

rnorth commented Sep 10, 2018

We have this out in a Release Candidate build (1.9.0-rc1) for anyone who is keen to try it!

Release notes

@kiview kiview removed this from the 1.9.0-rc1 milestone Jun 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants