CoSign
HOWTO: I found it difficult to install CoSign 2.0.2a because the documentation contains inaccuracies, and because it can be hard to debug configuration errors. Here's a working test configuration:
CoSign is composed of 3 main parts:
- filter: this is mod_cosign, allowing Apache to automatically check the user's CoSign cookie
- cgi: cosign.cgi and logout.cgi, for initial login and full logout
- cosignd: the daemon that centralises authentication sessions, can be called from different computers where filter and cgi are installed
Compilation and Installation
I'm using Apache2 here. Let's try to use FHS-compliant paths.
./configure --enable-apache2=/usr/bin/apxs2 \ --prefix=/var/lib/cosign \ --sbindir=/usr/local/sbin \ --mandir=/usr/local/share/man \ --with-filterdb=/var/lib/cosign/filter \ --with-cosigndb=/var/lib/cosign/daemon \ --with-cosignconf=/etc/cosign.conf \ --with-cosigncadir=/etc/cosign/certs/CA \ --with-cosigncert=/etc/cosign/certs/cert.pem \ --with-cosignkey=/etc/cosign/certs/key.pem
TODO: use /usr/lib and /usr/share appropriately, also allowing using the standard --prefix=/usr/local. Use FHS-compliant /usr/lib/cosign/cgi-ssl
make everything \ && sudo make install-all \ && sudo invoke-rc.d apache2 stop && sleep 1 && sudo invoke-rc.d apache2 start
# mod_cosign dir mkdir -p -m 750 /var/lib/cosign/filter chown www-data: /var/lib/cosign/filter # cosignd dir mkdir -p -m 750 /var/lib/cosign/daemon useradd cosign chown cosign /var/lib/cosign/daemon
Apache2 configuration
/etc/apache2/sites-available/default:
# Automatically added in /etc/apache2/httpd.conf by 'make install-all' LoadModule cosign_module /usr/lib/apache2/modules/mod_cosign.so NameVirtualHost *:80 NameVirtualHost *:443 # TLS VirtualHost 'cause CoSign requires https login <VirtualHost *:443> SSLEngine On SSLCertificateFile /etc/apache2/ssl/cert.pem SSLCertificateKeyFile /etc/apache2/ssl/key.pem Alias /cosign/ "/var/lib/cosign/html/" ScriptAlias /cosign-bin/ "/var/lib/cosign/cgi-ssl/" Include sites-available/common.inc </VirtualHost> <VirtualHost *:80> Include sites-available/common.inc </VirtualHost>
/etc/apache2/sites-available/common.inc:
CosignProtected Off CosignHostname localhost # Don't redirect to https if we come from http CosignHttpOnly On CosignRedirect https://localhost/cosign-bin/cosign.cgi CosignPostErrorRedirect https://localhost/cosign/post_error.html CosignService simpleservice CosignCrypto /usr/local/cosign/certs/mod_cosign.key /usr/local/cosign/certs/mod_cosign.crt /usr/local/cosign/certs/CA Alias /cosign/ "/var/lib/cosign/html/" ScriptAlias /cosign-bin/ "/var/lib/cosign/cgi-ssl/" Alias /services/ "/usr/local/cosign/services/" <Directory "/usr/local/cosign/services"> CosignProtected On </Directory>
CoSign configuration
Pitfalls:
- The
cgi
doesn't take more than 1 parameter - only service does. set cosignhost
is used by cgi, not cosignd. It specifies the host where cosignd runs, and is not related to replication.- Afaics 'factor' lines will always be ran and returns the name of the validated factor. This among others allows to filter which users can access to which service.
- TODO: fix documentation
- TODO: explain what
service
is for
/etc/cosign.conf:
# Allow CGI access from this host cgi localhost set cosignhost localhost set cosigncadir /etc/cosign/certs/CA/ set cosigncert /etc/cosign/certs/cgi.crt set cosignkey /etc/cosign/certs/cgi.key # Grant the given session type if credentials match the given factor cookie cosign-simpleservice reauth FACTOR-LDAP # Argument 3 and later are name of <FORM> fields from the template factor /usr/share/cosign/factor/ldap login password set cosignlogouturl http://localhost/ set cosignloopurl https://localhost/cosign/looping.html
Certificates generation
We'll generate our own Certificate Authority.
TODO: during tests it's frequent to remove and rebuild everything. Typing password is really inconvenient in this case, and -passin
doesn't work for 'ca'. Please find a way to make this unattented!
# Base OpenSSL install mkdir -p -m 755 /etc/cosign/certs/CA cd mkdir -p /etc/cosign/certs umask 0027 mkdir -m 700 demoCA pushd demoCA mkdir -m 755 newcerts mkdir -m 700 private echo "01" > serial touch index.txt popd # Root CA # subj model from `openssl x509 -noout -text -in machin.cert` openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO/CN=Root CA/" \ -x509 -days 365 -keyout demoCA/private/cakey.pem -out demoCA/cacert.pem chmod a+r demoCA/cacert.pem # Certificate request and private key # cosignd openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cosignd/CN=localhost/" \ -nodes -keyout "cosignd.key" -out "cosignd.csr" # Sign certificate openssl ca -in "cosignd.csr" -out "cosignd.crt" # CGI openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cgi/CN=localhost/" \ -nodes -keyout "cgi.key" -out "cgi.csr" # Sign certificate openssl ca -in "cgi.csr" -out "cgi.crt" chgrp www-data cgi.key cgi.crt # mod_cosign openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO mod_cosign/CN=localhost/" \ -nodes -keyout "mod_cosign.key" -out "mod_cosign.csr" # Sign certificate openssl ca -in "mod_cosign.csr" -out "mod_cosign.crt" # Allowed certs path mkdir -m 755 CA ln demoCA/cacert.pem CA/ c_rehash CA/ # Tests - cf. http://www.umich.edu/~umweb/software/cosign/faq.html openssl verify -CApath CA/ -purpose sslclient cgi.crt mod_cosign.crt openssl verify -CApath CA/ -purpose sslserver cosignd.crt openssl s_client -connect localhost:6663 -cert cgi.crt -key cgi.key -CApath CA/ \ -showcerts -state -debug -crlf -starttls smtp # DO CHECK THAT YOU GET "Verify return code: 0 (ok)"!!! # (s_client won't stop on error)
cosignd server start
TODO: use/adapt scripts/startup/cosignd
# Launch server with different keys just in case: cd /var/lib/cosign/certs/ /usr/local/sbin/cosignd -y cosignd.crt -z cosignd.key -x /usr/local/cosign/certs/CA/ # CA path must be a full path, because cosignd chdir to /var/lib/cosign/daemon
Factor
Here's a simple LDAP factor (/usr/share/cosign/factor/ldap
):
#!/usr/bin/perl # Basic LDAP "factor" for CoSign # Copyright (C) 2007 Cliss XXI # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Author: Sylvain Beucler <beuc@beuc.net> use strict; # Base configuration my $base = "ou=users,dc=cliss21,dc=com"; my $host = "ldap://192.168.1.149"; my $user_filter = "(objectClass=posixAccount)"; # ----- # If authentication is successful, the external authenticator writes # the factor name on stdout (file descriptor 1) and exits with a value # of 0. If an error occurs, the external authenticator writes an # error message on stdout and exits with a value of 1. All other exit # values are reserved for future use. # -- cosign.conf(5) use Net::LDAP; # Grab CoSign parameters from standard input my $login = <STDIN>; chomp $login; my $pass = <STDIN>; chomp $pass; # Get the login's DN my $binddn = ""; my $bindpw = ""; my $filter = "&$user_filter(uid=$login)"; my $attrs = []; my $ldap = Net::LDAP->new($host); if (!defined($ldap)) { # Be sure to catch the error manually and not use die, otherwise # the return code will be different than 1, making CoSign unhappy, # and the error will be print on stderr, so will be missing in the # web interface print "Cannot connect to LDAP server: $host\n"; exit 1; } $ldap->bind("$binddn", password => "$bindpw", version => 3); my $mesg = $ldap->search(base => $base, filter => $filter, attrs => $attrs); my $count = $mesg->count; if ($count == 0) { print "Unknown user: $login\n"; exit 1; } my $entry = $mesg->entry(); my $dn = $entry->dn(); # Try to login with the DN $binddn = $dn; $bindpw = $pass; my $result = $ldap->bind("$binddn", password => "$bindpw", version => 3); if ($result->code()) { print "Login failed (LDAP error: " . $result->error . ")\n"; exit 1; } $ldap->unbind(); print "FACTOR-LDAP\n"; # factor name exit 0; # success
Test
Now hit https://localhost/services/
Beyond
Check what monsterd is :)