apacheconf

How to change the Apache 2 server signature on Debian Etch

Tagged disable, apache2, signature, server, header  Languages apacheconf

Install mod_security, for some stupid reason it's not included in Debian Etch, and for some even more stupid reason you're not allowed to change the value of the Server header.

Anyway, to change the server signature, and enable voodoo magic:

<IfModule mod_security2.c>
    # Basic configuration options
    SecRuleEngine On
    SecRequestBodyAccess On
    SecResponseBodyAccess Off

    # Handling of file uploads
    # TODO Choose a folder private to Apache.
    # SecUploadDir /opt/apache-frontend/tmp/
    SecUploadKeepFiles Off

    # Debug log
    SecDebugLog /var/log/apache2/modsec_debug.log
    SecDebugLogLevel 0

    # Serial audit log
    SecAuditEngine RelevantOnly
    SecAuditLogRelevantStatus ^5
    SecAuditLogParts ABIFHZ
    SecAuditLogType Serial
    SecAuditLog /var/log/apache2/modsec_audit.log

    # Maximum request body size we will
    # accept for buffering
    SecRequestBodyLimit 131072

    # Store up to 128 KB in memory
    SecRequestBodyInMemoryLimit 131072

    # Buffer response bodies of up to
    # 512 KB in length
    SecResponseBodyLimit 524288

    SecServerSignature "Dummy value"
</IfModule>

How to hide X-Powered-By and Server headers

Tagged apache, apache2, headers, servertokens, server, x-powered-by, mod_rails, passenger  Languages apacheconf

First enable the mod_headers module:

sudo a2enmod headers

Then add this to your apache2.conf:

# Hide X-Powered-By and Server headers
Header always unset "X-Powered-By"
ServerTokens Prod
ServerSignature Off

Now restart Apache:

/etc/init.d/apache2 force-reload

This is security through obscurity at it's finest...

SEO optimizing CarrierWave URLs

Tagged carrierwave, filename, url, seo  Languages apacheconf
class ProductImageUploader < CarrierWave::Uploader::Base

  def store_dir
    "product"
  end

  def filename
    ext = File.extname(original_filename) if original_filename
    "#{model.name.parameterize}#{ext}"
  end
end

Configuring Apache to be a forward proxy

Tagged rewrite, proxy, forward, apache, forward proxy  Languages apacheconf

First enable the proxy and proxy_http modules:

sudo a2enmod proxy
sudo a2enmod proxy_http

Then use this configuration to make Apache act as an HTTP proxy:

<VirtualHost *:8080>
# Enable forward proxy
ProxyRequests On
# Add "Via" header
ProxyVia On
#ProxyRemote * http://...:8080 Uncomment to route requests through another proxy

<Proxy *>
    Order deny,allow
      Deny from all
  # Allow access only from local network
  Allow from 192.168.1
</Proxy> 
    
# Enable caching proxy
CacheRoot "/tmp"
CacheMaxExpire 24
CacheLastModifiedFactor 0.1
CacheDefaultExpire 1

ServerName my-proxy

ErrorLog "/var/log/apache2/proxy-error.log"
CustomLog "/var/log/apache2/proxy-access.log" common
</VirtualHost>

Also read this.

Tips

You can use mod_rewrite to rewrite requests. To rewrite root (/) to /temporary_outage you could use the following rewrite:

RewriteCond %{HTTP_HOST} ^(www\.)?xxx\.com 
RewriteRule /$ http://%{HTTP_HOST}/temporary_outage/ [P,L]

Rails+Mongrel+Apache 2 on Mac OSX Leopard

Tagged rails, apache, osx, mongrel  Languages apacheconf

I use this configuration on my development machine when I need mod_rewrite; it's not meant for production:

<VirtualHost *:80>
ServerName dev.xxx.com

# Enable URL rewriting
RewriteEngine On

# Rewrite index to check for static pages
RewriteRule ^/$ /index.html [QSA]

# Rewrite to check for Rails cached page
RewriteRule ^([^.]+)$ $1.html [QSA]

# Redirect all non-static requests to cluster
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^/(.*)$ balancer://mongrel_cluster%{REQUEST_URI} [P,QSA,L]

DocumentRoot  "/Users/christian/Documents/Projects/xxx/public"
<Directory "/Users/christian/Documents/Projects/xxx/public">
     Options Indexes FollowSymLinks

     AllowOverride None
     Order allow,deny
     Allow from all
 </Directory>

</VirtualHost>

<Proxy balancer://mongrel_cluster>
  BalancerMember http://127.0.0.1:3000
</Proxy>

How to log request processing times with Apache

Tagged performance, mod_headers, apache, x-request-received, x-request-processing-time  Languages apacheconf

To log the time it takes for Apache to process a request add this to your apache configuration file:

LogFormat "%h %D %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

%D means you'll see the time it took Apache to process the request in the access log. The time is measured in microseconds.

To make it even easier to debug page load times, add this to your configuration file:

Header set X-Request-Received: %t
Header set X-Request-Processing-Time: %D

Remember to enable mod_headers first by executing a2enmod headers.

Now you should see these headers in the response:

X-Request-Received  t=1286995673038485
X-Request-Processing-Time   D=251

How to Secure an nginx Server with Fail2Ban

Tagged configuration, security, nginx, throttling, fail2ban, filters  Languages apacheconf

Our Security Requirements

  • Block anyone trying to run scripts (.pl, .cgi, .exe, etc)
  • Block anyone trying to use the server as a proxy
  • Block anyone failing to authenticate using nginx basic authentication
  • Block anyone failing to authenticate using our application's log in page
  • Block bad bots
  • Limit the number of connections per session

We could use ModSecurity to support these requirements, but it's not compatible with nginx. We want a lightweight and easy-to-use solution. We can fulfill all these requirements with fail2ban and nginx.

Configuring Fail2ban

All except the last requirement of connection throttling is supported by Fail2Ban. To start blocking unwanted guests, put this in Fail2Ban's jail.conf file:

[nginx-auth]
enabled = true
filter = nginx-auth
action = iptables-multiport[name=NoAuthFailures, port="http,https"]
logpath = /var/log/nginx*/*error*.log
bantime = 600 # 10 minutes
maxretry = 6

[nginx-login]
enabled = true
filter = nginx-login
action = iptables-multiport[name=NoLoginFailures, port="http,https"]
logpath = /var/log/nginx*/*access*.log
bantime = 600 # 10 minutes
maxretry = 6
 
[nginx-badbots]
enabled  = true
filter = apache-badbots
action = iptables-multiport[name=BadBots, port="http,https"]
logpath = /var/log/nginx*/*access*.log
bantime = 86400 # 1 day
maxretry = 1
 
[nginx-noscript]
enabled = true
action = iptables-multiport[name=NoScript, port="http,https"]
filter = nginx-noscript
logpath = /var/log/nginx*/*access*.log
maxretry = 6
bantime  = 86400 # 1 day
 
[nginx-proxy]
enabled = true
action = iptables-multiport[name=NoProxy, port="http,https"]
filter = nginx-proxy
logpath = /var/log/nginx*/*access*.log
maxretry = 0
bantime  = 86400 # 1 day

Filter configuration

The following filter configuration files are stored in /etc/fail2ban/filter.d/:

# Proxy filter /etc/fail2ban/filter.d/nginx-proxy.conf:
#
# Block IPs trying to use server as proxy.
#
# Matches e.g.
# 192.168.1.1 - - "GET http://www.something.com/
#
[Definition]
failregex = ^<HOST> -.*GET http.*
ignoreregex =
 
# Noscript filter /etc/fail2ban/filter.d/nginx-noscript.conf:
#
# Block IPs trying to execute scripts such as .php, .pl, .exe and other funny scripts.
#
# Matches e.g.
# 192.168.1.1 - - "GET /something.php
#
[Definition]
failregex = ^<HOST> -.*GET.*(\.php|\.asp|\.exe|\.pl|\.cgi|\scgi)
ignoreregex =
 
#
# Auth filter /etc/fail2ban/filter.d/nginx-auth.conf:
#
# Blocks IPs that fail to authenticate using basic authentication
#
[Definition]
 
failregex = no user/password was provided for basic authentication.*client: <HOST>
            user .* was not found in.*client: <HOST>
            user .* password mismatch.*client: <HOST>
 
ignoreregex =
#
# Login filter /etc/fail2ban/filter.d/nginx-login.conf:
#
# Blocks IPs that fail to authenticate using web application's log in page
#
# Scan access log for HTTP 200 + POST /sessions => failed log in
[Definition]
failregex = ^<HOST> -.*POST /sessions HTTP/1\.." 200
ignoreregex =

Testing fail2ban Rules

You should test your fail2ban rules with the fail2ban-regex command:

# Test against a log file
# fail2ban-regex <log file> <filter configuration>
fail2ban-regex /var/log/nginx/access.log /etc/fail2ban/filter.d/nginx-login.conf 

# Test using strings
# fail2ban-regex <e.g. row from log file> <failure regex from filter configuration>
fail2ban-regex "127.1.1.1 - - [21/Feb/2012:10:31:08 +0200] \"POST /sessions HTTP/1.1\" 200 1532 \"https://xxx.com/sessions\" \"Mozilla/5.0 Safari/535.11\" \"-\"" "^<HOST> -.*POST /sessions HTTP/1\..\" 200"

Limiting the number of connections

We limit the number of connections to 2 per second and allow a burst of 50 with the following nginx configuration:

http {
    limit_req_zone  $binary_remote_addr  zone=app:10m   rate=2r/s; 
    ... 
    server {
         ... 
        location / {
            limit_req   zone=app burst=50;
        }

Note that you could also use Rack::Attack to implement many of these requirements.

References

http://wiki.nginx.org/HttpLimitReqModule http://www.fail2ban.org/wiki/index.php/Main_Page http://serverfault.com/questions/307575/fail2ban-doesnt-process-jail-even-though-regex-matches

How to filter post parameters with nginx and the Perl module

Tagged parameters, nginx, post, perl, filter  Languages apacheconf

This example demonstrates how to inspect and filter POST parameters sent through nginx to a backend server with Perl:

nginx => filter (perl) => backend

This could also be done with the Lua module.

nginx.conf:

http {
    perl_modules  /tmp;
    perl_require  filter.pm;

    server {
        # The action we want to filter
        location = /bananas/create {
          perl  filter::handler;
        }
        # The backend server
        location = /backend {
            internal; # Only allowed internally
            proxy_pass http://backend/bananas/create;
        }
        # The action that handles blocked requests
        location = /blocked {
            internal;
            perl filter::block;
        }
}

/tmp/filter.pm:

package filter;
use nginx;

# Handle request
sub handler {
  my $r = shift;
  if ($r->has_request_body(\&checkRequest)) {
    return OK;
  }
  return OK;
}

# Print a message
sub blocked {
  my $r = shift;
  $r->send_http_header("text/html");
  $r->print("Blocked...\n<br/>");
  $r->rflush;
  return OK;
}

# Check request for invalid parameter
sub checkRequest {
  my $r = shift;
  my $body = $r->request_body;
  # Try to detect invalid POST parameters
  if ($body =~ /credit_card=/) {
     $r->internal_redirect("/blocked"); # Redirect to /blocked
  } else {
     $r->internal_redirect("/backend"); # Redirect to /backend
  }
  return OK;
}

1;
__END__

Apache+Unicorn

Tagged unicorn, apache  Languages apacheconf

Configuration for Apache and Unicorn:

<VirtualHost *:80>
  ServerName    xxx
  ServerAlias   xxx

  <Proxy balancer://unicornservers>
    BalancerMember http://127.0.0.1:5000
  </Proxy>

  RewriteEngine On
  # Static files are served by Apache rest by Unicorn
  RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
  RewriteRule ^/(.*)$ balancer://unicornservers%{REQUEST_URI} [P,QSA,L]

  # NOTE Uncomment if you want Unicorn to serve static content
  # ProxyPass / balancer://unicornservers/
  # ProxyPassReverse / balancer://unicornservers/
  # ProxyPreserveHost on

  <Proxy *>
    Order deny,allow
    Allow from all
  </Proxy>
</VirtualHost>