I'm trying to communicate a simple message between my host machine and my docker container.
I began by creating my docker container with the host network driver:
docker run -t -d --network host --name cfalcon ubuntu
Next I entered the container:
docker exec -it cfalcon
I identified my host IP as 192.168.0.109 and my container's IP as 172.17.0.1 using:
hostname -I
I made a file at ~/Documents/tcp_server.c in my container with the following code:
TCP_SERVER.C
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char* argv[]) {
char address[] = "192.168.0.109";
// Creating Server Socket
int server_socket;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
// Defining Server Address
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
// Specify the port, 9001 seems safe
server_address.sin_port = htons(9001);
server_address.sin_addr.s_addr = inet_addr(address);
// Binding our Socket to our Specified IP, 192.168.0.109
bind(server_socket, (struct sockaddr *) &server_address, sizeof(server_address));
// Listen() Function Begins Listening for a Connection
listen(server_socket, 1);
// Ability to Accept Connections
int client_socket;
client_socket = accept(server_socket, NULL, NULL);
char server_message[256] = "Its-a me, a-mario!";
// Send a message with send()
send(client_socket, server_message, sizeof(server_message), 0);
close(server_socket);
return 0;
}
Returning to my host machine, I decided to make a new file at ~/Documents/tcp_client.c with the following code:
TCP_CLIENT.C
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char* argv[]) {
char address[] = "172.17.0.1";
// Creating a Socket
int network_socket;
// socket(domain of socket (generally AF_INET, a constant), TCP socket, protocol)
network_socket = socket(AF_INET, SOCK_STREAM, 0);
// Specifying an Address for the Socket (Address family is same as parameter one of socket())
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
// Specify the port, 9001 seems safe
server_address.sin_port = htons(9001);
server_address.sin_addr.s_addr = inet_addr(address);
// Returns an integer for error handling
int connection_status = connect(network_socket, (struct sockaddr *) &server_address, sizeof(server_address));
// Error Handling
if (connection_status == -1) {
printf("There was an error making a connection to the remote socket.\n\n");
}
// If we send data, we receive a response from the server in recv
char server_response[256];
recv(network_socket, &server_response, sizeof(server_response), 0);
// Printing the Server's Response
printf("The server responded with: %s\n", server_response);
// Avoiding data leaks
close(network_socket);
return 0;
}
Unfortunately, I did not receive "Its-a me, a-Mario!", but rather "There was an error making a connection to the remote socket".
I believed I perfectly linked the two using respective IP's and identical port numbers, but it would appear I haven't. Please let me know if you have any guidance toward my predicament.
Edit: Update! I installed Netcat on my container and established a connection between my host and my container.
Host: $ nc -l 9001
Container: $ nc localhost 9001
Any message sent from the container in the following prompt reaches my host machine, which tells me this is solely an error with my code.
At the lowest level, your server code bind(2) to 192.168.0.9, but your client code connect(2) to 172.17.0.1. The server will only accept connections to the single address it's bound to. If you're using Docker host networking, the server process is indistinguishable from any other process running on the host from a network point of view, and while 172.17.0.1 will presumably reach the host as well, since it's not exactly the same address, your connection will be refused.
If the server is running in a Docker container, and the client is running directly on the same host:
bind(2) to 0.0.0.0. (Binding to 127.0.0.1 will make the process unreachable from outside the container; the container-private IP address is unpredictable.)
Do not start the container with --net host. Do start it with an appropriate -p option mapping the container port to some host port; for example, -p 9001:9001.
From the client, running not in Docker, on the same host, connect(2) to 127.0.0.1 port 9001. From a different host (Docker or otherwise), use the first host's name.
If the client is running in a different container on the same host:
bind(2) to 0.0.0.0.
docker create network something. You don't need any additional arguments, but you do need to create the network.
When you docker run both containers, start them with --net something matching the network you previously created, and a useful --name.
In the client, use getaddrinfo(3) to look up the server's container name; then connect(2) to that sockaddr.
Note that neither of these paths involve looking up the container-private IP address (except as a low-level detail in an ordinary "connect to this hostname" path). This changes routinely (whenever you delete and recreate a container), isn't accessible from off-host, and isn't even accessible from the host console on some Docker setups. There's never a need to do look this up.
Also note that neither path involves docker exec. It's a useful debugging tool but it isn't usually part the core container workflow. (If you find yourself "docker run a container and then immediately docker exec", generally you should change the container startup sequence to do whatever the docker exec thing is itself.)
Related
How can I create a docker network using testcontainers which:
allows for all containers in the network to communicate with each
allows for containers to map ports to the host
but does not allow containers to have access to the internet
I have tried to do this using an internal network:
private Network generateInternalNetwork() {
// Consumer which operates on the final CreateNetworkCmd which will be run to
// make sure the 'internal' flag is set.
Consumer<CreateNetworkCmd> cmdModifier = (createNetworkCmd) -> {
createNetworkCmd.withInternal(true);
};
return Network.builder()
.createNetworkCmdModifier(cmdModifier)
.build();
}
However, when I run this I cannot have my port mapped. An exception is thrown:
Caused by: java.lang.IllegalArgumentException: Requested port (8024) is not mapped
If I run it without withInternal(true) it works fine but of course the containers have internet access.
I think you can get what you want by (a) creating normal networks, and then (b) adding a DROP rule to your DOCKER-USER firewall chain:
iptables -I DOCKER-USER -j DROP
In my quick experiment just now, this let me map ports from containers, but prevented the containers from accessing the internet (because this chain is called from the FORWARD chain, to it prevents containers from forwarding traffic through the host to the outide internet).
After spending a few days trying different things I have come up with a hack of a solution that kind-of works:
/**
* Set an invalid DNS for the given container.
* This is done as a workaround so that the container cannot access
* the internet.
*/
void setInvalidDns() {
GenericContainer<?> container = getContainer();
Consumer<CreateContainerCmd> modifier = (cmd) -> {
// Amend the config with the garbage DNS.
String invalidDns = "255.255.255.255";
HostConfig hostConfig = cmd.getHostConfig();
hostConfig.withDns(invalidDns);
cmd.withHostConfig(hostConfig);
};
container.withCreateContainerCmdModifier(modifier);
}
This sets the container's DNS to an invalid IP and then when you try to make a HTTP request in the container it will throw a java.net.ConnectException.
I have configured my router to expose http 80 on my local machine ip address:
ie '192.168.0.79', and exposed both inbound and outbound ip address, including allowing through firewall. For the purpose of this example lets say its "200.200.200.200"
I have a node server running locally on this same ip address which works and I can see 'hello world' when I visit my exposed ip address, eg: 200.200.200.200 on my web browser. This works.
import yargs from 'yargs';
import express from 'express';
const app = express();
const argv = yargs.argv;
const host = argv.host ;
const port = argv.port;
app.get('/', (req, res) => res.send('Hello World!'));
app.listen(port, host, function() {
console.log('listening on ', host, ':', port);
});
when I stop the node server and instead run a docker container on the same ip address as follows:
docker run -p 192.168.0.79:80:8080 -p 50000:50000 --name myjenkins -v %cd%/jenkins:/var/jenkins_home jenkins/jenkins
I can see this locally on my machine, but when trying to access it from external webbrowser, eg: "200.200.200.200" it simply returns - HTTP ERROR 504
Is there something else I need to expose via the docker container to make this visible online?
I'm having the same issue with an nginx image. So i'm convinced there is something missing in my docker arguments.
Dockerfile
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY dist /usr/share/nginx/html/dist
COPY nginx/default.conf /etc/nginx/conf.d/
docker build -t nginx_image .
docker run -p 192.168.0.79:80:8080 nginx_image
Sounds like a return route issue. Log onto your docker container and see if you can ping 8.8.8.8. Also run netstat -r and see what the default route is. It should be the internal IP address of your firewall.
ok so on much exhaustive research it seems there might be a problem with windows exposing these containers. Or it might be something more advanced regarding proxying this container to the outside.
My solution. Create a node server that proxys to the localhost on my machine.
step 1 - get my ip address for this particular desktop computer on the ethernet
start > cmd
ipconfig
Ethernet adapter Ethernet 4 (Yours will be different. Which ever is connected to the internet):
...
IPv4 Address. . . . . . . . . . . : 192.168.0.79
step 2 - configure router, sky or other, to expose this ip to the internet
visit 192.168.0.2
user: admin
pass: sky
Advanced > Lan IP Setup > LAN TCP/IP Setup
LAN TCP/IP Setuphelp
IP Address:
192. 168. 0. 1
IP Subnet Mask:
255. 255. 255. 0
TICK - Use Router as DHCP Serverhelp
Starting IP Address:
192. 168. 0. 2
Ending IP Address:
192. 168. 0. 254
Address Reservation > Add
cmd
ip address: 192.168.0.79
Mac adress: (This number will look something like 4c:a2:e0 etc.... - can by got by going to a website and typing whats my ip)
Device Name: (Right click my computer > properties) MYCOMPUTERNAME
Security > Firewall Rules > Outbound services > Edit
Service: http: tcp 80
action: allow always
access from: any
0 0 0 0
Security > Firewall Rules > Inbound services > Edit
Service: http: tcp 80
action: allow always
Destination IPv4 LAN address: 192.168.0.79
access from: any
step 3 - create a docker container (ie jenkins), that will default to localhost, and expose the port on something other than 80, ie 81. (We need 80, to be exposed via our router)
Create docker container on localhost:81
docker run -p 81:8080 -p 50000:50000 --name myjenkins -v %cd%/jenkins:/var/jenkins_home jenkins/jenkins
step 4 - Create a node server or equivalent that will proxy the exposed ip address to this localhost
Create a proxy server that redirects 192.168.0.79 to localhost:81
import express from 'express';
import httpProxy from 'http-proxy';
const app = express();
const host = '192.168.0.79' ;
const port = '80';
const apiProxy = httpProxy.createProxyServer();
app.all('/*', (req, res) => {
console.log('redirecting to docker container - http://localhost:81');
apiProxy.web(req, res, {target: 'http://localhost:81'});
});
app.listen(port, host, function() {
console.log('listening on ', host, ':', port);
});
step 5 - type into a webbrowser - whats my ip
ipv4 will by something like 30.132.323.11
Now type this into a webbrowser and you should see your docker container exposed via the node server proxy.
I have a go grpc service. I'm developing on a mac, sierra. When running a grpc client against the service locally, all is well, but when running same client against same service in the docker container I get this error:
transport: http2Client.notifyError got notified that the client transport was broken EOF.
FATA[0000] rpc error: code = Internal desc = transport is closing
this is my docker file:
FROM golang:1.7.5
RUN mkdir -p /go/src/github.com/foo/bar
WORKDIR /go/src/github.com/foo/bar
COPY . /go/src/github.com/foo/bar
# ONBUILD RUN go-wrapper download
RUN go install
ENTRYPOINT /go/bin/bar
EXPOSE 51672
my command to build the image:
docker build -t bar .
my command to launch the docker container:
docker run -p 51672:51672 --name bar-container bar
Other info:
client program runs fine from within the docker container
connecting to a regular rest endpoint works fine (http2, grpc related?)
running the lsof command in OS X yields these results
$lsof -i | grep 51672
com.docke 984 oldDave 21u IPv4 0x72779547e3a32c89 0t0 TCP *:51672 (LISTEN)
com.docke 984 oldDave 22u IPv6 0x72779547cc0fd161 0t0 TCP localhost:51672 (LISTEN)
here's a snippet of my server code:
server := &Server{}
endpoint := "localhost:51672"
lis, err := net.Listen("tcp", endpoint)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer(grpc.Creds(creds))
pb.RegisterExpServiceServer(s, server)
// Register reflection service on gRPC server.
reflection.Register(s)
log.Info("Starting Exp server: ", endpoint)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
When you specify a hostname or IP address​ to listen on (in this case localhost which resolves to 127.0.0.1), then your server will only listen on that IP address.
Listening on localhost isn't a problem when you are outside of a Docker container. If your server only listens on 127.0.0.1:51672, then your client can easily connect to it since the connection is also made from 127.0.0.1.
When you run your server inside a Docker container, it'll only listen on 127.0.0.1:51672 as before. The 127.0.0.1 is a local loopback address and it not accessible outside the container.
When you fire up the docker container with "-p 51672:51672", it'll forward traffic heading to 127.0.0.1:51672 to the container's IP address, which in my case is 172.17.0.2.
The container gets an IP addresses within the docker0 network interface (which you can see with the "ip addr ls" command)
So, when your traffic gets forwarded to the container on 172.17.0.2:51672, there's nothing listening there and the connection attempt fails.
The fix:
The problem is with the listen endpoint:
endpoint := "localhost:51672"
To fix your problem, change it to
endpoint := ":51672"
That'll make your server listen on all it container's IP addresses.
Additional info:
When you expose ports in a Docker container, Docker will create iptables rules to do the actual forwarding. See this. You can view these rules
with:
iptables -n -L
iptables -t nat -n -L
If you use docker container to run grpc service,you should make sure grpc service and grpc client on ths same bridge
if you are using docker container, u can define network by IP and give this IP to your IP address(like: 178.20.0.5).
I'm using Windows 10 and Visual Studio 2016. I can't bind to port 80. I can bind to all other ports. The error printed is: "Bind of IP address 0.0.0.0 returned an error, port 80: No error"
Here is my code:
/* bind this socket to the server's Internet address */
if( bind(fd,(struct sockaddr *)server_addr,sizeof(struct sockaddr_in))<0 )
{
printf("Bind of IP address %s returned an error, port %d: %s\n",
inet_ntoa(server_addr->sin_addr), ntohs(server_addr->sin_port),
strerror(errno));
//close(fd);
return -1;
}
Use "netstat -o -q -a -n". Then use task manager and look at the Details tab. Click to sort the PID as low to high. Find the PID and notice the name of the program that has the port open. In my case System is listening on port 80 and since you can't kill System then you basically can't bind to port 80.
Well, you can use netstat to see if anyone else is listening, see this article:
https://technet.microsoft.com/en-us/library/bb490947.aspx
Find which process is already using port 80 and stop it.
You also need to be an admin or explicitly grant access to the user you're running as if you're binding port < 1024. See here
HttpListener Access Denied
I have the DNS server Unbound in a docker container. This container has the following port mapping in the docker deamon:
0.0.0.0:53->53/tcp, 0.0.0.0:53->53/udp
The docker host has the IP address 192.168.24.5 and a local DHCP server announces the host's IP as the local DNS server. This works fine all over my local network.
The host itself uses this DNS server through the IP 192.168.24.5. That's the address that is put to the host's /etc/resolv.conf. (I know it would not work with docker if there was 127.0.0.1 as the nameserver address.)
I have some other docker containers and they are supposed to use this DNS server as well. The point is, they don't.
What actually happens is this:
Whithin a random container I can ping the host's address as well as the address of the unbound-container. But when I use dig inside a container I get these results:
# dig #172.17.0.6 ...
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 22778
;; flags: qr rd ad; QUERY: 0, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
# dig #192.168.24.5 ...
;; reply from unexpected source: 172.17.0.1#53, expected 192.168.24.5#53
This looks like some internal DNS server intercepts the queries and tries to answer them. That would be fine if it would use the host's DNS server to get an answer, but it doesn't. DNS doesn't work at all in the containers.
Am I doing wrong or is docker doing something it should not ?
The issue is iptables UDP nat for DNS server. You're querying the host IP while it's the docker bridge network's response.
To fix this issue in at least to ways:
Use container IP (DNS container) as DNS resolver if possible.
or
Provide --net=host to your DNS server container and remove port mapping altogether. Then host IP DNS would work as expected.