Redirect reverse-proxy with SNI and subdomain - ssl

I have one machine with a loadbalancer, nginx in a container and I want to redirect 2 domains to one server throught 443, this works.
But when I add the server c with a subdomain as a ssl_preread_server_name
it doesn't work. (the first still working)
I tried with regex, I searched and I didn't found anything related to ssl_preread and subdomains
map $ssl_preread_server_name $name {
*.my.domain.com c;
a.another.domain.com a;
b.another.domain.com b;
}
upstream a {
server "ip1:443" max_fails=2 fail_timeout=30s;
}
upstream b {
server "ip1:443" max_fails=2 fail_timeout=30s;
}
upstream c {
server "ip2:33390" max_fails=2 fail_timeout=30s;
}
log_format stream_routing '$remote_addr [$time_local] '
'with SNI name "$ssl_preread_server_name" '
'proxying to "$name" '
'$protocol $status $bytes_sent $bytes_received '
'$session_time';
server {
listen 443;
ssl_preread on;
proxy_pass $name;
access_log /var/log/nginx/stream_443.log stream_routing;
}
when I add the server c with a subdomain as a ssl_preread_server_name
the error is:
*3 no host in upstream ""
when I curl the server should redirect to the upstream c

Related

How to set up Varnish in a dedicated server with mutiple IPs and domains?

I have a dedicated server with many domains. Some domains have their own dedicated IP and some IPs are shared by domains.
I want to set up varnish for all domains and IPs.
Server: CentOS 7.2 with cPanel 11.58
Do you know of any cpanel plugin? or simple code or a efficient method which can setup varnish for all domains/IPs automatically/easily.
After installing varnish, I changed to apache port to "8080". I changed port to "80" in varnish.params and to "8080" in default.vcl. Now varnish works for primary IP and domain of the server.
I tried to modify default.vcl by following method, but after that varnish gives error when I try to load. I am confused and tried so many things to solve it, but unfortunately nothing worked.
backend example1 {
.host = "5.135.166.39";
.port = "8080";
}
backend example2 {
.host = "46.105.40.241";
.port = "8080";
}
sub vcl_recv {
if (server.ip == "5.135.166.39") {
set req.backend = example1;
} elseif (server.ip == "46.105.40.241") {
set req.backend = example2;
} else {
set req.backend = default;
}
}
You need to define your conditions by the domain name itself not with its ip. so lets say you have 2 doamins:
www.foo.com with A record 5.135.166.39
www.bar.com with A record 46.105.40.241
inside vcl_recv you need to do the following:
if (req.http.host ~ "foo.com") {
set req.backend = example1;
}
if (req.http.host ~ "bar.com") {
set req.backend = example2;
}
use ~ to match any url that contains foo.bar or you can use == "www.foo.com" to match www.foo.com only, same goes for bar.com

Why is Varnish redirecting as 301?

I have been deploying a mediawiki docker container (appscontainer/mediawiki) based on Apache2 on a VPS, and I put a fresh install of Varnish on top of it, to be able to proxied different subdomains to the proper applications on the same server.
My current default.vcl configuration file look like the following:
backend default {
.host = "127.0.0.1";
.port = "8080";
}
backend wikimedia {
.host = "localhost";
.port = "8080";
}
sub vcl_recv {
if(req.http.host == "wiki.virtual-assembly.org") {
set req.backend_hint = wikimedia;
}
set req.backend_hint = default;
}
My issue is that when I request the URL http://wiki.virtual-assembly.org, I got redirected via a 301 to the IP adress of the server on port 8080 (port on which the apache2 instance is listening).
Is there a way to tell Varnish to keep the location to be http://wiki.virtual-assembly.org, or is it an apache2 misconfiguration ?
Thanks in advance,
PS: I know my two backends are equivalent, I will change the default in the future when I will have deployed more apps.
Shot in the dark answer. Do you still get a 301 if you put the default backend_req into an else statement instead of outside the if?

How to serve two web applications behind an nginx reverse proxy

I have two web applications (node.js express apps), web1 and web2. These web apps expect to be hosted on sites that are typically something like http://www.web1.com and http://www.web2.com. I'd like to host them behind an nginx reverse proxy as https://www.example.com/web1 and https://www.example.com/web2. I do not want to expose the two web apps as two subdomains on example.com.
Here is a snippet of my nginx configuration (without SSL termination details) that I had hoped would accomplish this:
server {
listen 443;
server_name .example.com;
location /web1 {
proxy_pass http://www.web1.com:80;
}
location /web2 {
proxy_pass http://www.web2.com:80;
}
}
This works, except for the relative links that the web apps use. So web app web1 will have a relative link like /js/script.js which won't be handled correctly.
What is the best/standard way to accomplish this?
You should be able to do this by checking the $http_referer, something like:
location / {
if ($http_referer ~ ^http://(www.)?example.com/web1) {
proxy_pass http://www.web1.com:80;
}
if ($http_referer ~ ^http://(www.)?example.com/web2) {
proxy_pass http://www.web2.com:80;
}
}
The browser would be setting the referer to http://example.com/web1/some/page when it requests /js/script.js so the apps shouldn't need to change, unless they need to process or care about the referer internally.
The $http_referer does not seem to be easy to find in nginx docs, but is mentioned in a few sites:
http://nginx.2469901.n2.nabble.com/HTTP-Referer-Module-td3356604.html
https://stackoverflow.com/a/18917016/1422492
I think something like this:
server {
listen 443;
server_name .example.com;
location /web1 {
proxy_pass http://www.web1.com:80;
}
location /web2 {
proxy_pass http://www.web2.com:80;
}
location / {
if ($http_referer ~* (/web1) ) {
proxy_pass http://www.web1.com:80;
}
if ($http_referer ~* (/web2) ) {
proxy_pass http://www.web2.com:80;
}
}
How about using cookie and ngx_http_map_module?
Add add_header Set-Cookie "site=web1;Path=/;Domain=.example.com;"; to location /web1 {...} (web2 too).
Add map to under http
map $cookie_site $site {
default http://www.web1.com:80;
"web2" http://www.web2.com:80;
}
Default location is this
location / {
proxy_pass $site;
}
You can pass the value of cookie to proxy_pass directly. But, using map is more secure way.

Nginx config: how to use auth_basic authentication if ssl_client_certificate none provided?

I'm trying to set up Nginx server as follows:
First, the server should check whether the user provides the client SSL certificate (via ssl_client_certificate).
If the SSL certificate is provided, then give access to the site,
If the SSL certificate is NOT provided, then ask the user to enter a password and logs through auth_basic.
I was able to configure both the authentication method at the same time. But this config is superfluous.
To make check, whether the user provides its SSL certificate I try the config like this:
18: if ($ssl_client_verify != SUCCESS) {
19: auth_basic "Please login";
20: auth_basic_user_file .passfile;
21: }
But Nginx returns an error:
"auth_basic" directive is not allowed here in .../ssl.conf:19
How can I to set the condition in this case?
You can set auth_basic configuration in the if clause like this:
server {
listen 443;
auth_basic_user_file .htpasswd;
ssl_client_certificate ca.cert;
ssl_verify_client optional;
...
location / {
...
if ($ssl_client_verify = SUCCESS) {
set $auth_basic off;
}
if ($ssl_client_verify != SUCCESS) {
set $auth_basic Restricted;
}
auth_basic $auth_basic;
}
}
Now, authentication falls back to HTTP Basic if no client certificate has been provided (or if validation failed).
I'm unable to test this currently, but would something like this work?
server {
listen 80;
server_name www.example.com example.com;
rewrite ^ https://$server_name$request_uri? permanent;
}
server {
listen 443;
...
if ($ssl_client_verify != SUCCESS) {
rewrite ^ http://auth.example.com/ permanent;
}
location / {
...
}
}
server {
listen 80;
server_name auth.example.com;
location / {
auth_basic "Please login";
auth_basic_user_file .passfile;
}
}
So basically:
- Accept all initial request (on port 80 for whatever name you're using) and rewrite to ssl
- Check if there's an the client is verified.
- If not, rewrite to an alternate domain that uses basic auth
Like I said, I can't test it right now, but I'll try to get around to it! Let me know if it helps, I'm interested to see if it works.
You may try using a map.
map $ssl_client_verify $var_auth_basic {
default off;
SUCCESS "Please login";
}
server {
....
auth_basic $var_auth_basic;
auth_basic_user_file .passfile;
that way the value depends on $ssl_client_verify but is alsa always defined and auth_basic and auth_basic_user_file is always inside server { block.
Nginx provides no way to fall back to basic authentication when client cert fails. As an alternative you can use variables to restrict access:
location / {
if ($ssl_client_verify = "SUCCESS") {
set $authorized 1;
}
if ($authorized != 1) {
error_page 401 #basicauth;
return 401;
}
}
location #basicauth {
auth_basic "Please login";
auth_basic_user_file .passfile;
set $authorized 1;
rewrite /(.*) /$1;
}
*keep in mind that IfIsEvil and these rules may work incorrectly or interfere with other parts of a larger configuration.
Forget about it, it won't work.
The reason why it fails is because if is not part of the general configuration module as one should believe. if is part of the rewrite module and auth_basic is another module. You just cannot have dynamic vhosts with basic auth.
On the other hand...
You can have dynamic vhosts with their own error pages. The following example is designed for a custom 404 page but you can implement it into your code.
server {
listen 80;
server_name _;
set $site_root /data/www/$host;
location / {
root $site_root;
}
error_page 404 =404 /404.html;
location /404.html {
root $site_root/error_files;
internal;
error_page 404 =404 #fallback_404;
}
location #fallback_404 {
root /var/www/;
try_files /404.html =404;
internal;
}
error_log /var/log/nginx/error.log info;
access_log /var/log/nginx/access.log;
}
What happens...
you are telling Nginx to use /404.html in case of HTTP_NOT_FOUND.
changing the location root to match the Web site error_pages directory.
internal redirection
returning a 404 http code
configure the fallback 404 page in location #fallback_404: In this location, the root is changed to /var/www/ so it will read files from that path instead of $site_root
at the last stage the code returns /var/www/404.html if it exists with a 404 http code.
NOTE: According to Nginx documentation :
Specifies that a given location can only be used for internal
requests. For external requests, the client error 404 (Not Found) is
returned. Internal requests are the following:
requests redirected by the error_page, index, random_index, and try_files directives;
requests redirected by the “X-Accel-Redirect” response header field from an upstream server;
subrequests formed by the “include virtual” command of the ngx_http_ssi_module module and by the ngx_http_addition_module module
directives;
requests changed by the rewrite directive.
Also:
There is a limit of 10 internal redirects per request to prevent
request processing cycles that can occur in incorrect configurations.
If this limit is reached, the error 500 (Internal Server Error) is
returned. In such cases, the “rewrite or internal redirection cycle”
message can be seen in the error log.
Check this link for more, hope that helps.

varnish 3 and multiple IPs (virtualhosts) isn't working out too well for me

Complete newbie to Varnish so apologies ahead of time if this seems rather silly.
Here's the situation. I have a server with 5 IPs. Using ISPconfig for most tasks but that's probably irrelevant.
I have multiple apache virtual hosts configured across multiple IPs.
The issue is that varnish is putting out a 503, fetch error no backend connection (according to varnishlog) on any of the non-default virtual hosts i.e. ones with a static IP defined in vhosts. Any *:8080 vhosts are working ok. So I'm missing something somewhere. All of the vhost error logs show file does not exist errors though the path looks correct.
Suggestions are much appreciated.
I have of course manually edited all vhost entries and configured them accordingly i.e.
<VirtualHost 00.11.22.33:8080>
DocumentRoot /var/www/shop.example1.com/web
Here's my vcl config
backend default {
.host = "127.0.0.1";
.port = "8080";
}
backend example1 {
.host = "00.11.22.33";
.port = "8080";
}
backend example2 {
.host = "11.22.33.44";
.port = "8080";
}
acl purge {
"localhost";
}
sub vcl_recv {
if (req.http.host ~ "(?i)^(www.)?example1.com")
{
set req.http.host = "www.example1.com";
set req.backend = example1;
}
if (req.http.Host ~ "shop\.example2\.com")
{
set req.http.Host = "shop.example2.com";
set req.backend = example2;
}
set req.grace = 2m;
set req.http.Cookie = regsuball(req.http.Cookie, "(^|;\s*)(_[_a-z]+|has_js)=[^;]*", "");
set req.http.Cookie = regsub(req.http.Cookie, "^;\s*", "");
if (req.url ~ "/wp-(login|admin|cron)") {
return (pass);
}
set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", "");
set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", "");
set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", "");
if (req.url ~ "wp-content/themes/" && req.url ~ "\.(css|js|png|gif|jp(e)?g)") {
.......
'
Damn obvious thing of course.
port.conf had:
NameVirtualHost *:8080
Listen 127.0.0.1:8080
What it needed was:
NameVirtualHost *:8080
Listen 127.0.0.1:8080
Listen my_IP1:8080
Listen my_IP2:8080