Web Programming, Linux System Administation, and Entrepreneurship in Athens Georgia

Author: Brandon (Page 22 of 29)

Sending Attributes with Accept packets using FreeRadius and MySQL backend

The company I work at is having to change who their Dialup modem pool goes through. The new company blocks outgoing SMTP by default to prevent spamming. To enable port 25 for our mail servers, they we have to send them some attributes in the Radius ACCEPT packet during authentication. I haven’t really gotten into the Radius server that we use, because it has always worked and we’ve never had to change anything on it until now. So I began digging into FreeRadius, and found out that it is pretty useful.

Our server was configured to authenticate against a MySQL database that contains all of the configuration. I believe it is a pretty standard configuration. In the radius configuration, it defines certain queries to run for authentication, and also for attributes to send in the accept packet. Specifically, it’s was configured to do this query for the attributes:

authorize_reply_query = "
  SELECT id,UserName,Attribute,Value,op 
  FROM ${authreply_table} 
  WHERE Username = '%{SQL-User-Name}'
  ORDER BY id"

So, I can just put additional rows into the ${authreply_table} to add the attributes that this new provider requires. With the default setup, though, I would have to add the attributes for each user. That would get to be a mess, because I would have to modify the billing system which populates the radius database. Instead I modified the SQL query to include rows where the Username = ALLUSERS, like this:

authorize_reply_query = "
    SELECT id,UserName,Attribute,Value,op
    FROM ${authreply_table}
    WHERE (
        Username = '%{SQL-User-Name}' OR
        Username = 'ALLUSERS'
    ) 
   ORDER BY id"

Now, I can just insert rows for those attributes once, and no further modification is necessary. Pretty handy.

Compare used book purchase prices quickly

I was reading the blog of a friend of a friend and came across a discussion about selling used books online. It sounded like there are a bunch of different sites that buy used books. Each of them allows you to put in an ISBN number to see what they are willing to buy it for.

To find the best price, you would have to browse all of these sites to see who was offering the most money. Sometimes the a book may sell for a dollar at one site, but nine dollars at another, so it is worth your time to check out all of the sites.

Sounds like a good candidate for automation to me. I am already doing a pretty similar, but more complicated, version of this with GamePriceWatcher.com. It didn’t take me much time to write some scripts to scrape prices from about eight of these sites. I also included the Perl WWW::Scraper::ISBN module to retrieve some of the details about the book and it has turned out pretty well.

I have it working now at https://avazio.com/sellbooks.php, and may move it over to its own domain if it seems like anybody is using it.

Building an RPM isn’t that hard

We’re installing the Amanda backup client on a bunch of servers that I administer. We’ve decided to tunnel Amanda connections through SSH though, and that feature isn’t available in the current RPM builds that are available.

We started out compiling it from scratch on each box, and then following a bunch of steps to add the appropriate users, create directories, setup SSH keys, etc. Obviously, that gets to be a tedious process pretty quick and is prone to errors and missing steps. The obvious solution is to create an RPM for this, since that is what they are for.

Creating an RPM is pretty easy. I found some useful instructions here and here. Basically, the buildrpm package goes through the standard configure; make; make install steps for you. You can create simple shell scripts to do whatever you want at each of those processes when building the RPM. Additionaly, you can also run commands before and after RPM installation, and before and after RPM removal. For this particular script, I added a bunch of stuff to the postinstall script. As an example, here is my .spec file:

Summary: Amanda Client with SSH
Name: amanda_client_ssh
Version: 2.5.2p1
Release: 2
License: GPL
Group: Amanda Backup
Source: amanda_client_ssh-2.5.2p1.tgz

BuildRoot: %{_builddir}/%{name}-root

%description
Amanda Client compiled with SSH authentication

%prep

%setup
./configure '--a-bunch' '--of options' --'can go here'

%build
make

%install
rm -rf $RPM_BUILD_ROOT
make DESTDIR=$RPM_BUILD_ROOT install
if [[ ! -d $RPM_BUILD_ROOT/etc/amanda ]]; then
    mkdir $RPM_BUILD_ROOT/etc/amanda
fi
cat >> $RPM_BUILD_ROOT/etc/amanda/amanda-client.conf < EOF
conf "MyConfig"
index_server "backup.mydomain.com"
tape_server "backup.mydomain.com"
auth "ssh"
ssh_keys "/root/.ssh/id_rsa_amrecover"
EOF

%pre

%post
useradd -M -n -g disk -o -r -d /var/lib/amanda -s /bin/bash
        -c "Amanda user" -u 33 amanda >/dev/null 2>&1 || :

if [[ ! -d /var/lib/amanda/.ssh ]]; then
    mkdir -p /var/lib/amanda/.ssh
fi
if [[ ! -d /var/log/amanda ]]; then
    mkdir -p /var/log/amanda
fi

touch /etc/amandates

cat >> /etc/amanda/exclude <
/var/spool/mail
/tmp
EOF
mkdir ~amanda/gnutar-lists
chown amanda:disk /etc/amandates /etc/amanda/exclude ~amanda/gnutar-lists /var/log/amanda/

if [[ ! -f /root/.ssh/id_rsa_amrecover ]]; then
    ssh-keygen -t rsa -N "" -f /root/.ssh/id_rsa_amrecover
fi

cat >> /var/lib/amanda/.ssh/authorized_keys <EOF
ssh-rsa abcdefgABigLongPublicKeyGoesHere== [email protected]
EOF

%preun
userdel amanda

%clean
rm -rf $RPM_BUILD_ROOT

%files
... a bunch of files listed here ...

%changelog

It took a couple revisions of trial and error to get everything correct. That part took the longest because I didn’t see an option to skip the configure and make steps, so anytime I changed the postinstall commands it had to rebuild the whole app. But now that I’ve got the RPM, installing it should be pretty straightforward on the rest of the boxes it needs to be installed on.

Courier Imap “NO Unable to open this mailbox” error

I had some problems with a mail server today and had to reboot it. After booting it back up all the services started, but any attempts to access a mailbox via IMAP generated an error that said “Unable to open this mailbox”. Testing it through telnet looked like this:

[root@ny ~]# telnet localhost 143
>> Trying 127.0.0.1
>> Connected to mail.somedomain.com (127.0.0.1).
>> Escape character is '^]'.
>> * OK [CAPABILITY IMAP4rev1 UIDPLUS CHILDREN NAMESPACE THREAD=ORDEREDSUBJECT
>> THREAD=REFERENCES SORT QUOTA IDLE ACL ACL2=UNION STARTTLS] Courier-IMAP ready. Copyright
>> 1998-2004 Double Precision, Inc.  See COPYING for distribution information.
<< 01 LOGIN [email protected] mypassword
>> 01 OK LOGIN Ok.
<< 02 LIST "" *
>> * LIST (HasNoChildren) "." "INBOX.Junk"
>> * LIST (HasNoChildren) "." "INBOX.Drafts"
>> * LIST (HasNoChildren) "." "INBOX.Trash"
>> * LIST (HasNoChildren) "." "INBOX.Sent"
>> * LIST (Marked HasChildren) "." "INBOX"
>> 02 OK LIST completed
<< 03 SELECT INBOX
>> 03 NO Unable to open this mailbox.

After digging into file permissions, restarting courier-imap, and anything else I could think of, I was getting puzzled. Then a fellow sysadmin noticed that time time on the box was way off:

mail:~# date
Wed Jan  7 04:09:17 MST 1925

I corrected that and it imap suddenly started working again. I wish that courier-imap would have given a little more descriptive error message, but glad that it was nothing difficult to fix.

Disabling dmraid (fakeraid) on CentOS 5

I recently installed CentOS 5 on a server with a Promise PDC20621 SATA Raid card in it (according to lspci). This particular card, of course, is a FAKE raid device, meaning that the physical card is nothing more than a regular SATA controller, and they provide drivers that emulate RAID functionality. This is supposed to be useful for Windows users that don’t have a native software raid service available, but it is kindof useless for Linux since most distros provide md for creating a software raid device.

When trying to create a new software raid array, I would get a bunch of errors about the devices being busy, like this:

[root@www ~]#  mdadm --create --verbose /dev/md0 --level=5 --raid-devices=4 /dev/sda1 /dev/sdb1 /dev/sdc1 /dev/sdd1
mdadm: layout defaults to left-symmetric
mdadm: chunk size defaults to 64K
mdadm: Cannot open /dev/sda1: Device or resource busy
mdadm: Cannot open /dev/sdb1: Device or resource busy
mdadm: Cannot open /dev/sdc1: Device or resource busy
mdadm: Cannot open /dev/sdd1: Device or resource busy

lsof didn’t show any processes that were using these files, and it took a little while to finally find out that ‘dmraid’ was the culprit. dmraid is the linux driver for fake raid controllers like the Promise FastTrak and nVidia on-board SATA controllers. From what I could tell, it is loaded from initrd and automatically attaches itself to any partitions that are of type ‘fd’ (Linux raid autodetect).

After a few hours of googling for answers, I had become pretty familiar with the topic. Many of the search results were from people trying to get mdraid working for these devices before it was stable and widely included in distros.

Unfortunately, it looks like the default CentOS 5 install has the dmraid drivers built into the initrd, and there was no way to disable it from taking control of the drives. I tried looking for an argument to pass to the kernel to disable dmraid support, but couldn’t find anything. A few of the posts and emails that I came across on the subject suggested removing the ‘dmraid’ package, and a few people appeared to have some success with that. But when I tried a ‘yum erase dmraid’ on my box, it wanted to remove the kernel, which would probably be bad.

After a little more searching, I found that mkinitrd had an option to rebuild the initrd without dmraid support. The was an upgrade available for my kernel, so I did a ‘yum update’ to install a new one, which also gave me one to fall-back to if this didn’t work. Once the new kernel was running, I installed the ‘kernel-devel’ and ‘kernel-headers’ packages to pull down some necessary headers, then ran this command to create a new initrd without the troublesome dmraid drivers:

mkinitrd --omit-dmraid /boot/NO_DMRAID_initrd-2.6.18-8.1.6.el5.img 2.6.18-8.1.6.el5

Then, simply change /etc/grub.conf to create an option that pointed to my new initrd. My /etc/grub.conf looks like this:

default=0
timeout=5
splashimage=(hd0,0)/grub/splash.xpm.gz
hiddenmenu

## My new non-dmraid boot option
title CentOS (2.6.18-8.1.6.el5) WITHOUT DMRAID GARBAGE
  root (hd0,0)
  kernel /vmlinuz-2.6.18-8.1.6.el5 ro root=/dev/hda1
  initrd /NO_DMRAID_initrd-2.6.18-8.1.6.el5.img
## The regular option
title CentOS (2.6.18-8.1.6.el5)
  root (hd0,0)
  kernel /vmlinuz-2.6.18-8.1.6.el5 ro root=/dev/hda1
  initrd /initrd-2.6.18-8.1.6.el5.img
## My working backup option:
title CentOS (2.6.18-8.el5)
  root (hd0,0)
  kernel /vmlinuz-2.6.18-8.el5 ro root=/dev/hda1
  initrd /initrd-2.6.18-8.el5.img

Now, I just rebooted off the first option, and it didn’t load all of the dmraid junk. I can now access the partitions without the ‘resource busy’ problem, and create a software raid array like I’m used to.

Terminating Cat5 cables . . . at 70 feet in the air!

I work for a company that is a Wireless ISP, so I deal a fair amount with wireless networking equipment. Usually, though, I work on them from behind a keyboard and occasionally on the ground in a lab-type environment. We’ve been working recently on adding some 802.11a equipment to one of our towers. I worked on getting everything ready on the ground, including terminating the Cat5 cables that plugged into the radios.

Unfortunately, I forgot to run the cables through the weatherproofing connector before sending the wire up the tower. We had a professional lineman mount all of the radios and antennas for us, but he wasn’t able to re-terminate the Cat5 ends before he had to finish up.

So, up I went to re-do the termination. It actually wasn’t too bad. With the right equipment, climbing up and working was pretty easy and I felt pretty secure the whole time. In fact, the thing that worried me the most was that I felt pretty comfortable with it and was afraid that I’d forget that I was 70 feet in the air and do something dumb.
TowerTower

PHP 4’s call_user_func passes everything by value

I spent quite a while today debugging a problem where call_user_func was not passing a parameter by reference. I was trying to pass an object into a function whose name is not known until run time.

Passing it by reference means that changes made to $var inside foo() are made to the actual variable instead of to a copy of the value (when passed by value).  However, for some reason, when calling a function with call_user_func(), it passes everything by value, regardless of how the function is defined.

function foo(&$var)
{
  $var++;
}

$bar = 1;
foo($bar);
echo $bar;    // outputs '2'

$function = 'foo';

call_user_func($function, $bar);
echo $bar;  // you'd expect this to output 3 now, but it still outputs 2

$function($bar);
echo $bar;  // outputs 3 now

As the sample code shows, the solution is to avoid the use of the call_user_func() function by using a variable function name. Thanks to Steve Hannah’s blog post at https://www.sjhannah.com/blog/?p=86 for helping me to solve this one.

mod_auth_mysql makes managing Apache authentication simple

I administer about 20 different web applications, each of which uses Apache authentication to control access. In the past, I’ve just used simple htpasswd authentication because it works and is readily available. However when adding or removing employee’s access, it required pretty manual editing of all of the htpasswd files every time that we added or removed and employee

I just starting using mod_auth_mysql which provides a way to centralize the authentication. It is available as a package on any distro that I’ve used, and is pretty simple to configure. Just create a database with the following tables:

CREATE TABLE users (
  user_name CHAR(30) NOT NULL,
  user_passwd CHAR(20) NOT NULL,
  PRIMARY KEY (user_name)
);
CREATE TABLE groups (
  user_name CHAR(30) NOT NULL,
  user_group CHAR(20) NOT NULL,
  PRIMARY KEY (user_name, user_group)
);

Populate the users table with username/passwords taken straight from the .htpasswd file. Optionally, you can make users a member of a group via the groups table. Create a database user with permission to SELECT from those two tables.

Then configure the following in the Apache config or .htaccess file for each your web applications:

AuthName "Some Webapp"
AuthType Basic
AuthMySQLEnable on
AuthMySQLHost myauthserver.someplace.com
AuthMySQLUser YourDatabaseName
AuthMySQLPassword YourDatabaseUserPassword
AuthMySQLDB YourDatabaseName
AuthMySQLUserTable users
AuthMySQLNameField user_name
AuthMySQLPasswordField user_passwd
AuthMySQLGroupTable groups
AuthMySQLGroupField user_group

require valid-user
#require group ThisApp

Now you can centrally manage your Apache authentication. Uncomment the ‘require group’ line and add an appropriate entry in the groups table for any users you want to allow specifically to this app.

Credit Card Validation using the mod10 algorithm in PHP

I’m working on a site that will use the Paypal API for submitting merchant account transactions to them. I’d like to validate as much credit card information as possible before passing any information to a 3rd party, since are different kind of credit cards companies and options, so I’ve been reading to find out more about it. I came across the mod10 check that credit cards use and wrote a little PHP function to validate a card number

