Tech Stuff

Aegir

Aegir is a hosting system for managing Drupal sites.

Below are links to the project page, git repositories and community resources.

aegirproject.org
Bug tracker
Aegir Group
Git repos
Mailing lists

Application Server

Apache

Debian, Apache, SSL, Multiple Virtual Hosts

After much trial and grief, I've finally nailed how to have multiple SSL virtual hosts running on Apache on our Debian servers alongside non-SSL hosts.

In the 'default' apache config, put:


NameVirtualHost *:80
NameVirtualHost *:443

Then for each virtual host config that you ever create, the opening VirtualHost tag MUST include the relevant port as follows:


(for SSL site)
(for standard site)

Note that I have added a space between the tags due to Drupal attempting to format as HTML. Remove the spaces in your config file.

You must then have the ServerName specified inside each Virtual Host.

Somehow it didn't seem that easy at the start!

Rewriting URLs to use www with permanent redirect

The following rewrite rule enables all requests to a site to go through via its www URL, theoretically improving search engine 'optimization' (if there is such a thing).

The important thing here is that any file or directory request following the url is kept during the redirect (I've seen poor rewrites on sites that just redirect from http://whatever.com to the front page and the user loses their path to the link they clicked on once already).


RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$
RewriteRule ^.*$ http://www.example.com%{REQUEST_URI} [R=permanent,L]

Database

Fileserving

Playing with CouchDB

Experimenting with a bunch of VMs and CouchDB for
scalable, replicated filesystem purposes.

I like the way it stores any object as a 'document' in its database, doesn't depend on another
database backend like MogileFS seems to.

I'm slightly wary of the fact that the replication isn't built as part of the daemon's
responsibility automatically on the fly, by way of which you could control via the config ini
file. Instead replication is a manually executed process either via the web interface or via
curl commands that one could at least build into bash scripts... and maybe via the API i.e it
could be done in the app itself that is pushing files up into the database?

Not sure if that's a good feature or not yet.. also the fact that it's uni-directional
replication, making it not so easy to get a master-master HA solution happening.. you can
replicate both ways but what happens when conflict occurs (and they seem to)..

Watch this space!

MySQL

Daily Database Backup

#!/bin/bash
DATE=`date +%Y%m%d`
FILENAME=$1/$1_daily_$DATE.sql.bz2
mkdir -p $1
mysqldump --user=root --password=changeme --host=localhost $1 | bzip2 -c > ${FILENAME}
scp -i /home/web/.ssh/password_less_ssh_key $FILENAME <a href="mailto:user@remote.server">user@remote.server</a>:/data/backup/dbbackups/$1/

Export a MySQL query to an output file

Tags:

mysql -p DATABASE

(enter password)

SELECT * INTO OUTFILE "/tmp/table.txt" FROM TABLE;

Hourly Database Backup

#!/bin/bash
DATE=`date +%H`
FILENAME=$1/$1_hourly_$DATE.sql.bz2
mkdir -p $1
mysqldump --user=root --password=changeme --host=localhost $1 | bzip2 -c > ${FILENAME}

How to reset forgotten mysql root password

Tags:

/etc/init.d/mysql stop
mysqld_safe --skip-grant-tables &
mysql -u root

mysql> use mysql;
mysql> update user set password=PASSWORD("newrootpassword") where user='root';
mysql> flush privileges;
mysql> quit

/etc/init.d/mysql stop
/etc/init.d/mysql start
mysql -u root -p

MySQL Master-master replication notes

I seem to have to keep looking up notes whenever I need to set up a multi-master MySQL replication ring. I thought I'd put it all down in one place that I can find easily - on my own blog.

This is not a howto, it's just notes, I can't guarantee these are accurate or without faults.

1. Install MySQL server on Server A and B

apt-get install mysql-server

2. On Server A, grant replication privileges to a replication user

grant replication slave on *.* to 'replication'@'server_b' identified by 'slavepw';

3. MySQL config on Server A:

server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M
binlog_do_db = testdb
binlog_ignore_db =mysql
relay-log=mysqld-relay-bin

Restart Server A MySQL

4) Edit Server B config:

server-id = 2
log_bin = /var/log/mysql/mysql-bin.log
expire_logs_days = 10
max_binlog_size = 100M
binlog_do_db = testdb
binlog_ignore_db =mysql
relay-log=mysqld-relay-bin
master-host = server_a
master-user = replication
master-password = slavepw
master-port = 3306

5) Restart MySQL on Server B, then:

start slave;
show slave status\G;

