Kubernetes Ingress/Reverse and Forward Proxy with ssl interception - nginx

I have a requirement that incoming as well as outgoing SSL traffic to a POD in a Namespace has to terminate at a proxy (the same), this proxy should look at a special part in the header of the packet and decide if the packet is allowed for out or in, if not the proxy has to send an 403.
I already took look at Istio and Envoy, but I couldn't find a solution for my problem.
Now I decided to start a separate NGINX-POD in my namespace and always route the traffic through it. So I'll be able to create a custom python module that does the Checks for me.
But I would rather work with native methods, if possible. Now I wanted to ask you, if you have an idea, what to use for this scenario.
Client -> nginx ingress (ssl pathrough) -> nginx (reverse/forward) proxy -> app
app -> nginx (reverse/forward) proxy -> Client
EDIT: or should I take a look at squid or something like that? :O

Related

Reverse proxy with http inbound, https outbound, and parent proxy

I have an application that needs to use a proxy (call it proxy1) to access some https endpoints outside of its network. The application doesn't support proxy settings, so I'd like to provide it a reverse proxy url, and I would prefer not to provide tls certs for proxy1, so I would use http for application -> proxy1.
I don't have access to the application host or forward proxy mentioned below, so I cannot configure networking there.
The endpoints the application needs are https, so proxy1 must make its outbound connections via https.
Finally, this whole setup is within a corporate network that requires a forward proxy (call it proxy2) for outbound internet, so my proxy1 needs to chain to proxy2 / use it as a parent.
I tried squid and it worked well for http only, but I couldn't get it to accept http inbound while using https outbound. Squid easily supported the parent proxy2.
I tried haproxy, but had the same result as with squid.
I tried nginx and it did what I wanted with http -> proxy -> https, but doesn't support a parent proxy. I considered setting up socat as in this answer, or using proxy_pass and proxy_set_header as in this answer, but I can't shake the feeling there's a cleaner way to achieve the requirements.
This doesn't seem like an outlandish setup, is it? Or is there a preferred approach for it? Ideally one using squid or nginx.
You can achive this without the complexity by using a port forwarder like socat. Just install it on a host to do the forwarding (or locally on the app server if you wish to) and create a listener that forwards connections through the proxy server. Then on your application host use a local name resolution overide to map the FQDN to the forwarder.
So, the final config should be the app server using a URI that points to the forwarding server (using its address if no name resolution excists), which has a socat listener that points to the the corporate proxy. No reverse proxy required.
socat TCP4-LISTEN:443,reuseaddr,fork \
PROXY:{proxy_address}:{endpoint_fqdn}:443,proxyport={proxy_port}
Just update with your parameters.

Is it possible to have client certificates with HTTP (not HTTPS)?

I have an application set up like this:
There is a server, with a reverseproxy/load balancer that acts as the HTTPS termination (this is the one that has a server certificate), and several applications behind it(*)
However, some applications require authentication of the client with a certificate. Authentication cannot happen in the reverse proxy. Will the application be able to see the user certificate, or will it be jettisoned by the HTTPS->HTTP transfer?
(*) OK, so this is a Kubernetes ingress, and containers/pods.
It will be lost. I think you need to extract it in the reverse proxy (i.e. Nginx) and pass it in as a HTTP header if you really must. See for example https://serverfault.com/questions/788895/nginx-reverse-proxy-pass-through-client-certificate. Not very secure as the cert is passed in the clear!
I don't know if we have that level of control over the ingress, personally I'm using a normal Nginx server for incoming traffic instead.

Map DNS entry to specific port

