-
Notifications
You must be signed in to change notification settings - Fork 81
Client SSL authentication
Ruben Stranders edited this page Feb 9, 2015
·
1 revision
Using the nginx_pass_ssl_client_cert
parameter, nginx can be configured to support SSL client authentication. This allows clients to authenticate themselves by with an SSL certificate that has been signed by your own SSL certificate authority.
- Read this page for general information about SSL mutual authentication.
- See here for instructions on how to create your own SSL certificate authority.
- See here for details on the
nginx_pass_ssl_client_cert
parameter.
Instructions.
- Put the code below in lib/ssl_authentication/ssl_authentication.rb
- Add the line
require 'ssl_authentication/ssl_authentication'
to environment.rb, above the initialization of the Rails application. - In the controller where you wish to allow authentication using a client SSL certificate, add the line
authenticates_from_ssl_certificate ROOT_CA_FILE
, whereROOT_CA_FILE
contains the root authority X509 certificate(s) that you trust. Client SSL certificates signed by this root authority are granted access.
module SSLAuthentication
module ControllerAdditions
def self.included(base)
base.extend ClassMethods
end
def authenticate_from_ssl_certificate(ssl_ca_files)
# This performs mutual authentication with the certificate supplied by the client when establishing the
# SSL connection
certificate = load_certificate_from_headers
if certificate.nil?
Rails.logger.warn('[SSLAuthentication failed] No client certificate supplied')
head :unauthorized # certificate wasn't supplied or was of incorrect format
return
end
cert_store = OpenSSL::X509::Store.new
ssl_ca_files.each { |f| cert_store.add_file f } # load the certificates and/or authorities that we trust
if cert_store.verify(certificate) # verify that the certificate was signed by the root CA
Rails.logger.info("[SSLAuthentication success] Trusted certificate supplied #{certificate.public_key}")
else
Rails.logger.warn("[SSLAuthentication failed] Untrusted certificate supplied #{certificate.public_key}")
head :unauthorized
end
end
def load_certificate_from_headers
raw_certificate = request.headers['X-Client-Cert'] ||
(request.headers['rack.session'] && request.headers['rack.session']['X-Client-Cert'])
return nil unless raw_certificate.is_a?(String)
# This is a hack. Due to a problem with nginx, the certificate has to be sent as a single line string.
# Here we reconstruct the certificate if it does not start with "-----BEGIN CERTIFICATE-----"
certificate_content = if raw_certificate.starts_with?('-----BEGIN CERTIFICATE-----')
raw_certificate.gsub("\t", "") # certificate passed by nginx as request header
else
certificate_body = raw_certificate.scan(/.{1,64}/m).join("\n")
"-----BEGIN CERTIFICATE-----\n#{certificate_body}\n-----END CERTIFICATE-----"
end
OpenSSL::X509::Certificate.new(certificate_content)
end
module ClassMethods
def authenticates_from_ssl_certificate(*ca_files)
before_filter { authenticate_from_ssl_certificate ca_files }
end
end
end
end
if defined? ActionController::Base
ActionController::Base.class_eval do
include SSLAuthentication::ControllerAdditions
end
end