These should be both Yes, also check that the Master Host, binlog and positions all match (by comparing with 'show master status;' on Server A

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

At this point we've a master->slave relationship. It's time to make Server B a master of Server A as well.

6. Set the replication privileges on Server B to become a master of Server A

grant replication slave on *.* to 'replication'@'server_a' identified by 'slavepw';

7. On Server B, add the master info to my.cnf so it knows it's a slave of Server B

master-host = server_b
master-user = replication
master-password = slavepw
master-port = 3306

8. Restart Server B and then Server A

(not sure if it matters what order really)

9. On Server A:

start slave;
show slave status\G;

These should be both Yes, also check that the Master Host, binlog and positions all match (by comparing with 'show master status;' on Server B

Slave_IO_Running: Yes
Slave_SQL_Running: Yes

10. start slave on Server B

(I guess, or was it already started when MySQL restarted? It should do it automatically, try to remember from 2008 what we did here to make sure it does)

Unless there's errors for either node connecting to each other, should be ok to create the database testdb on Primary or Secondary (the creation will replicate to the other node), and start creating tables/data on either node.

Mysql + PostgreSQL 'all databases' backup script

Backup script to find and backup all databases (postgres and mysql)

#!/bin/bash
 
today=$(date +%y%m%d)
 
# local dir where the backups go
myDir='/data/dbbackups'
# remote dir where we'll send them offsite
myRemoteDir='/data/backup/li35-166/dbbackups'
 
 
# this is for PostgreSQL. If you don't need it, you
# could leave it here, but remove the 'backup_pgsql'
# function call at the end of the script
 
function backup_pgsql {
for db in `su postgres -c "psql -U postgres -qAtc '\l'" | cut -f1 -d\|
| grep -v '^template[01]'`; do
mkdir -p $myDir/${db};
su postgres -c "pg_dump $db" | bzip2 > $myDir/${db}/${db}-$today.bz2;
done;
}
 
# MySQL backup function
 
function backup_mysql {
myFile='/etc/mysql/debian.cnf'
myUser=`cat $myFile | grep user | awk '{print $3}' | uniq`
myPass=`cat $myFile | grep password | awk '{print $3}' | uniq`
 
for db in `mysql -u$myUser -p$myPass -e "show databases" | cut -f2 -d\
| grep -v Database`; do
mkdir -p $myDir/${db};
mysqldump -u$myUser -p$myPass $db | bzip2 >
$myDir/${db}/${db}-$today.bz2;
done;
}
 
# here are the functions that actually get called.
# You might want to comment out the postgres one
 
backup_pgsql
backup_mysql
 
# rsync the contents of the local backup store
# to the remote machine. Since rsync is incremental
# this will only transfer today's dumps across,
# presuming that the remote end has all up until that
# point already
# obviously using ssh auth keys here
 
rsync -aHPq $myDir/ git.mig5.net:$myRemoteDir/

PostgreSQL

Check number of postgresql connections

psql template1;

SELECT COUNT(*) FROM pg_stat_activity;

You can see max allowed connections with SHOW max_connections; - but this is just as easily read from the postgresql.conf in /etc/postgresql/(version).. etc

Daily database backup

#!/bin/bash
DATE=`date +%Y%m%d`
FILENAME=$1/$1_daily_$DATE.sql
mkdir -p $1
pg_dump -cxO -f $FILENAME $1
gzip -f $FILENAME
echo $FILENAME
scp -i /home/user/.ssh/password_less_ssh_key $FILENAME.gz user@remote.backup:/data/backup/dbbackups/$1/

Hourly database backup

#!/bin/bash
DATE=`date +%H`
FILENAME=$1/$1_hourly_$DATE.sql
mkdir -p $1
pg_dump -cxO -f $FILENAME $1
gzip -f $FILENAME
echo $FILENAME

How to see size of postgres databases / tables

Tables:


SELECT pg_tables.tablename, pg_tables.schemaname, pg_size_pretty(pg_relation_size((pg_tables.schemaname::text || '.'::text) || pg_tables.tablename::text)) AS pg_size_pretty FROM pg_tables ORDER BY pg_relation_size((pg_tables.schemaname::text || '.'::text) || pg_tables.tablename::text) DESC;

Database entirely (I swear I had this sites statistics script somewhere that contained all this, including a MySQL version.. where did that go?):


select pg_database.datname, pg_size_pretty(pg_database_size(pg_database.datname)) AS size from pg_database WHERE pg_database.datname = 'foobar';

High Availability

DRBD

Example DRBD conf from $charity

global {
usage-count no;
}
common {
syncer {
rate 100M;
}
}
resource nfs {
protocol C;
startup {
degr-wfc-timeout 120; # 2 minutes.
}
disk {
# on-io-error detach;
}
net {
cram-hmac-alg sha1;
shared-secret "removed";
}
on server-nfs-01 {
device /dev/drbd1;
disk /dev/mapper/ubuntu-SrvNfs;
address 10.0.1.91:7788;
meta-disk /dev/mapper/ubuntu-drbd[0];
}
on server-nfs-02
device /dev/drbd1;
disk /dev/mapper/ubuntu-SrvNfs;
address 10.0.1.92:7788;
meta-disk /dev/mapper/ubuntu-drbd[0];
}
}

Heartbeat

Example ha.cf from $charity

# debug output
debugfile /var/log/ha-debug.log
# all other logs
logfile /var/log/ha-log.log
logfacility local0
keepalive 1
deadtime 10
warntime 3
initdead 20
bcast eth0
auto_failback off
# STONITH
#stonith_host * external/ssh 10.0.1.90 root nottherootpassword
node server-nfs-01
node server-nfs-02

Example haresources from $charity

server-nfs-01 charitynfs IPaddr::10.0.1.90/27/eth0 drbddisk::nfs Filesystem::/dev/drbd1::/srv/nfs::xfs

Keepalived

Mail

"The email is not valid!" or "The alias is not valid!" in postfixadmin

Before I forget... because it freaked the shit out of me and made me think I broke the postfixadmin database.

When you create a domain in Postfixadmin, that's all fine and dandy. But when you create Mailboxes or Alias in this domain, postfixadmin performs a nameserver lookup only at THIS point in time to check the domain is valid. The helpful response is:

"The EMAIL is not valid!" (for mailbox), or "The ALIAS is not valid!".

In fact, what it means is that the domain is not valid, so this is quite misleading. So don't panic!

I thought I broke things, but all that had happened was that for this particular subdomain, while I had created an MX record for it in the zone, I hadn't reloaded DNS yet, and so a nameserver lookup was failing.

You could wait for DNS propagation (still don't understand why it wasn't almost instant, mail server was also the primary NS), or a quick getaround is to change

$CONF['emailcheck_resolve_domain']='YES';

to

$CONF['emailcheck_resolve_domain']='NO';

In the config.inc.php of postfixadmin, and it'll skip the nameserver lookup.

Phew.

Dovecot fails to lookup db after PostgreSQL-8.3 upgrade (Lenny 5.0.1)

Be wary when you upgrade Debian Lenny server to 5.0.1 and you use Postfix & Dovecot with a PostgreSQL backend and PostgreSQL-8.3 gets upgraded and restarts itself. My Dovecot stopped being able to connect to the database to authenticate my login requests from Thunderbird all
of a sudden with the error 'you have enabled secure authentication and this server does not support it'.

I knew I hadn't changed any Dovecot settings on how I authenticate my IMAP session so assumed it was the PostgreSQL upgrade, though PostgreSQL was running fine.

This in the dovecot logs

dovecot: 2009-04-13 10:38:15 Error: auth(default): sql(<a href="mailto:example@some--address.com">example@some--address.com</a>),): Password query failed: FATAL: terminating connection due to administrator command
dovecot: 2009-04-13 10:38:15 Error: auth(default): io_loop_handle_remove: epoll_ctl(2, 9): Bad file descriptor

