Some software I've found useful in my research, occasionally with personal forks, but no major time investments. When I've put in some more serious work, the appriopriate tag is code.

I spent some time today configuring Postfix so I could send mail from home via SMTPS. Verizon, our ISP, blocks port 25 to external domains, forcing all outgoing mail through their outgoing.verizon.net exchange server. In order to accept mail, they also require you authenticate with your Verizon username and password, so I wanted to use an encrypted connection.

For the purpose of this example, our Verizon username is jdoe, our Verizon password is YOURPASS, you're running a local Postfix server on mail.example.com for your site at example.com, and 12345 is a free local port.

# cat /etc/postfix/main.cf
myhostname = mail.example.com
relayhost = [127.0.0.1]:12345
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/saslpass
sender_canonical_maps = hash:/etc/postfix/sender_canonical
# cat /etc/postfix/saslpass
[127.0.0.1]:12345 jdoe@verizon.net:YOURPASS
    # postmap /etc/postfix/saslpass
    # cat /etc/postfix/sender_canonical
    root@mail.example.com jdoe@example.com
root@example.com jdoe@example.com
root@localhost jdoe@example.com
jdoe@mail.example.com jdoe@example.com
jdoe@localhost jdoe@example.com
    # postmap /etc/postfix/sender_canonical
# cat /etc/stunnel/stunnel.conf
[smtp-tls-wrapper]
accept = 12345
client = yes
connect = outgoing.verizon.net:465
# /etc/init.d/stunnel restart
# postfix reload

Test with:

$ echo 'testing 1 2' | sendmail you@somewhere.com

Here's what's going on:

  • You hand an outgoing message to your local Postfix, which decides to send it via port 12345 on your localhost (127.0.0.1) (relayhost).
  • Stunnel picks up the connection from Postfix, encrypts everything, and forwards the connection to port 465 on outgoing.verizon.net (stunnel.conf).
  • Postfix identifies itself as mail.example.com (myhostname), and authenticates using your Verizon credentials (smtp_sasl_…).
  • Because Verizon is picky about the From addresses it will accept, we use sender_canonical to map addresses to something simple that we've tested.

And that's it :p. If you're curious, there's more detail about all the Postfix config options in the postconf man page, and there's good SASL information in the SASL_README.

There's also a blog post by Tim White which I found useful. Because Verizon lacks STARTTLS support, his approach didn't work for me out of the box.

Posted Tue Nov 15 19:48:33 2011 Tags: tools

Verizon blocks outgoing connections on port 25 (SMTP) unless you are connecting to their outgoing.verizon.net message exchange server. This server requires authentication with your Verzon username/password before it will accept your mail. For the purpose of this example, our Verizon username is jdoe, our Verizon password is YOURPASS, and were sending email from me@example.com to you@target.edu.

$ nc outgoing.verizon.net 25
220 vms173003pub.verizon.net -- Server ESMTP (...)
mail from: <jdoe@example.com>  
550 5.7.1 Authentication Required
quit
221 2.3.0 Bye received. Goodbye.

Because authenticating over an unencrypted connection is a Bad Idea™, I was looking for an encrypted way to send my outgoing email. Unfortunately, Verizon's exchange server does not support STARTTLS for encrypting connections to outgoing.verizon.net:25:

$ nc outgoing.verizon.net 25
220 vms173003pub.verizon.net -- Server ESMTP (...)
ehlo example.com
250-vms173003pub.verizon.net
250-8BITMIME
250-PIPELINING
250-CHUNKING
250-DSN
250-ENHANCEDSTATUSCODES
250-HELP
250-XLOOP E9B7EB199A9B52CF7D936A4DD3199D6F
250-AUTH DIGEST-MD5 PLAIN LOGIN CRAM-MD5
250-AUTH=LOGIN PLAIN
250-ETRN
250-NO-SOLICITING
250 SIZE 20971520
starttls
533 5.7.1 STARTTLS command is not enabled.
quit
221 2.3.0 Bye received. Goodbye.

Verizon recommends pre-STARTTLS approach of wrapping the whole SMTP connection in TLS (SMTPS), which it provides via outgoing.verizon.net:465:

