Tuesday, 5 October 2021

Properly test a Rails app with shibboleth auth

A Rails app with Shibboleth is not as easy to test as an app with say LDAP authentication. 

Functional tests work, but at a cost: the ENV name changes. In production (mod_passenger) the surname might be 'sn'. In test it is 'HTTP_SN'.

Sometime people write some env-driven if blocks to stub a user's login in development. Each time you need to change user's data you need to fix the controller.

The solution is:

  • outfactor the data fetching from ENV is a custom class;
  • use system test to mock the class above.

Some source code on github shows you the details.

Check the system test:

test/system/login_users_test.rb
The translation class is in:
app/lib

Monday, 3 May 2021

Rails6 shibboleth single logout through notify

This is a proof of concept of a Ruby on Rails application with Shibboleth authentication, pure rails session and a working single sign off.

This application follows: SLOWebappAdaptation .

Short things first: it is not worth. There are easier ways to have Ruby on Rails and SAML2 auth to go hand in hand, with single logout included.

The hard part is not the Shibboleth one: you just need to add a Notify

The matter is this setup is completely against a few Rails' conventions:

  • SOAP support has been dropped from Rails since version 1.2;
  • you are not expect to tweak with arbitrary session objects which in turn means:
    • you can't use the trusted cookie session store but you have to switch to the harder database-backed session store, which by the way you need to customize a bit to hold the Shib-Session-ID data;
    • you need to fight against encrypted sessions, the default.

If nevertheless interested, have a look at source code on github.

Sunday, 11 April 2021

Shibboleth-Idp4 impersonation: fetch the impersonating user as an attribute.

In the Shibboleth IdP it's possible to enable the impersonation intercept to enable help desk to impersonate users.

The impersonation is an amazing feature very well designed.

You can enable it in three steps:

  • enable the module;
  • add a p:postAuthenticationFlows="#{ {'impersonate'} }" attr to the SAML2 property to the relying party list involved (relying-party.xml);
  • configure it on conf/access-control.xml (legacy configuration file in conf/intercept is useless).

The documentation proposes a workflow where a help desk operator has to be authorized to impersonate johndoe user by adding the johndoe value to a certain operator's attribute.

If your requirements allow any help desk operator to impersonate any user, the configuration can be simply:

The impersonation event is logged on the IdP. But if you want to let the SP to know about it, you can leverage the "populate the impersonated principal name into the attached SubjectContext" phase of the module. What you need is a acript that picks the added Subject and turns it to an attribute:

Add it to attribute-resolver.xml:

Of course the attribute format can be embedded in the declaration or be pulled in from a property file in conf/attributes/custom:

Monday, 23 September 2019

Italian health insurance card: read some public data.

Since a few years, Italian health insurance card works as a contactless card as well as a smart card.

In both cases informations are presented in a filesystem-like structure similar to Calypso cards.

Apart certificates and keys, on card there are some cardholder's public data that can be handy to read.

I was successful with a ACR122U NFC reader and a ACS ACR38 smart card reader. Both devices access the same data structure where you can find card ID number, cardholder's name, surname, taxpayer number.

It wasn't easy for me to find the details needed to make sense of data, so I decided to write them in a tessera_sanitaria GitHub repository.

Wednesday, 4 February 2015

Groovy as scripting language in Shibboleth-IdPv3

The default language to write ScriptedAttributeDefinition is EcmaScript, but it's easy to switch to groovy.

First of all download latest groovy, unzip it, locate the groovy-x.x.x.jar and groovy-jsr223-x.x.x.jar and copy the in the webapp/WEB-INF/lib/ dir of the unpacked shibboleth-idp source. Install again shibboleth-idp.

As proof of concept, just rewrite the "email" attribute definition which is, in the distribution attribute-resolver.xml, a template attribute.

Modify attribute-resolver.xml to include:

The included script file is:

Copy it in the $IDP_HOME/script/ directory

In order to test it, just add: <logger name="org.example.idp.scripted.groovy" level="DEBUG"/> to logback.xml, be sure to reload both attribute-resolver (./bin/reload-service.sh -id shibboleth.AttributeResolverService) and logger (./bin/reload-service.sh -id shibboleth.LoggingService), and run a aacli call.

DEBUG [org.example.idp.scripted.groovy.email:13] - value: [a_user@example.org]

Tuesday, 27 January 2015

A pure ruby Attribute Authority client

Even if you can query IdP Attribute Authority with resolvertest, and you can test the tiny details with a curl call, a ruby attribute authority client is handy should you desire, for instance, to bulk load the IdP with many parallel requests.