function sumdigits($number)
{
  $sum = 0;
  for($i = 0; $i <= strlen($number) - 1; $i++) {
    $sum += substr($number, $i, 1);
  }
  return $sum;
}

function mod10check($number)
{
  $sum_number = '';
  for($i = strlen($number) - 1; $i >= 0; $i--) {
    $thisdigit = substr($number, $i, 1);
    $sum_number .= ( $loop %2 == 0) ? $thisdigit : sumdigits($thisdigit * 2);
  }
  return sumdigits($sum_number) % 10 == 0 ? true : false;
}

Tracking down how hackers gain access through web apps

Hackers commonly use vulnerabilities in web applications to gain access to a server. Sometimes, though, it can be difficult to track down exactly how they gained access to a server. Especially if the server hosts a bunch of websites and there are lots of potentially vulnerable scripts.

I’ve tracked down more of these than I can count, and have sortof developed a pattern for investigating. Here are some useful things to try:

1- Look in /tmp and /var/tmp for possibly malicious files. These directories are usually world-writable, and commonly used to temporarily store files. Sometimes the files are disguised with leading dot’s, or they may be named something that looks similar to other files in the directory like “. ” (dot- space), or like a session files named sess_something.

If you are able to see any files, you can use the timestamps of the files to try and look through some Apache logs to find the exact hit that it came from

2- If a rogue process is still running, look at the /proc entry for that file to determine more information about it. The files in /proc/<PID> will tell you information like the executable file that created the process, it’s working directory, environment information, and plenty more details. Usually, the rogue processes are running as the apache user (httpd, nobody, apache).

If all of the rogue processes were being run by the Apace user, then the hacker likely didn’t gain root access. If you have rogue processes that were being run by root, it is much harder to clean up after. Usually the only truly safe method is to start over with a clean installation.

3- netstat -l will help you identify processes that are listening for incoming connections. Often times, these are a perl script. Sometimes they are named things that look legitmiate like ‘httpd’, so pay close attention. netstat-n will help you to see current connections that your server has to others.

4- Look in your error logs for files being downloaded with wget. A common tactic is for hackers to run a wget command to download another file with more malicious instructions. Fortunately, wget writes to STDERR, so it’s output is usually displayed in the error logs. Something like this is evidence of a successful wget:

--20:30:40--  https://somehackedsite.com/badfile.txt
            => `Lnx.txt'
Resolving somehackedsite.com... 12.34.56.78

Connecting to somehackedsite.com[12.34.56.78]:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 12,345 [text/plain]

     0K .......... ......                                     100%  263.54 KB/s

20:30:50 (263.54 KB/s) - `badfile.txt' saved [12,345/12,345]

You can use this information to try and recreate what the hacker did. Look for the file they downloaded (badfile.txt in this case) and look at what it does. You can also used these timestamps to look through access_logs to find the vulnerable script.

Since wget is a commonly used tool for this, I like to create a .wgetrc file that contains bogus proxy information, so that even if a hacker is able to attempt a download, it won’t work. Create a .wgetrc file in Apache’s home directory with this content:

http_proxy = https://bogus.dontresolveme.com:19999/
ftp_proxy = https://bogus.dontresolveme.com:19999/

5- If you were able to identify any timestamps, you can grep through Apache logs to find requests from that time. If you have a well-structured server where you have logs in a consistent place, then you can use a command like this to search all of the log files at onces:

grep "01\\/Jun\\/2007:10:20:" /home/*/logs/access_log

I usually leave out the seconds field because requests sometimes take several seconds to execute. If you have a server name or file name that you found was used by a wget, you can try searching for those too:

grep "somehackesite.com" /home/*/logs/access_log

6 – Turn of PHP’s register_globals by default and only enable it if truly needed. If you write PHP apps, learn how to program securely, and never rely on register_globals being on.

« Older posts Newer posts »

© 2025 Brandon Checketts

Theme by Anders NorenUp ↑