Kubernetes pod exec API exception: Response must not include 'Sec-WebSocket-Protocol' header if not present in request - google-chrome

I am trying to setup a websocket connection to the Kubernetes Pod Exec API, based on the suggestions given in this SO post: How to execute command in a pod (kubernetes) using API?.
Here's what I have done so far -
Installed Simple Web Socket Client extension in Chrome.
Started kubectl proxy --disable-filter=true to run proxy with WS connections allowed. kubectl.exe version is 1.8.
Used address ws://localhost:8001/api/v1/namespaces/default/pods/nginx-3580832997-26zcn/exec?container=nginx&stdin=1&stdout=1&stderr=1&tty=1&command=%2Fbin%2Fsh in the Chrome extension to connect to the exec api.
When I click connect, Chrome reports back an error with the message -
Error during WebSocket handshake: Response must not include 'Sec-WebSocket-Protocol' header if not present in request
Apparently, kubectl is sending back empty Sec-WebSocket-Protocol header in the response and Chrome is taking offense to that.
I tried changing the code of Simple Web Socket Client open method to send empty protocols parameter to the Websocket client creation call, like - ws = new WebSocket(url, []); to coax Chrome in sending empty header in request, but Chrome doesn't send empty header.
So what can be done to directly connect to the exec in Chrome?

This is a known issue; kubectl proxy does not support websockets. (You can verify this easily by starting up kubectl proxy and then attempting kubectl --server=http://127.0.0.1:8001 exec ...; you will receive the message error: unable to upgrade connection: <h3>Unauthorized</h3> if the filter is enabled and Error from server (BadRequest): Upgrade request required if the filter is disabled).
The confusion might come from the fact that the kube-apiserver proxy does support websockets, but that proxy is different from the kubectl proxy.
As I see you have 3 options now (in order of difficulty):
Access kube-apiserver directly. You will likely need authentication that kubectl proxy is handling for you now
Use SockJS, this is what Kubernetes Dashboard does for the exec feature
Fix #25126

After reading the code in https://github.com/kubernetes-ui/container-terminal/blob/master/container-terminal.js, found that exec uses base64.channel.k8s.io protocol. The Simple Web Socket Client code wouldn't have worked because of this and also that the stream communication is in base64, not plain text.
Leaving this as an answer for other folks trying to implement a WS based terminal emulator... as #janos-lenart mentioned, the code is pretty new and there may be issues using it in different browsers, best bet at this point is to read example code and start from there.

Related

Route to application stopped working in OpenShift 4.6

I have an application running in Openshift 4.6.
The pod is running, I can exec into it and check this, I can port-forward to it and access it.
when trying to access the application, I get the error message:
Application is not available The application is currently not serving
requests at this endpoint. It may not have been started or is still
starting.
Possible reasons you are seeing this page:
The host doesn't exist. Make sure the hostname was typed correctly and
that a route matching this hostname exists.
The host exists, but doesn't have a matching path. Check if the URL
path was typed correctly and that the route was created using the
desired path.
Route and path matches, but all pods are down. Make sure that the
resources exposed by this route (pods, services, deployment configs,
etc) have at least one pod running.
There could be multiple reasons for this. You don't really provide enough debugging details to get to the next steps. But I generally find it helps to work backwards through the request.
Can you access the pod via port-forward? You say you've already tested this, but I include it for completeness. But I also mention it to make sure that you are verifying that you are serving the protocol you expect. If you have HTTPS passthrough on the route, but you are serving HTTP from your pod, there will obviously be a problem.
Can you access the pod providing your service from outside the pod (but within the cluster)? e.g. create a debug pod and see if you can connect to your service with curl some other client. If this doesn't work, you may not be exposing the ports of your pod correctly. Check the pod definitions.
Can you access the service from outside the pod (but within the cluster)? e.g. from your debug pod, use the service directly. If this doesn't work, you may have the selector on your service wrong. Or some other problem with your service. Check the service definition.
Can you access the route from inside the cluster? e.g. from your debug pod, try to use the full route URL. If this doesn't work, you've narrowed it down to the route definition. Again, HTTPS vs HTTP can sometimes be a mistake here such as having HTTPS passthrough when your service doesn't support HTTPS. Check the route definition.
Finally, try accessing the route eternally. Which is sounds like you have already tried. But if you've narrowed it down such that your route works internally you've determined that the problem is something in the external network. It doesn't sound like this is your problem, but it's something to keep in mind.

Connect to MySQL database by using route exposed on openshift

I have just exposed my database on openshift and it gives me an 'https://....' url
Does anybody know how to connect using DBeaver by using this url that openshift gave to me.
The error that dbeaver says to me is the following
Malformed database URL, failed to parse the main URL sections.
Short answer: You can't with aRoute
Route can only expose http/https traffic
If you want to expose tcp traffic (like for a database), do not create aRouteand change yourServicetype to "NodePort"`
Check my previous answer for this kind of problem (exposing MQ in this case): How to connect to IBM MQ deployed to OpenShift?
OpenShift doc on NodePorts: https://docs.openshift.com/container-platform/4.7/networking/configuring_ingress_cluster_traffic/configuring-ingress-cluster-traffic-nodeport.html
There's another way to do this.
If your Route is set to "passthrough" it will just look at the SNI headers to determine where to route the traffic but won't unwrap it (and expect http inside) which will let it pass other traffic through to a pod.
I use this mechanism to run a ZNC bouncer (irc traffic) behind SNI.
The downside is you need to provide your own TLS cert inside the pod instead of leveraging the general one available to *.apps.(cluster).com
As for the specific error, "Malformed database URL", I've not used this software but from a quick websearch it looks like you want to rewrite the https://(appname).(clustername).com into a jdbc:.../hostname... string, and then enable TLS in settings.
I found this page that talks about setting it up, so it might be helpful if you've not around found it -- https://github.com/dbeaver/dbeaver/issues/9573

fabric8 kubernetes client exception

I am using the fabric8 library to create replication controllers on the kubernetes cluster. When I create areplication controller with the name rc-UUID.toString();
It errors aout with the following message
ReplicationController is forbidden. What does this indicate?
I don't think that the problem is the name.
Names like: rc-fa75ddfd-bea7-45b5-8d2f-ed806652b461 are valid.
Usually the message forbidden appears in one of the following cases:
i) http status code 401
ii) https status code 404
Error (i) appears when you are connecting to a remote Kubernetes Environment and can be solved by login in. Try kubectl login or oc login.
Error (ii) appears when the client is instantiated from within Kubernetes and the service account hasn't been properly configured.
Either way, I'd strongly encourage you to upgrade to a more recent version of the cabernets client, which has more meaningful error messages.
If none of my suggestion solves your issue, please attach the full output.
Cheers

Server Sent Events in Google Compute Engine

I'm trying to get an app that uses Server Sent events working on Google Compute Engine, when SSH'd into the box I can view them, but not externally via the ephermeral IP, aka
curl 0.0.0.0/route
works from inside the box but
curl xx.xx.xx.xx/route
just hangs, looking at the headers from other routes there seems to be some sort of cacheing proxy in between the box and the outside word that is preventing server sent events from getting out because the the connection hasn't completed, there is a similar issue with nginx until you set proxy_cache off, but as far as I can tell there is no documentation for configuring the proxy that compute engine uses.
Is it possible to do server sent events from Google Compute Engine and if so what do you have to do to get it to work?
edit:
Request is created with the browser EventSource object, so it has the default headers which look to be Accept:text/event-stream, Cache-Control:no-cache, plus Referer and User-Agent.
The headers I add are Content-Type:text/event-stream, Cache-Control:no-cache, and Connection:keep-alive.
When run in AWS all is fine when I run it behind nginx assuming I modify the config appropriately.
In Google Compute Engine other pages load fine but the route with Server Sent Events just hangs never even receiving headers. The reason I suspect google is sticking a proxy between the GCE box and the outside world is the addition of Via:HTTP/1.1 proxy10205 headers.
There may be magic on the lower network layers but there is no (transparent or otherwise) proxy between your VM and the internet on GCE for the external IP. I'm not sure where the Via header comes from, doesn't the browser/client have a proxy configured?
External IPs are not configured in the most straightforward way on GCE though which might be tripping up something in the stack. I think for external IPs, the external IP itself does not appear anywhere in the VM config, it's translated to the VM internal IP by 1-1 NAT. Loadbalanced IPs do end up on the host with external IP visible though (even though even these are configured in a funny way).
Even though I don't think anything should really care about the server IP for SSE, maybe try setting up a loadbalanced IP pointing to just that one instance and see if it works any better?
"Via:HTTP/1.1 proxy10205" in your HTTP response is not from Google Compute Engine.
The GCE does not strip out the Server-Sent-Events headers. I list the simple steps below which can help you to configure a demo Server-Sent Events on an GCE VM instance:
Create an GCE instance using CentOS image.
Install Apache web server and PHP:
$ sudo yum install httpd php
Create an index.html file with the HTML content from this page :
$ sudo vi /var/www/html/index.html
Create a PHP file called demo_sse.php in the www root directory ($ sudo vi /var/www/html/demo_sse.php ) with the following content:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$time = date('r');
echo "data: The server time is: {$time}\n\n";
flush();
?>
Now visit the webpage. You can also verify the header using curl command:
$ curl -H "Accept:text/event-stream" --verbos http://<YOUR-GCE-IP ADDRESS>/demo_sse.php

Connecting to HTML5 Websocket

I'm a bit confused about HTML5 Websockets. I've looked at numerous tutorials out there and a lot of them have different variations of connecting using different ports. What do these ports mean?
Adobe for instance, uses this:
new WebSocket('ws://localhost:1740');
Then another tutorial has this where no ports are required:
new WebSocket("ws://www.websockets.org");
And finally a third tutorial has a port, but it's completely different:
new WebSocket("ws://localhost:8080/echo");
My question would be, why do these vary? How do I know which ports to connect to? Also, I've attempted to do my own connection:
var ws = new WebSocket("ws://test.ontarget-network.com/");
But I get the following error: Unexpected response code: 200
I've tested around and tried connecting to various other "ports" (not knowing what I'm doing obviously, typing in random numbers) and this error would disappear, however, my code
ws.onopen = function(){
alert("Connection Established");
};
would not execute.
I'm trying to fully understand HTML5's Websockets API so I can experiment and create more dynamic applications. Thanks for the help.
The server should have an endpoint that accepts WebSocket connections. So, if that endpoint is /echo you would want to connect to:
ws://localhost:8080/echo/websocket
You will get the Unexpected response code: 200 error if you exclude the /websocket suffix after the endpoint. I was having the same confusion and this link cleared things up a bit for me.
The following comes from the latest WebSocket draft:
By default the WebSocket protocol uses port 80 for regular WebSocket
connections and port 443 for WebSocket connections tunneled over TLS
[RFC2818].
Really though, you should be able to use any valid port not in use. As long as clients are trying to connect to the same port that the server-side script opens for the socket connection, you should be fine.
A quick note on ports:
Port 80 is the HTTP port.
Port 8080 is the alternate HTTP port.
Port 443 is the HTTPS (i.e., HTTP with TLS) port.
Port 1740 in the Adobe code seems like some random port not already in use by other services.
For a full list of preset ports, please see the following:
http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
As for your "Unexpected response code: 200" error, I'm guessing that the WebSocket URL you're using on the client side is not pointing to a valid server-side script, but that's hard to comment on without more info.
I had the same issue, But to survive with
Unexpected response code: 200
You need to have either server-side script to handle the web socket, or you can use Node.js to build a you server script.
for the sake of education you can try to biuld your own websocket sever script.
Actually there is something else... You can not open a connection to every port since there is a list of blocked ports in every browser. I remember seeing the full list of ports in 'The tangled Web' from Michal Zalewski; however, I think a quick google will show this also.