Commit 56c480e5 authored by Max Woolf's avatar Max Woolf

Adds ability to enforce SSH key expiration

When enabled, this will cause SSH keys
that have expired to no longer work.
parent a2973bc3
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
= render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f = render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f
= render_if_exists 'admin/application_settings/enforce_pat_expiration', form: f = render_if_exists 'admin/application_settings/enforce_pat_expiration', form: f
= render_if_exists 'admin/application_settings/enforce_ssh_key_expiration', form: f
.form-group .form-group
= f.label :user_oauth_applications, _('User OAuth applications'), class: 'label-bold' = f.label :user_oauth_applications, _('User OAuth applications'), class: 'label-bold'
......
---
title: Add enforced SSH key expiration
merge_request: 51921
author:
type: added
# frozen_string_literal: true
class AddEnforceSshKeyExpirationToApplicationSettings < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :application_settings, :enforce_ssh_key_expiration, :boolean, default: false, null: false
end
end
f33cc3eebc9197db381d81150a140582e30905d3964d6fb444caad6c9eff1b31
\ No newline at end of file
...@@ -9410,6 +9410,7 @@ CREATE TABLE application_settings ( ...@@ -9410,6 +9410,7 @@ CREATE TABLE application_settings (
rate_limiting_response_text text, rate_limiting_response_text text,
invisible_captcha_enabled boolean DEFAULT false NOT NULL, invisible_captcha_enabled boolean DEFAULT false NOT NULL,
container_registry_cleanup_tags_service_max_list_size integer DEFAULT 200 NOT NULL, container_registry_cleanup_tags_service_max_list_size integer DEFAULT 200 NOT NULL,
enforce_ssh_key_expiration boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)), CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)),
......
...@@ -164,6 +164,40 @@ Once a lifetime for personal access tokens is set, GitLab will: ...@@ -164,6 +164,40 @@ Once a lifetime for personal access tokens is set, GitLab will:
- After three hours, revoke old tokens with no expiration date or with a lifetime longer than the - After three hours, revoke old tokens with no expiration date or with a lifetime longer than the
allowed lifetime. Three hours is given to allow administrators to change the allowed lifetime, allowed lifetime. Three hours is given to allow administrators to change the allowed lifetime,
or remove it, before revocation takes place. or remove it, before revocation takes place.
## Enforcement of SSH key expiration **(ULTIMATE ONLY)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276221) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.9.
> - It is deployed behind a feature flag, disabled by default.
> - It is disabled on GitLab.com.
> - It is not recommended for production use.
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-enforcement-of-ssh-key-expiration-feature). **(CORE ONLY)**
GitLab administrators can choose to enforce the expiration of SSH keys after their expiration dates.
If you enable this feature, this disables all _expired_ SSH keys.
To do this:
1. Navigate to **Admin Area > Settings > General**.
1. Expand the **Account and limit** section.
1. Select the **Enforce SSH key expiration** checkbox.
### Enable or disable enforcement of SSH key expiration Feature **(CORE ONLY)**
Enforcement of SSH key expiry is deployed behind a feature flag and is **disabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md) can enable it for your instance from the [rails console](../../../administration/feature_flags.md#start-the-gitlab-rails-console).
To enable it:
```ruby
Feature.enable(:ff_enforce_ssh_key_expiration)
```
To disable it:
```ruby
Feature.disable(:ff_enforce_ssh_key_expiration)
```
## Optional enforcement of Personal Access Token expiry **(ULTIMATE ONLY)** ## Optional enforcement of Personal Access Token expiry **(ULTIMATE ONLY)**
......
...@@ -50,6 +50,7 @@ module EE ...@@ -50,6 +50,7 @@ module EE
:elasticsearch_analyzers_kuromoji_search, :elasticsearch_analyzers_kuromoji_search,
:enforce_namespace_storage_limit, :enforce_namespace_storage_limit,
:enforce_pat_expiration, :enforce_pat_expiration,
:enforce_ssh_key_expiration,
:geo_node_allowed_ips, :geo_node_allowed_ips,
:geo_status_timeout, :geo_status_timeout,
:help_text, :help_text,
......
...@@ -8,12 +8,28 @@ module EE ...@@ -8,12 +8,28 @@ module EE
include UsageStatistics include UsageStatistics
scope :ldap, -> { where(type: 'LDAPKey') } scope :ldap, -> { where(type: 'LDAPKey') }
validate :expiration, if: -> { ::Key.expiration_enforced? }
def expiration
errors.add(:key, 'has expired and the instance administrator has enforced expiration') if expired?
end
end end
class_methods do class_methods do
def regular_keys def regular_keys
where(type: ['LDAPKey', 'Key', nil]) where(type: ['LDAPKey', 'Key', nil])
end end
def expiration_enforced?
return false unless enforce_ssh_key_expiration_feature_available?
::Gitlab::CurrentSettings.enforce_ssh_key_expiration?
end
def enforce_ssh_key_expiration_feature_available?
License.feature_available?(:enforce_ssh_key_expiration) && ::Feature.enabled?(:ff_enforce_ssh_key_expiration)
end
end end
end end
end end
...@@ -142,6 +142,7 @@ class License < ApplicationRecord ...@@ -142,6 +142,7 @@ class License < ApplicationRecord
dependency_scanning dependency_scanning
devops_adoption devops_adoption
enforce_pat_expiration enforce_pat_expiration
enforce_ssh_key_expiration
enterprise_templates enterprise_templates
environment_alerts environment_alerts
group_ci_cd_analytics group_ci_cd_analytics
......
- return unless Key.enforce_ssh_key_expiration_feature_available?
- form = local_assigns.fetch(:form)
.form-group
.form-check
= form.check_box :enforce_ssh_key_expiration, class: 'form-check-input'
= form.label :enforce_ssh_key_expiration, class: 'form-check-label' do
= _('Enforce SSH key expiration')
---
name: ff_enforce_ssh_key_expiration
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51921
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/299092
milestone: '13.9'
type: development
group: group::compliance
default_enabled: false
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe EE::ApplicationSettingsHelper do RSpec.describe EE::ApplicationSettingsHelper do
describe '.visible_attributes' do describe '.visible_attributes' do
context 'personal access token parameters' do context 'personal access token parameters' do
it { expect(visible_attributes).to include(*%i(max_personal_access_token_lifetime enforce_pat_expiration)) } it { expect(visible_attributes).to include(*%i(max_personal_access_token_lifetime enforce_pat_expiration enforce_ssh_key_expiration)) }
end end
end end
end end
...@@ -779,6 +779,23 @@ RSpec.describe Gitlab::GitAccess do ...@@ -779,6 +779,23 @@ RSpec.describe Gitlab::GitAccess do
end end
end end
describe '#check_valid_actor!' do
context 'key expiration is enforced' do
let(:actor) { build(:personal_key, expires_at: 2.days.ago) }
before do
stub_licensed_features(enforce_ssh_key_expiration: true)
stub_feature_flags(ff_enforce_ssh_key_expiration: true)
stub_ee_application_setting(enforce_ssh_key_expiration: true)
end
it 'does not allow expired keys', :aggregate_failures do
expect { push_changes }.to raise_forbidden('Your SSH key has expired and the instance administrator has enforced expiration.')
expect { pull_changes }.to raise_forbidden('Your SSH key has expired and the instance administrator has enforced expiration.')
end
end
end
private private
def access def access
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Key do
describe 'validations' do
describe 'expiration' do
using RSpec::Parameterized::TableSyntax
where(:key, :expiration_enforced, :valid ) do
build(:personal_key, expires_at: 2.days.ago) | true | false
build(:personal_key, expires_at: 2.days.ago) | false | true
build(:personal_key) | false | true
build(:personal_key) | true | true
end
with_them do
it 'checks if ssh key expiration is enforced' do
expect(Key).to receive(:expiration_enforced?).and_return(expiration_enforced)
expect(key.valid?).to eq(valid)
end
end
end
end
describe '.expiration_enforced?' do
using RSpec::Parameterized::TableSyntax
where(:feature_enabled, :licensed, :application_setting, :available) do
true | true | true | true
true | true | false | false
true | false | true | false
true | false | false | false
false | true | true | false
false | true | false | false
false | false | true | false
false | false | false | false
end
with_them do
before do
stub_feature_flags(ff_enforce_ssh_key_expiration: feature_enabled)
stub_licensed_features(enforce_ssh_key_expiration: licensed)
stub_ee_application_setting(enforce_ssh_key_expiration: application_setting)
end
it 'checks if ssh key expiration is enforced' do
expect(described_class.expiration_enforced?).to be(available)
end
end
end
end
...@@ -10911,6 +10911,9 @@ msgstr "" ...@@ -10911,6 +10911,9 @@ msgstr ""
msgid "Enforce DNS rebinding attack protection" msgid "Enforce DNS rebinding attack protection"
msgstr "" msgstr ""
msgid "Enforce SSH key expiration"
msgstr ""
msgid "Enforce personal access token expiration" msgid "Enforce personal access token expiration"
msgstr "" msgstr ""
......
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