$ python -c 'from base64 import *; print b64encode("\0jdoe@verizon.net\0YOURPASS")'
AGpkb2VAdmVyaXpvbi5uZXQAWU9VUlBBU1M=
$ openssl s_client -connect outgoing.verizon.net:465
...
220 vms173013pub.verizon.net -- Server ESMTP (...)
ehlo example.com
250-vms173013pub.verizon.net
250-8BITMIME
250-PIPELINING
250-CHUNKING
250-DSN
250-ENHANCEDSTATUSCODES
250-HELP
250-XLOOP 9380A5843FE933CF9BD037667F4C950D
250-AUTH DIGEST-MD5 PLAIN LOGIN CRAM-MD5
250-AUTH=LOGIN PLAIN
250-ETRN
250-NO-SOLICITING
250 SIZE 20971520
auth plain AGpkb2VAdmVyaXpvbi5uZXQAWU9VUlBBU1M
235 2.7.0 plain authentication successful.
mail from: <me@example.com>
250 2.5.0 Address Ok.
rcpt to: <you@target.edu>
250 2.1.5 you@target.edu OK.
data
354 Enter mail, end with a single ".".
From: Me <me@example.com>
To: You <you@target.edu>
Subject: testing

hello world 
.
250 2.5.0 Ok, envelope id 4BHMFEZ7PHSETMT6@vms173013.mailsrvcs.net
quit
221 2.3.0 Bye received. Goodbye.
closed

This works, but with the rise of STARTTLS, getting your local Postfix mail server to support SMTPS requires a bit of fancyness with stunnel. The stunnel workaround is not too complicated, but I also wanted to look into the submission protocol (port 587), which adapts SMTP (designed for message transfer) into a similar protocol for message submission. Unfortunately, Verizon does not support STARTTLS here either.

$ nc outgoing.verizon.net 587
220 vms173005.mailsrvcs.net -- Server ESMTP (...)
ehlo example.com
250-vms173005.mailsrvcs.net
250-8BITMIME
250-PIPELINING
250-CHUNKING
250-DSN
250-ENHANCEDSTATUSCODES
250-EXPN
250-HELP
250-XADR
250-XSTA
250-XCIR
250-XGEN
250-XLOOP DA941C5B31BE4B102BB69B809BC66C4A
250-AUTH DIGEST-MD5 PLAIN LOGIN CRAM-MD5
250-AUTH=LOGIN PLAIN
250-NO-SOLICITING
250 SIZE 20971520
starttls
533 5.7.1 STARTTLS command is not enabled.
quit
221 2.3.0 Bye received. Goodbye.

In conclusion, Verizon supports a number of email submission standards, but the only secure approach is to use the outdated SMTPS. See my Postfix post for details on configuring Postfix to use Verizon's server for outgoing mail.

There are a number of good SMTP authentication tutorials out there. I used John Simpson and Erwin Hoffmann's tutorials. For cleaner examples of my testing tools (nc and openssl s_client), see my simple servers post.

Posted Tue Nov 15 19:45:35 2011 Tags: tools

I was recently trying to add bookmarks to a PDF I'd generated with pdftk. It turns out to be fairly simple to add bookmarks to a PDF using Ghostscript, following maggoteer's post to the Ubunto forums. The syntax is:

$ gs -dBATCH -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=out.pdf in-*.pdf pdfmarks

Where out.pdf is the generated PDF, in-*.pdf are the input PDFs, and pdfmarks is a text file with contents like:

[/Title (Title Page) /Page 1 /OUT pdfmark
[/Title (Table of Contents) /Page 3 /OUT pdfmark
...

Nice and easy.

For nested levels, use the /Count attribute. For example:

[/Count 3 /Title (Chapter 1) /Page 1 /OUT pdfmark
[/Count -2 /Title (Section 1.1) /Page 2 /OUT pdfmark
[/Title (Section 1.1.1) /Page 3 /OUT pdfmark
[/Title (Section 1.1.2) /Page 4 /OUT pdfmark
[/Count -1 /Title (Section 1.2) /Page 5 /OUT pdfmark
[/Title (Section 1.2.1) /Page 6 /OUT pdfmark
[/Title (Section 1.3) /Page 7 /OUT pdfmark

The argument to /Count gives the number of immediately subordinate bookmarks. The sign of the argument sets the default display (negative for closed, positive for open).

You can also setup the document info dictionary with something like:

[ /Title (My Test Document)
  /Author (John Doe)
  /Subject (pdfmark 3.0)
  /Keywords (pdfmark, example, test)
  /DOCINFO pdfmark

If you want more detail, take a look at Adobe's pdfmark reference.

I've bundled the whole pdfmarks-generation bit into a script, pdf-merge.py, which generates the pdfmark file and runs Ghostscript automatically. Think of it as a bookmark-preserving version of pdftk's cat. The script uses pdftk internally to extract bookmark information from the source PDFs.

The script also adds a bit of PostScript to ignore any bookmarks in the source PDFs during the Ghostscript run. The only bookmarks in the output will be the ones you specify explicitly in the pdfmarks file. If for some reason the automatically generated pdfmarks are not quite what you want, the script can pause (via --ask) to allow you to tweak the pdfmarks manually before running Ghostscript.

Posted Mon Sep 26 13:57:47 2011 Tags: tools

I've been streamlining my procedure for burning audio CDs, and I like what I've come up with. Unfortunately, I'll never remember it on my own, so here's a note to myself (any whoever else cares) on what to do.

First, build your playlist in MPD, using my one-liner to calculate the playlist duration (my CD-Rs hold 80 minutes). When you've got your playlist arranged to your satisfaction, convert the files to WAVs using:

$ i=1; for x in $(mpc -f '%file%' playlist); do j=$(seq -f '%02g' $i $i); let "i += 1"; flac -d --apply-replaygain-which-is-not-lossless=t -o $j.$(basename ${x/.flac/.wav}) /var/lib/mpd/music/$x; done

This assumes that all the files in your playlist are FLAC files, which is a good idea (disk space is cheap, FLAC is lossless with good open source support). It also assumes you've already stored ReplayGain settings in your FLAC files. If you haven't, you'll get warnings like:

WARNING: can't get track (or even album) ReplayGain tags

If that happens, go back and read about replay gain, add the tags, and try again ;).

See my replay gain post for more details on the --apply-replaygain-which-is-not-lossless option. After the decoding step, you'll have a directory full of WAVs that have been normalized to a standard track-level loudness. The bit about i and j ensures that *.wav will list the tracks in the order in which they appear in your playlist. Then burn the tracks to a CD using cdrecord:

$ cdrecord -v speed=1 dev=/dev/cdrom -eject -dao -audio -pad *.wav

If you don't care about the order, use $(ls *.wav | shuf) instead of *.wav.

That's it! Audio CDs from MPD playlists in two lines.

Posted Fri Sep 23 01:37:18 2011 Tags: tools

The 2.X branch of GnuPG comes with gpg-agent for caching passphrases. The documentation is good, but here are my notes outlining my usual usage.

Add

if [ -f "${HOME}/.gnupg/agent-info" ]; then
  source "${HOME}/.gnupg/agent-info"
fi

Start the agent with

$ GPG_TTY=$(tty)
$ gpg-agent --daemon --write-env-file "${HOME}/.gnupg/agent-info"
$ echo "GPG_TTY='${GPG_TTY}'; export GPG_TTY" >> "${HOME}/.gnupg/agent-info"
$ source "${HOME}/.gnupg/agent-info"

The GPG_TTY bit will spawn the pinentry call in the designated TTY. This avoids troublesome issues like pinentry clobbering Mutt if they are both using ncurses.

I didn't like any of the pinentry programs available on my system, so I wrote my own: pinentry.py. To use my script, save it somewhere on your system and add a line like the following to your ~/.gnupg/gpg-agent.conf.

pinentry-program /path/to/pinentry.py

When you are done with the agent, kill it with

$ killall gpg-agent
$ rm -f "${HOME}/.gnupg/agent-info"
Posted Tue Jun 21 21:09:21 2011 Tags: tools

GnuTLS is the GNU SSL/TLS implementation, because OpenSSL's license is incompatible with the GPL. There are a number of small compatibility issues between the two, so it's best to use the OpenSSL tools to create certs and keys for use by OpenSSL-linked servers and the GnuTLS tools to create certs and keys for use by GnuTLS-linked servers. See X.509 certificates for details on creating self-signed keys with both packages.

Posted Tue Jun 21 08:25:40 2011 Tags: tools

I'm using LDAP (RFC 4510) to maintain a centralized address book at home. Here are my setup notes, mostly following Gentoo's LDAP howto.

Install OpenLDAP with the ldap USE flag enabled:

# emerge -av openldap

If you get complaints about a cyrus-saslopenldap dependency cycle, you should temporarily (or permanently) disable the ldap USE flag for cyrus-sasl:

# echo 'dev-libs/cyrus-sasl -ldap' > /etc/portage/package.use/ldap
# -ldap" emerge -av1 cyrus-sasl
# emerge -av openldap

Generate an administrative password:

$ slappasswd 
New password: 
Re-enter new password: 
{SSHA}EzP6I82DZRnW+ou6lyiXHGxSpSOw2XO4

Configure the slapd LDAP server. Here is a very minimal configuration, read the OpenLDAP Admin Guide for details:

# emacs /etc/openldap/slapd.conf
# cat /etc/openldap/slapd.conf
include         /etc/openldap/schema/core.schema
include         /etc/openldap/schema/cosine.schema
include         /etc/openldap/schema/inetorgperson.schema
pidfile         /var/run/openldap/slapd.pid
argsfile        /var/run/openldap/slapd.args
database        hdb
suffix          "dc=example,dc=com"
checkpoint      32      30
rootdn          "cn=Manager,dc=example,dc=com"
rootpw          {SSHA}EzP6I82DZRnW+ou6lyiXHGxSpSOw2XO4
directory       /var/lib/openldap-data
index   objectClass     eq

Note that inetorgperson is huge, but it's standardized. I think it's better to pick a big standard right off, than to outgrow something smaller and need to migrate.

Gentoo creates the default database directory for you, so you can ignore warnings about needing to create it yourself.

Configure LDAP client access. Again, read the docs for details on adapting this to your particular situation:

# emacs /etc/openldap/ldap.conf
$ cat /etc/openldap/ldap.conf
BASE    dc=example,dc=com
URI     ldap://ldapserver.example.com

You can edit '/etc/conf.d/slapd' if you want command line options passed to slapd when the service starts, but the defaults looked fine to me.

Start slapd:

# /etc/init.d/slapd start

Add it to your default runlevel:

# eselect rc add /etc/init.d/slapd default

Test the server with

$ ldapsearch -x -b '' -s base '(objectclass=*)'

Build a hierarchy in your database (this will depend on your organizational structure):

$ emacs /tmp/people.ldif
$ cat /tmp/people.ldif
version: 1

dn: dc=example, dc=com
objectClass: dcObject
objectClass: organization
o: Example, Inc.
dc: example

dn: ou=people, dc=example,dc=com
objectClass: organizationalUnit
ou: people
description: All people in organisation

dn: cn=Manager, dc=example,dc=com
objectClass: organizationalRole
cn: Manager
description: Directory Manager
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/people.ldif
$ rm /tmp/people.ldif

abook

If you currently keep your addresses in abook, you can export them to LDIF with:

$ abook --convert --infile ~/.abook/addressbook --outformat ldif \
  | abook-ldif-cleanup.py --basedn 'ou=people,dc=example,dc=com' > dump.ldif

where abook-ldif-cleanup.py does some compatibility processing using the python-ldap module.

Add the people to your LDAP database:

$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f dump.ldif

To check if that worked, you can list all the entries in your database:

$ ldapsearch -x -b 'dc=example,dc=com' '(objectclass=*)'

Then remove the temporary files:

$ rm -rf dump.ldif

Aliases

Ok, we've put lots of people into the people OU, but what if we want to assign them to another department? We can use aliases (RFC 4512), the symlinks of the LDAP world. To see how this works, lets create a test OU to play with:

$ emacs /tmp/test.ldif
$ cat /tmp/test.ldif
version: 1
dn: ou=test, dc=example,dc=com
objectClass: organizationalUnit
ou: testing
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/test.ldif
$ rm /tmp/test.ldif

Now assign one of your people to that group:

$ emacs /tmp/alias.ldif
$ cat /tmp/alias.ldif
version: 1
dn: cn=Jane Doe, ou=test,dc=example,dc=com
objectClass: alias
aliasedObjectName: cn=Jane Doe, ou=people,dc=example,dc=com
$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/alias.ldif
$ rm /tmp/alias.ldif

The extensibleObject class allows us to add the DN field, without it you get:

$ ldapadd -D "cn=Manager,dc=example,dc=com" -xW -f /tmp/alias.ldif
Enter LDAP Password: 
adding new entry "cn=Jane Doe, ou=test,dc=example,dc=com"
ldap_add: Object class violation (65)
        additional info: attribute 'cn' not allowed

You can search for all entries (including aliases) with

$ ldapsearch -x -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
...
dn: cn=Jane Doe,ou=test,dc=example,dc=com
objectClass: alias
objectClass: extensibleObject
aliasedObjectName:: Y249TWljaGVsIFZhbGxpw6hyZXMsb3U9cGVvcGxlLGRjPXRyZW1pbHksZGM9dXM=
...

You can control dereferencing with the -a option:

$ ldapsearch -x -a always -b 'ou=test, dc=example,dc=com' '(objectclass=*)'
...
dn: cn=Jane Doe,ou=people,dc=example,dc=com
cn: Jane Doe
sn: Doe
...

Once you've played around, you can remove the test OU and its descendants:

$ ldapdelete -D "cn=Manager,dc=example,dc=com" -xW -r ou=test,dc=example,dc=com

shelldap

There are a number of tools to make it easier to manage LDAP databases. Command line junkies will probably like shelldap:

$ shelldap --server ldapserver.example.com
~ > ls
cn=Manager
ou=people
~ > cat cn=Manager 

dn: cn=Manager,dc=example,dc=com
objectClass: organizationalRole
cn: Manager

~ > cd ou=people 
ou=people,~ > ls

Shelldap's edit command spawns your EDITOR on a temporary file populated by the entry you're editing. You can either alter the entry as you see fit, or try something fancier in LDIF.

Mutt

If you use the Mutt email client (or just want a simple way to query email addresses from the command line) there are a number of scripts available. Pick whichever sounds most appealing to you. I wrote up mutt-ldap.py, which lets you configuration the connection details via a config file (~/.mutt-ldap.rc) rather than editing the script itself. Usage details are available in the docstring.

Apple Address Book

You can configure Apple's Address Book to search an LDAP directory. See Humanizing OS X for details.

SSL/TLS

It took me a bit of work to get SSL/TLS working with my GnuTLS-linked OpenLDAP. First, you'll probably need to generate new SSL/TLS keys (/etc/openldap/ssl/*) with certtool (see X.509 certificates). Then add the following lines to /etc/openldap/slapd.conf:

TLSCipherSuite NORMAL
TLSCACertificateFile /etc/openldap/ssl/ca.crt
TLSCertificateFile /etc/openldap/ssl/ldap.crt
TLSCertificateKeyFile /etc/openldap/ssl/ldap.key
TLSVerifyClient never

Where ca.crt, ldap.crt, and ldap.key are your new CA, certificate, and private key. If you want to disable unencrypted connections completely, remove the ldap:// entry from your slapd command line by editing (on Gentoo) /etc/conf.d/slapd so it has

OPTS="-h 'ldaps:// ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock'"

Now you should be able to restart slapd so it will use the new configuration.

Have clients running on your server use the local socket by editing /etc/openldap/ldap.conf to set:

URI     ldapi://%2fvar%2frun%2fopenldap%2fslapd.sock

Test your server setup by running (on the server)

$ ldapsearch -x -b '' -s base '(objectclass=*)'

Copy your CA over to any client machines (I put it in /etc/openldap/ssl/ldapserver.crt), and set them up with the following two lines in /etc/openldap/ldap.conf:

URI         ldaps://ldapserver.example.com
TLS_CACERT  /etc/openldap/ssl/ldapserver.crt

Test your client setup by running (on the client)

$ ldapsearch -x -b '' -s base '(objectclass=*)'

You can configure shelldap with the following lines in ~/.shelldap.rc:

server: ldaps://ldapserver.example.com
tls: yes
tls_cacert: /etc/openldap/ssl/ldapserver.crt

You can configure mutt-ldap.py with the following lines in ~/.mutt-ldap.rc:

port = 636
ssl = yes

Debian-based systems

I wanted to mirror my home LDAP info on my public Ubuntu server. Here's a quick rundown of the Ubuntu setup. Install OpenLDAP:

$ sudo apt-get install slapd ldap-utils

Don't serve in the clear:

$ cat /etc/default/slapd
...
SLAPD_SERVICES="ldaps:/// ldapi:///"
...

Avoid Unrecognized database type (hdb) by loading the hdb backend module before declaring hdb databases:

$ sudo cat /etc/ldap/slapd.conf
...
moduleload back_hdb
database hdb
...

Convert the old school slapd.conf to the new slapd.d:

$ sudo mv slapd.d{,.bak}
$ sudo mkdir slapd.d
$ sudo slaptest -f slapd.conf -F slapd.d
...
hdb_db_open: database "dc=example,dc=com": db_open(/var/lib/slapd/id2entry.bdb) failed: No such file or directory (2).
...
slap_startup failed (test would succeed using the -u switch)
...
$ sudo chown -R openldap.openldap slapd.d

Don't worry about that db_open error, the conversion to slapd.d will have completed successfully.

Set permissions on the database directory (note that the databases should be under /var/lib/ldap to match Ubuntu's default apparmor config. Otherwise you'll see invalid path: Permission denied errors when slapd tries to initialize the databaes).

$ sudo chown openldap.openldap /var/lib/ldap/
$ sudo chmod 750 /var/lib/ldap/

Configure your clients

$ cat /etc/ldap/ldap.conf
BASE    dc=example,dc=com
URI     ldaps://example.com
TLS_CACERT  /etc/ldap/ssl/ldapserver.crt

Start slapd and add it to your default runlevel:

$ sudo /etc/init.d/slapd start
$ sudo update-rc.d slapd defaults

Finally, import your directory data. Dump the data on your master server:

master$ sudo slapcat -b 'dc=example,dc=com' > database.ldif

Load the data on your slave:

$ sudo /etc/init.d/slapd stop
$ sudo slapadd -l database.ldif
$ sudo /etc/init.d/slapd start

References

There's a good overview of schema and objectclasses by Brian Jones on O'Reilly. If you want to use inetOrgPerson but also include the countryName attribute, ...

Posted Sun Jun 12 15:11:07 2011 Tags: tools

Over the years I've watched Kerberos and related tools from afar, interested in the idea, but not interested enough to figure out the installation, configuration, etc. Well, in an attempt to secure assorted NFS mounts around my home, I decided to take the plunge today and install NFSv4 + Kerberos. Here are my notes for my Gentoo systems, mostly following the Kerberos install guide. I'll use the following settings for my examples:

  • Domain: d.net
  • Kerberos realm: R.EDU
  • Server: server.d.net
  • Client: client.d.net
  • User: jdoe (on both the client and server)

Setup the Kerberos server

Emerge the Kerberos server (app-crypt/mit-krb5) and PAM module:

# USE=-openldap emerge -av pam_krb5

-openldap breaks an OpenLDAP <-> Kerberos dependency loop.

Setup DNS to centralize service location management (krb manual):

# emacs /etc/bind/pri/d.net.zone
# /etc/init.d/named restart

I added the following entries to the $ORIGIN d.net. section of my zone file:

_kerberos TXT   "R.EDU"
kerberos  A     192.168.0.2
krb5      A     192.168.0.2
_kerberos-adm._tcp     SRV  0 0 749 krb5
_kerberos._udp         SRV  0 0 88  krb5
_kerberos-master._udp  SRV  0 0 88  krb5
_kpasswd._udp          SRV  0 0 464 krb5

Configure Kerberos and the KDC (krb manual):

# cp /etc/krb5.conf{.example,}
# emacs /etc/krb5.conf
# cat /etc/krb5.conf
[libdefaults]
        default_realm = R.EDU
        dns_fallback = yes
        kdc_ports = 88

[realms]
        R.EDU = {
                kdc = "server.d.net"  # HACK?
                admin_server = "server.d.net"  # DNS support not yet complete
        }

[domain_realm]
        .d.net = R.EDU
        d.net = R.EDU

[logging]
        kdc = FILE:/var/log/krb5/kdc.log
        admin_server = FILE:/var/log/krb5/kadmind.log
        default = FILE:/var/log/krb5/krblib.log
# cp /var/lib/krb5kdc/kdc.conf{.example,}
# emacs /var/lib/krb5kdc/kdc.conf
# cat /var/lib/krb5kdc/kdc.conf
[realms]
        R.EDU = {
                admin_server = server.d.net  # DNS support not yet complete
                database_name = /var/lib/krb5kdc/principal
                admin_keytab = FILE:/etc/krb5.keytab
                acl_file = /var/lib/krb5kdc/kadm5.acl
                key_stash_file = /var/lib/krb5kdc/.k5.R.EDU
                kdc_ports = 88
                max_life = 10h 0m 0s
                max_renewable_life = 7d 0h 0m 0s
        }

Create the database and stash file (krb manual):

# kdb5_util create -r R.EDU -s

Add administrators to the access control list (krb manual):

# emacs /var/lib/krb5kdc/kadm5.acl
# cat /var/lib/krb5kdc/kadm5.acl
jdoe/admin@R.EDU  x
# kadmin.local
kadmin.local:  add_principal jdoe/admin@R.EDU
WARNING: no policy specified for jdoe/admin@R.EDU; defaulting to no policy
Enter password for principal "jdoe/admin@R.EDU": 
Re-enter password for principal "jdoe/admin@R.EDU": 
Principal "jdoe/admin@R.EDU" created.
kadmin.local:  quit

Start the Kerberos daemons:

# /etc/init.d/mit-krb5kdc start
# /etc/init.d/mit-krb5kadmind start

Add them to your default runlevel with:

# eselect rc add /etc/init.d/mit-krb5kadmin default
# eselect rc add /etc/init.d/mit-krb5kadmind default

Add new principals (krb manual):

$ kadmin -p jdoe/admin
Authenticating as principal jdoe/admin with password.
Password for jdoe/admin@R.EDU: 
kadmin:  list_principals
...
kadmin:  add_principal jdoe
WARNING: no policy specified for jdoe@R.EDU; defaulting to no policy
Enter password for principal "jdoe@R.EDU": 
Re-enter password for principal "jdoe@R.EDU": 
Principal "jdoe@R.EDU" created.
kadmin:  quit

Now you can get your ticket granting ticket (TGT) with

$ kinit

and do all the other standard Kerberos stuff.

Setup the Kerberos client

Not much to do here, just

# emerge -av pam_krb5

and scp /etc/krb5.conf from your Kerberos server onto the client.

Check that everything works by running

$ kinit
Password for jdoe@R.EDU: 
$ klist 
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: jdoe@R.EDU

Valid starting     Expires            Service principal
06/02/11 10:32:30  06/02/11 20:32:30  krbtgt/R.EDU@R.EDU
        renew until 06/03/11 10:32:30

Setup the NFS server

Now we'll setup NFSv4 using Kerberos authentication. There don't seem to be authoritative docs, but there are a number of good tutorials (1, 2, 3, 4).

Emerge nfs-utils with the kerberos USE flag set (homepage). You may also want app-crypt/kstart (homepage) to automatically renew your server and client tickets. Now is also a good time to check your kernel config. I was missing CRYPTO_CTS, which lead to

error writing to downcall channel /proc/net/rpc/auth.rpcsec.context/channel: Invalid argument

If your realm is not your uppercased domain name, you probably also want a version of libnfsidmap >0.21 to avoid the

get_ids: failed to map name 'nfs/<fqdn>@REALM' to uid/gid: Invalid argument

bug (discussion).

Since we'll be running the NFS service, we'll need a nfs/<fqdn>@REALM principal for the service. Because we want that service to start automatically at boot, we neek to keep its key in a keytab file (krb manual).

# kadmin.local -p jdoe/admin
Authenticating as principal jdoe/admin with password.
Password for jdoe/admin@R.EDU: 
kadmin.local:  add_principal -randkey nfs/server.d.net
WARNING: no policy specified for nfs/server.d.net@R.EDU; defaulting to no policy
Principal "dns/server.d.net@R.EDU" created.
kadmin.local:  ktadd nfs/server.d.net
Entry for principal nfs/server.d.net...
...
kadmin.local:  quit

You need use kadmin.local here (instead of kadmin) so the process has premission to create and edit the keytab file.

Read through /etc/idmapd.conf to see if you need to make any changes for your setup. I set Domain = d.net and Local-Realms = R.EDU. You probably also want to look through /etc/conf.d/nfs. I added -vvv to OPTS_RPC_GSSD, OPTS_RPC_IDMAPD, and OPTS_RPC_SVCGSSD to aid in debugging.

Setup your export filesystem. NFSv4 wants all its exports to live under a single root, so do something like:

# mkdir /export
# mkdir /export/home
# mount --bind /home /export/home

And then setup /etc/exports:

# cat /etc/exports
/export  *(rw,fsid=0,insecure,sec=krb5p,root_squash,no_subtree_check,crossmnt)
/export/a/ *(rw,insecure,sec=krb5p,root_squash,no_subtree_check)

Note that the syntax has changed somewhat, and there seem to have been a few versions of the NFSv4 syntax. exports(5) should contain good documentation for whatever version of nfs-utils you have installed on your system.

If you used mount --bind to populate /export, make sure you add appropriate entries to /etc/fstab so the mounts come up when you reboot.

# cat /etc/fstab
...
/home /export/home none rw,bind 0 0

Start the NFS server:

# /etc/init.d/nfs start

Add it to your default runlevel with:

# eselect rc add /etc/init.d/nfs default

Setup the NFS client

In order to use private (sec=krb5p) mounts, you'll need to enable RPCSEC_GSS_KRB5. Without it, you'll get error messages such as

gss_create: Pseudoflavor 390005 not found!

You'll also need nfs-utils here

# USE="kerberos" emerge -av nfs-utils

You'll need a client principal for secured mounts, so head back over to the server and run

server.d.net# kadmin.local
kadmin.local:  add_principal -randkey nfs/client.d.net
kadmin.local:  ktadd -k /tmp/krb5.keytab nfs/client.d.net
Entry for principal nfs/client.d.net ...
...
kadmin.local:  quit

Then scp the new keyfile over to /etc/krb5.keytab on the client and remove the temporary version from the host. You can list the keys in a keytab with klist -e -k /path/to/keytab if you find a keytab lying around but forget what's inside it.

On the client, you'll need gssd and idmapd running (both part of nfs-utils).

# /etc/init.d/rpc.gssd start
# /etc/init.d/rpc.idmapd start

There's no need to add these to your default runlevel, since they should be started automatically if you have NFSv4 entries in your /etc/fstab (I have no idea how that works).

Now test your mount:

$ sudo mkdir /tmp/mnt
$ sudo mount -v -t nfs4 -o sec=krb5p server:/ /tmp/mnt
mount.nfs4: timeout set for Thu Jun  2 10:44:46 2011
mount.nfs4: trying text-based options '...'
server:/ on /tmp/mnt type nfs4 (rw,sec=krb5p)
$ ls /tmp/mnt 
ls: cannot access /tmp/mnt: Permission denied
$ klist 
klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_1000)
$ kinit 
Password for jdoe@R.EDU: 
$ ls /tmp/mnt/
home

Note that if you kestroy your key, you can still access the files:

$ kdestroy 
$ klist 
klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_1000)
$ ls /tmp/mnt/
home

This is because your credentials have been cached in the client's kernel. On AIX there seems to be an nfsauthreset command to manually flush cached GSSAPI information. Linux support is waiting on a new key ring implementation.

Other stuff

If you hadn't had the kerberos USE flag set before, you should consider adding it to your /etc/make.conf and running

$ sudo emerge -av --deep --newuse --update @world

to get Kerberized versions of any packages you have installed (e.g. cups, curl, cvs, emacs, openssh, most SASL libraries, ...).

For details on using Kerberos with SSH, check out the excellent description in the SSH definative guide. The key elements are host/<fqdn>@REALM principals for each host (with keyfiles on each server) and appropriate enabling of the GSSAPI* options in sshd_config and ssh_config.

There's also suite of Kerberos-aware utilities in app-crypt/mit-krb5-appl (krcp, krlogin, krsh, ktelnet, and kftp). I don't use the non-Kerberized versions, so I haven't tried any of these.

If you're using MPD on an NFS-mounted music repository, you might be interested in my kinit-mpd.sh script for granting the mpd user access to the NFS-mounted music as the nobody principal.

Posted Thu Jun 2 11:52:53 2011 Tags: tools

OpenSSH stores lists of SSH public keys in known_hosts files, so it can verify that the host you're logging into is the host you expect and not a man-in-the-middle attacker. To reduce the risk of island-hopping attacks, OpenSSH has a HashKnownHosts yes option to store HMAC-SHA1 encrypted versions of host names and IPs in your known_hosts files rather than the clear text. This makes it harder for an attacker to use the information stored in your known_hosts. However, it also makes it harder for you to use that information.

I was digging through my known_hosts file yesterday compiling a list of servers where I have login accounts. I keep better track of these things recently (using GPG to symmetrically encrypt the list), but my known_hosts file predates my quality-accounting phase. Anyhow, I wrote up some simple tools to make reverse-engineering a known_hosts file a bit less painful.

You can use your monkeysphere keyring to see if you recognize any of the public keys. This avoids having to deal with the hashed names at all, but assumes none of your servers are sharing keys. unhash-known-hosts.sh automates this:

$ unhash-known-hosts.sh path/to/known_hosts
GnuPG ID 01234567 (ssh://server.example.net) matches |1|Bvjsg3lqJJ/M9rTYz1HfY+T/RoM=|DhZlGg3GFMWtVcjz4LNfJ8afi7w=
did not match |1|vug6FlX6GCaIIzkv3wS3zftQyyw=|PdMYEIaWTzHCv/4ZhNiR2DD6E0A=
...

Once you've got the low-hanging fruit out of the way, you can get a list of the high-hanging fruit:

$ unhash-known-hosts.sh path/to/known_hosts | sed -n 's/^did not match //p' > unknown_hosts

Start guessing with crack known hosts.py! IPs are usually a good starting point, because any host in your known_hosts file must have an entry for its IP.

$ crack_known_hosts.py --known-hosts unknown_hosts --ip 192.168.*.*

You can also run a full scan of alphanumeric entries up to a specified length (this gets slow quickly, which is, after all, why you hashed the entries in the fiest place).

$ crack_known_hosts.py --known-hosts unknown_hosts --alphanum 16

Removing entries from unknown_hosts as you crack them will make future crack_known_hosts.py attempts on that file faster.

Once you've cracked one name, you can use unique known hosts.py to find other entries that share the same key.

$ unique_known_hosts.py path/to/known_hosts

And there you have it. Happy cracking! ;).

Posted Wed Jun 1 08:09:11 2011 Tags: tools
Tor

Tor is an anonomizing proxy, see their list of usage scenarios if you want more details. A basic Tor client is extremely easy to set up on Gentoo, just emerge net-misc/tor, possibly tweak the stock /etc/tor/torrc, and fire up the connection with

$ sudo /etc/init/tor start

which (by default) opens a SOCKS

To use your new Tor "proxy" with Firefox, go to Edit->Preferences->Advanced->Network->Connection->Settings->Manual proxy configuration and set

SOCKS host: localhost
Port: 9050
Version: SOCKSv5

To use remote DNS, enter about:config in the address bar, set the filter to dns, and set network.proxy.socks_remote_dns to true by double clicking.

To disable cookies, which would allow the sites you're connecting to to track you as your exit IP changes, go to Edit->Preferences->Privacy, select Firefox will: Use custom settings for history, and make sure Accept cookies from sites is unchecked.

To check that everything is properly configured, you can browse over to Tor's check utility.

Posted Tue May 24 07:01:17 2011 Tags: tools