I restarted Dovecot, and Postfix just to be sure :) and all is back to normal. I'm assuming it was Dovecot holding a stale connection open to PostgreSQL, and not the MTA, as it wasn't a problem with delivering mail (though it might've posed a problem for Postfix too, but didn't let it hang around long enough to find out)

I wonder, I know that some packages like openssl and whatnot, are able to check what other services are running that depend on it when it's upgraded, and offer a postinst (I assume) ncurses notification telling the user that some services should be restarted. PostgreSQL should have a similar check to see if it's being used for a vhost mail setup.

List of Mail SMTP status codes

Coz I always forget them!

http://www.fots.nl/index.php/ndr-and-smtp-reply-and-error-codes/

  • 2.0.0 (nonstandard success response, see rfc876)
  • 2.1.1 System status, or system help reply
  • 2.1.4 Help message
  • 2.2.0 Service ready
  • 2.2.1 Service closing transmission channel
  • 2.5.0 Requested mail action okay, completed
  • 2.5.1 User not local; will forward to
  • 3.5.4 Start mail input; end with .
  • 4.2.1 Service not available, closing transmission channel
  • 4.2.2 The recipient has exceeded their mailbox limit. It could also be that the delivery directory on the Virtual server has exceeded its limit. (Default 22 MB)
  • 4.3.1 Not enough disk space on the delivery server. Microsoft say this NDR maybe reported as out-of-memory error.
  • 4.3.2 Classic temporary problem, the Administrator has frozen the queue.
  • 4.4.1 Intermittent network connection. The server has not yet responded. Classic temporary problem. If it persists, you will also a 5.4.x status code error.
  • 4.4.2 The server started to deliver the message but then the connection was broken.
  • 4.4.6 Too many hops. Most likely, the message is looping.
  • 4.4.7 Problem with a timeout. Check receiving server connectors.
  • 4.4.9 A DNS problem. Check your smart host setting on the SMTP connector. For example, check correct SMTP format. Also, use square brackets in the IP address [197.89.1.4] You can get this same NDR error if you have been deleting routing groups.
  • 4.5.0 Requested mail action not taken: mailbox unavailable
  • 4.5.1 Requested action aborted: local error in processing
  • 4.5.2 Requested action not taken: insufficient system storage
  • 4.6.5 Multi-language situation. Your server does not have the correct language code page installed.
  • 5.0.0 Syntax error, command unrecognised. You get this NDR when you make a typing mistake when you manually try to send email via telnet. More likely, a routing group error, no routing connector, or no suitable address space in the connector. (Try adding * in the address space)
    This status code is a general error message in Exchange 2000. In fact Microsoft introduced a service pack to make sure now get a more specific code.
  • 5.0.1 Syntax error in parameters or arguments
  • 5.0.2 Command not implemented
  • 5.0.3 Bad sequence of commands
  • 5.0.4 Command parameter not implemented
  • 5.1.x Problem with email address.
  • 5.1.0 Often seen with contacts. Check the recipient address.
  • 5.1.1 Another problem with the recipient address. Possibly the user was moved to another server in Active Directory. Maybe an Outlook client replied to a message while offline.
  • 5.1.3 Another problem with contacts. Address field maybe empty. Check the address information.
  • 5.1.4 Two objects have the same address, which confuses the categorizer.
  • 5.1.5 Destination mailbox address invalid.
  • 5.1.6 Problem with homeMDB or msExchHomeServerName - check how many users are affected. Sometimes running RUS (Recipient Update Service) cures this problem. Mailbox may have moved.
  • 5.1.7 Problem with senders mail attribute, check properties sheet in ADUC.
  • 5.2.x NDR caused by a problem with the large size of the email.
  • 5.2.1 The message is too large. Else it could be a permissions problem. Check the recipient’s mailbox.
  • 5.2.2 Sadly, the recipient has exceeded their mailbox limit.
  • 5.2.3 Recipient cannot receive messages this big. Server or connector limit exceeded.
  • 5.2.4 Most likely, a distribution list or group is trying to send an email. Check where the expansion server is situated.
  • 5.3.0 Problem with MTA, maybe someone has been editing the registry to disable the MTA / Store driver.
  • 5.3.1 Mail system full. Possibly a Standard edition of Exchange reached the 16 GB limit.
  • 5.3.2 System not accepting network messages. Look outside Exchange for a connectivity problem.
  • 5.3.3 Remote server has insufficient disk space to hold email. Check SMTP log.
  • 5.3.4 Message too big. Check limits, System Policy, connector, virtual server.
  • 5.3.5 Multiple Virtual Servers are using the same IP address and port. See Microsoft TechNet article: 321721 Sharing SMTP. Email probably looping.
  • 5.4.0 DNS Problem. Check the Smart host, or check your DNS. It means that there is no DNS server that can resolve this email address. Could be Virtual Server SMTP address.
  • 5.4.1 No answer from host. Not Exchange’s fault check connections.
  • 5.4.2 Bad connection.
  • 5.4.3 Routing server failure. No available route.
  • 5.4.4 Cannot find the next hop, check the Routing Group Connector. Perhaps you have Exchange servers in different Routing Groups, but no connector.
  • 5.4.6 Tricky looping problem, a contact has the same email address as an Active Directory user. One user is probably using an Alternate Recipient with the same email address as a contact.
  • 5.4.7 Delivery time-out. Message is taking too long to be delivered.
  • 5.4.8 Microsoft advise, check your recipient policy. SMTP address should be cp.com NOT server.cp.com.
  • 5.5.0 Requested action not taken: mailbox unavailable. Underlying SMTP 500 error. Our server tried ehlo, the recipient’s server did not understand and returned a 550 or 500 error. Set up SMTP logging.
  • 5.5.2 Possibly the disk holding the operating system is full. Or ‘Requested mail action aborted: exceeded storage allocation’ if you are executing an SMTP.
  • 5.5.3 More than 5,000 recipients. Check the Global Settings, Message Delivery properties. Or ‘Requested action not taken: mailbox name not allowed’ if you are executing an SMTP.
  • 5.5.4 Transaction failed
  • 5.5.5 Wrong protocol version
  • 5.6.3 More than 250 attachments.
  • 5.7.1 Permissions problem. For some reason the sender is not allowed to email this account. Perhaps an anonymous user is trying to send mail to a distribution list. Check SMTP Virtual Server Access Tab. Try checking this box: Allow computers which successfully authenticate to relay. User may have a manually created email address that does not match a System Policy.
  • 5.7.2 Distribution list cannot expand and so is unable to deliver its messages.
  • 5.7.3 Check external IP address of ISA server. Make sure it matches the SMTP publishing rule.
  • 5.7.4 Extra security features not supported. Check delivery server settings
  • 5.7.5 Cryptographic failure. Try a plain message with encryption.
  • 5.7.6 Certificate problem, encryption level maybe to high.
  • 5.7.7 Message integrity problem.

