All posts by admin

Disable ipv6

What services are using ipv6 on the server?   None!   Sounds like a potential security issue.  These notes cover disabling ipv6 on Debian Wheezy.

Edit /etc/sysctl.conf and add those parameters to kernel. Also be sure to add extra lines for other network interfaces you want to disable IPv6.

net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
net.ipv6.conf.eth0.disable_ipv6 = 1

Better yet, create file in /etc/sysctl.d/disable-ipv6.conf with those values

Comment out all the IPV6 hosts in /etc/hosts

Add GRUB_CMDLINE_LINUX_DEFAULT=”ipv6.disable=1 quiet” to /etc/default/grub (grub2)
– for Xen domU add to /boot/grub/menu.lst
You have to reboot the system to make changes take effect.

### IMPORTANT: after editing /etc/default/grub you have to execute “update-grub”

For postfix you have to edit /etc/postfix/main.cf and change (or add) b/c by default it uses both ipv4 and ipv6 and you’ll get warnings on reload of postfix unless you put this

inet_protocols = ipv4

Edit exim4 settings or you’ll get entries in the exim4 paniclog.

Edit /etc/exim4/update-exim4.conf.conf add a new line

disable_ipv6='true'

and remove the ipv6 stuff from dc_local_interfaces line then execute update-exim4.conf and service exim4 restart.

Enable SELinux on Xen guest running Debian Wheezy

This post describes how to get SELinux running on a Xen guest (domU) that is running Debian Wheezy.

First, you need a guest that is booted by pvgrub and running a distro-supplied kernel ie, a regular (non-Xen) linux image. All that is convered in my previous post on booting unprivileged domains in Xen with pvgrub on Debian Wheezy. This is a required step.

Next we’ll follow Debian’s own SELinux set up, mostly.

You need to install some packages, but I left out some dependencies in particular that I didn’t want so I used –no-install-recommends.

apt-get –no-install-recommends install selinux-basics selinux-policy-default auditd audispd-plugins

In /etc/default/rcS change

set FSCKFIX=yes

I ran selinux-activate, but it doesn’t do what it is supposed to do. So you have to fix these issues by hand.

Add kernel options to enable SELinux in /boot/grub/menu.lst, mine looks like this:

kernel /vmlinuz root=/dev/xvda1 ro selinux=1 security=selinux

Fix pam by adding to /etc/pam.d/login

session required pam_selinux.so multiple

Touch /.autorelabel

Restart the guest. I use xm create -c /path/to/guest/config so I get the console and can watch for relabelling to occur. After relabelling the system should reboot, but it doesn’t. It shuts down and doesn’t come back up. So I xm create -c /path/to/guest/config again. Once I logged in to the guest I ran sestatus and got

SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: default
Current mode: permissive
Mode from config file: permissive
Policy MLS status: enabled
Policy deny_unknown status: denied
Max kernel policy version: 26

The main issues with Debian’s own SELinux how-to are 1) selinux-activate does not do what it is supposed to in Wheezy 2) we add the kernel options to enable SELinux in /boot/grub/menu.lst.

Also, at somepoint I had to remove a file, but I can’t find the info that I used to justify that step. Here is it:

rm -i /etc/udev/rules.d/010-no-legacy-ptys.rules

Using PvGrub to Start Xen Guests on Debian Wheezy

Goal:

Have guest domains boot with their own kernel, or in other words, be able to run a kernel different than the host. Moreover, this means maintainer of the guest can be responsible for dealing with kernel updates. You can do this with pygrub, but there are reasons you may want to use pvgrub. Note the very minor difference in the spelling of the names! Note: both pygrub and pvgrub are for paravirtualized guests. So if you’re using hardware virtualization, this isn’t for you. Pygrub is named because is a python script. Pvgrub stands for paravirtualized grub.

Caveats:

  • pvgrub is not compatible with grub2, this only applies to if you have grub2 on your guest; you can use it with grub 1 (aka grub-0.9) or no grub at all
  • In Debian 8 all of this will be moot because a grub2 compatible pvgrub is being added, yeah!

Turns out there are some special steps in Debian Wheezy and here’s how I got it working along with reference article I used to figure it out.

These are the required steps and I’ll go into detail on each of the below.

  1. install pvgrub image on the host (dom0)
  2. install linux image on guest and create /boot/grub/menu.lst
  3. edit guest config file on host
  4. start the guest!

Morale Booster:

  1. I figured this out. So can you. I’m a git.
  2. Even when it didn’t work, ie guest didn’t boot, I was still able to shutdown guest, edit guest config file to boot the “old” way and see what I needed to change.
  3. YMMV

In this instance the host is Debian Wheezy running Xen 4.1.4 (for reference).

There is no pvgrub in Debian, so you have to get it from somewhere else. In this case I actually grabbed the _current_ xen release from github and compiled it. This creates the pvgrub images. I was concerned the pvgrub image would not work wtih 4.1.4 but it did, so I’m happy (for now).

The xen.org wiki was the main source where I got this information from and this should be considered a supplement to that article, not a replacement.

Before compiling xen I also needed the following packages in addition to all of the ones lists in the xen wiki post:

libaio-dev
texinfo

Here’s the part that wasn’t clear to me: pvgrub goes on the host, much like pygrub is run from the host. Duh! But that didn’t occur to me at first. So put pvgrub image on your host.

So to complete step #1 install pvgrub on the host you need to:

1) install any of these packages if you don’t already have them:

python-dev gettext bin86 bcc iasl uuid-dev libncurses5-dev pkg-config libglib2.0-dev libyajl-dev libpixman-1-dev bzip2 libc6-dev-i386 libaio-dev texinfo

2) git the xen source:

git clone git://xenbits.xen.org/xen

3) compile xen:
./configure
make

The pvgrub images are gz files in dist/install/usr/local/lib/xen/boot and they are

pv-grub-x86_32.gz
pv-grub-x86_64.gz

and you’ll need the correct one depending on what the guest machine is going to run. My guests are all x86_64. As a side note: I didn’t even compile xen on the host, I did it on another machine running Debian Wheezy then copied over the files I wanted.

4) Copy the pvgrub images to the host machine, in my case I use only amd64 flavors for host and all guests, so I just copied that one to /usr/local/lib/pv-grub-x86_64.gz Made sure its owned by root and permissions are 0644.

To complete step #2 – Install Linux images on guest:

1) on the guest install a linux image of your choosing
2) create /boot/grub/menu.lst with these contents

default 0
timeout 2
title Debian GNU/Linux
root (hd0)
kernel /vmlinuz root=/dev/xvda1 ro
initrd /initrd.img
title Debian GNU/Linux (recovery mode)
root (hd0)
kernel /vmlinuz root=/dev/xvda1 ro single
initrd /initrd.img

where /vmlinuz is a symlink to the kernel image you installed. In my case the guest disk is not partitioned, otherwise the (hd0) would be (hd0,0).

Create /etc/modprobe.conf with contents. Why? Because xen wiki article says to.

alias eth0 xennet
alias scsi_hostadapter xenblk

Likewise update /etc/inittab per xen wiki article:

hvc0:2345:respawn:/sbin/getty 38400 hvc0
xvc0:2345:respawn:/sbin/getty 38400 xvc0

Now we are ready to complete step #3, telling the host how we are going to boot the guest by editing the guest’s configuration file. These are the lines you need in the configuration file:

# points to PVgrub on host (dom0)
kernel = '/usr/local/lib/pv-grub-x86_64.gz'
# location of menu.lst on domU
extra = '(hd0)/boot/grub/menu.lst'

Please note the is usually an entry something like

root = '/dev/xvda1 ro'

in the guest’s configuration file. Remove it! Otherwise when you create the guest you’ll get dumped out on at a grub prompt. I just commented it out.

Now you can halt the guest and then

xm create -c /path/to/guest/config/file

to start the guest, with any luck.

Final thoughts…I’ve not dealt with this yet but the original author suggests making a symlink to /boot/grub/grub.cfg from /boot/grub/menu.lst so updates that use grub.cfg don’t freak out.

JavaScript Check for Duplicate IDs on Page

Using jQuery:

function check_for_duplicate_ids() {
    // iterator over every element on page w/ id attribute set
    $('[id]').each(function(){
        // find all elements on page by id where id is this.id
        var ids = $('[id="'+this.id+'"]');
        if(ids.length>1 && ids[0]==this) {
        // we have more than 1 id and the id is same as
        // element we are iterating over
            console.warn('Multiple IDs #'+this.id);
        }
    });
}

CIDR Notation Explained Simply

A range of IP addresses can be expressed very simply with CIDR notation. I don’t want to discuss why classless inter-domain routing was developed, but only demonstrate how you can figure out what the CIDR notation for a range of IP addresses, or determine what the range of IP addresses are represented by a CIDR notation.

How to write an IP address as four octets

Consider the following IP address: 192.168.0.1

The IP address (IPv4 is only discussed in this CIDR tutorial) is made up of four octets. The first one is 192 which in binary notation would be 1100 0000 and I’ve written here with a space to make reading easier. In binary each 1 represents a value depending on which bit in the byte it is. Here are the values from left to right:

128 64 32 16 8 4 2 1

So 1100 0000 in binary is equal to 128 + 64 in decimal, or 192. If we write out the binary representation for each octet:

192.168.0.1

1100 0000 – 1010 1000 – 0000 0000 – 0000 0001

CIDR notation

CIDR notation looks like an IP address followed by a slash with a number, for example:

192.168.0.0/24

The number after the slash is the bit mask for the network. Simply put, it tells us how many bits are the same for each IP on the subnet. This also tells us which parts of the IP addresses can vary, and that gives us the range.

192.168.0.0/24 indicates that the first 24 bits are all the same on this range of IP addresses. Lets look at the first 24 bits:

1100 0000 – 1010 1000 – 0000 0000

That is the first 24 bits of 192.168.0.0. This means the remaining 8 bits can be either 0 or 1, but on this range of IP addresses the first 24 bits will always be the same.

If we set the part that can be 0 or 1 to all 1’s we can get the maximum IP value on the range:

1100 0000 – 1010 1000 – 0000 0000 – 1111 1111

All 1’s is 255, so the maximum IP on this range is 192.168.0.255.

The CIDR notation for the range of IP addresses from 192.168.0.0 thru 192.168.0.255 is thus 192.168.0.0/24.

Here is another range of IP addresses: 128.1.0.1 thru 191.255.255.254. How can this range of IP addresses be written in CIDR notation?

128.1.0.1 can be written in binary as

1000 0000 – 0000 0001 – 0000 0000 – 0000 0001

191.255.255.254 can be written as

1011 1111 – 1111 1111 – 1111 1111 – 1111 1110

We can see that the bits that are the same in this range, starting with the left most octet are

10

So there are only 2 bits in the mask, making the CIDR notation:

128.1.0.1/2

Here is another example of calculating the CIDR notation for a range of IP addresses:

69.4.128.0 – 69.4.159.255

Writing each IP in binary form we have

0100 0101 – 0000 0100 – 1000 0000 – 0000 0000

0100 0101 – 0000 0100 – 1001 1111 – 1111 1111

So we can see the bit mask, which is the left most set of bits that are the same are

0100 0101 – 0000 0100 – 100

which is 19 bits, so this range of IP addressed can be expressed in CIDR notation as

69.4.128.0/19

Hopefully this CIDR notation tutorial can help you quickly determine the CIDR notation for a given range of IP addresses, or how to determine the range if IP addresses represented by CIDR notation.

Bash one-liners

Get a list of URLs for a website using wget:

wget -r -nv -nd –delete-after “your-url-goes-here” 2>&1 | sed -n ‘/->/{/URL:/s/.*URL:\([^ ]*\).*/\1/;/^[ ]*$/d;p}’

Capture Fatal PHP Errors for Logging

When code runs without a user interface, for example a script invoked by cron, there often is not an easy way to see what errors are occuring. Another example would be php invoking another php script using exec(), for example. Here are some common fatal errors: unknown class referenced, unknown method called and parse error. Forgot a semicolon? Thats a parse error.

This post combines ideas others have used and gives an example of how to capture and log fatal php errors.

The php function set_error_handler() allows you to create a callback for handling errors. However, you cannot catch fatal errors using a callback registered with set_error_handler().

You can register a callback for php to invoke at the very end of execution via register_shutdown_function(). There is a way to see if there were any errors during script execution by calling error_get_last().

The problem is, how do we call register_shutdown_function()? If the script you want to monitor for errors has en error, like a parse error, and you put the register_shutdown_function() call in that script it will not work, because of the parse error. And using “include” or “require” will not work either because that essentially is still the same script, as far as php is concerned.

You can however use an INI file to tell php to automatically prepend a php file before invoking the script you want to monitor. By combining this with the log4php as the logging library you can create a very powerful error handler. You can read my log4php how-to if you are not familiar with the log4php library.

Imagine this is the script to be called by cron, it has a parse error. The code is saved in a file called mycron.php.

<?php
$foo = ;
?>

I put mycron.php in its own directory: /home/tom/bin.

In that same directory create php.ini telling php what files to automatically prepand and append:

; php.ini contents:
auto_prepend_file = /home/tom/bin/runtime_start.php

The contents of runtime_start.php is:

<?php

require_once 'log4php/Logger.php';

Logger::configure('logconfig.xml');

class Runtime
{
    function Runtime()
    {
        register_shutdown_function(array($this, 'shutdown'));
    }

    function shutdown()
    {
        $logger = Logger::getLogger('root');

        $e = error_get_last();

        if(is_null($e)) {
	    $logger->debug('Script ended normally');
	} else {
            $logger->error($e['message']);
        }
    }

    function finish()
    {
        $logger = Logger::getLogger('root');
        $logger->debug('Script ended normally');
    }
}

$runtime = new Runtime();
?>

One of the nice things about this approach is that the script I’m executing does not have to be altered in any way to add the error capturing and logging. You do however need to invoke it in a particular way. You have to tell php where to find the INI file:

php -c /home/tom/bin -f /home/tom/bin/mycron.php

You can get the full list of php command line invocation options using “php –help”.

When run the log file will contain a message like the following:

2011-09-15 11:08:01 PDT [ERROR] root: syntax error, unexpected ‘:’

Burn CDs from the Command Line

To burn cd images from the command line in Debian squeeze you need two packages:

(1) genisoimage
(2) wodim

To make an ISO image of a directory simply type:

genisoimage -o outfile_name some_directory

To burn the image use wodim. First figure out which optical drive to use by asking wodim for a list:


tom@osmium:~$ wodim -devices
tom@osmium:~$ wodim -devices
wodim: Overview of accessible drives (1 found) :
-------------------------------------------------------------------------
0 dev='/dev/scd0' rwrw-- : 'HP' 'CD-Writer+ 8000'
-------------------------------------------------------------------------

We need the device as in the /dev device so we can use it to tell wodim which device to use:

wodim dev=/dev/scd0 -v -data path_to_iso_file

Configuration options for wodim are in /etc/wodim.conf.

To see list of options for the drive:

wodim dev=/dev/scd0 driveropts=help -checkdrive

log4php example

This log4php example demonstrates how to the log4php library in a non-trivial way. log4php is a version of the Apache log4J package ported to PHP5. Please refer to the apache project for complete details.

log4php is a powerful logging mechanism that allows you to use a configuration file to specify what types of messages you want to route to various destinations (eg: file, email, database record). This makes it easy to change what messages are logged or where they are sent without tinkering around with code in the application.

In concept log4php has three core entities that work together:

(1) Loggers – loggers know what level of message to capture, they use one or more Appenders; loggers can be defined in a heirarchial manner, always stemming from the root logger. Loggers are named, and the periods in the name indicate the heirarchial levels (phpfunk, phpfunk.descendant, phpfunk.descendant.furtherDescenant, etc).

(2) Appenders – determine where the logged messages are sent and use Layouts to determine how the message should be formatted.

(3) Layouts specify the formating of the logged message.

In this example we want to capture all log messages to a file called default.log. We also want to capture serious errors to error.log and send an error message via email to the webmaster to let him know something is wrong.

First lets look at how simple the code is. I’m throwing an Exception just for fun. The configuration that I’ll explain below is the key to harnessing the power of log4php.

require_once 'log4php/Logger.php';
Logger::configure('logconfig.xml');

// get logger by name, if this name isn't listed 
// as a logger name in the configuration 
// file you will get the root level logger
$logger = Logger::getLogger('phpfunk');
$logger->info("beginning log4php example");
try {
    throw new Exception("log4php example error");
}
catch(Exception $e) {
    $logger->error($e->getMessage());
}

The log files now contain:
::::::::::::::
/tmp/default.log
::::::::::::::
2011-04-13 13:25:35 [INFO] phpfunk: beginning log4php example (at /home/tom/testlog.php line 8)
2011-04-13 13:25:35 [ERROR] phpfunk: log4php example error (at /home/tom/testlog.php line 12)
::::::::::::::
/tmp/error.log
::::::::::::::
2011-04-13 13:25:35 [ERROR] phpfunk: log4php example error (at /home/tom/testlog.php line 12)

And in addition an email was sent with the same message body as the message captured in error.log.

Now lets dig into the log4php configuration. I’m using an XML file for configuration because I want to use a Filter in the Appender. At the bottom of the configuration file I set the logging level and appender to use for the root logger and another logger named phpfunk, which descends from the root because the root logger is always the top most logger.

Appenders are referenced by name in the appender_ref element in the logger mark up. For phpfunk I have specified two appenders, a file and one to send messages via email. Both of those appenders further use filtering to control what level messages are captured in the logs; in this case ERROR and FATAL messages.

The root logger records DEBUG and higher messages. The Appender it uses writes these messages to default.log.

I want to use phpfunk logger, for this example, to capture all messages to default.log, but only capture ERROR and FATAL messages to error.log and also to email ERROR and FATAL messages to the webmaster. If I set the default logger level to ERROR for phpfunk then any INFO level messages it receives are discarded. By setting the level to ANY I ensure that INFO level messages get passed up to the default logger’s Appender and appear in default.log.

Here is the log4php XML configuration and you can download the example log4php configuration.

<?xml version="1.0"?>
<log4php:configuration xmlns:log4php="http://logging.apache.org/log4php/">
    <appender name="default" class="LoggerAppenderFile">
        <param name="file" value="/tmp/default.log" />
        <param name="datePattern" value="Ymd" />
        <layout class="LoggerLayoutPattern">
            <param name="ConversionPattern" value="%d{Y-m-d H:i:s} [%p] %c: %m (at %F line %L)%n" />
        </layout>
    </appender>
    <appender name="errorLog" class="LoggerAppenderFile">
        <param name="file" value="/tmp/error.log" />
        <param name="datePattern" value="Ymd" />
        <layout class="LoggerLayoutPattern">
            <param name="ConversionPattern" value="%d{Y-m-d H:i:s} [%p] %c: %m (at %F line %L)%n" />
        </layout>
        <filter class="LoggerFilterLevelRange">
            <param name="LevelMin" value="error" />
            <param name="LevelMax" value="fatal" />
        </filter>
        <filter class="LoggerFilterDenyAll" />
    </appender>
    <appender name="emailNotice" class="LoggerAppenderMail">
        <param name="from" value="webmaster@phpfunk.com" />
        <param name="to" value="tom@phpfunk.com" />
        <param name="subject" value="Error" />
        <layout class="LoggerLayoutPattern">
            <param name="ConversionPattern" value="%d{Y-m-d H:i:s} [%p] %c: %m (at %F line %L)%n" />
        </layout>
        <filter class="LoggerFilterLevelRange">
            <param name="LevelMin" value="error" />
            <param name="LevelMax" value="fatal" />
        </filter>
        <filter class="LoggerFilterDenyAll" />
    </appender>
    <root>
        <level value="DEBUG" />
        <appender_ref ref="default" />
    </root>
    <logger name="phpfunk" additivity="true">
        <!-- with additivity set to TRUE the default is also called, eg parent -->
        <level value="ALL" />
        <appender_ref ref="errorLog" />
        <appender_ref ref="emailNotice" />
    </logger>
</log4php:configuration>

So you see with a single configuration file its easy to switch around what level messages are captured, how they are handled (written to file, emailed) and how they are formatted. Hopefully you will find this log4php example to be a nice addition to the information presented in the log4php project documentation.