Moreover, the curl approach is very slow because you need to modify the request xml file to put a timely IssueInstant and a unique RequestID.

You can browse the source at the github project page

Tuesday, 9 September 2014

SASL/DIGEST-MD5 with OpenLDAP-2.4.39

In order to authenticate users with Digest-MD5 the first choice to do is either store passwords in a file (sasldb2) on ldap server or into directory. The olcSaslAuxprops attribute in cn=config defines the path to follow.

Passwords in salsdb2

Start with:
sudo apt-get install sasl2-bin
than create a user:
sudo saslpasswd2 -c francesco
(script prompts for password and confirm password)
sudo chown openldap /etc/sasldb2
Modify cn=config with the following:
dn: cn=config
changetype: modify
replace: olcSaslAuxprops
olcSaslAuxprops: sasldb
create a map for the user:
dn: cn=config
changetype: modify
delete: olcAuthzRegexp
olcAuthzRegexp: "uid=francesco,cn=digest-md5,cn=auth" "cn=joe,dc=example,dc=org"
The entry cn=joe,dc=example,dc=org can be created later, or not created at all, if you simply need a principal to fulfill a ACL rule.
Restart OpenLDAP in order to apply olcAuthzRegexp (required by mine experience on Debian, OpenLDAP-2.4.39).
Test with:
ldapwhoami -U francesco -H ldapi:/// -Y DIGEST-MD5
SASL/DIGEST-MD5 authentication started
Please enter your password: 
SASL username: francesco
SASL SSF: 128
SASL data security layer installed.
dn:cn=joe,dc=example,dc=org
Type the password choosed in the saslpasswd2 step. If by chance you are logged is as 'francesco', the -U francesco switch can be omitted.

Passwords in directory

Create the joe user:
dn: cn=joe,dc=unimore,dc=it
objectClass: inetOrgPerson
cn: joe
sn: user
uid: joe
description: Just plain Joe
userPassword: joesecret
Rollback the olcSaslAuxprops if you changed it (if not, this step is not necessary as it is the default):
dn: cn=config
changetype: modify
replace: olcSaslAuxprops
olcSaslAuxprops: slapd
Add the olcAuthzRegexp rules to map the MD5-DIGEST username to a directory entry, and you are ready:
ldapwhoami -U francesco -H ldapi:/// -Y DIGEST-MD5
then type the joe's password (joesecret in this example).
  • "client response doesn't match what we generated (tried bogus)" means you typed the wrong password.
  • "generic failure: unable to canonify user and get auxprops" could be either userPassword is not CLEARTEXT or userPassword is not readable by joe user (because of ACLs).
You are not going to do much with MD5-DIGEST without reading: OpenLDAP docs about Mapping Authentication Identities

Monday, 8 September 2014

DIGEST-MD5 what is good for?

DIGEST-MD5 sasl auth mech require OpenLDAP to store clear text passwords in the directory.

So you could wonder if it is worth implementing it.

DIGEST-MD5 protects passwords from sniffing even if an attacker could dump the whole network traffic. On the other hand, if network traffic is not ciphered a sniffer could eavesdrop the directory data.

The use case can't be about sensible data because if you plan to store in directory data which is supposed to be read only by authorized people, you are going anyhow to implement SSL or StartTLS. So you have no need to protect passwords on the wire as the traffic on the wire is encrypted.

Odd are good that if you have public data you are not asking authentication (anonymous access is enough). So DIGEST-MD5 is useless either.

What about a directory holding public data (work telephone number, for instance), with anonymous read access but authenticated update by selected people only?

I think this is the great use case for DIGEST-MD5: a directory that can be read by anonymous readers, without SSL nor StartTLS. Authorized users can update directory with plain text data but still userpassword is safe from sniffing.

Friday, 24 January 2014

Ruby spec a ffi-rzmq project

Intro

When working with async messaging, tests are more difficult. ffi-rzmq library is special because messages are received in a weird way:
receiver.recv_string(buffer = "")
The message payload is in the buffer variable, which is passed as a parameter. To achieve testing I had to turn buffer to a instance variable (@buffer) and I resorted to the mock results Arbitrary Handling
    # e is a Emitter instance, my fictionary zeromq-based class
    @endpoint.should_receive(:recv_string) do
      e.instance_variable_set("@buffer", my_data)
    end
    @endpoint.should_receive(:recv_string) do 
      e.instance_variable_set("@buffer", "__END_OF_DATA__")
    end
@endpoint is a mocked socket: when it receives the recv_string method, it replaces @buffer instance variable on e. Please have a look at the example code.

Tuesday, 7 January 2014

Protect your Hawt.IO ActiveMQ-5.9.0 console with LDAP

Starting from version 5.9.0, activemq is shipped with the hawt.io web console.

Out-of-the-box the credentials to access are admin:admin, istructions are at: $ACTIVEMQ_HOME/docs/WebConsole-README.txt.

In order to enable a LDAP based authorization, you need to modify the jaas conf file (default is: $ACTIVEMQ_HOME/conf/login.config) and a couple of Java Additional Parameters ($ACTIVEMQ_OPTS) which can be specified in various places, wrapper.conf, for example.

You also need a java LDAP library: ldaptive. Please copy the newest ldaptive jar file from http://www.ldaptive.org/download in your $ACTIVEMQ_HOME/lib.

login.config

The login.config should look like:
The cn=hawtio,ou=agents,dc=example,dc=org is a principal allowed to browse the groups' memberships.
Users allowed to access to hawt.io have to be members of the cn=the_admins_group,ou=groups,dc=example,dc=org group.

The LDAP server is ldap.example.org and it is contacted over TLS.

Java Additional Parameters

The relevant Java Additional Parameters are:
  • hawtio.realm=activemq: should match the key if config.login (activemq in the previous example);
  • hawtio.role=admins: group membership required. As the defaultRole membership is granted to the users found in the roleFilter, you need to match the defaultRole;
  • hawtio.rolePrincipalClasses=org.ldaptive.jaas.LdapRole: the java class of the role. The given value is correct for a ldaptive-granted role;
  • java.security.auth.login.config=%ACTIVEMQ_CONF%/login.config: location of the jaas config file.
These parameters can be specified in ./bin/activemq, in wrapper.conf or in /etc/default/activemq (Linux GNU/Debian).

Enable logging

This couple of lines in logback.xml can help you a lot:

Thursday, 1 August 2013

OpenLDAP ldap backend: SASL authentication

The problem

The OpenLDAP ldap backend acts as a reverse proxy to a LDAP server containing real data. The simple binds are simply verbatim transferred to the data server and users don't even feel there is a proxy in-between.
The problem is with SASL. With a SASL bind the proxy doesn't have a password to forward to the data server. Even if proxy authenticates user with GSSAPI or EXTERNAL, proxy obtains a dn but no password. So it has to perform an anonymous bind which is different from an authenticated one. This behaviour is warned by the log line:
ldap_back_dobind_int: DN="cn=admin,dc=example,dc=org" without creds, binding anonymously

Overview

The solution uses the id_assert directive in the ldap badabase section on the proxy in order to bind to the data server with a defined principal who then proxies to the original client identity thanks to the authzTo attribute.
The OpenLDAP need three modifies:
  • id_assert section in ldap database (proxy server)
  • creation of the id_assert principal with the authzTo attribute (data server)
  • enable authzPolicy to in cn=config (data server)

ldifs needed

To enable authzPolicy on the data server: This is necessary because OpenLDAP disregards authzTo attribute in a entry unless the global olcAuthzPolicy is set.
To enable id_assert on ldap database on proxy server: The id_assert is in effect only for authenticated users to avoid using a privileged bind for anonymous users.
The proxy user is defined as follow:

Check log entries

This is a log entry for the user "a_user" bound with GSSAPI requesting his own entry:
Log on proxy:
Log on data server: Note bind is performed by cn=proxy,ou=agents,dc=example,dc=org but then authorization is performed on behalf to "a_user".

Wednesday, 24 July 2013

OpenLDAP ldap backend as a proxy

Ldap backend works as a proxy: when a client searches data, proxy forwards request to ldap servers with real data, which are served to client. This is useful for:
  • high availability: ldap backend spots faulty servers and picks the first working in a list
  • firewalling: clients connect to a single IP no matter how many ldap server are involved.
Before using ldap backend, you have to enable it: create a ldif named 'add_module_ldap.ldif' and apply with: sudo ldapadd -H ldapi:/// -Y EXTERNAL -f add_module_ldap.ldif Now you are ready to create to database by inserting the following ldif. Now queries matching the basename "dc=example,dc=org" are forwarded to the first available server between ldap1.example.org or ldap2.example.org. The proxy might take some time to spot faulty server (maybe it has to wait for a timeout), but since the next call it forwards to the last used server, the first working one. Please note:
  • the "allow all" acl is required because ldap backend perform authorization. A request is fulfilled if both the proxy and the data server allow it. Serious acl are supposed to be on the data server only (it helps sanity);
  • remember to encrypt connection between proxy and data server with 'olcDbStartTLS: start'

Tuesday, 23 July 2013

