Category Archives: server

Configuring NGINX Load Balancer Reverse Proxy

Below is example of NGINX reverse proxy with 2 backend load balanced:

upstream backend {
  ip_hash;
  server localhost:8080 fail_timeout=3;
  server localhost:8081 fail_timeout=3;
}

server {
  listen 80;
  server_name mydomain.com;

  location / {
    proxy_pass http://backend/;
    proxy_redirect default;
    proxy_cookie_domain localhost mydomain.com;
  }
}
  • The upstream directive defines a cluster named backend of 2 backend servers (localhost:8080 and localhost:8081). fail_timeout parameter specifies request to the node will be deemed fail if no response is obtained after 3 seconds.
  • The ip_hash directive causes request coming from same ip to be associated with the same backend. This is often called sticky session. Other popular strategy is using cookie.
  • The cluster name backend is referenced by proxy_pass directive inside location

Add down parameter to avoid request being passed to specific backend:

upstream backend {
  ip_hash;
  server localhost:8080 fail_timeout=3;
  server localhost:8081 fail_timeout=3 down;
}

This is handy when performing no-outage release.

Don’t forget to reload the configuration using nginx -s reload

Read Related NGINX Docos

IP Whitelisting wp-admin In Nginx

Similar like Apache, Nginx also has allow & deny directives allowing you to block certain ip. Here’s a config I use to whitelist /wp-admin to certain IP only.

location / {
  try_files $uri $uri/ /index.php?$args;
}

location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
  access_log off; log_not_found off; expires max;
}

location ~ \.php$ {
  try_files                $uri =404;
  fastcgi_pass             localhost:9000;
  fastcgi_index            index.php;
  fastcgi_param            SCRIPT_FILENAME  $document_root$fastcgi_script_name;
  fastcgi_intercept_errors on;
  include                  fastcgi_params;
}

location ~ ^/wp-(admin|login) {
  allow /32;
  deny  all;
  location ~ \.php$ {
    try_files                $uri =404;
    fastcgi_pass             localhost:9000;
    fastcgi_index            index.php;
    fastcgi_param            SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    fastcgi_intercept_errors on;
    include                  fastcgi_params;
  }
}

Apart from any path starting with /wp-admin, this will also restrict /wp-login.php to specified IP only.

Hopefully this come handy when you’re configuring wordpress using nginx php-fpm.

WordPress Config For Nginx PHP-FPM

Here’s how you can setup nginx and php-fpm for use with wordpress. It is assumed you have nginx and and php-fpm installed.

Goals / Environment

  • Domain mycooldomain1.com pointing to (http only)
  • wordpress installed on /usr/share/mycooldomain1.com_wordpress
  • php-fpm listening on localhost port 9000

On your nginx.conf (typically located at /etc/nginx/nginx.conf), add following server element somewhere down the inner-bottom of http:

http {
  ...
  server {
    listen      80;
    server_name mycooldomain1.com;
    access_log  /var/log/nginx/mycooldomain1.com.access.log main;
    error_log   /var/log/nginx/mycooldomain1.com.error.log;
    root        /usr/share/mycooldomain1.com_wordpress;
    index       index.php index.html index.htm;

    # This location block matches everything, but subsequently the first matching
    # regex location block (the ones starting with ~) will be used instead if any.
    # The try_files statement below will check if the request matches any file or
    # directory and serve it directly, otherwise it will redirect into /index.php
    # for nice permalink URLs processing
    location / {
      try_files $uri $uri/ /index.php?$args;
    }

    # Avoid logging these extensions and set maximum cache expiry. This is as
    # recommended by http://codex.wordpress.org/Nginx
    location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
      access_log off; log_not_found off; expires max;
    }

    # Any requests ending with .php will be processed using this block instead of above,
    # including request to root (http://mycooldomain1.com/). This is true because we've set 
    # index.php to be one of the index file searched above
    location ~ \.php$ {
      try_files      $uri =404;
      fastcgi_pass   localhost:9000;
      fastcgi_index  index.php;
      fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
      include        fastcgi_params;
    }
  }
}

The directive include fastcgi_params imported directives from external file. fastcgi_params is a default file which comes with nginx / php-fpm installation, in case you don’t have it here’s the default fastcgi_params I have on my box.

Restart both nginx and php-fpm once you’ve updated the config:

sudo service nginx restart
sudo service php-fpm restart

And yes nginx config is pretty mundane to learn and debug. I’ve spent few hours reading the official doc as well as various posts to make it work. One most important paragraph of the official doc is probably this (from http_core_module location directive)