Notes on Dovecot Login Process

Tags:

Login process creation

login_processes_count and login_max_processes_count setting control how new login processes are created. login_processes_count specifies the number of login processes that are tried to be kept listening for new connections. However when a lot of connections arrive at the same
time this number will increase automatically as described below.

To prevent fork-bombing Dovecot's login process creation works in a similiar way to Apache: Initially set "wanted listening process count" to same as login_processes_count and start the processes. Then check every second if we need to start up new processes to keep the listening
process count the same as the wanted count. If all the listening processes have been used, double the wanted count. If we haven't used all of them, decrease the wanted count by one (unless it goes below login_processes_count), but don't destroy already created processes.

login_max_processes_count specifies the maximum number of login processes there can exist at a time (listening, non-listening and SSL/TLS-proxying processes combined).

Postfix, placing a temporary hold on recipients

Didn't know you could do this.. both the ability to specify individuals rather than whole domains, and the retry: logic.

In the transport map

< user >@< destination.com >               retry:4.0.0 Temporarily suspended

Using Dovecot SASL for SMTP authentication

I was migrating my mail server to a new machine the other day and in the process, I sorted out my smtp/sasl logic.

I'm using virtual domains/mailboxes/aliases with Postfix, PostfixAdmin and Postgresql on Debian servers, works like a charm, but my sasl config was such that it wasn't using the same postfix database for password lookups and I was having to use saslpasswd2 to add accounts so that I could send mail using this server for SMTP as it was using its own sasldb or whatever.

Madness.

This time I've implemented Dovecot's SASL mechanism, it reduced double-up and means the authentication is done via the database in the same way that logging in to *retrieve* mail was being performed (as far as I understand it anyway. I'm not a mailserver guru).

All I changed was in /etc/dovecot/dovecot.conf
# It's possible to export the authentication interface to other programs:
 
socket listen {
client {
path = /var/spool/postfix/private/auth-client
mode = 0660 user = postfix group = postfix
}
}
And in /etc/postfix/main.cf
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth-client
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated,
reject_unauth_destination

Using Virtual Vacation with PostfixAdmin

Other than following the steps outlined in the INSTALL.txt file of Virtual Vacation’s directory in postfix admin, I had to do these to make it work properly as well:

Added

vacation_destination_recipient_limit = 1

to /etc/postfix/main.cf

At this point things work, so long as your recipient who's on holidays is not a member of any virtual aliases (ie he or she doesn't receive mail sent to generic addresses like info@ or help@).

If they do, you'll soon notice a heap of 'permission denied' errors being spat back either as a bounce or in mail.log, syslog, and the vacation-debug log. This is because the vacation user doesn't have access to select from the alias table in your postfix database.

To fix, you need to grant this privilege to the vacation user with an SQL query:


GRANT SELECT ON TABLE alias TO vacation;

All fixed, now email sent to an alias will send back a vacation auto-reply as well.

Vacation out-of-office responder

Making vacation autoresponder in Debian

Make the .forward file in user's home directory:

\<a href="mailto:user@email.com">user@email.com</a>
"|/usr/bin/vacation user"

Make the .vacation.msg in user's home directory:

From: <a href="mailto:user@email.com">user@email.com</a> (Joe Bloggs)
Subject: I am on Annual Leave
Delivered-By-The-Graces-Of: The Vacation program
Precedence: bulk
E-mail message goes here

cd out of the user's home directory to /home and run:

vacation -i user && chmod 644 user/.vacation.db && chown user:user user/.vacation.db

imapsync

imapsync --host1 server_a --user1 user@server_a --password1 xxxxxxxxxxx --ssl1 --authmech1 login --host2 server_b --user2 user@server_b --password2 xxxxxxxxxxx --ssl2 --fast --nofoldersizes --subscribe --authmech2 login