pkcs11 ssh authentication

This post is about using ssh without password, with a certificate stored on a smartcard. I think it could be really useful with notebooks. You can securely connect to ssh servers without storing the ssh secret key on the notebook, which can be stolen, can be lost etc.

Setup

Of course you have to be able to read the certificate on the smartcard so:
  • get a smartcard reader; be sure it is supported. We lost a lot of time because ACR38UR didn't work (ACR38UC works fine);
  • install pcscd;
  • get the crypto api for yor smartcard. 'opensc' should work fine, sometimes the certificate issuer require other libraries (Italian CNS work with libbit4ipki.so -- you can find it with the software 'dike').

Get public keys

$ ssh-keygen -D /usr/lib/libbit4ipki.so
ssh-rsa AAAAB3NzaC1yc2[...]J6KIcjjROKtdJ2CHOftZExSkNyNNQ==
ssh-rsa AAAAB3NzaC1yc2[...]kRxbZfOVWb8X5C4X++iiXS4UDpWhQ==
Copy one of the line beginning with "ssh-rsa" to the '.ssh/authorized_keys' on the ssh server (chmod 600).

Load private keys

$ ssh-agent /bin/bash
$ ssh-add -s /usr/lib/libbit4ipki.so
Enter passphrase for PKCS#11:
Card added: /usr/lib/libbit4ipki.so
$ ssh-add -l
1024 f8:8a:e3:[...]:cb:ab:db:67:da:3e /usr/lib/libbit4ipki.so (RSA)
1024 bc:9f:e9:[...]:27:7a:13:55:81:bf /usr/lib/libbit4ipki.so (RSA)
Then you can happily login to ssh server with a simple ssh command.

Wednesday, 10 July 2013

OpenLDAP: force TLS on authentication only

Imagine your directory has public data which can be accessed anonymously. Suppose there are also confidential data whose access requires authentication.

You want authenticated access to be on the secure channel (to protect both password and data from sniffing) while you don't want to enforce TLS to anonymous access to public data (maybe some clients are hard to configure properly for TLS).

Setting:
olcSecurity: ssf=36
in cn=config would require all user to use TLS: otherwise OpenLDAP issues a "confidentiality required" error. This setting is maybe overkill.

TLS can be enforced with ACL as well.

Create a ldif file named "add_tls_for_auth.ldif" as following: and apply to config with:
ldapmodify -H ldapi:/// -Y EXTERNAL -f add_tls_for_auth.ldif
(this code assumes the default acl setup by Debian).

Explanation

The break keyword means that if you match that rule you should check next rule for the same what. So, if your ssf is strong enought or your IP is 127.0.0.1 you are allowed to check next rule about access to attrs=userPassword,shadowLastChange. Otherwise the none means userPassword is not returned so no authentication can ever succeed.
In short the break keyword is a kind on logical AND between two rules.

Notes

There are two points to note:
  • Users are still allowed to try connection with clear text password on ldap://. Simply authntication never succeed so in a while they should stop;
  • To enable ldapi:/// authenticated connection you might need to set olcLocalSSF=128 in cn=config:

Tuesday, 2 July 2013

Basic setup in Debian slapd package

After issuing apt-get install slapd a few steps are required in order to:
  • change basename suffix;
  • enable logging;
  • speed up admin authentication.
The Debian version is release 7 Wheezy.

Change basename suffix

Package creates a database with suffix aligned to domain name. Domain name is read from /etc/resolv.conf or the like. If you want to change it, the dpkg command can help you:
sudo dpkg-reconfigure slapd
The second time you can choose the domain name.

Enable logging

To enable logging, create a ldif modify file:
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: Stats
name it enable_log.ldif and apply to openLDAP with:
sudo ldapmodify -H ldapi:/// -Y EXTERNAL -f enable_log.ldif
Next ensure slapd sends log to a facility, for example local6. This is done in /etc/default/slapd:
# Additional options to pass to slapd
SLAPD_OPTIONS="-l local6"
(then restart slapd). By the way, to avoid filling the hard drive with openldap log, instruct logrotate to handle them: drop in /etc/logrotate.d/ a file called 'ldap':
/var/log/ldap.log
{
       rotate 90
       daily
       missingok
       notifempty
       delaycompress
       compress
}

Speed up admin authentication

In order to avoid typing admin password to populate directory, authorize SASL/EXTERNAL with root access to do that. Create a ldif file (enable_sasl_acl.ldif):
dn: olcDatabase={1}hdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external
 ,cn=auth manage by * break
