Using Apache Web Server as a reverse-proxy for ASP.NET Core

Apache is a very popular HTTP server and can be configured as a proxy to redirect HTTP traffic similar to nginx. In this guide, we will learn how to set up Apache on CentOS 7 and use it as a reverse-proxy to welcome incoming connections and redirect them to the ASP.NET Core application running on Kestrel. For this purpose, we will use the mod_proxy extension and other related Apache modules.

Prerequisites

  1. A server running CentOS 7, with a standard user account with
    sudo privilege.
  2. An existing ASP.NET Core application.

Publish your application

Run dotnet publish -c Release from your development environment to package your
application into a self-contained directory that can run on your server. The published application must then be copied to the server using SCP, FTP, etc.

Under a production deployment scenario, a continuous integration workflow does the work of publishing the application and copying the assets to the server.

Configure a proxy server

A reverse proxy is a common setup for serving dynamic web applications. The reverse proxy terminates the HTTP request and forwards it to the ASP.NET application.

A proxy server is one which forwards client requests to another server instead of fulfilling them itself. A reverse proxy forwards to a fixed destination, typically on behalf of arbitrary clients. In this guide, Apache is being configured as the reverse-proxy running on the same server that Kestrel is serving the ASP.NET Core application.

These instances could exist on separate physical machines, Docker containers, or a combination of configurations depending on your architectural needs or restrictions.

Install Apache

Installing the Apache web server on CentOS is a single command, but first let's update our packages.

    sudo yum update -y

This ensures that all of the installed packages are updated to their latest version. Install Apache using yum

    sudo yum -y install httpd mod_ssl

The output should reflect something similar to the following.

    Downloading packages:
    httpd-2.4.6-40.el7.centos.4.x86_64.rpm               | 2.7 MB  00:00:01     
    Running transaction check
    Running transaction test
    Transaction test succeeded
    Running transaction
    Installing : httpd-2.4.6-40.el7.centos.4.x86_64      1/1 
    Verifying  : httpd-2.4.6-40.el7.centos.4.x86_64      1/1 

    Installed:
    httpd.x86_64 0:2.4.6-40.el7.centos.4                                                                           

    Complete!

In this example the output reflects httpd.86_64 since the CentOS 7 version is 64 bit. The output may be different for your server. To verify where Apache is installed, run whereis httpd from the command line.

Configure Apache for reverse-proxy

Configuration files for Apache are located within the /etc/httpd/conf.d/ directory. Any file with the .conf extension will be processed in alphabetical order in addition to the module configuration files in /etc/httpd/conf.modules.d/, which contains any configuration files necessary to load modules.

Create a configuration file for your app, for this example we'll call it hellomvc.conf

    <VirtualHost *:80>
        ProxyPreserveHost On
        ProxyPass / http://127.0.0.1:5000/
        ProxyPassReverse / http://127.0.0.1:5000/
        ErrorLog /var/log/httpd/hellomvc-error.log
        CustomLog /var/log/httpd/hellomvc-access.log common
    </VirtualHost>

The VirtualHost node, of which there can be multiple in a file or on a server in many files, is set to listen on any IP address using port 80. ProxyRequests allows or prevents Apache httpd from functioning as a forward proxy server. In a typical reverse proxy or gateway configuration, this option should be set to Off. The next two lines are set to pass all requests received at the root to the machine 127.0.0.1 port 5000 and in reverse. For there to be bi-directional communication, both settings ProxyPass and ProxyPassReverse* are required.

Logging can be configured per VirtualHost using ErrorLog and CustomLog directives. ErrorLog is the location where the server will log errors and CustomLog sets the filename and format of log file. In our case this is where request information will be logged. There will be one line for each request.

Save the file, and test the configuration. If everything passes, the response should be Syntax [OK].

    sudo service httpd configtest

Restart Apache.

    sudo systemctl restart httpd
    sudo systemctl enable httpd

Monitoring our application

Apache is now setup to forward requests made to http://localhost:80 on to the ASP.NET Core application running on Kestrel at http://127.0.0.1:5000. However, Apache is not setup to manage the Kestrel process. We will use systemd and create a service file to start and monitor the underlying web app. systemd is an init system that provides many powerful features for starting, stopping and managing processes.

Create the service file

Create the service definition file

    sudo nano /etc/systemd/system/kestrel-hellomvc.service

An example service file for our application.

[Unit]
    Description=Example .NET Web API Application running on CentOS 7

    [Service]
    ExecStart=/usr/local/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll
    Restart=always
    RestartSec=10                                          # Restart service after 10 seconds if dotnet service crashes
    SyslogIdentifier=dotnet-example
    User=apache
    Environment=ASPNETCORE_ENVIRONMENT=Production 

    [Install]
    WantedBy=multi-user.target

User If apache is not used by your configuration, the user defined here must be created first and given proper ownership for files

Save the file and enable the service.

    systemctl enable kestrel-hellomvc.service

Start the service and verify that it is running.

    systemctl start kestrel-hellomvc.service
    systemctl status kestrel-hellomvc.service

    ● kestrel-hellomvc.service - Example .NET Web API Application running on CentOS 7
        Loaded: loaded (/etc/systemd/system/kestrel-hellomvc.service; enabled)
        Active: active (running) since Thu 2016-10-18 04:09:35 NZDT; 35s ago
    Main PID: 9021 (dotnet)
        CGroup: /system.slice/kestrel-hellomvc.service
                └─9021 /usr/local/bin/dotnet /var/aspnetcore/hellomvc/hellomvc.dll

With the reverse-proxy configured and Kestrel managed through systemd, the web application is fully configured and can be accessed from a browser on the local machine at http://localhost. Inspecting the response headers, the Server still shows the ASP.NET Core application being served by Kestrel.

    HTTP/1.1 200 OK
    Date: Tue, 11 Oct 2016 16:22:23 GMT
    Server: Kestrel
    Keep-Alive: timeout=5, max=98
    Connection: Keep-Alive
    Transfer-Encoding: chunked

Viewing logs

Since the web application using Kestrel is managed using systemd, all events and processes are logged to a centralized journal. However, this journal includes all entries for all services and processes managed by systemd. To view the kestrel-hellomvc.service specific items, use the following command.

    sudo journalctl -fu kestrel-hellomvc.service

For further filtering, time options such as --since today, --until 1 hour ago or a combination of these can reduce the amount of entries returned.

    sudo journalctl -fu kestrel-hellomvc.service --since "2016-10-18" --until "2016-10-18 04:00"

Securing our application

Configure firewall

Firewalld is a dynamic daemon to manage firewall with support for network zones, although you can still use iptables to manage ports and packet filtering; it is not recommended to use them both at the same time. Firewalld should be installed by default, if not use yum to install it.

    sudo yum install firewalld -y

Using firewalld you can open only the ports needed for the application. In this case, port 80 and 443 are used. The following command permanently sets these to open.

    sudo firewall-cmd --add-port=80/tcp --permanent
    sudo firewall-cmd --add-port=443/tcp --permanent

Reload the firewall settings, and check the available services, ports on the default zone. Options are available by inspecting firewall-cmd -h

    sudo firewall-cmd --reload
    sudo firewall-cmd --list-all
    public (default, active)
    interfaces: eth0
    sources: 
    services: dhcpv6-client
    ports: 443/tcp 80/tcp
    masquerade: no
    forward-ports: 
    icmp-blocks: 
    rich rules: 

SSL configuration

To configure Apache for SSL, the mod_ssl module is used. This was installed initially when we installed the httpd module. If it was missed or not installed, use yum to add it to your configuration.

    sudo yum install mod_ssl

To enforce SSL, install mod_rewrite

    sudo yum install mod_rewrite

The hellomvc.conf file that was created for this example needs to be modified to enable the rewrite as well as adding the new VirtualHost section for HTTPS.

    <VirtualHost *:80>
        RewriteEngine On
        RewriteCond %{HTTPS} !=on
        RewriteRule ^/?(.*) https://%{SERVER_NAME}/ [R,L]
    </VirtualHost>

    <VirtualHost *:443>
        ProxyPreserveHost On
        ProxyPass / http://127.0.0.1:5000/
        ProxyPassReverse / http://127.0.0.1:5000/
        ErrorLog /var/log/httpd/hellomvc-error.log
        CustomLog /var/log/httpd/hellomvc-access.log common
        SSLEngine on
        SSLProtocol all -SSLv2
        SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:!RC4+RSA:+HIGH:+MEDIUM:!LOW:!RC4
        SSLCertificateFile /etc/pki/tls/certs/localhost.crt
        SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
    </VirtualHost>    

This example is using a locally generated certificate. SSLCertificateFile should be your primary certificate file for your domain name. SSLCertificateKeyFile should be the key file generated when you created the CSR. SSLCertificateChainFile should be the intermediate certificate file (if any) that was supplied by your certificate authority

Save the file, and test the configuration.

    sudo service httpd configtest

Restart Apache.

    sudo systemctl restart httpd

Additional Apache suggestions

Additional Headers

In order to secure against malicious attacks there are a few headers that should either be modified or added. Ensure that the mod_headers module is installed.

    sudo yum install mod_headers

Secure Apache from clickjacking

Clickjacking is a malicious technique to collect an infected user's clicks. Clickjacking tricks the victim (visitor) into clicking on an infected site. Use X-FRAME-OPTIONS to secure your site.

Edit the httpd.conf file.

    sudo nano /etc/httpd/conf/httpd.conf

Add the the line Header append X-FRAME-OPTIONS "SAMEORIGIN" and save the file, then restart Apache.

MIME-type sniffing

This header prevents Internet Explorer from MIME-sniffing a response away from the declared content-type as the header instructs the browser not to override the response content type. With the nosniff option, if the server says the content is text/html, the browser will render it as text/html.

Edit the httpd.conf file.

    sudo nano /etc/httpd/conf/httpd.conf

Add the the line Header set X-Content-Type-Options "nosniff" and save the file, then restart Apache.

Load Balancing

This example shows how to setup and configure Apache on CentOS 7 and Kestrel on the same instance machine. However, in order to not have a single point of failure; using mod_proxy_balancer and modifying the VirtualHost would allow for managing mutliple instances of the web applications behind the Apache proxy server.

    sudo yum install mod_proxy_balancer

In the configuration file, an additional instance of the hellomvc app has been setup to run on port 5001 and a the Proxy section has been set with a balaber configuration with two members to load balance byrequests.

    <VirtualHost *:80>
        RewriteEngine On
        RewriteCond %{HTTPS} !=on
        RewriteRule ^/?(.*) https://%{SERVER_NAME}/ [R,L]
    </VirtualHost>

    <VirtualHost *:443>
            ProxyPass / balancer://mycluster/ 

            ProxyPassReverse / http://127.0.0.1:5000/
            ProxyPassReverse / http://127.0.0.1:5001/

            <Proxy balancer://mycluster>
                BalancerMember http://127.0.0.1:5000
                BalancerMember http://127.0.0.1:5001 
                ProxySet lbmethod=byrequests
            </Proxy>

            <Location />
                SetHandler balancer
            </Location>
            ErrorLog /var/log/httpd/hellomvc-error.log
            CustomLog /var/log/httpd/hellomvc-access.log common
            SSLEngine on
            SSLProtocol all -SSLv2
            SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:!RC4+RSA:+HIGH:+MEDIUM:!LOW:!RC4
            SSLCertificateFile /etc/pki/tls/certs/localhost.crt
            SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
    </VirtualHost>

Rate Limits

Using mod_ratelimit, which is included in the htttpd module you can limit the amount of bandwidth of clients.

    sudo nano /etc/httpd/conf.d/ratelimit.conf

The example file limits bandwidth as 600 KB/sec under the root location.

    <IfModule mod_ratelimit.c>
        <Location />
            SetOutputFilter RATE_LIMIT
            SetEnv rate-limit 600
        </Location>
    </IfModule>
Tweet Post Share Update Email RSS

Hi, I'm Shayne Boyer, I write this site, work on ASP.NET Core content and Open Source, speak at national and community events while helping teams architect web and cloud applications.

Tags:
apache centos aspnetcore