0

I'd like to set up certificate-based login authentication in Grails speaking REST.

Following Burt Beckwith's docs I use the Spring Security Plugin (spring-security-core:2.0-RC3) to set up the X509 Authentication:

grails.plugin.springsecurity.userLookup.userDomainClassName = 'de.app.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'de.app.UserRole'
grails.plugin.springsecurity.authority.className = 'de.app.Role'
grails.plugin.springsecurity.useX509 = true
grails.plugin.springsecurity.portMapper.httpsPort = "8443"
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
    '/':                              ['permitAll'],
    '/index':                         ['permitAll'],
    '/index.gsp':                     ['permitAll'],
    '/**/js/**':                      ['permitAll'],
    '/**/css/**':                     ['permitAll'],
    '/**/images/**':                  ['permitAll'],
    '/**/favicon.ico':                ['permitAll']
]

The generation of the certificate files and the Tomcat configuration seem a little tricky but is already pointed out in another answer. I did so and using the current standard I have to configure Tomcat 7, first and foremost keystoreFile, keystorePass, and clientAuth. I cannot access the server.xml directly. Thus, I have to find another place. The Tomcat plugin's configuration regarding this looks limited to the parameters keystorePath and keystorePassword. I give it a try by adding this to the BuildConfig:

grails.tomcat.keystorePath = "${basedir}/grails-app/conf/server.jks"
grails.tomcat.keystorePassword = "password"

Don't know if these two lines suffice. I found this solution setting more connector arguments but obviously the event isn't triggered anymore and so the code has no effect.

I found three ways to do my request with the certificate, i.e. Firefox, cURL, and wget. None is working! Logging all in my Grails app I found out that obviously no certificate arrives in Spring Security:

web.FilterChainProxy /vehicle at position 3 of 9 in additional filter chain; firing Filter: 'X509AuthenticationFilter'
x509.X509AuthenticationFilter Checking secure context token: null
x509.X509AuthenticationFilter No client certificate found in request.
x509.X509AuthenticationFilter No pre-authenticated principal found in request

The curl request:

curl -v -H "Content-Type: application/xml" --cert ./username.p12 -k -i https://localhost:8443/de.app/vehicle

and its alternative:

wget —v --header='Content-Type: application/xml' --certificate='./username.p12' --no-check-certificate -S https://localhost:8443/de.app/vehicle

(There is a bug in curl on Mac OS X Mavericks I wanted to sort out.) The response is always a 403 (forbidden):

* Adding handle: conn: 0x7f840280fe00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7f840280fe00) send_pipe: 1, recv_pipe: 0
* About to connect() to localhost port 8443 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 8443 (#0)
* TLS 1.0 connection using TLS_RSA_WITH_AES_128_CBC_SHA
* Server certificate: Web Server
> GET /de.app/vehicle HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8443
> Accept: */*
> Content-Type: application/xml
> 
< HTTP/1.1 403 Forbidden
HTTP/1.1 403 Forbidden
* Server Apache-Coyote/1.1 is not blacklisted
< Server: Apache-Coyote/1.1

What am I doing wrong? Either the certificate has not been sent or the certificate is not taken correctly from the request. I believe the latter. Any guesses how I can take a deeper look?

Community
  • 1
  • 1

1 Answers1

0

You need to tell Tomcat that you would like to use Client Authentication for SSL. Along with the keystore configuration you've already added to BuildConfig, add this:

grails.tomcat.clientAuth = "want"

or

grails.tomcat.clientAuth = "true"

The first will not automatically fail authentication if a certificate is not present, whereas the true value will.

Also, if you plan to use self-signed certificates for your clients, you will need to create a trusted keystore and configure Tomcat to use that as well.

grails.tomcat.truststorePath = "${grailsSettings.baseDir}/conf/ssl/server/truststore.jks"
grails.tomcat.truststorePassword = "changeit"
rmlan
  • 4,387
  • 2
  • 21
  • 28