A location can either be defined by a prefix string, or by a regular expression. Regular expressions are specified with the preceding “~*” modifier (for case-insensitive matching), or the “~” modifier (for case-sensitive matching). To find location matching a given request, nginx first checks locations defined using the prefix strings (prefix locations). Among them, the location with the longest matching prefix is selected and remembered. Then regular expressions are checked, in the order of their appearance in the configuration file. The search of regular expressions terminates on the first match, and the corresponding configuration is used. If no match with a regular expression is found then the configuration of the prefix location remembered earlier is used.

Enjoy!

Internal SSL (TLS) Certification Authority Using OpenSSL

WARNING
The method described on this article is only intended for low critical testing, not production use. I am not liable for any damages / security breaches caused by following this method.

Scenario

I have few servers for development and testing purpose. I need to setup apache on those servers listening over HTTPS but it will only be used by people in my team, it’ll be a bit overkill to purchase SSL certificate just for this pupose.

I can create a self-signed certificate for each servers, and ask individual user to trust it (or otherwise they’ll get red browser warnings), but this is tedious. A better approach is to create a certificate for each server and chain it to our own internal CA. Each user only need to trust one certificate, and anything chained to it is automatically trusted (this can even be done to the whole network using domain controller auto enrollment).

This article will highlight how to create your own Certification Authority (CA), install it on apache and import it to your Windows / Firefox keystore. The tool I’m using is openssl. Following is the hypothetical setting for example purpose:

  • Company name: Sunny Happy Co Ltd
  • Company e-mail:
  • Server name: shcdev01 (web server will be installed on https://shcdev01)
  • OpenSSL is installed on C:OpenSSL-Win32

Setup openssl

Most UNIX system come with package manager where you can install openssl. If you’re on windows, download and install openssl here.

Setup Certification Authority (CA)

  1. Create directory where user certificates generation / chaining will take place, eg: C:SSLCerts
  2. Create a directory where CA files will be stored: C:SSLCertsSunnyHappyCA
  3. Copy contents of C:OpenSSL-Win32binPEMdemoCA to C:SSLCertsSunnyHappyCA. This folder contains infrastructure file necessary for certificate chaining. This is a sample folder provided by OpenSSL installation so we don’t have to start from scratch
  4. Copy C:OpenSSL-Win32binopenssl.cfg to C:SSLCerts. This is the configuration file used when signing certificates. Few values need to be adjusted. Find following configuration keys on openssl.cfg and update it:
    • dir = C:/SSLCerts/SunnyHappyCA (This will be used as a relative path of other config items)
    • default_days = 7300 (Here I’m setting the default validity of chained certificate to be 20 years)
    • default_md = sha1
  5. Create a new folder C:SSLCertsSunnyHappyCAnewcerts
  6. Create a new file C:SSLCertsSunnyHappyCAindex.txt.attr and write the value unique_subject = no inside
  7. Open a command prompt with root / Administrator privilege and cd to C:SSLCerts. Tell openssl where your config file is by running set OPENSSL_CONF=C:/SSLCerts/openssl.cfg
  8. Create the CA public / private keypair using following command:
    openssl req -new -x509 -keyout SunnyHappyCA/private/cakey.pem -out SunnyHappyCA/cacert.pem -days 7300

    OpenSSL will prompt you for the key password, make sure you keep it safe since you will be using this again. On the next steps it will prompt you for geographical location and other info, this is what I put:

    c:SSLCerts>openssl req -new -x509 -keyout SunnyHappyCA/private/cakey.pem -out SunnyHappyCA/cacert.pem -days 7300
    Generating a 1024 bit RSA private key
    .++++++
    ..++++++
    writing new private key to 'SunnyHappyCA/private/cakey.pem'
    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:AU
    State or Province Name (full name) [Some-State]:NSW
    Locality Name (eg, city) []:Sydney
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Sunny Happy Co Ltd
    Organizational Unit Name (eg, section) []:
    Common Name (e.g. server FQDN or YOUR name) []:Sunny Happy Root CA
    Email Address []:
    

    And I (again) set the validity to be 20 years (7300 days). The private key will go to SunnyHappyCA/private/cakey.pem and the public key SunnyHappyCA/cacert.pem. You now have a root CA keypair done!

Server Certificate Generation & Chaining

Server in here means our web / app server for testing purpose. In our case it sits on the unix host schdev01.

  1. Generate private key and certificate request for the server
    openssl req -new -keyout shcdev01_key.pem.orig -out shcdev01_req.pem

    Openssl will again prompt for password but this is different than the CA password. It will also prompt for similar locality information. It’s very important you put the Common Name into the corresponding server DNS name (schdev01 in this case), because this is what will be used on handshake.

    c:SSLCerts>openssl req -new -keyout shcdev01_key.pem.orig -out shcdev01_req.pem
    Generating a 1024 bit RSA private key
    .....................................................++++++
    ..................................++++++
    writing new private key to 'shcdev01_key.pem.orig'
    Enter PEM pass phrase:
    Verifying - Enter PEM pass phrase:
    -----
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    What you are about to enter is what is called a Distinguished Name or a DN.
    There are quite a few fields but you can leave some blank
    For some fields there will be a default value,
    If you enter '.', the field will be left blank.
    -----
    Country Name (2 letter code) [AU]:
    State or Province Name (full name) [Some-State]:NSW
    Locality Name (eg, city) []:Sydney
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Sunny Happy Co Ltd
    Organizational Unit Name (eg, section) []:
    Common Name (e.g. server FQDN or YOUR name) []:shcdev01
    Email Address []:
    
    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:
    An optional company name []:
    

    By now we will have 2 additional files on C:SSLCerts: shcdev01_key.pem.orig (the encrypted private key) and shcdev01_req.pem (the certification request).

  2. Run following command to decrypt the user’s private key. This is necessary so we can use it later for chaining purpose.
    openssl rsa -in shcdev01_key.pem.orig -out shcdev01_key.pem
  3. And finally the big step: chaining your certificate to Sunny Happy Root CA. Run this command:
    openssl ca -in shcdev01_req.pem -out shcdev01_cert.pem

    Openssl will ask for the CA key’s password. Once you entered it will also ask you to confirm to certify. Sign it by entering y

    c:SSLCerts>openssl ca -in shcdev01_req.pem -out shcdev01_cert.pem
    Using configuration from C:/SSLCA/openssl.cfg
    Enter pass phrase for C:/SSLCA/SunnyHappyCA/private/cakey.pem:
    Check that the request matches the signature
    Signature ok
    Certificate Details:
            Serial Number: 286 (0x11e)
            Validity
                Not Before: Jan 24 05:00:19 2014 GMT
                Not After : Jan 22 05:00:19 2024 GMT
            Subject:
                countryName               = AU
                stateOrProvinceName       = NSW
                organizationName          = Sunny Happy Co Ltd
                commonName                = shcdev01
                emailAddress              = 
            X509v3 extensions:
                X509v3 Basic Constraints:
                    CA:FALSE
                Netscape Comment:
                    OpenSSL Generated Certificate
                X509v3 Subject Key Identifier:
                    40:27:EF:28:95:26:FF:19:BA:99:92:35:F2:AA:C4:2B:1E:43:77:5E
                X509v3 Authority Key Identifier:
                    keyid:BA:F7:51:AA:23:59:89:27:9A:22:47:49:6C:86:68:2C:4E:34:AD:82
    
    Certificate is to be certified until Jan 22 05:00:19 2024 GMT (3650 days)
    Sign the certificate? [y/n]:y
    
    
    1 out of 1 certificate requests certified, commit? [y/n]y
    Write out database with 1 new entries
    Data Base Updated
    

By the end of these steps, you’ll have a user certificate chained to our CA. You can rename .pem file into .crt and view it using Windows certificate viewer.

You can repeat these steps for as many apache servers you have. You will be using the private key (schdev01_key.pem) and certificate file schdev01_cert.pem on apache config.

Importing Root CA Certificate to Client’s PC

In windows, additional third party Root CA can be added via the certificate manager tool:

  1. Rename your CA certificate file (C:/SSLCerts/SunnyHappyCA/cacert.pem) into .crt file
  2. Open a command prompt as administrator and run certmgr.msc
  3. On the left tree select Trusted Root Certification Authorities/Certificates, right click > All Tasks > Import…
    importcacert
  4. Select the cacert.crt file we generated above when prompted. When the wizard asks where to keep the certificate, choose Place all certificates in the following store, click Browse, make sure Show physical stores is ticked and select Third-Party Root Certification Authorities/Local Computer. See this superuser forum Q&A if you can’t find it.
    savecacert

Once you’ve done this, you can inspect your shcdev01_cert.crt (after renaming it from pem) in windows and it will show it is chained to our company’s CA

clientcert

Credits

Thanks to Philippe Camacho for the OpenSSL tutorial from which I based this post.