Category: Server

  • Hackers came along…

    There I was, minding my own business… Coding… drinking coffee… and you know… working 🙂

    Then some hackers came along and ruined all the fun. Digitalocean were fast to take action and closed all the eth-interfaces on the server and contacted me.

    Fortunately it’s a redundant setup, so closing down one server didn’t cause any issues for our customers.
    It did however, leave me with a great deal of work in order to fix the server again.

    I never reuse a server that has been hacked. “Delete it, create a new, and rethink the solution to make it better”.

    The server only exposes ssh and a webserver to the world, so one of those would have had to be the target.

    From what I could see in various logs, they (the hackers) simply tried a million logins to get into the server, using ssh.
    So if brute forcing their way in, is the only way they can think of, we might as well ban their IPs.

    I found a nice little opensource project called fail2ban which installs nicely on all major linux distros, using the packagemanagers. (which means no compiling and dependency management and manually updating the script for me!)
    It simply parses the /var/log/auth.log file (on ubuntu/debian, and /var/log/secure.log on other distros) and sees if a certain IP has been trying too many times to signin without success. (well, this is the default SSH behaviour at least).

    However, on ubuntu I found a problem with the auth.log file, since is grouped redundant messages.
    So if fail2ban tests for multiple matching lines, and ubuntu groups similar lines, then it wouldn’t work… and it didn’t.

    It was an easy fix though.
    Simply edit /etc/rsyslog.conf and find “$RepeatedMsgReduction on”. Set this to “off”, save the file, restart the service (sudo service rsyslog restart) and you are good to go.

    fail2ban will now start to parse the log file (/var/log/auth.log) and little by little the attackers are banned in the firewall.

    Simple and efficient.

    The default settings is to ban the attackers for 10mins after 3-6 tries.
    Now you might ask: “why not just ban them forever?”… well, because I sometimes type passwords wrong 🙂
    I use randomly generated passwords and I could easily hit 3 tries and get banned. So banning IPs for 10mins seems like a fair solution 🙂

    And remember kids, always update the software on your servers at least once a week 🙂
    A lot of critical errors are fixed all the time, and if you install your apps using the packagemanager, it doesn’t take more than a few mins per server.

  • Problems connecting to unix:///var/mysql/mysql.sock

    In a previous post I talked about MySQL 5.5 and Mac OSX.

    In this post I’ll go through fixing the problems with PHP and connecting to your local mysql install, using “localhost”.

    The problems began a while back, with lots of errors in my apache error log saying:

    [error] [client ::1] PHP Warning:  mysql_connect(): [2002] No such file or directory (trying to connect via unix:///var/mysql/mysql.sock) in ...

    In the rush I was in, I quickly changed my mysql connection to use: 127.0.0.1, which is the IP of your localhost. So basically the same thing.

    Today I’m doing some freelance work for a customer, who has some problems with his server, after the PHP version was upgraded. I decided to fetch all his php files to my local Mac and then run through his webshop, fixing any errors I might see in the log etc.
    When starting the my apache and running the website, I quickly found the MySQL connect error again.

    Since this project should be a “search and fix” mission, I didn’t have time to change the mysql_connect(xxx) statements in the code (yes yes, not my code, so I didn’t create the mess…), so instead, I wanted to fix my local PHP->MySQL connection.

    The fix was relatively easy, and only contains 1 to 2 steps:

    Step 1 (if needed)

    If you haven’t activated php.ini on your local install, open a Terminal and write the following command:

    sudo cp /etc/php.ini.default /etc/php.ini

    This copies the default php settings to the php.ini file, which the apache server uses.

    Then restart your apache server. (Using System Preferences->Sharing->Web sharing)

    Your PHP is now using the php.ini file.

    Step 2

    Open /etc/php.ini file using your favorite text editor.

    Goto line 1216 (or search for “mysql.default_socket = ” without the quotes) and change /var/mysql/mysql.sock to /tmp/mysql.sock

    Restart your apache server and you should now be able to connect to localhost again.

    Still have problems?

    If you still have problems, then try the following:

    Open Terminal and write:

    mysqladmin version

    It should print something like this:

    mysqladmin  Ver 8.42 Distrib 5.1.53, for apple-darwin10.3.0 on i386
    Copyright 2000-2008 MySQL AB, 2008 Sun Microsystems, Inc.
    This software comes with ABSOLUTELY NO WARRANTY. This is free software,
    and you are welcome to modify and redistribute it under the GPL license
    
    Server version		5.1.53
    Protocol version	10
    Connection		Localhost via UNIX socket
    UNIX socket		/tmp/mysql.sock
    Uptime:			2 hours 52 min 6 sec
    
    Threads: 3  Questions: 58  Slow queries: 0  Opens: 16  Flush tables: 1  Open tables: 9  Queries per second avg: 0.5

    The path in the UNIX socket is the “localhost” connection point. So go back to Step 2 and use that path instead.

  • Handle different environments with PHP

    Being both a Rails and PHP developer, I’m often lacking a few things when I’m switching from Rails to PHP.

    One of the things I miss the most, is the different environments that Rails has, which makes testing, developing, staging and production environments easy.
    However, I found a way to do this in PHP as well, without using frameworks like Zend, CakePHP etc.

    Getting started

    There are basically two things you need to do, in order to get this setup to work properly.

    Configure your server (apache)

    First we need to configure the apache server (If you are using nginx, look at their documentation for help).

    The magic lies in using the SetEnv function in the apache server.
    This functions makes a variable available in the $_SERVER object in PHP, which is what we are using to differentiate between the environments.

    Virtual hosts

    If you are using virtual hosts, then simply add it with in the section.

    An example configuration with a “test” environment could be:

    <VirtualHost *:80>
      ServerName my_site.test.dk
      DocumentRoot /var/www/my_site.test.dk
    
      SetEnv APPLICATION_ENV test
    
      <Directory /var/www/my_site.test.dk>
        Options Indexes FollowSymLinks -MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
      </Directory>
    
      ErrorLog /var/log/apache2/my_site.test.dk.error.log
    
      # Possible values include: debug, info, notice, warn, error, crit,
      # alert, emerg.
      LogLevel warn
    
      CustomLog /var/log/apache2/access.log combined
    </VirtualHost>
    

    Using the apache.conf / httpd.conf

    On my mac the file is called httpd.conf, but on the ubuntu linux servers I’m managing it’s called apache.conf.

    Anyways, just head to the bottom of the file located here:
    Linux: /etc/apache/apache.conf
    Mac: /etc/apache/httpd.conf

    And add the following line:

    SetEnv APPLICATION_ENV "development"
    

    Exchange “development” with the environment you are configuring (In my setup, production = no value)

    Configure your application

    Now we need to use the variable passed to the $_SERVER object, in order to see what environment we are “in”, and configure the application accordingly.

    In most projects I have an environment.inc.php file, which has the following structure:

    <?php
    
    // initializing the configuration array (mostly to avoid null warnings)
    $envConfiguration = array();
    
    // the environment configuration for the development environment (local machine)
    if(isset($_SERVER['APPLICATION_ENV']) && $_SERVER['APPLICATION_ENV'] == 'development') {
      $envConfiguration = array(
        'db_password' => '12345',
        'db_user' => 'root',
        'db_host' => '127.0.0.1',
        'db_name' => 'my_dev_db'
      );
    }
    // the environment configuration for the unit testing environment (local machine)
    if(isset($_SERVER['APPLICATION_ENV']) && $_SERVER['APPLICATION_ENV'] == 'unittest') {
      $envConfiguration = array(
        'db_password' => '12345',
        'db_user' => 'root',
        'db_host' => '127.0.0.1',
        'db_name' => 'my_unittest_db'
      );
    }
    // add more environments here... E.g. staging, test etc
    
    // Not having the APPLICATION_ENV variable set, forces the application to
    // use PRODUCTION settings!
    // The reason for this is, that I don't always have control of the production
    // servers, while I have control over the staging and test servers. 
    // (You can of course have a production value set
    else {
      // production environment settings here.
      $envConfiguration = array(
        'db_password' => 'some_strong_password',
        'db_user' => 'some_production_user',
        'db_host' => '127.0.0.1',
        'db_name' => 'production_database'
      );
    }
    ?>
    

    Pretty simple ye?

    What we are doing is simply checking if the APPLICATION_ENV variable is set in the $_SERVER object, and if it is, we test what it is.

    The reason I’m checking if the APPLICATION_ENV isset, is because it gives a lot of warnings if the variable is not set (which would be in production for my setup).

    What about unit testing? (phpunit)

    Well, I have an answer there as well.

    Since the $_SERVER variable is not available in unit tests, we simply create it ourselves and set the APPLICATION_ENV to “unittest”.

    Being both a Rails and PHP developer, I’m often lacking a few things when I’m switching from Rails to PHP.

    One of the things I miss the most, is the different environments that Rails has, which makes testing, developing, staging and production environments easy.
    However, I found a way to do this in PHP as well, without using frameworks like Zend, CakePHP etc.

    Getting started

    There are basically two things you need to do, in order to get this setup to work properly.

    Configure your server (apache)

    First we need to configure the apache server (If you are using nginx, look at their documentation for help).

    The magic lies in using the SetEnv function in the apache server.
    This functions makes a variable available in the $_SERVER object in PHP, which is what we are using to differentiate between the environments.

    Virtual hosts

    If you are using virtual hosts, then simply add it with in the section.

    An example configuration with a “test” environment could be:

    <VirtualHost *:80>
      ServerName my_site.test.dk
      DocumentRoot /var/www/my_site.test.dk
    
      SetEnv APPLICATION_ENV test
    
      <Directory /var/www/my_site.test.dk>
        Options Indexes FollowSymLinks -MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
      </Directory>
    
      ErrorLog /var/log/apache2/my_site.test.dk.error.log
    
      # Possible values include: debug, info, notice, warn, error, crit,
      # alert, emerg.
      LogLevel warn
    
      CustomLog /var/log/apache2/access.log combined
    </VirtualHost>
    

    Using the apache.conf / httpd.conf

    On my mac the file is called httpd.conf, but on the ubuntu linux servers I’m managing it’s called apache.conf.

    Anyways, just head to the bottom of the file located here:
    Linux: /etc/apache/apache.conf
    Mac: /etc/apache/httpd.conf

    And add the following line:

    SetEnv APPLICATION_ENV "development"
    

    Exchange “development” with the environment you are configuring (In my setup, production = no value)

    Configure your application

    Now we need to use the variable passed to the $_SERVER object, in order to see what environment we are “in”, and configure the application accordingly.

    In most projects I have an environment.inc.php file, which has the following structure:

    <?php
    
    // initializing the configuration array (mostly to avoid null warnings)
    $envConfiguration = array();
    
    // the environment configuration for the development environment (local machine)
    if(isset($_SERVER['APPLICATION_ENV']) && $_SERVER['APPLICATION_ENV'] == 'development') {
      $envConfiguration = array(
        'db_password' => '12345',
        'db_user' => 'root',
        'db_host' => '127.0.0.1',
        'db_name' => 'my_dev_db'
      );
    }
    // the environment configuration for the unit testing environment (local machine)
    if(isset($_SERVER['APPLICATION_ENV']) && $_SERVER['APPLICATION_ENV'] == 'unittest') {
      $envConfiguration = array(
        'db_password' => '12345',
        'db_user' => 'root',
        'db_host' => '127.0.0.1',
        'db_name' => 'my_unittest_db'
      );
    }
    // add more environments here... E.g. staging, test etc
    
    // Not having the APPLICATION_ENV variable set, forces the application to
    // use PRODUCTION settings!
    // The reason for this is, that I don't always have control of the production
    // servers, while I have control over the staging and test servers. 
    // (You can of course have a production value set
    else {
      // production environment settings here.
      $envConfiguration = array(
        'db_password' => 'some_strong_password',
        'db_user' => 'some_production_user',
        'db_host' => '127.0.0.1',
        'db_name' => 'production_database'
      );
    }
    ?>
    

    Pretty simple ye?

    What we are doing is simply checking if the APPLICATION_ENV variable is set in the $_SERVER object, and if it is, we test what it is.

    The reason I’m checking if the APPLICATION_ENV isset, is because it gives a lot of warnings if the variable is not set (which would be in production for my setup).

    What about unit testing? (phpunit)

    Well, I have an answer there as well.

    Since the $_SERVER variable is not available in unit tests, we simply create it ourselves and set the APPLICATION_ENV to “unittest”.

    Here is a sample unittest include file, which should be included at the very top of your unittest.
    Let’s call this file unitTestConfiguration.inc.php and put it in a folder called tests

    <?php
    
    // includes the phpunit framework
    require_once 'PHPUnit/Framework.php';
    
    // constructs the SERVER variable to set the environment to unittest.
    $_SERVER = array(
      'APPLICATION_ENV' => 'unittest',
      'SERVER_NAME' => 'localhost',
      'REQUEST_URI' => ''
    );
    // SERVER_NAME and REQUEST_URI is not needed, but nice to have
    
    // includes our environment file (remember to add a unittest section there!
    include('config/environment.inc.php');
    
    // includes the database file, which reads the $envConfiguration variable
    // (which is set in the environment.inc.php file) and connects to the database
    include('config/db.inc.php');
    
    // sets the default timezone (Because strftime will throw a warning in PHP5+)
    date_default_timezone_set("Europe/Copenhagen");
    
    ?>
    

    When creating your unit test, simply do the following:

    <?php
    
    // includes the unit test configuration (including the PHPUnit framework)
    include('tests/unitTestConfiguration.inc.php');
    
    class EnvironmentTest extends PHPUnit_Framework_TestCase {
      /**
       * A small test to see if our environment is actually set.
       * (You don't need this test in your test files, this is 
       * just for the scope of this post!)
       */
      function testEnvironment() {
        $this->assertTrue(isset($_SERVER['APPLICATION_ENV']));
        $this->assertTrue($_SERVER['APPLICATION_ENV'] == 'unittest');
      }
    }
    ?>
    

    To run the unit test, simply open a terminal / command / cmd (or what ever you are using), and go to the project folder.
    There you should run the following command:

    phpunit tests/environmentTest.php
    

    On my machine that gives the following output:

    $ phpunit tests/environmentTest.php 
    PHPUnit 3.4.14 by Sebastian Bergmann.
    
    .
    
    Time: 0 seconds, Memory: 5.75Mb
    
    OK (1 test, 2 assertions

     variable is not available in unit tests, we simply create it ourselves and set the APPLICATION_ENV to “unittest”.

    Here is a sample unittest include file, which should be included at the very top of your unittest.
    Let’s call this file unitTestConfiguration.inc.php and put it in a folder called tests

    <?php 
    // includes the phpunit framework
    require_once 'PHPUnit/Framework.php';
    // constructs the SERVER variable to set the environment to unittest.
    $_SERVER = array(
    'APPLICATION_ENV' => 'unittest',
    'SERVER_NAME' => 'localhost',
    'REQUEST_URI' => ''
    );
    // SERVER_NAME and REQUEST_URI is not needed, but nice to have
    // includes our environment file (remember to add a unittest section there! include('config/environment.inc.php');
    // includes the database file, which reads the $envConfiguration variable
    // (which is set in the environment.inc.php file) and connects to the database include('config/db.inc.php');
    // sets the default timezone (Because strftime will throw a warning in PHP5+)
    date_default_timezone_set("Europe/Copenhagen"); ?>

    When creating your unit test, simply do the following:

    <?php 
    // includes the unit test configuration (including the PHPUnit framework) include('tests/unitTestConfiguration.inc.php');

    class EnvironmentTest extends PHPUnit_Framework_TestCase {
    /**
    * A small test to see if our environment is actually set.
    * (You don't need this test in your test files, this is
    * just for the scope of this post!)
    */
    function testEnvironment() {
    $this->assertTrue(isset($_SERVER['APPLICATION_ENV']));
    $this->assertTrue($_SERVER['APPLICATION_ENV'] == 'unittest');
    }
    }

    To run the unit test, simply open a terminal / command / cmd (or what ever you are using), and go to the project folder.
    There you should run the following command:

    phpunit tests/environmentTest.php
    

    On my machine that gives the following output:

    $ phpunit tests/environmentTest.php  
    PHPUnit 3.4.14 by Sebastian Bergmann. .
    Time: 0 seconds, Memory: 5.75Mb OK (1 test, 2 assertions

    Files for this post: 2011-01-08_php_testing_article.zip