Category Archive for Sysadmin

Tab completion for Capistrano tasks in bash

May 03, 2007

I love tab completion for rake tasks, and tonight I got annoyed that I didn’t have the same thing for Capistrano tasks. Here’s what I came up with:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env ruby

# Save this somewhere, chmod 755 it, then add
#   complete -C path/to/this/script -o default cap
# to your ~/.bashrc or ~/.bash_login
#
# If you update your tasks, just $ rm ~/.captabs*
#
# Originally adapted from 
# http://onrails.org/articles/2006/08/30/namespaces-and-rake-command-completion

exit 0 unless File.file?(File.join(Dir.pwd, 'config', 'deploy.rb'))
exit 0 unless /^cap\b/ =~ ENV["COMP_LINE"]

def cap_tasks
  if File.exists?(dotcache = File.join(File.expand_path('~'), ".captabs-#{Dir.pwd.hash}"))
    File.read(dotcache)
  else
    tasks = `cap show_tasks 2>/dev/null|tail +3|cut -c 1-30|grep "[a-z]"|grep -v "^after_"|sort`
    File.open(dotcache, 'w') { |f| f.puts tasks }
    tasks
  end
end

after_match = $'
task_match = (after_match.empty? || after_match =~ /\s$/) ? nil : after_match.split.last
tasks = cap_tasks.split("\n").map { |t| t.gsub(/\s/, '') }
tasks = tasks.select { |t| /^#{Regexp.escape task_match}/ =~ t } if task_match

puts tasks
exit 0

Enjoy.

Update: I switched to Capistrano 2.0. Here’s the new tab completion code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/env ruby

# Save this somewhere, chmod 755 it, then add
#   complete -C path/to/this/script -o default cap
# to your ~/.bashrc
#
# If you update your tasks, just $ rm ~/.captabs*
#
# Adapted from 
# http://onrails.org/articles/2006/08/30/namespaces-and-rake-command-completion

exit 0 unless File.file?(File.join(Dir.pwd, 'config', 'deploy.rb'))
exit 0 unless /^cap\b/ =~ ENV["COMP_LINE"]

def cap_tasks
  if File.exists?(dotcache = File.join(File.expand_path('~'), ".captabs-#{Dir.pwd.hash}"))
    File.read(dotcache)
  else
    tasks = `cap -T|grep "^cap"|cut -d " " -f 2`
    File.open(dotcache, 'w') { |f| f.puts tasks }
    tasks
  end
end

after_match = $'
task_match = (after_match.empty? || after_match =~ /\s$/) ? nil : after_match.split.last
tasks = cap_tasks.split("\n").map { |t| t.gsub(/\s/, '') }
tasks = tasks.select { |t| /^#{Regexp.escape task_match}/ =~ t } if task_match

# handle namespaces
if task_match =~ /^([-\w:]+:)/
  upto_last_colon = $1
  after_match = $'
  tasks = tasks.map { |t| (t =~ /^#{Regexp.escape upto_last_colon}([-\w:]+)$/) ? "#{$1}" : t }
end

puts tasks
exit 0

Eviscerating Gmail spam with greylisting, SPF and blacklists

April 23, 2007

For a while now, I’ve been wanting to try greylisting my email to reduce spam. Not that Gmail is bad at handling spam, of course, but as a geek I’m always looking for incremental improvements.

When I recently switched email addresses, I finally took the time to implement greylisting along with remote blacklists and Sender Policy Framework checks with Postfix. So far, I’m thrilled with the results. Here’s my new configuration:

  • My server is the primary MX record for brynary.com.
  • My server immediately rejects messages from blacklisted servers or that fail SPF checks.
  • If the sending server is not yet on my greylisting whitelist, my server greylists the message for a few minutes.
  • Messages that pass the greylisting step get forwarded to Gmail for content filtering.
  • If the message made it this far, and it passes Gmail’s content filtering, it lands in my inbox.

Fortunately, most of the information I needed to set this up was in this Filtering spam with Postfix article. Here are my notes on the process which might save you some time:

  • I left out the SASL authentication lines, since they don’t seem to be relevant to my configuration.
  • After setting up the check_helo_access directive, be sure to remember that you need to run postmap /etc/postfix/helo_access
  • relays.ordb.org is no longer providing RBL services, so leave that line out to avoid errors.
  • I had to do some extra Googling to find good instructions for setting up the Perl-based Postfix SPF implementation.
  • Running the manual tests at the bottom of that SPF article did not work as described, but, according to my logs, my SPF implementation is working properly.
  • Setting up postgrey on debian is not hard, but this article helped.

For reference, my resulting /etc/postfix/main.cf is:

# See /usr/share/postfix/main.cf.dist for a commented, more complete version

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

# Uncomment the next line to generate "delayed mail" warnings
# delay_warning_time = 4h

# alias_maps = hash:/etc/aliases
# alias_database = hash:/etc/aliases
# myorigin = /etc/mailname

myhostname = mail.brynary.com
virtual_alias_domains = brynary.com
virtual_alias_maps = hash:/etc/postfix/virtual

relayhost = 

mydestination = stewie, localhost.localdomain, localhost
mynetworks = 127.0.0.0/8
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = all

smtpd_delay_reject = yes
smtpd_helo_required = yes
smtpd_helo_restrictions = 
   permit_mynetworks,
   check_helo_access hash:/etc/postfix/helo_access,
   reject_non_fqdn_hostname,
   reject_invalid_hostname,
   permit

smtpd_sender_restrictions =
   permit_mynetworks,
   permit_sasl_authenticated,
   reject_non_fqdn_sender,
   reject_unknown_sender_domain,
   permit

smtpd_recipient_restrictions = 
   reject_unauth_pipelining,
   reject_non_fqdn_recipient,
   reject_unknown_recipient_domain,
   permit_mynetworks,
   permit_sasl_authenticated,
   reject_unauth_destination,
   reject_rbl_client list.dsbl.org,
   reject_rbl_client sbl-xbl.spamhaus.org,
   check_policy_service unix:private/policy,
   check_policy_service inet:127.0.0.1:60000,
   permit

/etc/postfix/master.cf is now:

# 
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       -       -       -       smtpd
#submission inet n      -       -       -       -       smtpd
#       -o smtpd_etrn_restrictions=reject
#628      inet  n       -       -       -       -       qmqpd
pickup    fifo  n       -       -       60      1       pickup
cleanup   unix  n       -       -       -       0       cleanup
qmgr      fifo  n       -       -       300     1       qmgr
#qmgr     fifo  n       -       -       300     1       oqmgr
rewrite   unix  -       -       -       -       -       trivial-rewrite
bounce    unix  -       -       -       -       0       bounce
defer     unix  -       -       -       -       0       bounce
trace     unix  -       -       -       -       0       bounce
verify    unix  -       -       -       -       1       verify
flush     unix  n       -       -       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
smtp      unix  -       -       -       -       -       smtp
relay     unix  -       -       -       -       -       smtp
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq     unix  n       -       -       -       -       showq
error     unix  -       -       -       -       -       error
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       n       -       -       lmtp
anvil     unix  -       -       n       -       1       anvil
#
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
#
# maildrop. See the Postfix MAILDROP_README file for details.
#
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -d -t$nexthop -f$sender $recipient
scalemail-backend unix  -       n       n       -       2       pipe
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}

# only used by postfix-tls
#tlsmgr   fifo  -       -       n       300     1       tlsmgr
#smtps    inet  n       -       n       -       -       smtpd -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes
#587      inet  n       -       n       -       -       smtpd -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes

policy    unix  -       n       n       -       -       spawn
          user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl

tlsmgr    unix  -       -       -       1000?   1       tlsmgr
scache    unix  -       -       -       -       1       scache
discard   unix  -       -       -       -       -       discard

And my /etc/default/postgrey file:

# postgrey startup options, created for Debian
# (c)2004 Adrian von Bidder 
# Distribute and/or modify at will.

# you may want to set
#   --delay=N   how long to greylist, seconds (default: 300)
#   --max-age=N delete old entries after N days (default: 30)
# see also the postgrey(8) manpage

POSTGREY_OPTS="--inet=127.0.0.1:60000 --delay=180 --auto-whitelist-clients=1"