Commit 1d94757c authored by Douwe Maan's avatar Douwe Maan Committed by Yorick Peterse

Merge branch 'container-registry-token-ttl' into 'master'

Add Application Setting to configure Container Registry token expire delay (default 5min)

This adds an option to configure Container Registry token expire delay. The default is set to 5mins (something that is also used by Docker Hub).

What is left:
* [x] Write test to check the expire_delay

Fixes: https://gitlab.com/gitlab-org/gitlab-ce/issues/17890

@stanhu I think that this should land in patch release of 8.8.

See merge request !4364
parent 0df4fa78
...@@ -10,6 +10,7 @@ v 8.8.3 ...@@ -10,6 +10,7 @@ v 8.8.3
- Fix import URL migration not rescuing with the correct Error. !4321 - Fix import URL migration not rescuing with the correct Error. !4321
- Fix health check access token changing due to old application settings being used. !4332 - Fix health check access token changing due to old application settings being used. !4332
- Make authentication service for Container Registry to be compatible with < Docker 1.11. !4363 - Make authentication service for Container Registry to be compatible with < Docker 1.11. !4363
- Add Application Setting to configure Container Registry token expire delay (default 5min). !4364
v 8.8.2 v 8.8.2
- Added remove due date button. !4209 - Added remove due date button. !4209
......
...@@ -107,6 +107,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -107,6 +107,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:repository_checks_enabled, :repository_checks_enabled,
:metrics_packet_size, :metrics_packet_size,
:send_user_confirmation_email, :send_user_confirmation_email,
:container_registry_token_expire_delay,
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [], import_sources: [],
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: []
......
...@@ -51,6 +51,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -51,6 +51,10 @@ class ApplicationSetting < ActiveRecord::Base
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
validates :container_registry_token_expire_delay,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates_each :restricted_visibility_levels do |record, attr, value| validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil? unless value.nil?
value.each do |level| value.each do |level|
...@@ -125,7 +129,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -125,7 +129,8 @@ class ApplicationSetting < ActiveRecord::Base
akismet_enabled: false, akismet_enabled: false,
repository_checks_enabled: true, repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [], disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false send_user_confirmation_email: false,
container_registry_token_expire_delay: 5,
) )
end end
......
module Auth module Auth
class ContainerRegistryAuthenticationService < BaseService class ContainerRegistryAuthenticationService < BaseService
include Gitlab::CurrentSettings
AUDIENCE = 'container_registry' AUDIENCE = 'container_registry'
def execute def execute
...@@ -17,6 +19,7 @@ module Auth ...@@ -17,6 +19,7 @@ module Auth
token = JSONWebToken::RSAToken.new(registry.key) token = JSONWebToken::RSAToken.new(registry.key)
token.issuer = registry.issuer token.issuer = registry.issuer
token.audience = AUDIENCE token.audience = AUDIENCE
token.expire_time = token_expire_at
token[:access] = names.map do |name| token[:access] = names.map do |name|
{ type: 'repository', name: name, actions: %w(*) } { type: 'repository', name: name, actions: %w(*) }
end end
...@@ -30,6 +33,7 @@ module Auth ...@@ -30,6 +33,7 @@ module Auth
token.issuer = registry.issuer token.issuer = registry.issuer
token.audience = params[:service] token.audience = params[:service]
token.subject = current_user.try(:username) token.subject = current_user.try(:username)
token.expire_time = ContainerRegistryAuthenticationService.token_expire_at
token[:access] = accesses.compact token[:access] = accesses.compact
token token
end end
...@@ -75,5 +79,9 @@ module Auth ...@@ -75,5 +79,9 @@ module Auth
def registry def registry
Gitlab.config.registry Gitlab.config.registry
end end
def self.token_expire_at
Time.now + current_application_settings.container_registry_token_expire_delay.minutes
end
end end
end end
...@@ -178,6 +178,14 @@ ...@@ -178,6 +178,14 @@
.col-sm-10 .col-sm-10
= f.number_field :max_artifacts_size, class: 'form-control' = f.number_field :max_artifacts_size, class: 'form-control'
- if Gitlab.config.registry.enabled
%fieldset
%legend Container Registry
.form-group
= f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :container_registry_token_expire_delay, class: 'form-control'
%fieldset %fieldset
%legend Metrics %legend Metrics
%p %p
......
# This is ONLINE migration
class AddContainerRegistryTokenExpireDelayToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
def change
add_column :application_settings, :container_registry_token_expire_delay, :integer, default: 5
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160509201028) do ActiveRecord::Schema.define(version: 20160530150109) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -43,45 +43,47 @@ ActiveRecord::Schema.define(version: 20160509201028) do ...@@ -43,45 +43,47 @@ ActiveRecord::Schema.define(version: 20160509201028) do
t.datetime "created_at" t.datetime "created_at"
t.datetime "updated_at" t.datetime "updated_at"
t.string "home_page_url" t.string "home_page_url"
t.integer "default_branch_protection", default: 2 t.integer "default_branch_protection", default: 2
t.text "restricted_visibility_levels" t.text "restricted_visibility_levels"
t.boolean "version_check_enabled", default: true t.boolean "version_check_enabled", default: true
t.integer "max_attachment_size", default: 10, null: false t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility" t.integer "default_project_visibility"
t.integer "default_snippet_visibility" t.integer "default_snippet_visibility"
t.text "restricted_signup_domains" t.text "restricted_signup_domains"
t.boolean "user_oauth_applications", default: true t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path" t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false t.integer "session_expire_delay", default: 10080, null: false
t.text "import_sources" t.text "import_sources"
t.text "help_page_text" t.text "help_page_text"
t.string "admin_notification_email" t.string "admin_notification_email"
t.boolean "shared_runners_enabled", default: true, null: false t.boolean "shared_runners_enabled", default: true, null: false
t.integer "max_artifacts_size", default: 100, null: false t.integer "max_artifacts_size", default: 100, null: false
t.string "runners_registration_token" t.string "runners_registration_token"
t.boolean "require_two_factor_authentication", default: false t.boolean "require_two_factor_authentication", default: false
t.integer "two_factor_grace_period", default: 48 t.integer "two_factor_grace_period", default: 48
t.boolean "metrics_enabled", default: false t.boolean "metrics_enabled", default: false
t.string "metrics_host", default: "localhost" t.string "metrics_host", default: "localhost"
t.integer "metrics_pool_size", default: 16 t.integer "metrics_pool_size", default: 16
t.integer "metrics_timeout", default: 10 t.integer "metrics_timeout", default: 10
t.integer "metrics_method_call_threshold", default: 10 t.integer "metrics_method_call_threshold", default: 10
t.boolean "recaptcha_enabled", default: false t.boolean "recaptcha_enabled", default: false
t.string "recaptcha_site_key" t.string "recaptcha_site_key"
t.string "recaptcha_private_key" t.string "recaptcha_private_key"
t.integer "metrics_port", default: 8089 t.integer "metrics_port", default: 8089
t.boolean "akismet_enabled", default: false t.boolean "akismet_enabled", default: false
t.string "akismet_api_key" t.string "akismet_api_key"
t.integer "metrics_sample_interval", default: 15 t.integer "metrics_sample_interval", default: 15
t.boolean "sentry_enabled", default: false t.boolean "sentry_enabled", default: false
t.string "sentry_dsn" t.string "sentry_dsn"
t.boolean "email_author_in_body", default: false t.boolean "email_author_in_body", default: false
t.integer "default_group_visibility" t.integer "default_group_visibility"
t.boolean "repository_checks_enabled", default: false t.boolean "repository_checks_enabled", default: false
t.text "shared_runners_text" t.text "shared_runners_text"
t.integer "metrics_packet_size", default: 1 t.integer "metrics_packet_size", default: 1
t.text "disabled_oauth_sign_in_sources" t.text "disabled_oauth_sign_in_sources"
t.string "health_check_access_token" t.string "health_check_access_token"
t.boolean "send_user_confirmation_email", default: false
t.integer "container_registry_token_expire_delay", default: 5
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
......
...@@ -37,7 +37,8 @@ Example response: ...@@ -37,7 +37,8 @@ Example response:
"created_at" : "2016-01-04T15:44:55.176Z", "created_at" : "2016-01-04T15:44:55.176Z",
"default_project_visibility" : 0, "default_project_visibility" : 0,
"gravatar_enabled" : true, "gravatar_enabled" : true,
"sign_in_text" : null "sign_in_text" : null,
"container_registry_token_expire_delay": 5
} }
``` ```
...@@ -64,6 +65,7 @@ PUT /application/settings ...@@ -64,6 +65,7 @@ PUT /application/settings
| `restricted_signup_domains` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. | | `restricted_signup_domains` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. |
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider | | `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
| `after_sign_out_path` | string | no | Where to redirect users after logout | | `after_sign_out_path` | string | no | Where to redirect users after logout |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
```bash ```bash
curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1 curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1
...@@ -90,6 +92,7 @@ Example response: ...@@ -90,6 +92,7 @@ Example response:
"default_snippet_visibility": 0, "default_snippet_visibility": 0,
"restricted_signup_domains": [], "restricted_signup_domains": [],
"user_oauth_applications": true, "user_oauth_applications": true,
"after_sign_out_path": "" "after_sign_out_path": "",
"container_registry_token_expire_delay": 5
} }
``` ```
...@@ -362,6 +362,7 @@ module API ...@@ -362,6 +362,7 @@ module API
expose :restricted_signup_domains expose :restricted_signup_domains
expose :user_oauth_applications expose :user_oauth_applications
expose :after_sign_out_path expose :after_sign_out_path
expose :container_registry_token_expire_delay
end end
class Release < Grape::Entity class Release < Grape::Entity
......
...@@ -40,6 +40,7 @@ module Gitlab ...@@ -40,6 +40,7 @@ module Gitlab
two_factor_grace_period: 48, two_factor_grace_period: 48,
akismet_enabled: false, akismet_enabled: false,
repository_checks_enabled: true, repository_checks_enabled: true,
container_registry_token_expire_delay: 5,
) )
end end
......
...@@ -17,6 +17,21 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do ...@@ -17,6 +17,21 @@ describe Auth::ContainerRegistryAuthenticationService, services: true do
shared_examples 'a valid token' do shared_examples 'a valid token' do
it { is_expected.to include(:token) } it { is_expected.to include(:token) }
it { expect(payload).to include('access') } it { expect(payload).to include('access') }
context 'a expirable' do
let(:expires_at) { Time.at(payload['exp']) }
let(:expire_delay) { 10 }
context 'for default configuration' do
it { expect(expires_at).not_to be_within(2.seconds).of(Time.now + expire_delay.minutes) }
end
context 'for changed configuration' do
before { stub_application_setting(container_registry_token_expire_delay: expire_delay) }
it { expect(expires_at).to be_within(2.seconds).of(Time.now + expire_delay.minutes) }
end
end
end end
shared_examples 'a accessible' do shared_examples 'a accessible' do
......
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