Commit 277830e9 authored by Nick Thomas's avatar Nick Thomas

Merge branch 'if-10526-smartcard_support_different_hostname' into 'master'

Make hostname configurable for smartcard authentication

See merge request gitlab-org/gitlab!26411
parents 7184b409 d8df043d
---
title: Make hostname configurable for smartcard authentication
merge_request: 26411
author:
type: added
...@@ -752,7 +752,9 @@ production: &base ...@@ -752,7 +752,9 @@ production: &base
# Path to a file containing a CA certificate # Path to a file containing a CA certificate
ca_file: '/etc/ssl/certs/CA.pem' 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 # client_certificate_required_port: 3444
# Browser session with smartcard sign-in is required for Git access # Browser session with smartcard sign-in is required for Git access
......
...@@ -77,6 +77,7 @@ end ...@@ -77,6 +77,7 @@ end
Gitlab.ee do Gitlab.ee do
Settings['smartcard'] ||= Settingslogic.new({}) Settings['smartcard'] ||= Settingslogic.new({})
Settings.smartcard['enabled'] = false if Settings.smartcard['enabled'].nil? 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['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['required_for_git_access'] = false if Settings.smartcard['required_for_git_access'].nil?
Settings.smartcard['san_extensions'] = false if Settings.smartcard['san_extensions'].nil? Settings.smartcard['san_extensions'] = false if Settings.smartcard['san_extensions'].nil?
......
...@@ -5,25 +5,58 @@ class SmartcardController < ApplicationController ...@@ -5,25 +5,58 @@ class SmartcardController < ApplicationController
skip_before_action :verify_authenticity_token skip_before_action :verify_authenticity_token
before_action :check_feature_availability 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 def auth
certificate = Gitlab::Auth::Smartcard::Certificate.new(certificate_header) redirect_to extract_certificate_smartcard_url(extract_certificate_url_options)
sign_in_with(certificate)
end end
def ldap_auth def extract_certificate
certificate = Gitlab::Auth::Smartcard::LdapCertificate.new(params[:provider], certificate_header) redirect_to verify_certificate_smartcard_url(verify_certificate_url_options)
sign_in_with(certificate) end
def verify_certificate
sign_in_with(client_certificate)
end end
private 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) def sign_in_with(certificate)
user = certificate.find_or_create_user user = certificate.find_or_create_user
unless user&.persisted? unless user&.persisted?
flash[:alert] = _('Failed to signing using smartcard authentication') 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 return
end end
...@@ -33,13 +66,43 @@ class SmartcardController < ApplicationController ...@@ -33,13 +66,43 @@ class SmartcardController < ApplicationController
sign_in_and_redirect(user) sign_in_and_redirect(user)
end 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 def check_feature_availability
render_404 unless ::Gitlab::Auth::Smartcard.enabled? render_404 unless ::Gitlab::Auth::Smartcard.enabled?
end end
def check_certificate_headers def check_certificate_required_host_and_port
# Failing on requests coming from the port not requiring client side certificate unless request.host == ::Gitlab.config.smartcard.client_certificate_required_host &&
unless certificate_header.present? 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) access_denied!(_('Smartcard authentication failed: client certificate header is missing.'), 401)
end end
end end
...@@ -52,20 +115,6 @@ class SmartcardController < ApplicationController ...@@ -52,20 +115,6 @@ class SmartcardController < ApplicationController
AuditEventService.new(user, user, options).for_authentication.security_event AuditEventService.new(user, user, options).for_authentication.security_event
end 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) def after_sign_in_path_for(resource)
stored_location_for(:redirect) || stored_location_for(resource) || root_url(port: Gitlab.config.gitlab.port) stored_location_for(:redirect) || stored_location_for(resource) || root_url(port: Gitlab.config.gitlab.port)
end end
......
- if smartcard_enabled? - if smartcard_enabled?
.login-box.tab-pane{ id: 'smartcard', role: 'tabpanel', class: active_when(form_based_auth_provider_has_active_class?(:smartcard)) } .login-box.tab-pane{ id: 'smartcard', role: 'tabpanel', class: active_when(form_based_auth_provider_has_active_class?(:smartcard)) }
.login-body .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-container
= submit_tag _('Login with smartcard'), class: 'btn btn-success' = submit_tag _('Login with smartcard'), class: 'btn btn-success'
...@@ -9,8 +9,7 @@ ...@@ -9,8 +9,7 @@
= _('Use your smart card to authenticate with the LDAP server.') = _('Use your smart card to authenticate with the LDAP server.')
.login-body .login-body
= form_tag(smartcard_ldap_auth_url(provider: server['provider_name'], = form_tag(auth_smartcard_url(provider: server['provider_name']),
port: smartcard_config_port),
html: { 'aria-live' => 'assertive'}) do html: { 'aria-live' => 'assertive'}) do
.submit-container .submit-container
= submit_tag(_('Sign in with smart card'), = submit_tag(_('Sign in with smart card'),
......
# frozen_string_literal: true # frozen_string_literal: true
post 'smartcard/auth' => 'smartcard#auth' resource :smartcard, only: [], controller: :smartcard do
post 'smartcard/ldap_auth' => 'smartcard#ldap_auth' 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