Security

Basic firewall


#!/bin/bash

# IP interfaces
eth0=`ifconfig eth0 | grep "inet addr" | awk '{print $2}' | cut -d: -f2`

# Change to 1 to enable logging of dropped packets
LOG=0

flush() {
iptables --flush
iptables --delete-chain
}

start() {

# Flush just in case 'start' has been called twice without stop or restart
flush

# Default policies
iptables --policy INPUT DROP
iptables --policy OUTPUT DROP
iptables --policy FORWARD DROP

# Accept all on loopback
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# Accept all packets that are part of an established connection
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT

# Pings
iptables -A INPUT -p icmp --icmp-type 8 -s 0/0 -d $eth0 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -p icmp --icmp-type 0 -s $eth0 -d 0/0 -m state --state ESTABLISHED,RELATED -j ACCEPT

# SSH
iptables -I INPUT -d $eth0 -p tcp --dport 22 -j ACCEPT
# HTTP
iptables -A INPUT -d $eth0 -p tcp --dport 80 -j ACCEPT
# DNS
iptables -A INPUT -d $eth0 -p udp --dport 53 -j ACCEPT

if [ $LOG -eq 1 ]; then
# Create a LOGDROP chain to log and drop packets
iptables -N LOGDROP
iptables -A LOGDROP -j LOG
iptables -A LOGDROP -j DROP
# Drop and log all other traffic inbound
iptables -A INPUT -j LOGDROP
else
# Drop all other traffic inbound
iptables -A INPUT -j DROP
fi
}

stop() {
flush
iptables --policy INPUT ACCEPT
iptables --policy OUTPUT ACCEPT
iptables --policy FORWARD ACCEPT
}

case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
;;
esac

Nmap Conficker Check

nmap --script=smb-check-vulns --script-args=safe=1 -p445 -d (IP)

PCI Compliance and general good security

SSL:
/etc/apache2/mods-enabled/ssl.conf


SSLProtocol all -SSLv2
SSLCipherSuite HIGH:MEDIUM:!ADH

Squid
When SSL terminates at a Squid reverse proxy, instead make changes to the squid.conf likeso:

https_port 443 vhost cert=whatever.ssl key=whatever.key cafile=whatever.cer defaultsite=<a href="http://www.examplecom">www.examplecom</a> cipher=DEFAULT:!EXPORT:!LOW options=NO_SSLv2

Other Squid changes:

# PCI Verizon scan results
reply_header_access X-Cache-Lookup deny all
reply_header_access X-Cache deny all
reply_header_access All allow all
via off
httpd_suppress_version_string on

PHP:

/etc/php5/apache2/php.ini

expose_php = Off

This gets rid of those horrible mountains of 'safe_mode' vulnerabilities reported to exist in versions of PHP 5.2.8 or lower, because it hides the version number. Hiding information like this that is sent back in HTTP headers is a good idea and also something else that the scan complains about.
Kind of a dodgy fix, obviously in a better world we'd be upgrading to newer version of PHP but maybe that isn't an option for whatever reason.

Don't leave any pages that call phpinfo() without checking the requestor's IP too:

if( $_SERVER['REMOTE_ADDR'] == '1.2.3.4' )
{
        phpinfo();
}

Apache:

/etc/apache2/apache2.conf


ServerTokens Prod

(hides apache/php/ssl versions in the footer of pages, i.e when you hit a 404, probably headers too)

In any vhost, including the 000-default, prevent TRACE with mod_rewrite


RewriteEngine On
RewriteCond %{REQUEST_METHOD} ^TRACE
RewriteRule .* - [F]

The TRACE method is an HTTP command used for debugging purposes. A client sending the TRACE command to a web server will receive an echo of the entire request, including HTTP headers. It is possible for a malicious user to obtain sensitive information from the headers, such as cookies or authentication data.

Sysadmin

Bash

Backup of subversion repository

This script does an svn hotcopy of a subversion repository and then rsyncs it to an external location. Easy to script, nice trouble-free way of maintaining subversion backups.

#!/bin/bash
export RSYNC_RSH=/usr/bin/ssh

# some variables in case they change later
server=destination.com # where we are sending the backup to
user=miguel
tempcopy=/home/${user}/svnbackup
dest=/media/disk/svnserver/svnbackup

# example svn repository
# make the backup with svnadmin hotcopy
svnadmin hotcopy /data1/svn/repo/ ${tempcopy}/repo/

# rsync it to the external hard drive on my desk
rsync -aHPvz ${tempcopy}/repo/ "${user}@${server}:${dest}/repo/"

# finally, remove the backup on svnserver to free up some space
rm -rf ${tempcopy}/repo

Getting a cron to execute on the last day of month

Tags:

It had never occured to me before, but suddenly I had the need to run a script on the last day of every month, in order to get the right results per month.

Given that a month's last date can be 28, 29 30 or 31, it wasn't as simple as usual!

After a bit of googling I found the easy solution. Simply get the crontab to run every day, but add into the crontab a check to see if tomorrow's date of the month is '01'. If it isn't, do nothing. If it is, then continue with the rest of the script.

36 14 * * * [ `date -d tomorrow +%d` -eq '01'] && /execute/the/real/script.sh

Edit: Hmm, got a syntax error there:

/bin/sh: -c: line 0: unexpected EOF while looking for matching ``' /bin/sh: -c: line 1: syntax error: unexpected end of file

Ah well, since I'm only executing a shell script, I just added this to my script:

#!/bin/bash
TODAY=`/bin/date +%d`
TOMORROW=`/bin/date +%d -d "1 day"`
# See if tomorrow's day is less than today's
if [ $TOMORROW -lt $TODAY ]; then
(real script executes here)
exit 0
fi
exit 1

Sharing bash sessions

Both articles from Sébastien Wains's blog

Named pipe

You may know “screen”, tool that can help you put your session on hold and and get back to it whenever you want.
Described below is a way to share your session with someone… this can come in handy while doing support or if you just want to share your session for some reason

You should follow the following steps carefully

User 1 will work in the terminal
User 2 will watch what user 1 is doing

1. User 1 :

mkfifo /tmp/file

2. User 2 :

cat /tmp/file

3. User 1 :

script -f /tmp/file

User 1 can start working, user 2 will be able to follow his work, user 1 should type ctrl + D if wanting to stop sharing the session…

If user 2 tries to input something, the connection to the pipe will be lost and you should get back to step 2.

Screen method

This is a great way if you can’t install anything on the machine. If you are able to get “screen” installed, screen provides a much easier way, which allows all connected users to interact on the shared session. The named pipe method only allowed one user to watch what the other user was doing.

So here it goes…

1. user 1 connects to the machine and type the command “screen”
2. inside the screen terminal, user 1 hits ctrl + a and then type “:multiuser on”
3. user 2 joins in by typing “screen -x”
4. other users can join as described at point 3

Both users are now sharing the session.

Ctrl + a then d will close the screen session

Links :

Source : http://linuxhelp.blogspot.com/2005/01/screen-window-manager-for-console....

rsync backup script

VERY simple rsync script. It rsyncs a remote directory across to a local one. There's no special reason why I did this, other than I wanted to run the script on my local machine. It could just as easily rsync a local directory to a remote one.

#!/bin/sh
export RSYNC_RSH=/usr/bin/ssh
origin=remote.machine.com # the remote machine to rsync from
user=miguel # the remote user
rsync -aHPvz "${user}@${origin}:/remote/files/" "/local/files/"

I run ssh-keygen -t rsa and create a password-less key on the remote machine, and copy the .pub file into my authorized_keys. This way I can run this script on a cron, say in the middle of the night, and not have to be around to enter a password at the prompt.

Bash shortcuts

Ctrl-a -> go to the start of command line
Ctrl-e -> go to the end of command line
Ctrl-p -> previous command in history
Ctrl-n -> next command in history
Ctrl-f -> next character in command line
Ctrl-b -> previous character in command line
Ctrl-r -> reverse search in history file
Ctrl-d -> delete current character
Ctrl-k -> delete from the prompt to the end of command line
Ctrl-_ -> undo (yes, but limited)
Meta-< -> go to beginning of history file
Meta-> -> go to end of history file
Meta-f -> go to next word in command line
Meta-b -> go to previous word in command line
Meta-d -> delete next word in command line (from the actual position of the prompt)

Better rsync backup script


#!/bin/bash
# Basic backup script
# Written in February 2010

# Set to q to disable verbosity
VERBOSITY="v"

# The program we're using to backup
PROGRAM="rsync"

# The arguments to the program
OPTIONS="-aHP$VERBOSITY"

# The remote machine we're backing up
SERVER="max"

# The local disk to back up to
DISK="/mnt/disk"

# An array of shares on $SERVER to back up
SHARES=(
hr
finance
clients
operations
newsvn
svn
backup/jira
backup/svn/noah
backup/dbbackups/kontrol_live
backup/dbbackups/god
)

# The magic
for share in ${SHARES[*]}; do
echo "Backup of $share beginning at" `date`
$PROGRAM $OPTIONS $PROGRAM://$SERVER/$share/ $DISK/$share/
echo "Backup of $share completed at" `date`
done

Bash: Monitor file addition/deletion in a directory

Silly little script to monitor changes in a directory (new or removed files)

Takes the destination dir to monitor from stdin

One day I'll learn how to use inotify.


#!/bin/bash

# Monitor a directory for file changes
# (add or remove) as requested

touch /tmp/testdirb.$$
while true
do
find $1 -print > /tmp/testdira.$$
diff /tmp/testdira.$$ /tmp/testdirb.$$ > /tmp/dirdiff
if [ -s /tmp/dirdiff ]
then
sed -i s/" sed -i s/">"/"Removed files"/ /tmp/dirdiff
/usr/bin/mail -s "Files changed in $1" "you@example.com" There have been file changes in $1 since the last hour.
`cat /tmp/dirdiff`
EOF
fi

cp /tmp/testdira.$$ /tmp/testdirb.$$
sleep 3600
done

How to deny caching of specific hosts in Squid

Tags:

acl confickerworkinggroup dstdomain .confickerworkinggroup.org
 
cache deny confickerworkinggroup

How to update cygwin /etc/passwd to get all latest windoze users

mkpasswd -cl > /etc/passwd

mkgroup --local > /etc/group

Install HP Color Laserjet 2600n driver on Linux

http://wiki.markoschulz.de/index.php/Install_and_setup_HP_Color_LaserJet


apt-get install cupsys cupsys-bsd a2ps psutils gs-esp build-essential


wget -O foo2zjs.tar.gz http://foo2zjs.rkkda.com/foo2zjs.tar.gz


cp foo2zjs.tar.gz /usr/local/src/


cd /usr/local/src/


tar zxf foo2zjs.tar.gz


cd foo2zjs


make


make install


make cups

http://localhost:631/admin

Add the printer

socket address is the IP of the printer

choose the 2600n driver

So easy!

Learning Git

Bunch of good resources

Networking

Using tcpdump to analyse arp who-has requests on internal network

Couldn't really tell you how useful this is, but it's interesting anyway.

Specifically, the article over at everythingsysadmin.com shows how you can use tcpdump to analyse what's happening over a network and potentially suss out any infected machines making too many arp who-has requests..