and switch it on with:
sudo ldapmodify -H ldapi:/// -Y EXTERNAL -f enable_sasl_acl.ldif
Now you are able to add ou, users and groups without password as long you are root, with '-H ldapi:/// -Y EXTERNAL' switch.

Monday, 24 June 2013

Securing the ActiveMQ 5.8.0 web console using LDAP based authentication with Ldaptive

This is a follow-up of the great post by Torsten Mielke, updated for the 5.8.0 activemq release with the great ldaptive ldap java library.
First of all, drop the ldaptive.jar in the ${activemq-home}/lib directory.
Next you need to edit two files:
  • login.config
  • jetty.xml
In jetty.xml, the key task is to substitute to the default securityLoginService based on a org.eclipse.jetty.security.HashLoginService a section like: where jetty-ldap has to match the label in login.config, and roleClassNames match the class role exported by ldaptive.
The property 'roles' should include the group names from ldap which define if a member is allowed to access web console. In this example, a user should be a member of the admins group.
Please note identityService has to be present even if it is default.
The login.config is: ldap1 and ldap2 are contacted via start_tls, with a ACTIVE_PASSIVE strategy, the groups are in the ou=groups,dc=test,dc=com and a authentication principal is required to browse them. "storepass=true" in the LdapLoginModule is required in order to allow to the filter 'memberUid={user}' to resolve the {user} placeholder.

The hardest part of this setup is the role mapping from group membership. These errors show up with the following error in web console following a successful login:
Problem accessing /. Reason:      !role
In this example groups/role mappings are in web.xml. They could be done in login.config as well with the following lines:
            roleFilter="(&(cn=admin_group)(memberUid={user}))"
     defaultRole="admin" 
If a user matches the filter (she's member of the admin_group group) is added to the admin role which in turn should match the roles property in securityConstraints.

The following lines in your logback.xml can help you a lot to spot errors:
Link to the complete jetty.xml.
Ldaptive jaas guide.

Thursday, 23 May 2013

ldaptive 1.0 test

ldaptive is a java ldap library which replaces vt-ldap shipped with shibboleth and grouper.

There are some nice surprises, like the handling of the ldap extended operation of Password Modify and Password Policy, used by openldap to check if a user's password is locked or need to be changed.

I have tried a ldapsearch authenticated with SASL TLS/EXTERNAL:


Please note that the private key password (the one you set to create the pkcs12) and the keystore password have to be the same.

Friday, 15 February 2013

Convert a openssl PEM RSA key to java keystore

Starting from jdk6, keytool allows importing pkcs12 bundles. The pkcs12 bundle includes key, certificate and CA chain so it is possible to require a certificate with openssl than using it with activemq, or other java projects.

The procedure is outlined the jetty ssl page, but there is a important point to note.

The keystore is password protected with the password set during keystore creation. The private key is password protected with the export password typed in openssl pkc12 command.

If a tool doesn't allow to specify both password, they must be the same.

The case is the activemq sslContext, which reads:

<sslContext>
                <sslContext 
                        keyStore="ateneo.store" keyStorePassword="bluehorror"
                        trustStore="client.ks" trustStorePassword="password"/>
        </sslContext>
The trick is to issue a keytool -keypasswd to align password. Failing to do that results in a:
Exception in thread "main" java.security.UnrecoverableKeyException: Cannot recover key
 at sun.security.provider.KeyProtector.recover(KeyProtector.java:311)
 at sun.security.provider.JavaKeyStore.engineGetKey(JavaKeyStore.java:121)
 at sun.security.provider.JavaKeyStore$JKS.engineGetKey(JavaKeyStore.java:38)
 at java.security.KeyStore.getKey(KeyStore.java:763)
Thanks to http://www.xinotes.org/notes/note/1395/

In case you want to follow the single steps, have a look at the puppet module to automatize certs export to keystore: https://github.com/francescm/certs

Friday, 21 September 2012

Openldap2.4 performance boosting

According to my experience, the single directive you should add to db section to improve performance is:

olcDbCacheSize

OpenLDAP tuning guide covers that in section: 21.4.2.

To enable a 200000 entries cachesize use this ldif:

The enumeration of the whole directory (150000 entries) took 15 seconds without cachesize and less than 6 seconds with it.

Thursday, 20 September 2012

Instant logger replacement to logback in activemq.5.6.0

In activemq-5.6.0 changing logger to logback is just a matter of seconds:
  • Copy the following jar in ${activemq.base}/lib:
    logback-access-1.0.0.jar
    logback-classic-1.0.0.jar
    
  • Create a logback.xml file in ${activemq.base}/conf, for example:

That's all.

What about the activemq logback logging to a jms system which is activemq?