Kubernets (k3s) kubectl acces through reverse proxy - nginx

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

Related

With NGINX upstreams, is it possible to proxy pass to both HTTP and HTTPS backends in the same upstream?

Suppose I want to proxy some portion of my traffic to a remote backend instead of the local listener on the server. For example:
upstream backends {
server 127.0.0.1:8080 weight=20; # local process (HTTP)
server other-remote-backend.company-internal.com:443; # remote server (HTTPS)
}
location / {
# ...other stuff...
proxy_pass http://backends;
}
In the above configuration, every 20 or so requests NGINX will try to route to http://other-remote-backend.company-internal.com:443 which is only listening for SSL.
Is there a way for the upstream to define its own protocol scheme? Right now this seems undoable without changing the local listener process to be SSL as well (which is a less than desirable change to make).
Thanks
As is the usual case, I've figured out my own problem and its quite obvious. If you're trying to accomplish the above the trick is quite simple.
First create a new NGINX Virtual Host that listens on HTTP and proxy_passes to your remote HTTPS backend like so:
/etc/nginx/sites-available/remote_proxy
upstream remote {
server other-remote-backend.company-internal.com:443;
}
server {
# other-remote-backend.company-internal.com:443;
listen 8181;
server_name my_original_server_name;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass https://remote;
}
}
You can now use just http for your upstreams in the original configuration listening on 443:
/etc/nginx/sites-available/default
upstream backends {
server 127.0.0.1:8080 weight=20; # local process (HTTP)
server 127.0.0.1:8181 # local nginx proxying to HTTPS remote
}
location / {
# ...other stuff...
proxy_pass http://backends;
}
Now just enable your new site and restart
$ ln -s /etc/nginx/sites-available/remote_proxy /etc/nginx/sites-enabled/ && systemctl restart nginx

Websocket works on EC2 url but not on ElasticBeanstalk URL

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

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/;
}
}

How does supervisord and nginx handle what tornado port is used?

I am using supervisord to spool 2 instances of tornado on different ports and I use nginx as a reverse proxy to these ports. I have noticed that all traffic is directing to only one port. How does supervisord or nginx decide which instance of tornado is used when a user makes a request from the web service?
nginx config:
http {
upstream frontends {
server xx.xxx.x.xxx:8001;
server xx.xxx.x.xxx:8002;
}
server {
listen 80;
server_name xx.xxx.x.xxx;
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://frontends;
}
}
}
From the nginx docs:
Requests are distributed according to the servers in round-robin manner with respect of the server weight.
By default, servers are given equal weight. Are you sure all requests are going to one port?
Also note that supervisord's role is simply process management - only nginx decides how to distribute traffic to the ports you've configured.

Resources