thepoch

0 notes

DHCP and PXE and TFTP and HTTP in CentOS 6 (Part 2) (aka how to boot Ultimate Boot CD via PXE)

So my previous post talks about setting up DHCP. Read that if you haven’t setup DHCP yet. You need it for PXE and TFTP and HTTP. So many acronyms…

So again, PXE is for booting up a machine via the network. DHCP gives the machine the necessary network configuration, and PXE gives it some files for actually booting up. TFTP and HTTP simply provide the files.

I keep mentioning HTTP. Normally you’ll only need TFTP, especially if you only want to be able install CentOS 6 off the network. I however ran into a situation where I needed to be able to boot the Ultimate Boot CD (UBCD) to diagnose a failing hard drive. That’s where HTTP comes in. Using PXE, I can boot off the UBCD ISO via HTTP. Sounds interesting? Then read on.

PXE and TFTP

So let’s setup PXE and TFTP first. Install syslinux and tftp-server:

yum install syslinux tftp-server

Now, TFTP stuff is located in the /var/lib/tftpboot/ directory. You need to copy some syslinux stuff to it and create the directory for the menu.

cp /usr/share/syslinux/pxelinux.0 /var/lib/tftpboot/
cp /usr/share/syslinux/menu.c32 /var/lib/tftpboot/
mkdir /var/lib/tftpboot/pxelinux.cfg

Now, add the following configuration options into /etc/dhcp/dhcpd.conf:

allow booting;
allow bootp;
next-server 10.0.0.1;
filename "pxelinux.0";

The “next-server” option is wherever the PXE/TFTP stuff is. Since my server’s IP address is 10.0.0.1, that’s what’s in the configuration. Change it to your server’s IP. If you followed my previous post, put these 4 lines just below the “authoritative;” option.

At this point, you should restart your DHCP server.

service dhcpd restart

Next, enable TFTP in xinetd. The configuration file is /etc/xinetd.d/tftp. Set

disable = yes

to

disable = no

Start xinetd and make it start on boot:

service xinetd start && chkconfig xinetd on

Boot images

So to keep things organized, I create an “images” directory inside the tftpboot/ directory.

mkdir /var/lib/tftpboot/images

I copy CentOS 6.2’s pxeboot images from our FTP server. You can get these from CentOS mirrors. The files are in the images/pxeboot/ directory. The files are:

initrd.img
vmlinuz

Copy these 2 files into the images/ directory. If you’ll have other versions of CentOS, Linux, or whatever, I suggest setting up a directory structure to organize the files in there.

Menu

So the menu is what you get once you network boot a computer. Since I like to have CentOS 6, CentOS 5, and memtest (more on this later) available, I have the menu structure setup like this:

default --> Boot local harddrive
        --> CentOS 6 x86_64
        --> CentOS 5 x86_64
        --> Tools

Now I created a file called “default” in that pxelinux.cfg/ directory created above. This configuration gives a 10 second timeout before booting from the local hard drive:

default menu.c32
prompt 0
timeout 100
ontimeout local

MENU TITLE Main Menu

LABEL local
    MENU LABEL Boot Local Hard Drive
    LOCALBOOT 0

LABEL centos_6_x86_64
    MENU LABEL CentOS 6 x86_64
    KERNEL menu.c32
    APPEND pxelinux.cfg/centos_6_x86_64

LABEL centos_5_x86_64
    MENU LABEL CentOS 5 x86_64
    KERNEL menu.c32
    APPEND pxelinux.cfg/centos_5_x86_64

LABEL tools
    MENU LABEL Tools
    KERNEL menu.c32
    APPEND pxelinux.cfg/tools

Then created the menu that is mentioned in the APPEND options above. I’ll just show the CentOS 6 (the i386 and CentOS 5 menus are similar) and Tools menu.

pxelinux.cfg/centos_6_x86_64:

MENU TITLE CentOS 6 x86_64

LABEL default
    MENU LABEL Main Menu
    KERNEL menu.c32
    APPEND pxelinux.cfg/default

LABEL centos_6_x86_64
    MENU LABEL CentOS 6 x86_64
    KERNEL images/centos/6/x86_64/vmlinuz
    APPEND initrd=images/centos/6/x86_64/initrd.img

pxelinux.cfg/tools:

MENU TITLE Tools

LABEL default
    MENU LABEL Main Menu
    KERNEL menu.c32
    APPEND pxelinux.cfg/default

The “default” in each submenu gives a way to go back to the root menu. The Tools menu is empty for now.

memtest

So what about memtest? Download it from here:

http://www.memtest.org/download/4.20/memtest86+-4.20.bin.gz

Then gunzip it then put it in the images/ directory.

Then add it to the Tools menu by appending the following:

LABEL memtest
    MENU LABEL Memtest86+
    KERNEL images/memtest/memtest86+

Make sure the “KERNEL” has the correct filename.

Ultimate Boot CD

This needs an HTTP server. You basically put the ISO into an HTTP server you can access. So find an HTTP server and put the ISO there. I’ll use 10.0.0.1 for my example.

Next, the pxelinux.0 stuff has to be replaced with gpxelinux.0. So, copy that and memdisk from the syslinux/ shared directory:

cp /usr/share/syslinux/gpxelinux.0 /var/lib/tftpboot/
cp /usr/share/syslinux/memdisk /var/lib/tftpboot/

and replace the the following line in /etc/dhcp/dhcpd.conf:

filename "pxelinux.0";

with this:

filename "gpxelinux.0";

and restart DHCP:

service dhcpd restart

Now, a menu has to be created for it. I appended this to the Tools menu:

LABEL ubcd
    MENU LABEL Ultimate Boot CD
    KERNEL memdisk
    APPEND iso initrd=http://10.0.0.1/pub/ubcd/ubcd511.iso

So this should boot up, and get the ubcd511.iso from the HTTP server. It’ll take awhile since that’s a 300mb ISO. Update that URL to whatever it is in your local network.

Conclusion

That’s it. You should be able to boot from the network, select CentOS X, and be able to install via FTP or HTTP or whatever other method you have. If you use Kickstart, you can even create separate menu entries for each type of server you need, and automatically install from there.

You should also now be able to test the memory on your computers, as well as boot up the Ultimate Boot CD and test hardware, or whatever other feature that has.

Again, fun huh?

Filed under dhcp pxe tftp http network boot syslinux ubcd memtest

1 note

DHCP and PXE and TFTP and HTTP in CentOS 6

So I wanted to be able to configure “static” IP addresses for all the computers in our network, plus give out IP addresses within a range for guest machines. I needed DHCP (Dynamic Host Configuration Protocol) for that.

I also wanted to be able to boot a machine off the network and install Linux on it without having to connect an optical drive. That’s what PXE (Preboot Execution Environment) is for. Together with Kickstart, I can automatically setup a Linux web server, workstation, etc. without even touching the keyboard (not really).

TFTP (Trivial File Transfer Protocol) and HTTP are simply the protocols used together with PXE to transfer some files to get a machine to boot off the network.

Anyway, let’s get started.

DHCP

So first thing is to install dhcp:

yum install dhcp

The configuration files for DHCP go in the /etc/dhcp/ directory. Since I only need ipv4, I’ll only be configuring dhcpd.conf and not dhcpd6.conf. Anyway, there’s a bunch of templates in /usr/share/doc/dhcp-x.x.x/ if you want. My configuration is below:

authoritative;

subnet 10.0.0.0 netmask 255.255.255.0 {
    range dynamic-bootp     10.0.0.240 10.0.0.250;

    option domain-name      "i4asiacorp.lan";
    option domain-name-servers  10.0.0.2, 10.0.0.3;

    option routers          10.0.0.1;

    default-lease-time      7200;
    max-lease-time          14400;
}

You always need the “authoritative;” option.

So I setup a “subnet” section, but I believe if you only have one network, you can put all these options in the “root” of the configuration file.

The “range” option is whatever IP addresses you want to provide to your network. The “dynamic-bootp” option in the range simply says that this range is also to be used for booting up network devices that support the protocol. Most machines these days do.

“option domain-name” is simply the local domain. My company only has a local private network, so I like to use .lan for that. I don’t really want to mix it up with our website at i4asiacorp.com.

“option domain-name-servers” is the DNS servers you will be using. Our network has two separate servers for this. If you just want to provide Internet access, perhaps setting it up to use something like Google’s DNS (8.8.8.8, 8.8.4.4) or OpenDNS (208.67.222.222, 208.67.220.220) will do.

“option ruters” is whatever is your gateway to the Internet.

“default-lease-time” is how long to provide the lease if the client doesn’t specify an expiration and “max-lease-time” is the maximum lease time ever. So for me, that’s 2 and 4 hours respectively. I could probably optimize that.

After you set the proper IP addresses for your network, restart dhcpd and set it to start on server boot up:

service dhcpd restart && chkconfig dhcpd on

What about static IP addresses?

So I mentioned I like setting up “static” IP addresses for the computers on our network. Why? Well, I like to make sure I know what machine is accessing what. But I also don’t want to manually configure everything. So I use DHCP for this.

So how do you do this? Well, first thing you need is the MAC Address of the network device. You can get this in Linux or Mac by running the command:

ifconfig

and in Windows by:

ipconfig /all

It should look something like aa:bb:cc:dd:ee:ff. So now that you have it, you need to know what IP address you will be providing it. Then you’ll simply add that configuration to the end of dhcpd.conf. So, if you want to provide the machine with MAC Address aa:bb:cc:dd:ee:ff with IP address 10.0.0.123, you add the following configuration:

host 123 {
    hardware ethernet aa:bb:cc:dd:ee:ff; fixed-address 10.0.0.123; }

For the “host 123” the “123” part is simply whatever you want to call the section. For our internal setup, I use the name of the user. So if we had a user Michael, then I set the option up as “host michael”.

Then remember to restart dhcpd: service dhcpd restart

PXE and TFTP and HTTP

Umm, I think I’ll do the PXE stuff in another post to keep things short. Sorry about that. I have a tendency to ramble.

Filed under centos linux dhcp pxe tftp network boot kickstart

0 notes

MySQL Backup Script

So I wanted to share a backup script I have for MySQL. Nothing fancy. It uses mysqldump to generate a full backup. No incremental backup support.

mysql-backup.sh

So first, the backup script:

#!/bin/sh
#

# Configuration file that contains USERNAME, PASSWORD, BACKUPDIR, DBLIST, and DBLIST_NODATE.
CONFIG=`dirname $0`/mysql-backup.conf
if [ -f $CONFIG ]; then
    . $CONFIG
else
    echo "ERROR: Configuration file not found."
    exit 1
fi

# Today in YYYYMMDD format.
TODAY=`date +%Y-%m-%d`

# Let's go to the BACKUPDIR.
cd $BACKUPDIR

# Backup process - DATE
#
for DATABASE in $DBLIST; do
    mysqldump -h $HOSTNAME -u $USERNAME --password=$PASSWORD $DATABASE > $DATABASE-$TODAY.sql
    tar czf $DATABASE-$TODAY.tar.gz $DATABASE-$TODAY.sql
    rm $DATABASE-$TODAY.sql
done

# Backup process - NODATE
#
for DATABASE in $DBLIST_NODATE; do
    if [ -f $DATABASE-current.tar.gz ]; then
        mv $DATABASE-current.tar.gz $DATABASE-previous.tar.gz
    fi
    mysqldump -h $HOSTNAME -u $USERNAME --password=$PASSWORD $DATABASE > $DATABASE-$TODAY.sql
    tar czf $DATABASE-current.tar.gz $DATABASE-$TODAY.sql
    rm $DATABASE-$TODAY.sql
done

So the script has 5 important points.

  1. It has a corresponding configuration file that it looks for in the same location as where the script is. This file is called mysql-backup.conf. We’ll get to it in the next section.
  2. $TODAY variable gets set to today’s date using “date”.
  3. Then it changes to the $BACKUPDIR directory.
  4. It has 2 for loops. The first generates a dated backup file…
  5. … and the second generates a non-dated backup file.

mysql-backup.conf

So the configuration file is:

# Configuration file that contains USERNAME, PASSWORD, BACKUPDIR,
# DBLIST, and DBLIST_NODATE.

HOSTNAME=localhost
USERNAME=backups
PASSWORD=password
BACKUPDIR=/srv/backups/mysql-backup/BACKUPS

DBLIST="database1 database4"
DBLIST_NODATE="database2 database3"

Pretty simple:

  1. $HOSTNAME: set this to the MySQL server’s hostname.
  2. $USERNAME: set this to whatever is the backup username on the MySQL server.
  3. $PASSWORD: set this to the backup username’s password.
  4. $BACKUPDIR: set this to where you want the backups to go.

Now the $DBLIST variable has 2 types:

  1. $DBLIST: this is a space separated variable that contains the names of the databases you want to be backed up with dates. Meaning, when the script is run on a daily basis, each backup should be a separate file.
  2. $DBLIST_NODATE: this is the same as above, except it doesn’t set the backup filename up with a date. This means these backups overwrite each other. Any database listed here simply gets a -current and -previous backup.

Notes

So some requirements:

  • You need to create a backup account on the MySQL server that has access to the databases you want backed up. The minimum privileges you need for them are:
    • SELECT
    • LOCK TABLES
  • If this will be a remote MySQL server, then that account must be accessible from another server. Set it up as such, either with a proper hostname, or with the wildcard “%”.

Conclusion

So, there is a bit of a security risk. The backup account will be able to steal data from the server. But then, if it can’t get that data, it can’t really back it up now can it?

I have a database backup script setup as well for cPanel accounts. I’ll probably post that another day.

Thanks for reading.

Filed under script mysql backup linux

0 notes

So you need to create a lot of auto responders in cPanel

So you need to create a lot of auto responders in cPanel. But cPanel’s interface only allows it to be created one at a time. At least that’s what I’ve seen with the latest version (11.32 as of this moment). So I wrote a quick Python and bash script to quickly create the files necessary from a template and a list of email addresses. Since this is in connection with my client’s recent domain change, they required that the email contain the new email address of whoever is sending the auto response. Plus they had 240+ accounts and I wasn’t about to do it all manually.

Requirements

First of all, you need to have a way of uploading and making sure the permissions of the uploaded files are correct. I have WHM and shell access, so that made things easy. I have no idea if it’s the same with just a plain cPanel account.

And since this is a Python and bash script, Python and bash. There’s nothing fancy about my scripts.

Step 1: Get the list of emails

I just did a:

ls -1 ~/mails/domainname.com/ > ~/email-list.txt

Then I just edited that file and removed the trailing slash.

Step 2: The templates

So I had two template files. One contains the email, the other is a json file that contains the start to end duration to send the auto response, as well as the duration before sending it again to a recipient.

For the json file, I just used the default start to end of none (meaning forever) and the duration of 8 hours (28800 seconds). So here’s json-template.txt:

{"start":null,"stop":null,"interval":"28800"}

Then I created the template email. This requires the “From”, “Content-type” with “charset” (set to utf-8), “Subject”, and the actual content of the message. So it looks like this (we’ll call this email-template.txt):

From: "%from%" <EMAILVAR@domainname.com>
Content-type: text/plain; charset=utf-8
Subject: This is the subject

Hi, from now on please email me at EMAILVAR@newdomain.com. Thanks. Bye.

So note that it contains the string “EMAILVAR”. That’s what I’ll be replacing using the Python script.

Step 3: The Python script

So the script is simple. It just replaces the EMAILVAR with whatever you provide as an argument. In fact it’s so simple, that this could probably be done with just plain bash. Why did I use Python? Because I already new how to do it that way. So here’s, uh, fffuuu.py:

#!/usr/bin/python
#

import re
import sys

# This is the parameter.
strEmail = sys.argv[1]

# Email template here.
fileContent = 'email-template.txt'

##################################################
fp = open(fileContent)
strReplace = fp.read()
fp.close()
##################################################
strReplace = re.sub("EMAILVAR", strEmail, strReplace)
##################################################

print strReplace

It just outputs the template with replaced string straight to the console. Then…

Step 4: The bash script

… I just parse it using a while loop in bash. So, parse.sh:

while read line
do
EMAIL=`echo $line`
./fffuuu.py "$EMAIL" > output/"$EMAIL@domainname.com"
cp json-template.txt output/"$EMAIL@domainname.com.json"
done < email-list.txt

So we loop through email-list.txt, use the email-template.txt in the Python script, and out that to a file in an “output” directory. Then we copy our json-template.txt file with the same email address.

Step 5: Upload the files to .autorespond

