From 44433b601d79c7a028f0375e0b084fba9bc85173 Mon Sep 17 00:00:00 2001 From: Drew Blessing Date: Wed, 7 Sep 2022 11:17:45 -0500 Subject: [PATCH] Disable all types of personal access tokens when FIPS enabled When FIPS is enabled, disable the use of all types of personal access tokens including user PATs, group access tokens, project access tokens, and impersonation tokens. Changelog: added EE: true --- app/models/application_setting.rb | 4 + .../group/settings/group_access_tokens.md | 3 + doc/user/profile/personal_access_tokens.md | 3 + .../project/settings/project_access_tokens.md | 3 + ee/app/models/ee/application_setting.rb | 10 +++ ee/app/models/ee/personal_access_token.rb | 8 ++ .../models/gitlab_subscriptions/features.rb | 1 + .../lib/ee/gitlab/auth/auth_finders_spec.rb | 83 ++++++++++++++++++- ee/spec/lib/gitlab/auth_spec.rb | 38 +++++++++ ee/spec/models/application_setting_spec.rb | 46 ++++++++++ .../models/ee/personal_access_token_spec.rb | 18 ++++ spec/lib/gitlab/auth/auth_finders_spec.rb | 2 +- spec/models/application_setting_spec.rb | 6 ++ 13 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 ee/spec/lib/gitlab/auth_spec.rb diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index edb9a2053b1222..d6f729891883a8 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -791,6 +791,10 @@ def kroki_format_supported?(diagram_type) ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES.include?(diagram_type) end + def personal_access_tokens_disabled? + false + end + private def parsed_grafana_url diff --git a/doc/user/group/settings/group_access_tokens.md b/doc/user/group/settings/group_access_tokens.md index a22658bd60292a..64cbe68a9d4560 100644 --- a/doc/user/group/settings/group_access_tokens.md +++ b/doc/user/group/settings/group_access_tokens.md @@ -48,6 +48,9 @@ You cannot use group access tokens to create other group, project, or personal a Group access tokens inherit the [default prefix setting](../../admin_area/settings/account_and_limit_settings.md#personal-access-token-prefix) configured for personal access tokens. +NOTE: +Group access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../../development/fips_compliance.md) is enabled. + ## Create a group access token using UI > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7. diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 17c5e1dbfa7819..29dce202a3764e 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -45,6 +45,9 @@ For examples of how you can use a personal access token to authenticate with the Alternately, GitLab administrators can use the API to create [impersonation tokens](../../api/index.md#impersonation-tokens). Use impersonation tokens to automate authentication as a specific user. +NOTE: +Personal access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../development/fips_compliance.md) is enabled. + ## Create a personal access token > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days is populated in the UI. diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md index 16a884270ffb75..c1652251b9f6d4 100644 --- a/doc/user/project/settings/project_access_tokens.md +++ b/doc/user/project/settings/project_access_tokens.md @@ -48,6 +48,9 @@ You cannot use project access tokens to create other group, project, or personal Project access tokens inherit the [default prefix setting](../../admin_area/settings/account_and_limit_settings.md#personal-access-token-prefix) configured for personal access tokens. +NOTE: +Project access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../../development/fips_compliance.md) is enabled. + ## Create a project access token > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens. diff --git a/ee/app/models/ee/application_setting.rb b/ee/app/models/ee/application_setting.rb index 124c649dd8ad63..5c119468006eb7 100644 --- a/ee/app/models/ee/application_setting.rb +++ b/ee/app/models/ee/application_setting.rb @@ -415,6 +415,16 @@ def should_apply_user_signup_cap? ::Gitlab::CurrentSettings.new_user_signups_cap.present? end + override :personal_access_tokens_disabled? + def personal_access_tokens_disabled? + License.feature_available?(:fips_disable_personal_access_tokens) && ::Gitlab::FIPS.enabled? + end + + def disable_feed_token + personal_access_tokens_disabled? || read_attribute(:disable_feed_token) + end + alias_method :disable_feed_token?, :disable_feed_token + private def elasticsearch_limited_project_exists?(project) diff --git a/ee/app/models/ee/personal_access_token.rb b/ee/app/models/ee/personal_access_token.rb index 52879829670902..d7b4abb491eab7 100644 --- a/ee/app/models/ee/personal_access_token.rb +++ b/ee/app/models/ee/personal_access_token.rb @@ -27,6 +27,8 @@ module PersonalAccessToken end class_methods do + extend ::Gitlab::Utils::Override + def pluck_names pluck(:name) end @@ -39,6 +41,12 @@ def with_invalid_expires_at(max_lifetime, limit = 1_000) ] ) end + + # Disable lookup by token (token auth) when PATs disabled (FIPS) + override :find_by_token + def find_by_token(token) + super unless ::Gitlab::CurrentSettings.personal_access_tokens_disabled? + end end override :revoke! diff --git a/ee/app/models/gitlab_subscriptions/features.rb b/ee/app/models/gitlab_subscriptions/features.rb index b8bc869ce3a3fa..caa780ad6c5d7e 100644 --- a/ee/app/models/gitlab_subscriptions/features.rb +++ b/ee/app/models/gitlab_subscriptions/features.rb @@ -106,6 +106,7 @@ class Features feature_flags_related_issues feature_flags_code_references file_locks + fips_disable_personal_access_tokens geo generic_alert_fingerprinting git_two_factor_enforcement diff --git a/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb b/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb index 8987c8ac4995db..160c0faf537df6 100644 --- a/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb +++ b/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' -RSpec.describe EE::Gitlab::Auth::AuthFinders do +RSpec.describe Gitlab::Auth::AuthFinders do include described_class include ::EE::GeoHelpers - let(:current_request) { ActionDispatch::Request.new(env) } + let(:request) { ActionDispatch::Request.new(env) } let(:env) do { 'rack.input' => '' @@ -31,7 +31,7 @@ stub_current_geo_node(primary) env['SCRIPT_NAME'] = path - current_request.headers['Authorization'] = authorization_header + request.headers['Authorization'] = authorization_header end it { is_expected.to eq(user) } @@ -106,4 +106,81 @@ end end end + + describe '#find_user_from_bearer_token' do + context 'with a personal access token' do + before do + env[described_class::PRIVATE_TOKEN_HEADER] = create(:personal_access_token, user: user).token + end + + it 'returns user' do + expect(find_user_from_bearer_token).to eq user + end + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'raises unauthorized error' do + expect { find_user_from_bearer_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + end + end + end + end + + describe '#find_user_from_access_token' do + before do + env[described_class::PRIVATE_TOKEN_HEADER] = create(:personal_access_token, user: user).token + end + + context 'when validate_access_token! returns valid' do + it 'returns user' do + expect(find_user_from_access_token).to eq user + end + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'raised unauthorized error' do + expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + end + end + end + end + + describe '#find_user_from_feed_token' do + context 'when the request format is atom' do + before do + env['SCRIPT_NAME'] = 'url.atom' + env['HTTP_ACCEPT'] = 'application/atom+xml' + end + + context 'when feed_token param is provided' do + context 'when the feed token is valid' do + before do + request.update_param(:feed_token, user.feed_token) + end + + context 'when FIPS mode is enabled', :fips_mode do + it 'returns user' do + expect(find_user_from_feed_token(:rss)).to eq user + end + + context 'when fips_disable_personal_access_tokens feature is licensed' do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'returns nil' do + expect(find_user_from_feed_token(:rss)).to be_nil + end + end + end + end + end + end + end end diff --git a/ee/spec/lib/gitlab/auth_spec.rb b/ee/spec/lib/gitlab/auth_spec.rb new file mode 100644 index 00000000000000..9ba1a9eeae0a8e --- /dev/null +++ b/ee/spec/lib/gitlab/auth_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do + let_it_be(:project) { create(:project) } + + let(:auth_failure) { { actor: nil, project: nil, type: nil, authentication_abilities: nil } } + let(:gl_auth) { described_class } + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'fails authentication when using personal access tokens' do + personal_access_token = create(:personal_access_token, scopes: ['api']) + + expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')) + .to have_attributes(auth_failure) + end + + it 'fails authentication when using impersonation tokens' do + impersonation_token = create(:personal_access_token, :impersonation, scopes: ['api']) + + expect(gl_auth.find_for_git_client('', impersonation_token.token, project: nil, ip: 'ip')) + .to have_attributes(auth_failure) + end + + it 'fails authentication when using a resource access token' do + project_bot_user = create(:user, :project_bot) + access_token = create(:personal_access_token, user: project_bot_user) + + expect(gl_auth.find_for_git_client(project_bot_user.username, access_token.token, project: project, ip: 'ip')) + .to have_attributes(auth_failure) + end + end +end diff --git a/ee/spec/models/application_setting_spec.rb b/ee/spec/models/application_setting_spec.rb index fa54271a255eda..c4f7d980956067 100644 --- a/ee/spec/models/application_setting_spec.rb +++ b/ee/spec/models/application_setting_spec.rb @@ -961,4 +961,50 @@ def expect_is_es_licensed it { is_expected.to allow_value(true).for(:delayed_project_removal) } end end + + describe '#personal_access_tokens_disabled?' do + subject { setting.personal_access_tokens_disabled? } + + context 'when the licensed feature is not available' do + before do + stub_licensed_features(fips_disable_personal_access_tokens: false) + end + + it { is_expected.to eq(false) } + end + + context 'when the licensed feature is available' do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + context 'when FIPS mode is disabled', fips_mode: false do + it { is_expected.to eq(false) } + end + + context 'when FIPS mode is enabled', :fips_mode do + it { is_expected.to eq(true) } + end + end + end + + describe '#disable_feed_token' do + subject { setting.disable_feed_token } + + before do + setting.update!(disable_feed_token: false) + end + + context 'when personal access tokens are disabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it { is_expected.to eq(true) } + end + + context 'when personal access tokens are enabled' do + it { is_expected.to eq(false) } + end + end end diff --git a/ee/spec/models/ee/personal_access_token_spec.rb b/ee/spec/models/ee/personal_access_token_spec.rb index 7fb6c783a6eaf3..8b8356b6e56819 100644 --- a/ee/spec/models/ee/personal_access_token_spec.rb +++ b/ee/spec/models/ee/personal_access_token_spec.rb @@ -209,6 +209,24 @@ end end + describe '.find_by_token' do + let!(:token) { create(:personal_access_token) } + + it 'finds the token' do + expect(described_class.find_by_token(token.token)).to eq(token) + end + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'does not find the token' do + expect(described_class.find_by_token(token.token)).to be_nil + end + end + end + shared_context 'write to cache' do let_it_be(:pat) { create(:personal_access_token) } let_it_be(:cache_keys) { %w(token_expired_rotation token_expiring_rotation) } diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb index d0b44135a2f0f9..e2226952d15a57 100644 --- a/spec/lib/gitlab/auth/auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/auth_finders_spec.rb @@ -188,7 +188,7 @@ def set_bearer_token(token) end it 'returns nil if valid feed_token and disabled' do - stub_application_setting(disable_feed_token: true) + allow(Gitlab::CurrentSettings).to receive_messages(disable_feed_token: true) set_param(:feed_token, user.feed_token) expect(find_user_from_feed_token(:rss)).to be_nil diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b5f153e7add5a6..db64e8439a5d9a 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1453,4 +1453,10 @@ def expect_invalid expect(setting.personal_access_token_prefix).to eql('glpat-') end end + + describe '.personal_access_tokens_disabled?' do + it 'is false' do + expect(setting.personal_access_tokens_disabled?).to eq(false) + end + end end -- GitLab