Score:0

Apache virtualhosts conf let wrong servername requests going through proxy

cl flag

I have moved a server configuration to a new server and at the same time configured it with apache mod_proxy to access a gunicorn/django webserver (inside a docker container on port 8000). Previous server was using apache mod_wsgi to access django directly.

So I changed some virtualhosts configuration but I do not understand why now some requests are going through apache to django, when the request servername is not one of my DNS and virtualhosts configurations...

For now, django stops them with Invalid HTTP_HOST header errors, but I would like to filter them in apache configuration directly, and to understand how such scanning requests might pass DNS and apache configurations.

Here is what looks like a typical django error I received:

Report at /
Invalid HTTP_HOST header: 'sprucetips.cyou'. You may need to add 'sprucetips.cyou' to ALLOWED_HOSTS.

Request Method: GET
Request URL: https://sprucetips.cyou/
Django Version: 2.2.28
Python Executable: /usr/local/bin/python
Python Version: 3.5.10
...

Request information:
USER: [unable to retrieve the current user]
GET: No GET data
POST: No POST data
FILES: No FILES data
COOKIES: No cookie data

META:
HTTP_ACCEPT_ENCODING = 'gzip'
HTTP_CONNECTION = 'Keep-Alive'
HTTP_HOST = 'sprucetips.cyou'
HTTP_USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36'
HTTP_X_FORWARDED_FOR = '34.220.181.252'
HTTP_X_FORWARDED_HOST = 'sprucetips.cyou'
HTTP_X_FORWARDED_PROTO = 'https'
HTTP_X_FORWARDED_SERVER = 'mydomain.ca'
PATH_INFO = '/'
QUERY_STRING = ''
RAW_URI = '/'
REMOTE_ADDR = '172.23.0.1'
REMOTE_PORT = '47332'
REQUEST_METHOD = 'GET'
SCRIPT_NAME = ''
SERVER_NAME = '0.0.0.0'
SERVER_PORT = '8000'
SERVER_PROTOCOL = 'HTTP/1.1'
SERVER_SOFTWARE = 'gunicorn/20.1.0'
...

My DNS is configured on two domain names, mydomain.ca and mydomain.com. I previously set a DNS config for each subdomain but now I only set mydomain.ca *.mydomain.ca mydomain.com *.mydomain.com all to the same IP. Can this be a problem?

And there are my virtualhosts:

First 001-mydomain.ca.conf to redirect any 80 port request to 443 port

<VirtualHost *:80>
        ServerName mydomain.ca
        ServerAlias www.mydomain.com www.mydomain.ca mydomain.com sub1.mydomain.ca sub2.mydomain.ca sub3.mydomain.ca

        ErrorLog ${APACHE_LOG_DIR}/mydomain.ca.error.log
        LogLevel info
        CustomLog ${APACHE_LOG_DIR}/mydomain.ca.access.log combined

        RewriteEngine on
        RewriteCond %{SERVER_NAME} =www.mydomain.ca [OR]
        RewriteCond %{SERVER_NAME} =mydomain.com [OR]
        RewriteCond %{SERVER_NAME} =www.mydomain.com [OR]
        RewriteCond %{SERVER_NAME} =mydomain.ca
        RewriteRule ^ https://mydomain.ca%{REQUEST_URI} [END,QSA,R=permanent]

        RewriteCond %{SERVER_NAME} =sub1.mydomain.ca
        RewriteRule ^ https://sub1.mydomain.ca%{REQUEST_URI} [END,QSA,R=permanent]

        RewriteCond %{SERVER_NAME} =sub2.mydomain.ca
        RewriteRule ^ https://sub2.mydomain.ca%{REQUEST_URI} [END,QSA,R=permanent]

        RewriteCond %{SERVER_NAME} =sub3.mydomain.ca
        RewriteRule ^ https://sub3.mydomain.ca%{REQUEST_URI} [END,QSA,R=permanent]
</VirtualHost>

010-mydomain.ca-ssl.conf

<VirtualHost *:443>
        ServerName mydomain.ca
        ServerAlias www.mydomain.ca sub1.mydomain.ca sub2.mydomain.ca
        DocumentRoot /var/www/mydomain.ca

        ErrorLog ${APACHE_LOG_DIR}/mydomain.ca.error.log
        LogLevel debug

        CustomLog ${APACHE_LOG_DIR}/mydomain.ca.access.log combined

        Include /etc/letsencrypt/options-ssl-apache.conf
        SSLCertificateFile /etc/letsencrypt/live/mydomain.ca/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/mydomain.ca/privkey.pem

        RewriteEngine On
        RewriteMap exceptions /var/www/mydomain.ca/maintenance_exceptions.map

        RewriteCond /var/www/mydomain.ca/maintenance.html -f
        RewriteCond /var/www/mydomain.ca/maintenance.enable -f
        RewriteCond %{SCRIPT_FILENAME} !maintenance.html
        RewriteCond ${exceptions:%{REMOTE_ADDR}} !OK
        RewriteRule ^.*$ /maintenance.html [R=503,L]
        ErrorDocument 503 /maintenance.html

        ProxyRequests Off
        ProxyPreserveHost On
        RequestHeader set X_FORWARDED_PROTO 'https' env=HTTPS
        ProxyPass /static/ !
        ProxyPass /media/ !
        ProxyPass /favicon.ico !
        ProxyPass /maintenance.html !
        ProxyPass / http://localhost:8000/
        ProxyPassReverse / http://localhost:8000/

        Alias /favicon.ico /var/www/mydomain.ca/favicon.ico
        Alias /media/ /var/www/mydomain.ca/media/
        Alias /static/ /var/www/mydomain.ca/static/

        <Directory /var/www/mydomain.ca>
                Order allow,deny
                Allow from all
        </Directory>

        <Directory /var/www/mydomain.ca/static>
                Header set Cache-Control "max-age=86400, must-revalidate"
        </Directory>