Let's say I have this DNS entry: mysite.sample. I am developing, and have a copy of my website running locally in http://localhost:8080. I want this website to be reachable using the (fake) DNS: http://mysite.sample, without being forced to remember in what port this site is running. I can setup /etc/hosts and nginx to do proxing for that, but ... Is there an easier way?
Can I somehow setup a simple DNS entry using /etc/hosts and/or dnsmasq where also a non-standard port (something different than :80/:443) is specified? Without the need to provide extra configuration for nginx?
Or phrased in a simpler way: Is it possible to provide port mappings for dns entries in /etc/hosts or dnsmasq?
DNS has nothing to do with the TCP port. DNS is there to resolv names (e.g. mysite.sample) into IP addresses - kind of like a phone book.
So it's a clear "NO". However, there's another solution and I try to explain it.
When you enter http://mysite.sample:8080 in your browser URL bar, your client (e.g. browser) will first try to resolve mysite.sample (via OS calls) to an IP address. This is where DNS kicks in, as DNS is your name resolver. If that happened, the job of DNS is finished and the browser continues.
This is where the "magic" in HTTP happens. The browser is connecting to the resolved IP address and the desired port (by default 80 for http and 443 for https), is waiting for the connection to be accepted and is then sending the following headers:
GET <resource> HTTP/1.1
Host: mysite.sample:8080
Now the server reads those headers and acts accordingly. Most modern web servers have something called "virtual hosts" (i.e. Apache) or "sites" (i.e. nginx). You can configure multiple vhosts/sites - one for each domain. The web server will then provide the site matching the requested host (which is retreived by the browser from the URL bar and passed to the server via Host HTTP header). This is pure HTTP and has nothing to do with TCP.
If you can't change the port of your origin service (in your case 8080), you might want to setup a new web server in front of your service. This is also called reverse proxy. I recommend reading the NGINX Reverse Proxy docs, but you can also use Apache or any other modern web server.
For nginx, just setup a new site and redirect it to your service:
location mysite.example {
proxy_pass http://127.0.0.1:8080;
}
There is a mechanism in DNS for discovering the ports that a service uses, it is called the Service Record (SRV) which has the form
_service._proto.name. TTL class SRV priority weight port target.
However, to make use of this record you would need to have an application that referenced that record prior to making the call. As Dominique has said, this is not the way HTTP works.
I have written a previous answer that explains some of the background to this, and why HTTP isn't in the standard. (the article discusses WS, but the underlying discussion suggested adding this to the HTTP protocol directly)
Edited to add -
There was actually a draft IETF document exploring an official way to do this, but it never made it past draft stage.
This document specifies a new URI scheme called http+srv which uses a DNS SRV lookup to locate a HTTP server.
There is an specific SO answer here which points to an interesting post here

how to print dynamic port in nginx reverse proxy

I want to print the dynamic port created by Nginx in reverse proxy mode, so as to capture the specific packet.
I have go through the nginx docs, but not find something useful. by the way, any other alternatives are welcome

Atlasssian Crucible through AWS ELB with HTTPS

I've recently setup a Crucible instances in AWS connected via a HTTPS ELB. I have a nginx reverse proxy setup on the instance as well to redirect HTTP requests to HTTPS.
This partially works. However Crucible itself doesn't know it's running over HTTPS so serves up mixed content, and ajax queries often break due to HTTP -> HTTPS conflicts.
I've found documentation for installing a certificate in Crucible directly...
https://confluence.atlassian.com/fisheye/fisheye-ssl-configuration-298976938.html
However I'd really rather not have to do it this way. I want to have the HTTPS terminated at the ELB, to make it easier to manage centrally through AWS.
I've also found documentation for using Crucible through a reverse proxy...
https://confluence.atlassian.com/kb/proxying-atlassian-server-applications-with-apache-http-server-mod_proxy_http-806032611.html
However this doesn't specifically deal with HTTPS.
All I really need is a way to ensure that Crucible doesn't serve up content with hard coded internal HTTP references. It needs to either leave off the protocol, or set HTTPS for the links.
Setting up the reverse proxy configuration should help accomplish this. Under Administration >> Global Settings >> Server >> Web Server set the following:
Proxy scheme: https
Proxy host: elb.hostname.com
Proxy port: 443
And restart Crucible.
Making configuration on UI is one way. You can also change config.xml in $FISHEYE_HOME:
<web-server site-url="https://your-public-crucible-url">
<http bind=":8060" proxy-host=“your-public-crucible-url" proxy-port="443" proxy-scheme="https"/>
</web-server>
Make sure to shutdown FishEye/Crucible before making this change.
AFAIK, this configuration is the only way to tell internal Jetty of FishEye/Crucible to be aware of the reversed proxy in front of them.

Resources