tcpdump -l -n arp | egrep 'arp who-has' | head -100 | awk '{ print $NF }' |sort | uniq -c | sort -n

Suspect ddos attack? Check the current connections

This command displays current IP connections to the tcp/udp service.

netstat -an | grep 'tcp\|udp' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -n

PAM authentication against an LDAP server

Rather brief notes on getting LDAP auth running on a Debian server

Install dependencies

apt-get install libpam-ldap libnss-ldap

Debconf

LDAP server Uniform Resource Identifier: ldaps://10.179.43.21/
Distinguished name of the search base: dc=badwolf,dc=greenbeedigital,dc=com,dc=au
LDAP version to use: 3
Does the LDAP database require login? No
Special LDAP privileges for root? No
Make the configuration file readable/writeable by its owner only? No
Make local root Database admin. No
Does the LDAP database require login? No
Local crypt to use when changing passwords. crypt

/etc/libnss-ldap.conf modifications

# OpenLDAP SSL mechanism
# start_tls mechanism uses the normal LDAP port, LDAPS typically 636
ssl start_tls
ssl on

/etc/ldap/ldap.conf modifications

BASE dc=badwolf,dc=greenbeedigital,dc=com,dc=au
URI ldaps://10.179.43.21/
TLS_REQCERT never

/etc/nsswitch.conf modifications

passwd: files ldap
group: files ldap

/etc/pam.d/common-auth modifications

auth [success=1 default=ignore] pam_unix.so nullok_secure
auth required pam_ldap.so use_first_pass
auth required pam_permit.so

/etc/pam.d/common-account modifications

account sufficient pam_unix.so
account required pam_ldap.so

/etc/pam.d/common-session modifications

session required pam_unix.so
session required pam_mkhomedir.so skel=/etc/skel/ umask=0022

PHP

php: Strip characters out of a string to just leave numbers

Tags:

$string = "Test test total count 500000 bytes";
$string = preg_replace('#[^0-9]#','',strip_tags($string));
echo $string;

Perl

Convert timestamps in Nagios log

/var/log/nagios2/nagios.log prints computer timestamps that aren't that easy to read.

Run this instead:

perl -pe 's/(\d+)/localtime($1)/e' /var/log/nagios2/nagios.log
Or better yet put it in a script and just run it as 'nagioslog' or something...

Parsing HTML table into Excel spreadsheet

This script fetches a HTML page from the internet and parses the tables it finds in it, into an Excel spreadsheet.
This has been used for grabbing an AWstats index.html summary of sites and their bandwidth, and then parsing to Excel so one can tally up totals.

It's not perfect - I haven't used it in a while but from memory, it overwrites some rows due to being poorly coded :)

When the time comes that I need to use this again, I'll have another look at it.

#!/usr/bin/perl -w
use Spreadsheet::WriteExcel;
use HTML::TableExtract;
use LWP::Simple;
# Create a Table extraction
my $te = new HTML::TableExtract(gridmap=>1);
# Get the table out of my stats page
my $content = get("http://localhost/stats/index.html");
# Pass the data out of the table
$te->parse($content);
# Create a new Excel workbook
my $workbook = Spreadsheet::WriteExcel->new("totals.xls");
# Add a worksheet
my $worksheet = $workbook->add_worksheet();
# For each table, dump its parsed table rows into the worksheet
for my $ts ($te->table(1,0))
{
foreach my $row ($ts->rows)
{
$worksheet->write(0,0, $row, @{$row});
}
}

for my $ts ($te->table(1,1))
{
foreach my $row ($ts->rows)
{
$worksheet->write(1,0, $row, @{$row});
}
}

# Now add up the Total bandwidth
$worksheet->write_formula(2, 3, '=SUM(D1:D2)/1000000' );
#worksheet->write_formula(2, 3, '=SUM(D3/1000000)');

Perl script to check website with regex, alert if failure

Wrote this script today using the WWW::Curl::Easy example.

Background: Recently eaccelerator screwed up a dev server so that while Apache didn't die, php scripts weren't parsing, and I got no alerts.

This script parses a php script using curl, which contains a basic 'hello()' function that prints hello out.

The perl script checks the output for 'hello' and if it doesn't find it, it sends me an e-mail (it also sends me an SMS using our Clickatell account, but I'm not giving you the credentials, silly.)

#!/usr/bin/perl
#
# Checks a php script using curl and if there is a
# problem, e-mail me
#
use strict;
use WWW::Curl::Easy;
my $url = "http://localhost/test.php";
# Init the curl session
my $curl= WWW::Curl::Easy->new() or die "curl init failed!\n";
# Give curl the URL to use
$curl->setopt(CURLOPT_URL, $url);
# a subroutine which is called for each 'chunk' as the
# file is received.
sub body_callback {
my ($chunk,$context)=@_;
# add the chunk we received to the end of the array we've been given
push @{$context}, $chunk;
return length($chunk); # OK
}
# configure which subroutine to call when some data comes in
$curl->setopt(CURLOPT_WRITEFUNCTION, \&body_callback);
my @body;
# tell the subroutine which array to put the data into
$curl->setopt(CURLOPT_FILE, \@body);
if ($curl->perform() != 0) {
print "Failed ::".$curl->errbuf."\n";
};
my $output = join("",@body);
# Check if we got 'hello' out of the php script. If we did, ignore it
if ($output=~ m/hello/i) {
}
else {
#sms me, incidentally this is done with curl too, so I create another instance of WWW::Curl::Easy
my $alert_url = "http://the-clickatell-api-command-to-sms-me";
my $curl_alert= WWW::Curl::Easy->new() or die "curl init failed!\n";
$curl_alert->setopt(CURLOPT_URL, $alert_url);
$curl_alert->setopt(CURLOPT_WRITEFUNCTION, \&body_callback);
$curl_alert->setopt(CURLOPT_FILE, \@body);
if ($curl_alert->perform() != 0) {
print "Failed ::".$curl_alert->errbuf."\n";
};
# send me an e-mail now, using Sendmail
unless(open (MAIL, "|/usr/sbin/sendmail -t")) {
print "error.\n";
warn "Error starting sendmail: $!";
}
else {
print MAIL "From: web\@the-server.com\n";
print MAIL "To: miguel\@example.com\n";
print MAIL "Subject: PHP problem on the server\n\n";
print MAIL "The server has stopped parsing php! Panic! Panic!";
close(MAIL) || warn "Error closing mail: $!";
}
}