So now you just upload all the created files and put them in ~/.autorespond/. Make sure they get the permission of the account uploaded to. So that’s:

chmod user:user ~/.autorespond/*

Step 6: The only manual part

So, the catch is this doesn’t work immediately. The “autorespond” stuff isn’t necessarily enabled in the domain unless a user has created one before. The file to be edited is /etc/valiases/domainname.com. Now I wasn’t about to edit this file manually as I wasn’t sure if that might cause problems with forwarding addresses.

So what I did instead was, go to the cPanel Web UI, then go to the Auto Responders section. Then seeing as all the auto responders I created were there, just clicked “Edit” on every one of them and clicked the “Create/Modify” button on the bottom. Browser tabs worked wonders here.

Yeah this is the only lame solution I found for all this. I could have probably have scripted inserting the “autorespond” stuff in the configuration file itself but was pressed for time.

Conclusion

I wish cPanel could create a domain-wide auto responder option. This would be useful.

Also, I named the Python script fffuuu.py because, like I said, I was pressed for time.

Filed under cpanel python bash autoresponder script hosting

0 notes

Steps to rename an account’s domain in cPanel

So a client recently had to change their domain name to something new. They required that their website’s domain be changed, including all emails, forwarders, etc. Well thank goodness we had cPanel running on our server. I first asked our provider about the rename and how it would affect email accounts. My biggest worry was that the existing accounts would remain as is, and that I’d have to recreate over 200 accounts. Our provider’s support initially said it wouldn’t rename the email accounts. But after several testing on a dummy account, I saw that it would work.

Obviously you need a WHM account and not just a regular cPanel account.

So I’ll list the steps I took to make sure the change went smoothly. I’ll use “olddomain.com” and “newdomain.com” as examples.

Step 1: I set newdomain.com as a Parked Domain, and set newdomain.com’s NS to our server’s NS.

This makes sure that newdomain.com starts resolving to our server. I had this done more than 2 weeks in advance.

Step 2: Once newdomain.com was resolving I removed newdomain.com from the Parked Domain list.

Step 3: I modify the olddomain.com account, renaming the domain from olddomain.com to newdomain.com and username from “old” to “new”. This took awhile (about 5-10 minutes) as every related file and directory is renamed on the server. I left my browser alone at this point to make sure nothing goes wrong.

Step 4: I now added olddomain.com as a Parked Domain to newdomain.com. Now both domains are resolving to the same account.

Step 5: I then setup domain email forwarding from olddomain.com to newdomain.com. This makes sure that our client still receives emails to their old email addresses while they transition to their new email addresses.

Step 6: Since our client had a couple of database connection configurations for their website, I had to modify those since database names and usernames have also been renamed.

That’s it. Only thing left is for our client to reconfigure any email clients they use to start using mail.newdomain.com for POP3/IMAP/SMTP, and to also configure these to use @newdomain.com as their username. Their passwords remained the same so if a person doesn’t know how to reconfigure their email client, they can temporarily use Webmail.

Only thing remaining is to figure out how to script creating an auto responder for all email accounts if someone emails them at their old domain name.

So overall, it took a total of 6 steps. I recommend backing up before starting all this in case things go wrong. That should probably between Step 1 and 2.

cPanel has made this transition pretty painless for me and our client.

Filed under cpanel domain rename

0 notes

User Management Scripts

So I have a bunch of scripts (4 exactly) that I use on our file server for managing users. Specifically the 4 scripts create, delete, lock, and unlock a user. I created these scripts because I needed a quick way to handle those sets of situations with both a local Linux account and a Samba account linked together. Plus we have quotas for specific groups of users for their personal home directories. So I thought I’d share them online (I really should put these up on GitHub or something).

Lock and Unlock scripts

lockuser.sh:

#!/bin/sh
#

if [ -z $1 ]; then
    echo "No username provided. Usage:"
    echo "$0 {username}"
    exit 1
fi

if [ "$1" = "root" ]; then
    echo "ERROR. Cannot lock root account."
    exit 1
fi

log() {
    echo `date` - "$1" >> /home/account_log
}

USERNAME=$1

SMBPASSWD="/usr/bin/smbpasswd -d"
USERMOD="/usr/sbin/usermod -L"

$SMBPASSWD "${USERNAME}" && \
    $USERMOD "${USERNAME}"
[ $? = "0" ] && log "Locked: ${USERNAME}"

exit $?

So let me explain a bit here. The first if-statement simply checks if there’s a username provided. The second if-statement specifically checks for “root” as I don’t want that locked out. I also added in my local script a check for my user account so I don’t lock myself out :).

The log() function just appends text with a timestamp to the /home/account_log file.

SMBPASSWD and USERMOD are the commands to lock a user’s Samba and Linux account, respectively. Then it’s just a matter of calling the commands with the provided user name, and logging a “Locked” status in the /home/account_log file.

unlockuser.sh:

#!/bin/sh
#

if [ -z $1 ]; then
    echo "No username provided."
    echo "Usage: $0 {username}"
    exit 1
fi

log() {
    echo `date` - "$1" >> /home/account_log
}

USERNAME=$1

SMBPASSWD="/usr/bin/smbpasswd -e"
USERMOD="/usr/sbin/usermod -U"

$SMBPASSWD "${USERNAME}" && \
    $USERMOD "${USERNAME}"
[ $? = "0" ] && log "Unlocked: ${USERNAME}"

exit $?

So the unlock script is exactly the same, except the opposite of the lock script. There is no checking for user accounts since unlocking an account shouldn’t affect it.

Delete script

deluser.sh:

#!/bin/sh
#

if [ -z "$1" ]; then
    echo "No username provided."
    echo "Usage: $0 {username} iconfirmdelete"
    exit 1
fi

if [ -z "$2" ]; then
    echo "Confirmation word missing."
    echo "Usage: $0 {username} iconfirmdelete"
    exit 1
fi

if [ "$1" = "root" ]; then
    echo "ERROR. Cannot delete root account."
    exit 1
fi

log() {
    echo `date` - "$1" >> /home/account_log
}

USERNAME=$1

SMBPASSWD="/usr/bin/smbpasswd -x"
USERDEL="/usr/sbin/userdel -r"

case "$2" in
    iconfirmdelete)
        $SMBPASSWD "${USERNAME}" && \
            $USERDEL "${USERNAME}"
        [ $? = "0" ] && log "Deleted: ${USERNAME}"
        ;;
    *)
        echo "Usage: $0 {username} iconfirmdelete"
        exit 1
esac

exit $?

The delete script is pretty much exactly the same as the lock script, except it calls the commands for deleting a user, AND I added a required second argument “iconfirmdelete”. Why did I do this? In case I’m a little sleepy while deleting a user, I have to type some nonsense word to make sure I’m sure about deleting the user. I still check to make sure I don’t delete root (can it be deleted?), and my local script has a check for my own account.

So the case-statement checks for the second argument. If it’s “iconfirmdelete”, then the deletion goes through. Anything else and it spits out the usage statement to the user.

Create script

Now for the fun script.

adduser.sh:

#!/bin/sh
#

if [ -z "$1" ]; then
    echo "No username provided."
    echo "Usage: $0 {username} {accounting|admin|hrd|support}"
    exit 1
fi

if [ -z "$2" ]; then
    echo "No group provided."
    echo "Usage: $0 {username} {accounting|admin|hrd|support}"
    exit 1
fi

userexists() {
    if [ -d "$1" ]; then
        echo "ERROR. User seems to already exist as the directory $1 already exists."
        exit 1
    fi
}

log() {
    echo `date` - "$1" >> /home/account_log
}

USERNAME=$1
GROUPNAME=$2
RANDOMPASS=`openssl rand -base64 6`

USERADD="/usr/sbin/useradd -s /sbin/nologin"
CHPASSWD="/usr/sbin/chpasswd"
SMBPASSWD="/usr/bin/smbpasswd -a -s"
SETQUOTA="/usr/sbin/setquota"

case "$2" in
    accounting)
        HOMEBASE="-d /home/Accounting"
        GROUP="-g Accounting"
        QUOTASOFT="20000000"
        QUOTAHARD="25000000"
        userexists "/home/Accounting/${USERNAME}"

        $USERADD $HOMEBASE/"${USERNAME}" $GROUP "${USERNAME}" && \
            echo "${USERNAME}:${RANDOMPASS}" | $CHPASSWD && \
            (echo "${RANDOMPASS}"; echo "${RANDOMPASS}") | $SMBPASSWD ${USERNAME} && \
            $SETQUOTA "${USERNAME}" $QUOTASOFT $QUOTAHARD 0 0 /home

        [ $? = "0" ] && log "Created: ${USERNAME}, ${GROUPNAME}, ${RANDOMPASS}"
        ;;
    admin)
        HOMEBASE="-d /home/Admin"
        GROUP="-g Admin"
        QUOTASOFT="10000000"
        QUOTAHARD="15000000"
        userexists "/home/Admin/${USERNAME}"

        $USERADD $HOMEBASE/"${USERNAME}" $GROUP "${USERNAME}" && \
            echo "${USERNAME}:${RANDOMPASS}" | $CHPASSWD && \
            (echo "${RANDOMPASS}"; echo "${RANDOMPASS}") | $SMBPASSWD ${USERNAME} && \
            $SETQUOTA "${USERNAME}" $QUOTASOFT $QUOTAHARD 0 0 /home

        [ $? = "0" ] && log "Created: ${USERNAME}, ${GROUPNAME}, ${RANDOMPASS}"
        ;;
    hrd)
        HOMEBASE="-d /home/BizDev"
        GROUP="-g BizDev"
        QUOTASOFT="10000000"
        QUOTAHARD="15000000"
        userexists "/home/BizDev/${USERNAME}"

        $USERADD $HOMEBASE/"${USERNAME}" $GROUP "${USERNAME}" && \
            echo "${USERNAME}:${RANDOMPASS}" | $CHPASSWD && \
            (echo "${RANDOMPASS}"; echo "${RANDOMPASS}") | $SMBPASSWD ${USERNAME} && \
            $SETQUOTA "${USERNAME}" $QUOTASOFT $QUOTAHARD 0 0 /home

        [ $? = "0" ] && log "Created: ${USERNAME}, ${GROUPNAME}, ${RANDOMPASS}"
        ;;
    support)
        HOMEBASE="-d /home/Service"
        GROUP="-g Service"
        QUOTASOFT="10000000"
        QUOTAHARD="15000000"
        userexists "/home/Service/${USERNAME}"

        $USERADD $HOMEBASE/"${USERNAME}" $GROUP "${USERNAME}" && \
            echo "${USERNAME}:${RANDOMPASS}" | $CHPASSWD && \
            (echo "${RANDOMPASS}"; echo "${RANDOMPASS}") | $SMBPASSWD ${USERNAME} && \
            $SETQUOTA "${USERNAME}" $QUOTASOFT $QUOTAHARD 0 0 /home

        /usr/sbin/usermod -s /bin/bash "${USERNAME}"

        [ $? = "0" ] && log "Created: ${USERNAME}, ${GROUPNAME}, ${RANDOMPASS}"
        ;;
    *)
        echo "Usage: $0 {username} {accounting|admin|hrd|support}"
        exit 1
esac

exit $?

So let’s break this down. The first 2 if-statements simply checks for the username and groupname arguments. Otherwise, it spits out the usage statement.

userexists() {
    if [ -d "$1" ]; then
        echo "ERROR. User seems to already exist as the directory $1 already exists."
        exit 1
    fi
}

The userexists() function checks if the username exists already by checking if the home directory exists. Now that I’m typing this, I realize it would be better to check /etc/passwd instead.

RANDOMPASS=`openssl rand -base64 6`

This command creates a random string of characters. I use this as a password generator. I could have used apg, but that’s not available in the standard CentOS 6 repositories. I try and avoid having to install from 3rd party repositories if possible.

USERADD="/usr/sbin/useradd -s /sbin/nologin"

I specifically set a new user’s shell to /sbin/nologin since they will all be accessing files via Samba.

CHPASSWD="/usr/sbin/chpasswd"
SMBPASSWD="/usr/bin/smbpasswd -a -s"
SETQUOTA="/usr/sbin/setquota"

The CHPASSWD is for setting a password, the SMBPASSWD is for creating a new user with the password passed to the command. The SETQUOTA is for setting the quota.

For the case-statement I’ll just give one example:

accounting)
    HOMEBASE="-d /home/Accounting"
    GROUP="-g Accounting"
    QUOTASOFT="20000000"
    QUOTAHARD="25000000"
    userexists "/home/Accounting/${USERNAME}"

    $USERADD $HOMEBASE/"${USERNAME}" $GROUP "${USERNAME}" && \
        echo "${USERNAME}:${RANDOMPASS}" | $CHPASSWD && \
        (echo "${RANDOMPASS}"; echo "${RANDOMPASS}") | $SMBPASSWD ${USERNAME} && \
        $SETQUOTA "${USERNAME}" $QUOTASOFT $QUOTAHARD 0 0 /home

    [ $? = "0" ] && log "Created: ${USERNAME}, ${GROUPNAME}, ${RANDOMPASS}"
    ;;

So I simply create variables (HOMEBASE, GROUP) which holds the arguments to be passed to the USERADD command. For this situation, assuming the username is “thepoch” the whole command becomes:

/usr/sbin/useradd -s /sbin/nologin -d /home/Accounting -g Accounting

The CHPASSWD command sets the user with the random password we generated. The whole command becomes:

echo "thepoch:RANDOMPASSWORD" | /usr/sbin/chpasswd

The SMBPASSWD needs the password twice since the -s argument makes smbpasswd silent, accepting the password via standard input. Since smbpasswd asks for the new password twice, we give it twice. So, full command becomes:

(echo RANDOMPASSWORD; echo RANDOMPASSWORD) | /usr/bin/smbpasswd -a -s thepoch

The SETQUOTA command uses the QUOTASOFT and QUOTAHARD variables. I also only enabled quotas for /home on our server. If I needed to set quotas on other partitions, I can simply add the command here. So, the whole command becomes:

/usr/sbin/setquota "thepoch" 20000000 25000000

giving my account a 20GB quota, with a maximum hard limit of 25GB.

Some requirements

Some requirements for the adduser.sh script I should note:

  1. You have to prepare the group directories beforehand. I’m not sure if it’s automatically created for you via /usr/sbin/useradd.
  2. The groups have to be created beforehand as well.
  3. You obviously need to have Samba and quotas setup properly.

Things to improve on

I really need a script to reset a password. I also need to improve on checking if a user exists via /etc/passwd instead. These are going on my todo list.

Filed under linux centos samba user management bash script scripting

0 notes

Set up your server to forward root emails to an external email

So I mentioned in my previous post that I’ve setup our servers to forward emails for root to an external email. As long as your ISP isn’t blocking you from sending from your own SMTP, this should work. So if there isn’t anything blocking you from sending how do you forward root emails to an external email?

With a default CentOS 6.2 install, you can usually send emails already. So let’s test to see if you can receive emails from your server. Run the following command:

echo "Testing message" | mail -s "Testing from server" youremail@domain.com

replacing “youremail@domain.com” with your own email of course. If you don’t get the email, then your ISP may be blocking you from sending. If you do receive it, then we can continue.

First, you edit /etc/aliases. On our CentOS 6.2 systems, it’s the last line that’s important. By default it’s:

# Person who should get root's mail
#root:      marc

It’s commented out, and set to forward to “marc”. So, uncomment it by removing the “#” character in front of root. Then change “marc” to an email address you want to receive notifications. So now, it should look something like this:

# Person who should get root's mail
root:       notifications@example.com

Save the file. Now, in order for sendmail (or in CentOS 6.2’s case, postfix) to see the new alias, you have to run the following in your terminal:

newaliases

If there’s no output, then it should have worked.

So now we can test sending to just “root”:

echo "Testing message" | mail -s "Testing from server" root

Now, whoever is set to receive root’s emails will get that message.

You can also check your mail queue to see if any messages are “stuck”. The command is:

mailq

If your test messages are stuck there, then that’s probably something you should talk to your ISP about.

Filed under email forward root aliases newaliases centos sendmail postfix

2 notes

Be emailed when your Linux server starts up or shuts down

I like to be notified whenever my server starts up or shuts down or reboots. So I created an initscript for it. Why an initscript? This is a sure fire way to get the script called on shutdown. I used to have a script called in /etc/rc.d/rc.local, but that only gets called on startup. So initscript it is.

The Script

So here’s the script:

#!/bin/bash
#
# emailstartstop    Send an email on server startup and shutdown.
#
# chkconfig:    2345 99 01
# description:  Send an email on server startup and shutdown.

EMAIL="root"
STARTSUBJ=`hostname`" started on "`date`
STARTBODY="Just letting you know that server "`hostname`" has started on "`date`
STOPSUBJ=`hostname`" shutdown on "`date`
STOPBODY="Just letting you know that server "`hostname`" has shutdown on "`date`
lockfile=/var/lock/subsys/emailstartstop

# Send email on startup
start() {
    echo -n $"Sending email on startup: "

    echo "${STARTBODY}" | mail -s "${STARTSUBJ}" ${EMAIL}
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && touch $lockfile
    return 0
}

# Send email on shutdown
stop() {
    echo -n "Sending email on shutdown: "

    echo "${STOPBODY}" | mail -s "${STOPSUBJ}" ${EMAIL}
    RETVAL=$?
    echo
    [ $RETVAL = 0 ] && rm -f $lockfile
    return 0
}

# See how we were called.
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    *)
        echo $"Usage: $prog {start|stop}"
        exit 2
esac
exit ${RETVAL}

Now I only made this out of inspiration from the other initscripts on our CentOS 6.2 server, as well as some online blogs. For the quick and lazy, just do the following steps:

  1. Copy the script above, save it in a file called “emailstartstop”.
  2. Put that file in: /etc/rc.d/init.d/
  3. Change its permissions to be executable: chmod 755 /etc/rc.d/init.d/emailstartstop
  4. Make sure SELinux contexts are correct: restorecon /etc/rc.d/init.d/emailstartstop
  5. Add it to the chkconfig list: chkconfig —add emailstartstop
  6. Now test it out: service emailstartstop start && service emailstartstop stop
  7. Check the root account. It should get an email.
  8. Start it one more time: service emailstartstop start

By default this sends an email to root. On my systems, I set root as an alias to our notification email since multiple get this email. You can also just change the script and change the EMAIL variable.

NOTE: All this is assuming your email setup works.

Things to Note

So what happens with this script? I’d just like to break it down a bit.

# chkconfig:    2345 99 01

That line above tells chkconfig that this is used for runlevels 2, 3, 4, 5 (that’s the “2345”). The 99 makes the script start last on startup. The 01 makes the script start first on shutdown. This way, it makes sure that the local email daemon is working when the email is sent.

EMAIL="root"
STARTSUBJ=`hostname`" started on "`date`
STARTBODY="Just letting you know that server "`hostname`" has started on "`date`
STOPSUBJ=`hostname`" shutdown on "`date`
STOPBODY="Just letting you know that server "`hostname`" has shutdown on "`date`
lockfile=/var/lock/subsys/emailstartstop

This section is just the variables used through the script. You can modify it to suit your needs. The EMAIL is set to root by default since I like to alias root to another external email. The *SUBJ and *BODY variables is setup to get the hostname and current timestamp of the server. The lockfile, from what I understand, is needed for this to work. Never tested with or without as I didn’t really have time to. It shouldn’t cause any harm since it’s just an empty file.

Then we have the start() and stop() functions, and the case statement at the end. These should be pretty self explanatory.

So there you go. A quick script to get emailed when your server starts up or shuts down (or reboots, which is just a shutdown then startup sequence). Fun huh?

Filed under centos linux email notification startup shutdown script sysv initscript init

1 note

CentOS 6.2 + CUPS + AirPrint for iOS

So we share our printer via IPP using CUPS. What the heck is IPP? It’s just printing over HTTP. Why use IPP versus, say, Windows Printer Sharing? Simple: we’ve got a mixed environment of Windows XP, Windows 7, OS X 10.6, OS X 10.7, Fedora, Ubuntu, and CentOS workstations. IPP is supported by all of them. Plus, I could get AirPrint to work on it, which means you can add iOS in that list of Operating Systems above.

CUPS

So obviously you need to get CUPS to work first.

yum install cups ghostscript usbutils

So there’s the cups package, ghostscript, and usbutils. Since most printers these days are connected via USB, it’s always nice to have usbutils installed. Ghostscript is needed since most printers don’t really support PostScript so I install it just in case.

So once you’ve got those installed, you need printer drivers. With so many different printer manufacturers, I leave that up to you, dear reader, as to how to install it. You’ll usually need a PPD file. NOTE: if you’re printer is supported via foomatic or gutenprint, you can install drivers via:

yum install foomatic gutenprint-foomatic

This should install most drivers available. For example, we have an old Samsung ML-1510 that is supported by foomatic. We do have a newer Samsung ML-1860 that isn’t. That required me to download and install Samsung’s Unified Printer Driver (ugh). Its install script installed a bunch of .desktop files for every account on our server. So I had to search the entire server and remove all of these useless files. To Samsung: sheesh. This also installed an initscript called smfpd, which is a daemon for parallel port printers. Again, sheesh. You can’t disable this from startup via chkconfig. There is a commented out “exit 0” near the start. Uncomment that and the daemon should stop running on startup. Make sure you stop the daemon first as it consumes a bit of CPU and memory. Sheesh. Sorry for the semi-rant.

Anyway, the default CUPS configuration only allows access to localhost. Since I don’t worry too much about security, and you can secure it after, I change the following in /etc/cupsd.conf:

I add the following:

ServerAlias *
Port 631

which makes the server listen to any hostname (our server has a CNAME to it calling it ‘printer01’) on port 631.

Then I make sure to change all “Order deny,allow” to “Order allow,deny” and add an “Allow all” to everything after. This way, I can now remotely access the web GUI at:

http://hostname:631/

Change hostname for whatever you call your print server. If at anytime you’re asked for a username and password, use your server’s root account. So now that you have access to the web GUI, simply add your printer here.

Now you should be able to start CUPS:

service cups start && chkconfig cups on

Test your printer

So you should be able to add your printer to your workstations. The whole URL to your printer is:

http://hostname:631/printers/PRINTERNAME

or:

ipp://hostname/printers/PRINTERNAME

Special note for Mac users: for the “Queue” setting, make sure you include the “printers” portion. So ‘Address’ would be the hostname. ‘Queue’ would be “printers/PRINTERNAME”.

Once you’ve added the printer and selected/installed the correct driver, you should be good to go.

If not, there is a setting called ‘LogLevel’. By default it’s set as follows:

LogLevel warn

Change it to:

LogLevel debug

and monitor the log files in /var/logs/cups/error_log for any problems.

AirPrint

So now for the fun part: getting AirPrint to work so users can print from their iPhones and iPads. There are some great references online, plus a great script to automatically generate the configuration files needed to get your printer advertised by Avahi.

So, Timothy J. Fontaine of Ataraxia Consulting created a python script that automatically generates a .service file for the printers connected via CUPS. You can read his blog post about it. The GitHub repository is available here:

https://github.com/tjfontaine/airprint-generate

Anyway, you can (hopefully) download the file directly here:

https://raw.github.com/tjfontaine/airprint-generate/master/airprint-generate.py

First you’ll need to install Avahi:

yum install avahi avahi-compat-libdns_sd

Then you need to install the Python cups module. For CentOS 6.2, this is, strangely, in the “system-config-printer-libs” package. So:

yum install system-config-printer-libs

Then you can run the airprint-generate.py script. Copy the generated .service files into /etc/avahi/services. Then you can start the service:

service avahi-daemon start && chkconfig avahi-daemon on

I had some trouble with printers not being advertised. Since my local network is on br1 instead of eth1 (a KVM thing), I had to add the following to /etc/avahi-daemon.conf:

allow-interfaces=br1

If you had to add that, restart avahi-daemon.

And that’s it. Printers should start showing up on your iOS devices.

Filed under centos cups avahi bonjour printer printing samsung ios iphone ipad airprint apple ipp

2 notes

Local CentOS yum repository via FTP and rsync

So we have a local yum repository available through FTP. It saves us a lot of bandwidth since all our servers are running either CentOS 5.x or 6.x. It also allows us to setup a server using kickstart and PXE boot, installing straight from the FTP server (a topic for another time).

So what is needed? For our setup, a CentOS 6.2 server with vsftpd installed. Then we use my centos-rsync.sh script (which I may have copied from somewhere else) to sync from an rsync server.

FTP Server

This is quite simple:

yum install vsftpd

The default setup works out of the box. I personally make sure to comment out “write_enable=YES” in /etc/vsftpd/vsftpd.conf

Then we can enable vsftpd:

service vsftpd start && chkconfig vsftpd on

Make sure port 21 is open. You can test it out by accessing your FTP server. You should see the content of /var/ftp/ by default if you login anonymously.

Setup rsync destination

So now we need a place to actually put the CentOS mirror in. In /var/ftp I create a pub/ directory (not really needed), then inside the centos/6.2/ directory. Then I create a symlink to 6.2 called “6”. So the commands are:

mkdir -p /var/ftp/pub/centos/6.2
cd /var/ftp/pub/centos
ln -s 6.2 6

This way, if there’s a new CentOS 6 version to come out (eg 6.3), I just create a 6.3 directory, then symlink 6 to it.

The rsync script

So here’s the rsync script. Some points of note:

  1. I got this from somewhere or got the inspiration from somewhere. I don’t remember anymore.
  2. CENTOS6: Change this if there’s a new CentOS version available.
  3. RSYNCROOT: Change this if you want to use another rsync repository.
  4. FTPROOT: Change this if you want to use a different destination directory.

So, create this file as /usr/local/sbin/centos-rsync.sh:

#!/bin/sh
#

CENTOS6="6.2"

RSYNCROOT="rsync://mirrors.kernel.org/centos"
LOCKFILE="/var/lock/subsys/centos-rsync"
FTPROOT="/var/ftp/pub/centos"

if [ -f $LOCKFILE ]; then
    echo "centos-rsync.sh is already running."
    exit 0
fi

if [ -d $FTPROOT ]; then
    touch $LOCKFILE

    echo "----------"
    echo "CentOS $CENTOS6"
    echo "----------"
    /usr/bin/rsync --progress -avSHPrt --delete --exclude "local*" --exclude "SRPMS" --exclude "isos" --exclude "centosplus" --exclude "contrib" --exclude "fasttrack" --exclude "drpms" $RSYNCROOT/$CENTOS6/ $FTPROOT/$CENTOS6/

    chown -R root:root $FTPROOT/
    rm -f $LOCKFILE
else
    echo "Target directory $FTPROOT does not exist."
fi

Then make sure it is executable.

chmod 755 /usr/local/sbin/centos-rsync.sh

You can test the script and see if it syncs correctly. If you have the DVDs, I suggest that you first copy those to your FTP server to make the rsync go faster on first run. Otherwise, you’ll be downloading gigabytes of data. Be prepared for that.

Setup cron to call the script

Once you’ve confirmed that the script works, you can call it via cron. I’ve set mine to automatically sync Monday to Saturday at 4:30am. You can choose your own schedule if you wish.

/etc/cron.d/centos-rsync.cron:

30 4 * * 1-6 root /usr/local/sbin/centos-rsync.sh

crond will automatically load this new schedule once it’s saved.


So, um, there you go. Have fun.

Filed under linux centos rsync yum repo ftp