Websocket works on EC2 url but not on ElasticBeanstalk URL - nginx

Background
I have reverse proxy (nginx) pointing to ElasticBeanstalk (ELB) that is a singleInstance environment type and creates an EC2 instance (EC2). I am using a dockerized nodejs app. nginx is the entry point for our infrastructure.
Desciption
From browser, I can call directly Websocket URLs of an EC2 and of ELB with the same result:
Welcome to SockJS!
For nginx I use the following config (nginx conf.d/my.conf) where I only change URLs of EC2 or ELB on the line beginning with proxy_pass:
location /stomp {
proxy_pass <{EC2_URL}/stomp OR {ELB_URL}/stomp>;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Websockets on the EC2 URL are just working fine.
Websockets on the ELB URL return an error status back to my client:
code: 1002,
reason: "Cannot connect to server"
The strangest thing is that my nginx server doesn't even log the requests in case of ELB.
Question
Why is the ELB not working in the nginx configuration? How to correct it?
What I tried?
I have checked a question and a blog where they advice to override nginx config files like 00_elastic_beanstalk_proxy.conf located in located in /tmp/deployment/config/etc#nginx#nginx.conf. But my server doesn't have any nginx configuration files on the filesystem...

The issue is related to your proxy_set_header Host $host;. The issues is because of the way different server reacts to the Host name header.
Consider when you open the below urls in browser
http://ec2-52-59-53-38.eu-central-1.compute.amazonaws.com/stomp
http://stag-ws-server.eu-central-1.elasticbeanstalk.com/stomp
One being EC2 and one being an ELB. When you send the request to ec2 the Host name is sent as ec2-52-59-53-38.eu-central-1.compute.amazonaws.com. Now if you have service which listen on port 80 directly or through docker. It is listening to port 80 and doesn't care how the traffic comes to it.
It just knows that if someone reaches the server by any means we respond. Now if you had a nginx on that EC2, that only listens to a specific Virtual Host and doesn't respond to any other host name, then if you send it ec2-52-59-53-38.eu-central-1.compute.amazonaws.com and it expects to see abc.mydomain.com then nginx wont respond to the request.
Same is the case that is happening with your ELB server. You are hosting nginx at some domain name abc.domain.com and using proxy_pass to pass the traffic to ELB. But using proxy_set_header Host $host;, set the host name to abc.domain.com and ELB is not able to understand where that host is. So it wont server the request and hence the error

Related

How to fix 302 redirect proto error in Jenkins when using aws ELB?

I'm using Jenkins with ec2 and Elastic Balancer(ELB). Everything works fine until I add https protocol to ELB.
The architecture shown as below:
client (https) <-> ELB (Http) <-> Nginx (Http) <-> Jenkins
Protocol https only happens between client and ELB. When first visit Jenkins home page, visitor is redirected to the login page. Here comes the problem, as you can see, for Jenkins itself, it only can see the http request. However, the real address should start with https.
After debugging, it turns out that spring security component return the 302 response http://jenkins/login instead of https://jenkins/login. For further information, I found out that spring security component gets proto from servlet server, who only can see the http request.
I don't think this make sense or there are some problems, because in the nginx configuration file, I wrote this
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
I've tried to change the 302 Location header to https://jenkins/login, obviously, this is not the solution.
How can I solve this problem?

Kubernets (k3s) kubectl acces through reverse proxy

I'm running a Kubernetes (k3s) server on a raspberry pi cluster locally, which then is connect to a VM on digital ocean via a VPN (Tailscale), I've successfuly manage to make reverse proxy to my services on the cluster using nginx, but when I want to point a domain to my kube api server it just keep getting unauthorized responses.
In my Nginx config I've set it up something like this:
server {
server_name kube.domain.com;
location / {
proxy_pass https://xx.xx.xx.xx:6433;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
I'm using kubectl setting my server to: kube.comain.com
And here I get the 401, but if i set the server to my ip on the localhost it works fine, so im wondering why do I get a 401, since I clearly contact my Kube API Server.
Nginx Reverse proxy by default strips the certifcate data that kubectl sends with the request and there for the request will end up with at 401.
Solution should be to create a raw TCP stream targeting the IP of the kubernetes api server minimal example like so:
nginx.conf
stream {
upstream api {
server <kube-api-server-ip>:6443;
}
server {
listen <port>; # this is the port exposed by nginx on your proxy server
proxy_pass api;
proxy_timeout 20s;
}
}
Doing the above would proxy the raw request directly to the kube-apiserver.
more detailed answer can be found here both TCP L4 layer and L7 layer solutions: https://www.henryxieblogs.com/2021/12/how-to-expose-kube-api-server-via-nginx.html

How to setup Reverse Proxy on NGINX to External Website (with a path)

suppose i Have a server on ip 111.111.111.111
on which nginx server is installed as a web server
I want that if someone visit on
http://111.111.111.111/new
They should see the content of
http://example.github.io/new
Someone told me it is possible via NGINX Reverse Proxy or Apache Reverse Proxy
The trick of configuring sites-enabled/default works fine for locally hosted sites on different port but it is not working for external website.
What i have done for this (on server 111.111.111.111):
in /etc/nginx/sites-enabled/default
server {
listen 80;
location /new/ {
proxy_pass http://example.github.io/new;
proxy_redirect default;
proxy_buffering off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Protocol $scheme;
}
}
What i got :
404
There isn't a GitHub Pages site here.
GitHub Pages allows repository owners to associate a domain with that repository, which it verifies by making sure that domain's A/CNAME records are pointed to GitHub.
If you are pointing the entirety of your domain to this GitHub page, then you do not need a reverse proxy, as the A records will accurately point your domain to GitHub Pages.
Your configuration aims to only reverse proxy a directory of your domain, instead of your domain as a whole. However, you are setting your Host header to match the Host header as requested by the client. As a result, GitHub Pages is checking if that repository is configured to receive traffic as 111.111.111.111, which it is not. This is why you receive the 404 error -- there is not a GitHub Pages configuration that matches that repository and host combination.
Change the Host header in proxy_set_header to example.github.io so that, even though the client requested 111.111.111.111, GitHub receives the request as a request for example.github.io.

Websockets on ElasticBeanstalk giving 404

I'm trying to deploy a websocket server to Elastic Beanstalk.
I have a Docker container that contains both nginx and a jar server, with nginx just doing forwarding. The nginx.conf is like this:
listen 80;
location /ws/ { # <-- this part only works locally
proxy_pass http://127.0.0.1:8090/; # jar handles websockets on port 8090
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location / { # <-- this part works locally and on ElasticBeanstalk
proxy_pass http://127.0.0.1:8080/; # jar handles http requests on port 8080
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
I can run this docker locally and everything works fine - http requests are served, and I can connect websockets using ws://localhost:80/ws/ However, when I deploy to Elastic Beanstalk, http requests are still ok, but trying to connect websockets on ws://myjunk.elasticbeanstalk.com:80/ws/ gives a 404 error. Do I need something else to allow websockets on Elastic Beanstalk?
Ok, got it working. I needed the ElasticBeanstalk load balancer to use TCP instead of HTTP.
To do this from the AWS console (as it's laid out on 5/16/2015), go to your ElasticBeanstalk environment, choose "Configuration" on the left menu, under "Network Tier" there's a "Load Balancing" pane. Click its cog wheel, then you can change the load balancer protocol from http to tcp.

nginx reverse proxy to backend running on localhost

EDIT: It turns out that the my setup below actually works. Previously, I was getting redirections to port 36000 but it was due to some configuration settings on my backend application that was causing it.
I am not entirely sure, but I believe I might be wanting to set up a reverse proxy using nginx.
I have an application running on a server at port 36000. By default, port 36000 is not publicly accessible and my intention is for nginx to listen to a public url, direct any request to the url to an application running on port 36000. During this entire process, the user should not know that his/her request is being sent to an application running on my server's port 36000.
To put it in more concrete terms, assume that my url is http://domain.somehost.com/
Upon visiting http://domain.somehost.com/ , nginx should pick up the request and redirect it to an application already running on the server on port 36000, the application does some processing, and passes the response back. Port 36000 is not publicly accessible and should not appear as part of any url.
I've tried a setup that looks like:
server {
listen 80;
server_name domain.somehost.com
location / {
proxy_pass http://127.0.0.1:36000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
and including that inside my main nginx.conf
However, it requires me to make port 36000 publicly accessible, and I'm trying to avoid that. The port 36000 also shows up as part of the forwarded url in the web browser.
Is there any way that I can do the same thing, but without making port 36000 accessible?
Thank you.
EDIT: The config below is from a working nginx config, with the hostname and port changed.
You need to may be able to set the server listening on port 36000 as an upstream server (see http://nginx.org/en/docs/http/ngx_http_upstream_module.html).
server {
listen 80;
server_name domain.somehost.com;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-NginX-Proxy true;
proxy_pass http://localhost:36000/;
proxy_redirect http://localhost:36000/ https://$server_name/;
}
}

Resources