Commit d8df043d authored by Imre Farkas's avatar Imre Farkas

Make hostname configurable for smartcard authentication

Hostname is configurable for the endpoint requiring client side
certificate.
parent ed6dbb83
---
title: Make hostname configurable for smartcard authentication
merge_request: 26411
author:
type: added
......@@ -752,7 +752,9 @@ production: &base
# Path to a file containing a CA certificate
ca_file: '/etc/ssl/certs/CA.pem'
# Port where the client side certificate is requested by the webserver (NGINX/Apache)
# Host and port where the client side certificate is requested by the
# webserver (NGINX/Apache)
# client_certificate_required_host: smartcard.gitlab.example.com
# client_certificate_required_port: 3444
# Browser session with smartcard sign-in is required for Git access
......
......@@ -77,6 +77,7 @@ end
Gitlab.ee do
Settings['smartcard'] ||= Settingslogic.new({})
Settings.smartcard['enabled'] = false if Settings.smartcard['enabled'].nil?
Settings.smartcard['client_certificate_required_host'] = Settings.gitlab['host'] if Settings.smartcard['client_certificate_required_host'].nil?
Settings.smartcard['client_certificate_required_port'] = 3444 if Settings.smartcard['client_certificate_required_port'].nil?
Settings.smartcard['required_for_git_access'] = false if Settings.smartcard['required_for_git_access'].nil?
Settings.smartcard['san_extensions'] = false if Settings.smartcard['san_extensions'].nil?
......
......@@ -5,25 +5,58 @@ class SmartcardController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :check_feature_availability
before_action :check_certificate_headers
before_action :check_certificate_required_host_and_port, only: :extract_certificate
before_action :check_ngingx_certificate_header, only: :extract_certificate
before_action :check_certificate_param, only: :verify_certificate
def auth
certificate = Gitlab::Auth::Smartcard::Certificate.new(certificate_header)
sign_in_with(certificate)
redirect_to extract_certificate_smartcard_url(extract_certificate_url_options)
end
def ldap_auth
certificate = Gitlab::Auth::Smartcard::LdapCertificate.new(params[:provider], certificate_header)
sign_in_with(certificate)
def extract_certificate
redirect_to verify_certificate_smartcard_url(verify_certificate_url_options)
end
def verify_certificate
sign_in_with(client_certificate)
end
private
def extract_certificate_url_options
{
host: ::Gitlab.config.smartcard.client_certificate_required_host,
port: ::Gitlab.config.smartcard.client_certificate_required_port,
provider: params[:provider]
}.compact
end
def verify_certificate_url_options
{
host: ::Gitlab.config.gitlab.host,
port: ::Gitlab.config.gitlab.port,
provider: params[:provider],
client_certificate: request.headers['HTTP_X_SSL_CLIENT_CERTIFICATE']
}.compact
end
def client_certificate
if ldap_provider?
Gitlab::Auth::Smartcard::LdapCertificate.new(params[:provider], certificate_param)
else
Gitlab::Auth::Smartcard::Certificate.new(certificate_param)
end
end
def ldap_provider?
params[:provider].present?
end
def sign_in_with(certificate)
user = certificate.find_or_create_user
unless user&.persisted?
flash[:alert] = _('Failed to signing using smartcard authentication')
redirect_to new_user_session_path(port: Gitlab.config.gitlab.port)
redirect_to new_user_session_path
return
end
......@@ -33,13 +66,43 @@ class SmartcardController < ApplicationController
sign_in_and_redirect(user)
end
def nginx_certificate_header
request.headers['HTTP_X_SSL_CLIENT_CERTIFICATE']
end
def certificate_param
param = params[:client_certificate]
return unless param
unescaped_param = CGI.unescape(param)
if unescaped_param.include?("\n")
# NGINX forwarding the $ssl_client_escaped_cert variable
unescaped_param
else
# older version of NGINX forwarding the now deprecated $ssl_client_cert variable
param.gsub(/ (?!CERTIFICATE)/, "\n")
end
end
def check_feature_availability
render_404 unless ::Gitlab::Auth::Smartcard.enabled?
end
def check_certificate_headers
# Failing on requests coming from the port not requiring client side certificate
unless certificate_header.present?
def check_certificate_required_host_and_port
unless request.host == ::Gitlab.config.smartcard.client_certificate_required_host &&
request.port == ::Gitlab.config.smartcard.client_certificate_required_port
render_404
end
end
def check_ngingx_certificate_header
unless nginx_certificate_header.present?
access_denied!(_('Smartcard authentication failed: client certificate header is missing.'), 401)
end
end
def check_certificate_param
unless certificate_param.present?
access_denied!(_('Smartcard authentication failed: client certificate header is missing.'), 401)
end
end
......@@ -52,20 +115,6 @@ class SmartcardController < ApplicationController
AuditEventService.new(user, user, options).for_authentication.security_event
end
def certificate_header
header = request.headers['HTTP_X_SSL_CLIENT_CERTIFICATE']
return unless header
unescaped_header = CGI.unescape(header)
if unescaped_header.include?("\n")
# NGINX forwarding the $ssl_client_escaped_cert variable
unescaped_header
else
# older version of NGINX forwarding the now deprecated $ssl_client_cert variable
header.gsub(/ (?!CERTIFICATE)/, "\n")
end
end
def after_sign_in_path_for(resource)
stored_location_for(:redirect) || stored_location_for(resource) || root_url(port: Gitlab.config.gitlab.port)
end
......
- if smartcard_enabled?
.login-box.tab-pane{ id: 'smartcard', role: 'tabpanel', class: active_when(form_based_auth_provider_has_active_class?(:smartcard)) }
.login-body
= form_tag(smartcard_auth_url(port: smartcard_config_port), html: { 'aria-live' => 'assertive'}) do
= form_tag(auth_smartcard_url, html: { 'aria-live' => 'assertive'}) do
.submit-container
= submit_tag _('Login with smartcard'), class: 'btn btn-success'
......@@ -9,8 +9,7 @@
= _('Use your smart card to authenticate with the LDAP server.')
.login-body
= form_tag(smartcard_ldap_auth_url(provider: server['provider_name'],
port: smartcard_config_port),
= form_tag(auth_smartcard_url(provider: server['provider_name']),
html: { 'aria-live' => 'assertive'}) do
.submit-container
= submit_tag(_('Sign in with smart card'),
......
# frozen_string_literal: true
post 'smartcard/auth' => 'smartcard#auth'
post 'smartcard/ldap_auth' => 'smartcard#ldap_auth'
resource :smartcard, only: [], controller: :smartcard do
collection do
post :auth
get :extract_certificate
get :verify_certificate
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment