Skip to content

Entries tagged "httpd".

Apache to Lighttpd migration

Lighttpd is smaller and faster alternative to Apache web server. You can handle bigger traffic with the same memory and CPU constraints (it's important on virtual servers where resources are limited). Let's see how we can convert existing Apache+FastCGI stacks into Lighttpd:

Global options

There's special syntax to add options to all virtuals:

global {
    dir-listing.activate = "disable"
    server.follow-symlink = "enable"
}

In above example two config values are set: one is responsible for disabling directory listings ("Options +Indexes" in Apache), second for FollowSymlink counterpart.

Declaring virtual hosts

Lighttpd uses conditionals to specify configuration in general and the same strategy applies for virtuals definitions:

$HTTP["host"] =~ ".*aplikacja.info" {
    (...)
}

Above syntax matches domain and all subdomains. Pretty simple isn't it?

mod_rewrite rewritten

Let's see how RewriteRules could be translated into Lighttpd configuration syntax:

url.rewrite-once = (
    "^/$" => "/index.php",
    "^/\?(.*)$" => "/index.php?$1",
)

Above code converts URL without script path to /index.php and any other url into /index.php?ANY-URL.

Old school cgi-s

Sometimes you want (have to) call scripts using good old protocol: CGI (Common Gateway Interface). It's inefficient method (process creation overhead every request), but have one benefit: memory is freed directly after proces finish. Example:

cgi.assign = ( ".cgi" => "" )

This syntax defines that any script with given extension will be executed as CGI. "" means we use default script interpreter (declared by #!/path/to/file syntax).

Hide some files from HTTP

Sometimes you want to block access by HTTP to only specified URLs (and do not allow, for instance, to download you config file with database login & password). This can be achieved by the following syntax:

$HTTP["url"] !~ "^/index.php|^/static" {
    url.access-deny = ("")
}

In above example only /index.php and all content from static subdirectory is available (other URLs will give 401 unauthorized error).

PHP in FastCGI mode

It's very easy to setup PHP in FastCGI mode in Debian:

lighttpd-enable-mod fastcgi

Above command symlinks /etc/lighttpd/conf-available/10-fastcgi.conf to /etc/lighttpd/conf-enabled/10-fastcgi.conf. You must restart Lighttpd in order to see effects. Lets dig inside this config file:

server.modules   += ( "mod_fastcgi" )

## Start an FastCGI server for php (needs the php5-cgi package)
fastcgi.server    = ( ".php" =>
    ((
    "bin-path" => "/usr/bin/php-cgi",
    "socket" => "/tmp/php.socket",
    "max-procs" => 1,
    "idle-timeout" => 20,
    "bin-environment" => (
    "PHP_FCGI_CHILDREN" => "1",
    "PHP_FCGI_MAX_REQUESTS" => "10000"
    ),
    "bin-copy-environment" => (
    "PATH", "SHELL", "USER"
    ),
    "broken-scriptfilename" => "enable"
    ))
)

SSL Certificate for Lighttpd HOWTO

When your customer enters your website they do not want to make their passwords / credit card information to be visible for everyone (sniffing local network or one of routers in the way). That's why SSL (Secure Socket Layer) was born. Is simple words it wraps HTTP connection in a secure tunnel.

Another story is man-in-the-middle attack possibility or faking DNS servers response. You (as customer opening the webpage) should ensure that you are connecting to website you intended to (fake bank websites are big risk for your money, so it's important). That's why certification is closely bundled with connection encryption.

I'll show you how obtain and install SSL certificate under Lighttpd web server to make your website more trustworthy for your customers.

Fitrst, create directory structure that will make organisation easier:

# mkdir -p /etc/lighttpd/ssl/domain.com
# cd /etc/lighttpd/ssl/domain.com

Create server key (you will be prompted for a password) and CSR (Certificate Signing Request) that will be used for certification creation in one step:

# openssl req -newkey rsa:2048 -keyout domain.com.key -out domain.com.csr

Remove attached password (I do not want to have to pass the password on server restart):

# openssl rsa -in domain.com.key -out domain.com.nopass.key

Then, pass generated domain.com.csr to your SSL certificate provider. You will have to prove you own the domain (an email will be sent to root@domain.com with special URL). After succesfull verification certificate is created. Place (paste) this certificate inside /etc/lighttpd/ssl/domain.com.crt file.

Then you have to create pem file (not sure why it's organised that way):

# cat domain.com.nopass.key domain.com.crt > domain.com.pem

Then you have to tell Lighttpd to handle SSL traffic for given IP address and port:

$SERVER["socket"] == "IP-ADDRESS-HERE:443" {
    ssl.engine = "enable"
    ssl.pemfile = "/etc/lighttpd/ssl/domain.com/domain.com.pem"
    ssl.ca-file = "/etc/lighttpd/ssl/domain.com/domain.com.crt"
}

First note: for SSL traffic you have to specify IP address, not domain name. SSL handshake is done BEFORE headers are sent to server, so name based virtual hosts are not possible (certificates must be checked first).

Second note: if you use the same domain for HTTP and HTTPS traffic don't have to specify server.document-root and other domain-related parameters. They will be borrowed from:

$HTTP["host"] = "domain.com" {
(...)

(plain HTTP) section.

Now browser redirected to https://domain.com should show you your web-application without warnings.

Happy SSL-ing!

Watch your HTTPD logs

Recently I observed that AdWords-generated traffic dissapeared from Analytics panel. I thought: WTH?

I checked the logs and saw that URL called by AdWords:

http://my-site.com/?gclid=342343445345....

Generated 403 (Forbidden) server response. That was caused by recent change in Lighttpd filtering rules. I was paying for AdWords traffic but customer hit 403 error page. Ops!

In order to easily spot such problems in future I created the following scanner to easily find all error server responses.

awk '$9>=400' /var/log/lighttpd/access.log | less

If you are boring of 404 errors you can filter them out as well (leaving only 403 / 500 errors for investigation):

awk '$9>=400 && $9 != 404' /var/log/lighttpd/access.log | less

I discovered that the following URLs were inaccessible:

  • /robots.txt (exclusion rules for web crawlers)
  • /favicon.ico (icon used by web browsers)

Next step could be automation of this check (cron job that will send an alert if errant responses count is higher than N). It's left as exercise for the reader.

Web2py Lighttpd Deployment

Web2py is "full stack" Python Web Framework, Lighttpd is fast, multi-threaded HTTP server. I'll present a method to connect web2py-based application under lighttpd.

I assume the following setup is already done:

  • A domain named "myapp.com" is configured to point to your server
  • Python / lighttpd is already installed on server
  • Your web2py app is placed under /var/www/web2py
  • Your web2py app has application "myapproot" configured

First of all, you have to configure lighttpd to locate web2py application, create file /etc/lighttpd/conf-enabled/myapp.conf:

$HTTP["host"] =~ "(www\.)?myapp\.com" {
    server.indexfiles = ( "/myapproot" )
    server.document-root = "/var/www/myapp"
    server.dir-listing = "disable"
    fastcgi.server = (
        ".fcgi" => ("localhost" => (
            "check-local" => "disable",
            "min-procs" => "1",
            "max-procs" => "2",
            "socket" => "/tmp/myapp.sock")
        )
    )

    url.rewrite-once = (
        "^/$" => "/ad",
        "^(/.+?/static/.+)$" => "/applications$1",
        "(^|/.*)$" => "/fcgihandler.fcgi$1",
    )
    $HTTP["url"] !~ "^(/ad|/fcgihandler.fcgi|/applications/myapproot/static/)" {
        url.access-deny = ("")
    }
}

Explanation:

  • (www\.)?myapp\.com: regular expression to match domain with or without "www." prefix
  • server.indexfiles: specifies relative URL that should be called when only domain is given
  • server.document-root: specifies location of web2py app in filesystem
  • server.dir-listing: we do not want user to list our files using HTTP
  • fastcgi.server: specifies where socket file is located
  • url.rewrite-once: allow to use elegant (short) URLs
  • url.access-deny: files other than static directory should be forbidden (security)

Then you have to configure fcgihandler.fcgi script properly:

(...)
fcgi.WSGIServer(application, bindAddress='/tmp/myapp.sock').run()

Note that /tmp/myapp.sock must be the same as specified in lighttpd configuration.

Then you have to start the fcgihandler.fcgi proces and ensure it will start on every boot. That's all.

Hardening Apache Based Installations

Sometimes you want to test some server-side software on public server but don't want be hit by automated scripts that explore known vulnerabilities in software. The simplest solution is to add additional protection using Apache-based access restrictions.

Enable .htaccess in Apache

Changing configuration can be very flexible and as simple as placing special file in directory you want to protect. Special files ".htaccess" are fragments of Apachec configuration that can be placed in your WWW directory structure. But you have to enable them in apache config (/etc/apache2/sites-available/default):

        <Directory /var/www/>
                Options Indexes FollowSymLinks MultiViews
                AllowOverride All
                Order allow,deny
                allow from all
        </Directory>

Restrict by login / password

We would like to protect application installed under given path with additional login/password. We use digest method to protect password from sniffing even with HTTP connections.

First of all we need mod_auth_digest to be enabled in Apache (a module must be enabled): # a2enmod auth_digest

/etc/init.d/apache2 restart

Then we will create file with user passwords:

$ htdigest -c /home/www-data/.htpasswd app admi

And finally we need to point to that file (fill .htaccess in appropriate directory):

AuthType Digest
AuthName "app"
AuthUserFile /home/www-data/.htpasswd
Require user admin

Then browser should show you authentication window.

Even if installed software probably has some bugs and exploits you can safely test it on public site as long as you trust your users won't try to hack this site (site access is not public, requires Apache login).

Self-signed SSL certificate HOWTO

logo_sslSSL is used for (1) encrypting HTTP traffic and for (2) authentication server against browser's database of trusted certificates. Generating SSL certificate properly is important if you want your customer to use https properly. It costs few bugs per year, but your customers won't have any warnings in browser before SSL session (purpose number 2).

However, for internal applications, self-signed certificate may be a sufficient solution (purpose 1 only). You will find below a minimal commands to generate local SSL certificate (accept default values when asked for data on stdin): mkdir -p /etc/lighttpd/ssl/local cd /etc/lighttpd/ssl/local openssl genrsa -passout pass:1234 -des3 -out server.key 1024 openssl req -passin pass:1234 -new -key server.key -out server.csr cp server.key server.key.org openssl rsa -passin pass:1234 -in server.key.org -out server.key openssl x509 -req -in server.csr -signkey server.key -out server.crt cat server.key server.crt > server.pem Then lighttpd installation: $SERVER["socket"] == "<YOUR_IP_ADDRESS>:443" { ssl.engine = "enable" ssl.pemfile = "/etc/lighttpd/ssl/local/server.pem" ssl.ca-file = "/etc/lighttpd/ssl/local/server.crt" } Then you have to accept server certificate in your browser and voila!