Perl script to send e-mail

#!/bin/perl

# Build an array of e-mail addresses to be Bcc'd to
my @bcc_addresses = (
'foo@bar.com',
'foo@bar2.com');

# The main recipient
my $to='foofoo@bar.com';

# Join the bcc addresses from the array, separate with comma
my $bcc = join(", ", @bcc_addresses);

# Where it's coming from
my $from='miguel@foobar.com';

# e-mail Subject
my $subject="Perl mail example";

# The e-mail content follows
my $out = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Nullam vestibulum dictum lectus.
Etiam at sapien. Donec fermentum dictum nisi. In ornare adipiscing massa.";

# Now let's fire up sendmail and push the data into it, then send
open(MAIL, "|/usr/sbin/sendmail -t");
print MAIL "To: $to\n";
print MAIL "Bcc: $bcc\n";
print MAIL "From: $from\n";
print MAIL "Subject: $subject\n";
print MAIL $out;

close(MAIL);

Pro Git

Nice guide to Git

progit.org

Quick and dirty guide to CVS

Tags:

proftpd log time and date mixed up

On my Etch servers, proftpd's logs in /var/log/proftpd/ are all mixed up. Just logging in, transferring a file, logging out, each action or series of actions were often being logged with mixed dates and times, never in order, and always wrong.

Turns out by default proftp is logging times in GMT or somesuch, I guess based on the client's system? To force proftpd to log by the server's local time, add these two lines to /etc/proftpd/proftpd.conf

TimesGMT off
SetEnv TZ :/etc/localtime

/etc/init.d/proftpd restart

Should be all good now.

Virtualisation

VMware

Xen

Notes running Xen under Debian Lenny (dom0 and domU)

Tags:

These are possibly my worst written notes ever below. Beware!

Xen was remarkably easy to install on Lenny. I just installed the latest xen kernel
(2.6.26-2-xen-686) and xen-hypervisor-3.2-1-i386 xen-utils (3.0?), also these bridge-utils
iproute sysfsutils libc6-xen xen-tools

Made sure these were in /etc/xen/xend-config.sxp

(network-script network-bridge)<br>
(vif-script vif-bridge)<br>

/etc/xen-tools/xen-tools.conf was basically this:

dir = /opt/xen #mkdir'd this first
install-method = debootstrap
size = 4Gb # Disk image size.
memory = 128Mb # Memory size
swap = 128Mb # Swap size
# noswap = 1 # Don't use swap at all for the new system.
fs = ext3 # use the EXT3 filesystem for the disk image.
dist = lenny # Default distribution to install.
image = sparse # Specify sparse vs. full disk images.
gateway = 192.168.1.254
netmask = 255.255.255.0
broadcast = 192.168.1.255
passwd = 1
mirror = <a href="http://ftp.us.debian.org/debian/">http://ftp.us.debian.org/debian/
[/geshifilter-code]

After creating the DomU and firing it up, couldn't get a login prompt at the console, and ssh'ing in gave this error when logging in:

stdin is not a tty

From the docs:

"Additional note for domU on lenny using xen-tools
xen-tools don't use hvc0 as the console device in /etc/inittab and don't install udev (leading to /dev/pts missing in domU).
This makes logging in via xm console and via ssh impossible, because getty doesn't have a proper console to attach to and ssh can't attach to a pseudo terminal.
To fix this,
1. add to /etc/xen-tools/xen-tools.cfg:

serial_device = hvc0"

So added that line to /etc/xen-tools/xen-tools.cfg as required.

Also made these changes to /boot/grub/menu.lst on the Dom0 first:

title Xen 3.2-1-i386 / Debian
GNU/Linux, kernel 2.6.26-2-xen-686
root (hd0,0)
kernel /boot/xen-3.2-1-i386.gz
com1=9600,8n1 console=com1,vga
module /boot/vmlinuz-2.6.26-2-xen-686
root=/dev/sda1 ro console=tty0 console=hvc0
module /boot/initrd.img-2.6.26-2-xen-686

Changes being the console and com1 lines.

From the same notes above: reason is 'To get output from grub, XEN, the kernel and getty (login prompt) via both vga and serial console to work'.

Also modified /etc/inittab so it included this

1:2345:respawn:/sbin/getty 38400 hvc0<br>
2:23:respawn:/sbin/getty 38400 tty1<br>

From the same notes above, 'The tty1 will show up at the vga output, and the hvc0 will show up at the serial console.'

Then after a reboot of the Dom0, I could create new DomU like this:

xen-create-image --hostname=HOSTNAME --ip=IP --passwd --role udev<br>

and it would actually install udev properly, no having to fiddle with /etc/fstab with devpts like mentioned here, or having to install udev while mounting the image locally and chrooted etc etc etc. With the grub, inittab and xen-tools changes above, along with the '--role udev' when creating new images, it 'just works' (tm) both via console and ssh.

Other thing I did was quickly install openntpd on the DomU as this removed the 'clock went backwards' errors flooding the logs/console.

(edit : apparently this is how to deal with those 'clock backwards' errors)

Short story: I'm liking Xen a lot! And not just because Dom0 reminds me of this guy