</VirtualHost>

<VirtualHost *:443>
        ServerName mydomain.com
        ServerAlias www.mydomain.com

        RewriteEngine On
        RewriteRule ^ https://mydomain.ca%{REQUEST_URI} [END,QSA,R=permanent]
</VirtualHost>

One of the subdomains is redirected to another webserver with 011-sub3.mydomain.ca-ssl.conf

<VirtualHost *:443>
        ServerName sub3.mydomain.ca

        ErrorLog ${APACHE_LOG_DIR}/sub3.mydomain.ca.error.log
        LogLevel debug
        CustomLog ${APACHE_LOG_DIR}/sub3.mydomain.ca.access.log combined

        Include /etc/letsencrypt/options-ssl-apache.conf
        SSLCertificateFile /etc/letsencrypt/live/mydomain.ca/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/mydomain.ca/privkey.pem

        ProxyPreserveHost On
        ProxyRequests off
        AllowEncodedSlashes NoDecode
        ProxyPass /.well-know !
        ProxyPass /robots.txt !
        ProxyPass / http://localhost:3000/ nocanon
</VirtualHost>

And I finally manage others subdomains and direct IPrequests in 020-default.mydomain.ca.conf

<VirtualHost *:80 *:443>
        ServerName default.mydomain.ca
        ServerAlias *.mydomain.ca *.mydomain.com
        Redirect 404 /
        CustomLog ${APACHE_LOG_DIR}/mydomain.ca.other.log combined
</VirtualHost>

<VirtualHost *:80 *:443>
        ServerName <IP>
        Redirect 403 /
        ErrorDocument 403 "Direct IP access not allowed"
        UseCanonicalName Off
        CustomLog ${APACHE_LOG_DIR}/mydomain.ca.other.log combined
</VirtualHost>

Any tip, advise or magic answer would be so appreciated. Thanks.

Score:0
jp flag

Excerpt from Apache documentation

When a request is received, the server first maps it to the best matching based on the local IP address and port combination only. Non-wildcards have a higher precedence. If no match based on IP and port occurs at all, the "main" server configuration is used.

If multiple virtual hosts contain the best matching IP address and port, the server selects from these virtual hosts the best match based on the requested hostname. If no matching name-based virtual host is found, then the first listed virtual host that matched the IP address will be used. As a consequence, the first listed virtual host for a given IP address and port combination is the default virtual host for that IP and port combination.

You need to rename your 020-default.mydomain.ca.conf to 000-default.mydomain.ca.conf so it goes the first.

yo_e-h avatar
cl flag
I do not understand why, but moving these hosts as default virtual hosts like you suggest cause a SSL_ERROR_RX_RECORD_TOO_LONG error (firefox) or ERR_SSL_PROTOCOL_ERROR error (chrome) I tried with the same file and also with the suggestion of @esa-jokinen to separate 80 port virtualhost and 443 port virtualhost, but still an error. Also I was aware of this apache documentation. I was thinking mydomain.ca is my default virtual host, and the default.mydomain.ca is just here to manage any other subdomain or the cases when users use the IP directly.
Score:0
jp flag

In addition to AlexD's answer that the default VirtualHost should be the first one, <VirtualHost *:80 *:443> would not work, because *:80 is for plain HTTP and *:443 for HTTPS. You need to separate these configurations and add the TLS settings including any certificate for the <VirtualHost *:443>.


Furthermore, the RewriteRules in your HTTP to HTTPS redirection are a bit overly complicated.

For the subdomains you could use mod_rewrite with a single rule with variables:

<VirtualHost *:80>
    ServerName sub1.example.net
    ServerAlias sub2.example.net sub3.example.net

    RewriteEngine On
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
</VirtualHost>

For the hostnames that should be redirected to the canonical hostname, mod_alias would be preferable:

<VirtualHost *:80>
    ServerName example.net
    ServerAlias www.example.com www.example.net example.com 

    Redirect "/" "https://www.example.net/"
</VirtualHost>
yo_e-h avatar
cl flag
As I respond to @AlexD, modifying the order of my virtualhosts cause SSL errors. Splitting 443 and 80 directives is working well, as your suggestions for rewriting to HTTPS. Thanks. Still having Invalid HTTPS_HOST header errors, and I do not understand how such requests pass the proxy...
yo_e-h avatar
cl flag
Ok, I forgot to "add the TLS settings", now it is working well. I hope the port scanning requests will now be blocked by apache config.
jp flag
Please accept the answer if it solved your problem.
yo_e-h avatar
cl flag
Not totally, with the wildcard virtualhost set as default virtualhost, sub1/sub2/sub3.mydomain.ca are not available then. For now I split the default virtualhost: direct IP as default virtual host, and wildcards as last virtualhost as defined first. I will check if port-scanning requests still pass through or not: is it the virtualhost with direct IP that is suppose to stop them?
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.