Archive for the 'Linux' Category

Restricted shell account (SSH and Subversion)

Wednesday, October 18th, 2006

While trying to set up a restricted shared shell account for SVN access I read about an interesting feature of OpenSSH. From this section of the SVN manual I learned that svn supports the following syntax in $HOME/.ssh/authorized_keys

  command="program" TYPE KEY COMMENT

Program is a command that will be executed instead of a shell when connected and it also supports different configuration options. For example, if you want the account to be really restricted you may want to pass the following options no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty. In my case, I used the following line in the configuration file:

command="/usr/bin/svnserve -t --tunnel-user=tadekp -r /var/lib/svn/svncommon",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-dss AAAAB3N.... tadekp@server

Just a few points to remember:

  • many commands can execute shell internally, watch out for these
  • the command can modify authorized_keys file, the best way to prevent it is to make it read only on the filesystem level

Gallery2 plugin - displaying googlemaps with GPS coordinates from EXIF

Thursday, August 31st, 2006

After resuming my geotaggin script (see this post), I decided to do something useful with it. We’re using gallery2 to store our photos and with a googlemap plugin, but found it useful only for displaying a single pointer per album (see here). For a more fine-grained selection we needed something else.

Therefore, I decided to write my own plugin (yeah, there are already two out there, why not write my own? ;-)) and also learn Gallery2 API. The idea is to display a google map at the bottom of each photo, showing exactly where the photo was taken. Yes, it photo-specific and there’s only one pointer on the map. I find it nonetheless very useful.

Here’s a sample output (you can also admire the beautiful scenery of Melchsee ;-)). The plugin adds a new “block” in the template (therefore can be configured using a standard block management tool in Gallery2).

The position of the current photo is always in the middle (although you can move the map around, change the map type, zoom in and out etc.). The changes you make are stored as session cookies, and preserved between consecutive photo loads. Also, the whole panel can be hidden to speed up load and only shown on demand (show map|hide map).

Any comments? suggestions? ideas?

The plugin is currently in alpha stage, I will release it in a week or two (I want to create a webpage for it as well). In the meantime, if you’re interested in trying it out, drop me a line ;-)

BTW: I also found out that when iPhoto edits a photo, it converts Exif from Intel to Motorola (little endian -> big endian). There was a bug in exifer used in gallery, which corrpted the tags. The patch is only two lines long and can be found here (I also emailed the author):

--- gps.inc.orig        2006-08-31 10:25:27.000000000 +0200
+++ gps.inc     2006-08-31 10:36:37.000000000 +0200
@@ -116,13 +116,24 @@
                        $minutes = GPSRational(substr($data,16,16),$intel);
                        $hour = GPSRational(substr($data,32,16),$intel);

  • /* now we need a hack, since the whole data has been flipped in :103
    • the order here is sec:min:hour. However, in the motorla mode the data
    • has not been flipped and the order is h:m:s. This breaks compatibility
    • with Motorola exif. (Tadek) */
  • if($intel==1) $data = $hour+$minutes/60+$seconds/3600;
  • else
  •                                 $data = $seconds+$minutes/60+$hour/3600;
            } else if($tag=="0007") { //Time
                    $seconds = GPSRational(substr($data,0,16),$intel);
                    $minutes = GPSRational(substr($data,16,16),$intel);
                    $hour = GPSRational(substr($data,32,16),$intel);
    
  •                 /* I guess the same HACK as above. Tadek */
    
  • if ($intel==1) $data = $hour.”:”.$minutes.”:”.$seconds;
  • else
  • $data = $seconds.”:”.$minutes.”:”.$hour; } else { if($bottom!=0) $data=$top/$bottom; else if($top==0) $data = 0;

GeoTagging in EXIF (resumed)

Thursday, August 31st, 2006

After almost a year after I had done some initial experiments with geotagging my images. I decided to pursue this idea further. I also realized that taking an approach “It’s simple let’s write in Perl” is good, but “Let’s see if somebody hasn’t done it yet” is even better ;-)

First, I recall I had some problems with reading GPS data and hacked gpstransfer to do this. In the meantime, I found out that pygarmin is a nice and working interface to Garmin GPSs, which works. More recently I discovered a much better and more versatile tool gpsbabel. Like the tower of Babel it really does speak all the languages (including plurality of GPS and very exotic formats (see here). The user interface is a bit weird and not all types of information (i.e. waypoints, tracks) are supported in all formats. Sadly, if the format is not supported, the program does transfer all the data (which takes a while) and prints nothing just to confuse you ;-)

After a bit of experimenting I discovered a magic formula:

gpsbabel -t -i garmin -f /dev/ttyS0 -o psitrex -F <track_file>

“psitrex” stands for KuDaTa PsiTrex format, however, as exotic as it sounds, it is just a comma separated format. I tried a couple of others (actually all of them in my version (1.2.7) and the only ones I found useful were:

  • psitrex -> an easy to parse CSV format
  • gpx -> produces results in XML
  • nmea -> looks ok, but does not show the start and end of each segment, which is useful for determining whether to interpolate data or not.

I also revisited my geotagging script. First, as Diego pointed out there’s a nice tool on Mac to do this: iPhototoGoogleEarth, which unfortunately doesn’t work on intel-Macs yet. For geotagging they use GPSPhotoLinker, which in turn uses gpsbabel. So at the end we use the same backend tool.

In the meantime I revisited my geotagging script and made it a bit more useful adding a bunch of options, debugging it, etc. Now that I actually use it on my photos, I had to make sure that it actually works ;-)

Here it is:

$ ./geotag.pl 
ERROR: Need track file to proceed - use -t <trackfile>
Usage:
  ./geotag.pl -t <track_file> [-b] [-f] [-s <seconds>] [-a <seconds>] [-n <seconds>] [-z <tz>] files_to_process...


  Where:
  -b -> keep backup,
  -f -> force, overwrites an existing EXIF tag (or removes it)
  -s <seconds> -> time shift (in case of time discrepancies)
  -a <seconds> -> approximate non-continuous segements in the tracklog (default 300s)
  -n <seconds> -> don't approximate but take the closest segment (default 10800s)
  -z <tz> -> use the follwoing timezone for your camera (default current timezone)

  <track_file> can be best created using gpsbabel (http://www.gpsbabel.org):
   (e.g. Serial Garmin GPS: gpsbabel -t -i garmin -f /dev/ttyS0 -o psitrex -F <track file>)    
   at ./geotag.pl line 288.

Now what do I do with the script? Well… I wrote a gallery2 plugin to display google maps. See this post.

Multi-line matches with regular expressions (pcregrep)

Monday, June 26th, 2006

Proofreading my thesis I had the following problem: Find all occurrences of “alert processing system” (should be “alert-processing system”), which can span over multiple lines.

I found out (thanks Diego!) a number of ways of doing this.

  1. Writing my own perl program and defining INPUT_RECORD_SEPARATOR ($/)

  2. Using agrep, in which I can use -d ‘delim’ as the separator. The advantage of agrep is that is can also find mispelled words (with a certain editing distance).

  3. Using pcregrep!

I chose the last solution as it is the simplest one:

pcregrep -Mi 'alert\s+processing\s+system' <file>

IT WORKS!

Remote server backup (using dump and ssh)

Sunday, May 21st, 2006

I figured out how to quickly make a backup of my server:

ssh user@server "sudo dump -0 -f - /bin /boot /dev /etc/ /home /initrd /lib /opt /root /sbin /usr /var" > backup<date>.dump

The only problem is that dump does not easily allow you to exclude stuff (so I had to resort to moving things I do not want to backup to /nobackup and not listing it. Also, this solution works if sudo doesn’t ask for a password (you may need to open another sudo for that). I had to use sudo as I do not allow direct remote root access.

Pattern-based file renaming

Monday, April 3rd, 2006

Ever wanted to do bulk operations on files, similar to xargs, but much more flexible? For example:

  • rename all files .jpeg to .jpg
  • remove a prefix from many file names?
  • add a suffix/extension?
  • remove a prefix/suffx/extension?

Here’s a script I wrote:

!/bin/bash

#

Pattern-based file rename

#(c)2006 by Tadeusz Pietraszek

#

Usage:

./mv-pattern -i a *.txt <- delete all 'a's in file names

./mv-pattern -i jpeg -o jpg *.jpeg <- rename all jpegs to jpg

./mv-pattern -o .txt <- add an extension

./mv-pattern -i .txt <- remove an extension

#

if [ $# -eq 0 ] then echo "Usage: basename $0 [-i ] [-o ] [-c ] files" exit -1; fi

INPATTERN=""; OUTPATTERN=""; COMMAND="echo";

while getopts "i:o:c:" Option do case $Option in i ) INPATTERN=$OPTARG;; o ) OUTPATTERN=$OPTARG;; c ) COMMAND=$OPTARG;; * ) echo "Unimplemented option chosen. Has to be one of -i -o -c"; exit -1;; esac done

if [ -z "$INPATTERN" ]; then echo "No input pattern. Are you sure it's what you want?";

exit -1;

fi

if [ -z "$INPATTERN" ]; then echo "No input pattern. Are you sure it's what you want?";

exit -1;

fi

shift $(($OPTIND - 1))

Decrements the argument pointer so it points to next argument.

echo "in: $INPATTERN, out: $OUTPATTERN";

rename

for FILE in "$@" ; do if [ -f $FILE ]; then NEWFILE=echo $FILE | sed -re "s/(.*)$INPATTERN(.*)/\1$OUTPATTERN\2/"; if [ "$FILE" != "$NEWFILE" ]; then $COMMAND $FILE $NEWFILE; fi; fi; done

exit 0

On the perils of masquerading with Linux

Sunday, April 2nd, 2006

I recently discovered a potential security problem with the configuration of Linux-based masquerading firewalls, which (I must admit I fell prey of).

Suppose I a server with one outgoing IP address, which has two network cards (eth0 - outgoing link and eth1 - internal link). I want to set up masquerading of the internal network 192.168.0.0/24.

The way I would proceed is:

  1. Enable packet routing:

    net.ipv4.ip_forward = 1
    
  2. Configure a masquearing rule:

    iptables -t nat -A POSTROUTING -j MASQUERADE
    
  3. Set up a firewall rules:

    iptables -A INPUT -i lo -j ACCEPT
    iptables -A INPUT -i eth1 -j ACCEPT
    iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    iptables -A INPUT -p tcp -m multiport --dports <ports_here> -j ACCEPT
    iptables -P INPUT DROP
    

Does it work and is it secure? Yes… well… not really. Suppose the atacker is on the same network as I am and sets my host as his router. He will then be able to:

  1. Connect to my internal hosts (which will be helpfully masqueraded).
  2. Use my server to masquerade his connections (only on the allowed ports, but still).
  3. If the server has another interface (e.g. running an IPsec tunnel to a restricted network, it’s getting really scary.

What can I do:

  1. Control the FORWARD chain:

    iptables  -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    iptables -A FORWARD -i eth1 -j ACCEPT
    iptables -P FORWARD DENY
    
  2. Block the incoming connections in iptables -A INPUT -p tcp -m multiport --dports <ports_here> -j ACCEPT by specifying the destination IP address. However, this may be tricky, if the server uses a DHCP address (otherwise you wouldn’t be using MASQUERADE in the first place).

  3. Change masquerade so that it has an interface specified: iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE. This only partially solves the problem, as the packets will still be forwarded to internal networks.

Editing EPS/PDF files in Linux

Wednesday, March 29th, 2006

I recently wanted to make small changes to EPS charts I had once generated. It seems that EPS-editing is not so simple, but i have at least two solutions that works:

  1. Import the file into CorelDraw and save back as EPS. I had previously been importing EPS files there and the results were amazingly good (BTW: choose “interpreted EPS”, not the one decoded by the system driver!).
  2. Use pstoedit to save it to a .fig file, use xfig and then export back as EPS.

    pstoedit -f "fig:-startdepth 999" <file>.eps <file>.fig
    

I tried the last option so far and in kindof worked. The resulting file looked ok, but the bounding box was somewhat changed (that was probably the problem in the original file). Also the resulting EPS file was 10 times bigger then the original one. Well… nothing is perfect. Wonder how the CorelDraw solution works…

Anti-virus and anti-spam measures on my server

Tuesday, February 7th, 2006

After having thought about it for at least half a year and having researched the topic thoroughly for a good weekend, I finally got to implementing anti-spam and anti-virus measures on my server. It turned out to be more complex than I had initially thought (as always), but it seems to be working now.

To give a bit more background, I am running Postfix with Courier-IMAP and PostgreSQL as database backend. E-mail accounts reside in a virtual folder and have no corresponding Unix accounts.

I decided to use maildrop (I discussed Postfix and procmail issues here) and followed this tutorial, with the following exceptions:

  • I had to backport a few packages to sarge (wrote about it here).
  • I found out by trial and error that two packages courier-maildrop and maildrop have the same program working differently (essentially, maildrop from the maildrop package works, the other one doesn’t!)
  • I added a custom clamAV source to my sources.list files:

    deb http://ftp2.de.debian.org/debian-volatile sarge/volatile main
    
  • I wrote my own /etc/maildroprc

The idea is to have e-mail moved automatically to a folder containing spam if (and only if) such a folder exists. What I came up with is the following

This is the folder into which spam messages are delivered

SPAMFOLDER="$DEFAULT/.caughtspam/"

run the message through SpamAssassin

exception { xfilter "/usr/bin/spamc -u $LOGNAME" }

if the message is marked as spam AND SPAMFOLDER exists - deliver there

I have no idea how to check it other than executing [ -d ] in a shell

SPAMFOLDEROK=[ -d $SPAMFOLDER ]; echo $? if ( /^X-Spam-Flag:.*YES/ && $SPAMFOLDEROK == 0 ) { exception { to $SPAMFOLDER } }

What still needs to be done is:

  • automatic training on users’ emails (to enable per-user training)
  • inclusion of user-specific rules (still need to thnik about it a bit as it has serious security implications).

Useful links: