Question

How can I authorize an API in sinatra, so that only callers possessing a known client certificate (or one issued by a trusted CA) are allowed to call it?

Currently I am using the 'thin' webserver, but I am open to other options if that is necessary.

Was it helpful?

Solution

You can use nginx to take care of your client-certificate - here is a blog post which shows how to set it up:

server {
    listen        443;
    ssl on;
    server_name example.com;

    ssl_certificate      /etc/nginx/certs/server.crt;
    ssl_certificate_key  /etc/nginx/certs/server.key;
    ssl_client_certificate /etc/nginx/certs/ca.crt;
    ssl_verify_client optional;

    location / {
        root           /var/www/example.com/html;
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_param  SCRIPT_FILENAME /var/www/example.com/lib/Request.class.php;
        fastcgi_param  VERIFIED $ssl_client_verify;
        fastcgi_param  DN $ssl_client_s_dn;
        include        fastcgi_params;
    }
}

We specify our the server's certificate (server.crt) and private key (server.key) We specify the CA cert that we used to sign our client certificates (ca.crt) We set the ssl_verify_client to optional. This tells nginx to attempt to verify to SSL certificate if provided. My API allows both authenticated and unauthenticated requests, however if you only want to allow authenticated requests, you can go ahead and set this value to on.

You can use thin with nginx, but I believe that using passenger with nginx is more popular in this case, and quite easy to deploy.


ssl_verify_client optional is explained here:

ssl_verify_client

Syntax:   ssl_verify_client on | off | optional | optional_no_ca
Default:  off
Context:   http | server
Reference:    ssl_verify_client

This directive enables the verification of the client identity. Parameter 'optional' checks the client identity using its certificate in case it was made available to the server.

OTHER TIPS

Since you're using Thin I don't think this is possible at the moment because peer verification appears to be broken. See https://github.com/macournoyer/thin/pull/203:

"The get_peer_cert method of EM doesn't return anything unless the cert has been verified. The --ssl-verify option of thin actually doesn't do anything. These two behaviors combined mean that env['rack.peer_cert'], which was introduced in thin 1.2.8, always returns nil. Since --ssl-verify never actually caused a verification to happen, it is better to remove that option until a fully verification process is put in place. However, the peer_cert can be made available in --ssl mode by always "verifying" the cert, thereby providing the client supplied certificate, if there is one, available in env['rack.peer_cert']."

I believe Uri Agassi is partially correct by recommending passenger but I worry a nginx/thin combination introduces a security risk if you're expecting the cert to act as the authentication if thin is ever moved off the nginx server, thereby exposing your thin server. I think an embedded appserver solution is the way to go (a la passenger).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top