Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TCP+SNI support on the same port as HTTPS #169

Closed
mildred opened this issue Sep 28, 2016 · 20 comments
Closed

TCP+SNI support on the same port as HTTPS #169

mildred opened this issue Sep 28, 2016 · 20 comments

Comments

@mildred
Copy link

mildred commented Sep 28, 2016

I have a use case where I need to route on port 443 two type of services:

  • services that are already using https. fabio would just need to use SNI to forward the TCP connection to them
  • services which are plain HTTP, fabio need to wrap a SSL connection around that

I understand that fabio can do both, but not on the same port. Would it be possible to do that? Basically, it would need to :

  • read SNI information
  • check if the underlying service talks https, if so, proxy the TCP stream
  • if the underlying service only talks unencrypted http, wrap the http in SSL using a certificate from the store
@magiconair
Copy link
Contributor

Why do you want to use plain HTTP on port 443?

@mildred
Copy link
Author

mildred commented Sep 28, 2016

Sorry, I didn't explain correctly.

There are two use cases:

  • public port 443, HTTPS
  • fabio SNI+TCP proxy
  • private service, HTTPS, random port

and

  • public port 443, HTTPS
  • fabio HTTPS wrapping
  • private service, unencrypted HTTP, random port

There is no plain HTTP on port 443.

@mildred
Copy link
Author

mildred commented Sep 28, 2016

To know if a private service is talking HTTP or HTTPS, we could define a consul tag that would let us differentiate between the two. For example, if a service is registered in consul with the tag ssl, then fabio wouldn't try to find a certificate for this service and forward the whole TCP stream. If the tag ssl is absent, then fabio would manage the encryption with the external client and talk unencrypted with the internal service.

@magiconair
Copy link
Contributor

OK, I figured as much.

The problem is that in order to distinguish TCP+SNI from HTTPS I can only look at the hostname to make the routing decision since for TCP+SNI I cannot decrypt the traffic. In that case you could just setup two listeners on fabio for two different ip addresses for the two hostnames.

@magiconair
Copy link
Contributor

@mildred Would the proposed solution with the two listeners solve your problem?

@magiconair
Copy link
Contributor

@mildred ping

@mildred
Copy link
Author

mildred commented Oct 17, 2016

The problem is that in order to distinguish TCP+SNI from HTTPS I can only look at the hostname to make the routing decision since for TCP+SNI I cannot decrypt the traffic.

Correct

In that case you could just setup two listeners on fabio for two different ip addresses for the two hostnames

The problem is in case the two hostnames are linking to the same public IP. That's the main use case of virtual hosts, saving on public IPs.

@magiconair
Copy link
Contributor

@mildred so based on the hostname you either want fabio to pass through the encrypted traffic or decrypt it and forward it as a regular HTTP request. Is that correct?

@mildred
Copy link
Author

mildred commented Oct 18, 2016

correct

@magiconair
Copy link
Contributor

magiconair commented Oct 18, 2016

Wouldn't it be simpler to just use two different ip addresses for this or is that not possible? You already have the different host names.

@mildred
Copy link
Author

mildred commented Oct 18, 2016

As I said, the IP address is public and I only have one.

Bit it is still possible to have another frontend that does this. This is what I am currently doing, especially considering I also need to add basic authentication for some endpoints. Currently, I am using haproxy where I generate automatically the configuration file

@magiconair
Copy link
Contributor

OK, but ip addresses aren't that scarce that you could not get another one or can't you? I'm just wondering whether this feature is an edge case which is solved better in infrastructure than in code. Do you know if any other proxy provides this feature?

@mildred
Copy link
Author

mildred commented Oct 18, 2016

Perhaps I'm trying to use fabio for something it is not designed for. My setup is only a single physical server. No VM, no infrastructure as code here. Deployment is manual. The system boundary is the physical machine itself.

If you say this is out of scope for fabio, I would completely understand, even though I would expect this to be useful for some other use cases.

I also know infrastructure as code is very interesting, but I'm not using it where it matters. I much prefer small systems owned by the people themselves instead of relying on third party services.

@magiconair
Copy link
Contributor

What I'm saying is that if you get a second ip address for your server your problem is solved as far as I understand it since you already have two different hostnames. You just need one to point to the TCP+SNI listener and the other one pointing to the standard HTTPS listener.

@mildred
Copy link
Author

mildred commented Oct 18, 2016

Yes, it would work, but it would impact outside of the system boundary (need to allocate and route another IP address, and leak internal details to the DNS zone). Having a HAProxy configured to distinguish between the hostnames that it needs to encrypt itself and the hostnames that it forwards with the SSL layer intact is also a good solution that I prefer since it impact less outside of the system boundary.

@magiconair
Copy link
Contributor

I don't think you're leaking anything here since you're already exposing different hostnames for the different routes. So people will see that - if they care - and whether they resolve to the same or different IPs is not an issue IMO. Virtual hosting is an optimization strategy for saving IPv4 address space not an architecture style. You wouldn't request this feature if you had IPv6 or if you had a /24 routed to your server for example.

But in the end this is about solving a problem. I understand now what you are trying to do and my suggestion is to get a second ip for your server and be done with it. At this point I don't see an easy way of doing this with the code I have which means I'd have to do some digging. Since there is a solution to your problem I'd rather focus on other things first for which there isn't a solution yet (e.g. access logging, path stripping, ...) I'll keep this in mind since I think it is an interesting challenge nevertheless and might be useful to accept just any protocol on a given port.

@magiconair
Copy link
Contributor

I've decided not to add this feature. If there are more people requesting this I might reconsider this at a later point.

@OursDesCavernes
Copy link

Hello, i'm trying to reopen this issue for a different use case.

I've setup a fabio and try to have SNI and HTTPS on the same port.

The idea is that some micro services are served only with FQDN (myservice.domain.com) so SNI is OK for that, and some have path based routing (api.gw.com/locate/v1 or api.gw.cm/deal/v1.2 etc ...)

I can not use both of those system with fabio because SNI take the lead above HTTPS. So i need 2 ports to do that and it's not so easy because developpers need to know the ports of the fabio. I would prefer to use the 443 for the both (like a nginx or haproxy).

Is it possible to fabio to check the table a little differently : If fabio has a request, check the SNI hostname, if you find a exact match, do with SNI and if not, check the global url to find match

example :

src : tomcat.demo.com/ dest : http://192.168.1.2:32225 --> with SNI
src : tomcat.demo.com/book/v2 dest : http://192.168.1.2:32225 --> Classical https
src : api.demo.com/locate/v1 : http://192.168.4.2:17554 --> Classical https

One other thing, if i use the same port for HTTPS and SNI and set proto=https and tlsskipverify=true, SNI is used rather that HTTPS and HTTPS upstream ...

@magiconair
Copy link
Contributor

The TCP+SNI proxy mode is a pass-through TCP proxy which uses the SNI header for routing without decrypting the connection. It decodes the first packet to look at the SNI header and then uses that to forward the raw TLS packets including the ClientHello to the upstream server. Therefore, your tomcat would need to handle the TLS negotiation as well and would have access to the same certificates. Is that what you are after?

What you are trying to do should work with the normal HTTP routing and a TLS listener since fabio will decrypt the message and can look at the request.

Maybe I'm missing something here.

@shantanugadgil
Copy link
Contributor

Does this look fixed in version 1.5.14 with #784

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants