Smartcard configuration against Active Directory NoMethodError "(undefined method `dn' for nil:NilClass):" Results in a 500 error
Searched all over and cannot find anything very similar.
GitLab-ee 17.7.0 Self-Managed. We use our instance in the DoD and wish to use the smartcard capabilities with LDAP/Active Directory. Regular usernames and password work with the base specified, however, giving GitLab our smartcard cert results in an error I am not familiar with.
My LDAP configuration
gitlab_rails['ldap_servers'] = YAML.load <<-'EOS'
main: # 'main' is the GitLab 'provider ID' of this LDAP server
label: 'redacted'
uid: 'sAMAaccountName'
host: 'redacted'
port: 636
encryption: 'simple_tls'
verify_certificates: true
ca_file: '/etc/gitlab/trusted-certs/tls-ca-bundle.pem'
smartcard_auth: optional
smartcard_ad_cert_field: 'userPrincipalName'
smartcard_ad_cert_format: 'principal_name'
timeout: 30
active_directory: true
allow_username_or_email_login: false
attributes:
username: 'sAMAccountName'
email: 'mail'
name: 'displayName'
lowercase_usernames: true
block_auto_created_users: false
base: 'redacted'
Error Message
NoMethodError (undefined method `dn' for nil:NilClass):
ee/lib/gitlab/auth/smartcard/ldap_certificate.rb:20:in `find_user'
ee/lib/gitlab/auth/smartcard/base.rb:36:in `find_or_create_user'
ee/app/controllers/smartcard_controller.rb:66:in `sign_in_with'
ee/app/controllers/smartcard_controller.rb:26:in `verify_certificate'
actionpack (7.0.8.6) lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack (7.0.8.6) lib/abstract_controller/base.rb:215:in `process_action'
actionpack (7.0.8.6) lib/action_controller/metal/rendering.rb:165:in `process_action'
actionpack (7.0.8.6) lib/abstract_controller/callbacks.rb:234:in `block in process_action'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:118:in `block in run_callbacks'
lib/gitlab/ip_address_state.rb:11:in `with'
ee/app/controllers/ee/application_controller.rb:45:in `set_current_ip_address'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
app/controllers/application_controller.rb:506:in `set_current_admin'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
lib/gitlab/session.rb:11:in `with_session'
app/controllers/application_controller.rb:496:in `set_session_storage'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
lib/gitlab/i18n.rb:114:in `with_locale'
app/controllers/application_controller.rb:489:in `set_locale'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
app/controllers/application_controller.rb:480:in `set_current_context'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
marginalia (1.11.1) lib/marginalia.rb:109:in `record_query_comment'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
sentry-rails (5.21.0) lib/sentry/rails/controller_transaction.rb:32:in `block in sentry_around_action'
sentry-ruby (5.21.0) lib/sentry/hub.rb:108:in `with_child_span'
sentry-ruby (5.21.0) lib/sentry-ruby.rb:499:in `with_child_span'
sentry-rails (5.21.0) lib/sentry/rails/controller_transaction.rb:18:in `sentry_around_action'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:127:in `block in run_callbacks'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:138:in `run_callbacks'
actionpack (7.0.8.6) lib/abstract_controller/callbacks.rb:233:in `process_action'
actionpack (7.0.8.6) lib/action_controller/metal/rescue.rb:23:in `process_action'
actionpack (7.0.8.6) lib/action_controller/metal/instrumentation.rb:67:in `block in process_action'
activesupport (7.0.8.6) lib/active_support/notifications.rb:206:in `block in instrument'
activesupport (7.0.8.6) lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport (7.0.8.6) lib/active_support/notifications.rb:206:in `instrument'
actionpack (7.0.8.6) lib/action_controller/metal/instrumentation.rb:66:in `process_action'
actionpack (7.0.8.6) lib/action_controller/metal/params_wrapper.rb:259:in `process_action'
activerecord (7.0.8.6) lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack (7.0.8.6) lib/abstract_controller/base.rb:151:in `process'
actionview (7.0.8.6) lib/action_view/rendering.rb:39:in `process'
actionpack (7.0.8.6) lib/action_controller/metal.rb:188:in `dispatch'
actionpack (7.0.8.6) lib/action_controller/metal.rb:249:in `block in dispatch'
lib/gitlab/middleware/action_controller_static_context.rb:23:in `call'
actionpack (7.0.8.6) lib/action_controller/metal.rb:249:in `dispatch'
actionpack (7.0.8.6) lib/action_dispatch/routing/route_set.rb:49:in `dispatch'
actionpack (7.0.8.6) lib/action_dispatch/routing/route_set.rb:32:in `serve'
actionpack (7.0.8.6) lib/action_dispatch/journey/router.rb:50:in `block in serve'
actionpack (7.0.8.6) lib/action_dispatch/journey/router.rb:32:in `each'
actionpack (7.0.8.6) lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack (7.0.8.6) lib/action_dispatch/routing/route_set.rb:852:in `call'
gitlab-experiment (0.9.1) lib/gitlab/experiment/middleware.rb:19:in `call'
omniauth (2.1.0) lib/omniauth/strategy.rb:202:in `call!'
omniauth (2.1.0) lib/omniauth/strategy.rb:169:in `call'
flipper (0.26.2) lib/flipper/middleware/memoizer.rb:72:in `memoized_call'
flipper (0.26.2) lib/flipper/middleware/memoizer.rb:37:in `call'
lib/gitlab/metrics/elasticsearch_rack_middleware.rb:16:in `call'
lib/gitlab/middleware/sidekiq_shard_awareness_validation.rb:20:in `block in call'
lib/gitlab/sidekiq_sharding/validator.rb:42:in `enabled'
lib/gitlab/middleware/sidekiq_shard_awareness_validation.rb:20:in `call'
lib/gitlab/middleware/memory_report.rb:13:in `call'
lib/gitlab/middleware/speedscope.rb:13:in `call'
lib/gitlab/database/load_balancing/rack_middleware.rb:23:in `call'
lib/gitlab/middleware/rails_queue_duration.rb:33:in `call'
lib/gitlab/etag_caching/middleware.rb:21:in `call'
lib/gitlab/metrics/rack_middleware.rb:16:in `block in call'
lib/gitlab/metrics/web_transaction.rb:46:in `run'
lib/gitlab/metrics/rack_middleware.rb:16:in `call'
lib/gitlab/middleware/go.rb:21:in `call'
lib/gitlab/middleware/query_analyzer.rb:11:in `block in call'
lib/gitlab/database/query_analyzer.rb:83:in `within'
lib/gitlab/middleware/query_analyzer.rb:11:in `call'
lib/ci/job_token/middleware.rb:11:in `call'
batch-loader (2.0.5) lib/batch_loader/middleware.rb:11:in `call'
rack-attack (6.7.0) lib/rack/attack.rb:103:in `call'
apollo_upload_server (2.1.6) lib/apollo_upload_server/middleware.rb:19:in `call'
lib/gitlab/middleware/multipart.rb:173:in `call'
rack-attack (6.7.0) lib/rack/attack.rb:127:in `call'
warden (1.2.9) lib/warden/manager.rb:36:in `block in call'
warden (1.2.9) lib/warden/manager.rb:34:in `catch'
warden (1.2.9) lib/warden/manager.rb:34:in `call'
rack-cors (2.0.2) lib/rack/cors.rb:102:in `call'
rack (2.2.10) lib/rack/tempfile_reaper.rb:15:in `call'
rack (2.2.10) lib/rack/etag.rb:27:in `call'
rack (2.2.10) lib/rack/conditional_get.rb:27:in `call'
rack (2.2.10) lib/rack/head.rb:12:in `call'
actionpack (7.0.8.6) lib/action_dispatch/http/permissions_policy.rb:38:in `call'
actionpack (7.0.8.6) lib/action_dispatch/http/content_security_policy.rb:36:in `call'
lib/gitlab/middleware/read_only/controller.rb:50:in `call'
lib/gitlab/middleware/read_only.rb:18:in `call'
lib/gitlab/middleware/unauthenticated_session_expiry.rb:18:in `call'
rack (2.2.10) lib/rack/session/abstract/id.rb:266:in `context'
rack (2.2.10) lib/rack/session/abstract/id.rb:260:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/cookies.rb:704:in `call'
lib/gitlab/middleware/same_site_cookies.rb:27:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport (7.0.8.6) lib/active_support/callbacks.rb:99:in `run_callbacks'
actionpack (7.0.8.6) lib/action_dispatch/middleware/callbacks.rb:26:in `call'
sentry-rails (5.21.0) lib/sentry/rails/rescued_exception_interceptor.rb:14:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/debug_exceptions.rb:28:in `call'
lib/gitlab/middleware/path_traversal_check.rb:35:in `call'
lib/gitlab/middleware/handle_malformed_strings.rb:21:in `call'
sentry-ruby (5.21.0) lib/sentry/rack/capture_exceptions.rb:30:in `block (2 levels) in call'
sentry-ruby (5.21.0) lib/sentry/hub.rb:265:in `with_session_tracking'
sentry-ruby (5.21.0) lib/sentry-ruby.rb:412:in `with_session_tracking'
sentry-ruby (5.21.0) lib/sentry/rack/capture_exceptions.rb:21:in `block in call'
sentry-ruby (5.21.0) lib/sentry/hub.rb:59:in `with_scope'
sentry-ruby (5.21.0) lib/sentry-ruby.rb:392:in `with_scope'
sentry-ruby (5.21.0) lib/sentry/rack/capture_exceptions.rb:20:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/show_exceptions.rb:29:in `call'
lib/gitlab/middleware/basic_health_check.rb:25:in `call'
lograge (0.11.2) lib/lograge/rails_ext/rack/logger.rb:15:in `call_app'
railties (7.0.8.6) lib/rails/rack/logger.rb:25:in `block in call'
activesupport (7.0.8.6) lib/active_support/tagged_logging.rb:99:in `block in tagged'
activesupport (7.0.8.6) lib/active_support/tagged_logging.rb:37:in `tagged'
activesupport (7.0.8.6) lib/active_support/tagged_logging.rb:99:in `tagged'
railties (7.0.8.6) lib/rails/rack/logger.rb:25:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/remote_ip.rb:93:in `call'
lib/gitlab/middleware/handle_ip_spoof_attack_error.rb:25:in `call'
lib/gitlab/middleware/request_context.rb:15:in `call'
lib/gitlab/middleware/webhook_recursion_detection.rb:15:in `call'
request_store (1.5.1) lib/request_store/middleware.rb:19:in `call'
rack (2.2.10) lib/rack/method_override.rb:24:in `call'
rack (2.2.10) lib/rack/runtime.rb:22:in `call'
rack-timeout (0.7.0) lib/rack/timeout/core.rb:154:in `block in call'
rack-timeout (0.7.0) lib/rack/timeout/support/timeout.rb:19:in `timeout'
rack-timeout (0.7.0) lib/rack/timeout/core.rb:153:in `call'
config/initializers/fix_local_cache_middleware.rb:11:in `call'
lib/gitlab/middleware/compressed_json.rb:44:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/executor.rb:14:in `call'
lib/gitlab/middleware/rack_multipart_tempfile_factory.rb:19:in `call'
rack (2.2.10) lib/rack/sendfile.rb:110:in `call'
lib/gitlab/middleware/sidekiq_web_static.rb:20:in `call'
lib/gitlab/metrics/requests_rack_middleware.rb:79:in `call'
gitlab-labkit (0.37.0) lib/labkit/middleware/rack.rb:22:in `block in call'
gitlab-labkit (0.37.0) lib/labkit/context.rb:35:in `with_context'
gitlab-labkit (0.37.0) lib/labkit/middleware/rack.rb:21:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/request_id.rb:26:in `call'
actionpack (7.0.8.6) lib/action_dispatch/middleware/host_authorization.rb:131:in `call'
railties (7.0.8.6) lib/rails/engine.rb:530:in `call'
railties (7.0.8.6) lib/rails/railtie.rb:226:in `public_send'
railties (7.0.8.6) lib/rails/railtie.rb:226:in `method_missing'
lib/gitlab/middleware/release_env.rb:12:in `call'
rack (2.2.10) lib/rack/urlmap.rb:74:in `block in call'
rack (2.2.10) lib/rack/urlmap.rb:58:in `each'
rack (2.2.10) lib/rack/urlmap.rb:58:in `call'
puma (6.5.0) lib/puma/configuration.rb:279:in `call'
puma (6.5.0) lib/puma/request.rb:99:in `block in handle_request'
puma (6.5.0) lib/puma/thread_pool.rb:389:in `with_force_shutdown'
puma (6.5.0) lib/puma/request.rb:98:in `handle_request'
puma (6.5.0) lib/puma/server.rb:468:in `process_client'
puma (6.5.0) lib/puma/server.rb:249:in `block in run'
puma (6.5.0) lib/puma/thread_pool.rb:166:in `block in spawn_thread'
==> /var/log/gitlab/gitlab-workhorse/current <==
{"backend_id":"rails","content_type":"text/html; charset=utf-8","correlation_id":"01JGM5Z04HD1DSSG0YQ2AYP32C","duration_ms":475,"host":"redacted.mil","level":"info","method":"GET",
"msg":"access","proto":"HTTP/1.1","referrer":"https://redacted.mil/","remote_addr":"127.0.0.1:0","remote_ip":"127.0.0.1","route":"^/-/","route_id":"dash","status":500,"system":"htt
p","time":"2025-01-02T13:55:35-05:00","ttfb_ms":475,"uri":"/-/smartcard/verify_certificate?client_certificate=zMLMfj5D94GuzACBdw%252BbvZELnFkW9G0VYsINtJFJ%252B8YKER3Q%252B%252Be7zDvNvzW%252BGqs
tLOyuq5ox4%252BoOmWP5%252Ba1oF...=ldapmain","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0","written_bytes":1624}
==> /var/log/gitlab/nginx/gitlab_access.log <==
10.180.42.114 - - [02/Jan/2025:13:55:35 -0500] "GET /-/smartcard/verify_certificate?client_certificate=zMLMfj5D94GuzACBdw%252BbvZELnFkW9G0VYsINtJFJ%252B8YKER3Q%252B%252Be7zDvNvzW%252BGqs
tLOyuq5ox4%252BoOmWP5%252Ba1oF...=ldapmain HTTP/2.0" 500 1624 "https://redacted.mil/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0" -
Details
In our enterprise we use smartcards to login to our machines, websties, and access to our buildings. We have about 50 users with our instance. I have set my instance to use smartcards in my gitlab.rb:
###! Docs: https://docs.gitlab.com/ee/administration/auth/smartcard.html
gitlab_rails['smartcard_enabled'] = true
gitlab_rails['smartcard_ca_file'] = "/etc/gitlab/trusted-certs/tls-ca-bundle.pem"
#gitlab_rails['smartcard_client_certificate_required_host'] = 'redacted.mil'
#gitlab_rails['smartcard_client_certificate_required_port'] = 3444
gitlab_rails['smartcard_required_for_git_access'] = true
gitlab_rails['smartcard_san_extensions'] = true
Manual LDAP query with rails seems to work
=> 0
irb(main):003:0> adapter = Gitlab::Auth::Ldap::Adapter.new('ldapmain') # If `main` is the L
DAP provider
=>
#<Gitlab::Auth::Ldap::Adapter:0x00007fca205e6ae0
irb(main):005:0> Gitlab::Auth::Ldap::Person.find_by_uid('redacted.user', adapter)
=>
#<Gitlab::Auth::Ldap::Person:0x00007fca21a9a900
@entry=
#<Net::LDAP::Entry:0x00007fca21a9cbd8
@myhash=
{:dn=>
["CN=Redacted M. User -CTR,OU=Place,OU=Place,OU=Place,OU=Place,DC=Place,DC=Place,DC=MIL"],
:sn=>["User"],
:givenname=>["Redacted"],
:displayname=>["Redacted. User - CTR"],
:memberof=>
Lots of membership information,
:samaccountname=>["Redacted M. User"],
:mail=>["redacted.user@.mil"]}>,
@provider="ldapmain">
Our smartcards are provided to us and cannot change the contents on them. I have been trying to match based on principal name because in the SAN of our certs is the PN (pricipalName) which is would DoD ID. This is how active directory finds us in the directory. I have tried using other ways to match and find the users in active directory but without success.
The SAN in my cert
userPrincipalName in AD
It's almost as if the cert is not making it to the rails backend for processing.
What is the expected correct behavior?
Smartcard users in my organization should be able to have their principal name on their cert matched to their userPriniciaplName in active directory.


