diff options
author | Adam Niedzielski <adamsunday@gmail.com> | 2017-01-26 19:16:50 +0100 |
---|---|---|
committer | Adam Niedzielski <adamsunday@gmail.com> | 2017-02-06 11:49:30 +0100 |
commit | 5d3816652e13cde6bf5e9de814d2c9d1e6593601 (patch) | |
tree | f6137cb0baeb01330a1729d00c059988ced600ee | |
parent | 572fb0be9b1d45437b7c0ed1000399657f471ec7 (diff) | |
download | gitlab-ce-5d3816652e13cde6bf5e9de814d2c9d1e6593601.tar.gz |
Introduce maximum session time for terminal websocket connectionterminal-max-session-time
Store the value in application settings.
Expose the value to Workhorse.
-rw-r--r-- | app/controllers/admin/application_settings_controller.rb | 1 | ||||
-rw-r--r-- | app/models/application_setting.rb | 7 | ||||
-rw-r--r-- | app/models/project_services/kubernetes_service.rb | 11 | ||||
-rw-r--r-- | app/views/admin/application_settings/_form.html.haml | 10 | ||||
-rw-r--r-- | changelogs/unreleased/terminal-max-session-time.yml | 4 | ||||
-rw-r--r-- | db/migrate/20170126174819_add_terminal_max_session_time_to_application_settings.rb | 33 | ||||
-rw-r--r-- | db/schema.rb | 1 | ||||
-rw-r--r-- | doc/administration/integration/terminal.md | 10 | ||||
-rw-r--r-- | doc/api/settings.md | 7 | ||||
-rw-r--r-- | lib/api/entities.rb | 1 | ||||
-rw-r--r-- | lib/api/settings.rb | 3 | ||||
-rw-r--r-- | lib/gitlab/kubernetes.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/workhorse.rb | 3 | ||||
-rw-r--r-- | spec/lib/gitlab/workhorse_spec.rb | 6 | ||||
-rw-r--r-- | spec/models/project_services/kubernetes_service_spec.rb | 16 | ||||
-rw-r--r-- | spec/support/kubernetes_helpers.rb | 3 |
16 files changed, 107 insertions, 13 deletions
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 543d5eac504..3f10ae81767 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -137,6 +137,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :user_default_external, :user_oauth_applications, :version_check_enabled, + :terminal_max_session_time, disabled_oauth_sign_in_sources: [], import_sources: [], diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 2df8b071e13..9a4557524c4 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -111,6 +111,10 @@ class ApplicationSetting < ActiveRecord::Base presence: true, numericality: { only_integer: true, greater_than: :housekeeping_full_repack_period } + validates :terminal_max_session_time, + presence: true, + numericality: { only_integer: true, greater_than_or_equal_to: 0 } + validates_each :restricted_visibility_levels do |record, attr, value| unless value.nil? value.each do |level| @@ -204,7 +208,8 @@ class ApplicationSetting < ActiveRecord::Base signin_enabled: Settings.gitlab['signin_enabled'], signup_enabled: Settings.gitlab['signup_enabled'], two_factor_grace_period: 48, - user_default_external: false + user_default_external: false, + terminal_max_session_time: 0 } end diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index fa3cedc4354..f2f019c43bb 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -1,4 +1,5 @@ class KubernetesService < DeploymentService + include Gitlab::CurrentSettings include Gitlab::Kubernetes include ReactiveCaching @@ -110,7 +111,7 @@ class KubernetesService < DeploymentService pods = data.fetch(:pods, nil) filter_pods(pods, app: environment.slug). flat_map { |pod| terminals_for_pod(api_url, namespace, pod) }. - map { |terminal| add_terminal_auth(terminal, token, ca_pem) } + each { |terminal| add_terminal_auth(terminal, terminal_auth) } end end @@ -170,4 +171,12 @@ class KubernetesService < DeploymentService url.to_s end + + def terminal_auth + { + token: token, + ca_pem: ca_pem, + max_session_time: current_application_settings.terminal_max_session_time + } + end end diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index e7701d75a6e..2c64de1d530 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -509,5 +509,15 @@ .help-block Number of Git pushes after which 'git gc' is run. + %fieldset + %legend Web terminal + .form-group + = f.label :terminal_max_session_time, 'Max session time', class: 'control-label col-sm-2' + .col-sm-10 + = f.number_field :terminal_max_session_time, class: 'form-control' + .help-block + Maximum time for web terminal websocket connection (in seconds). + Set to 0 for unlimited time. + .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/changelogs/unreleased/terminal-max-session-time.yml b/changelogs/unreleased/terminal-max-session-time.yml new file mode 100644 index 00000000000..db1e66770d1 --- /dev/null +++ b/changelogs/unreleased/terminal-max-session-time.yml @@ -0,0 +1,4 @@ +--- +title: Introduce maximum session time for terminal websocket connection +merge_request: 8413 +author: diff --git a/db/migrate/20170126174819_add_terminal_max_session_time_to_application_settings.rb b/db/migrate/20170126174819_add_terminal_max_session_time_to_application_settings.rb new file mode 100644 index 00000000000..334f53f9145 --- /dev/null +++ b/db/migrate/20170126174819_add_terminal_max_session_time_to_application_settings.rb @@ -0,0 +1,33 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddTerminalMaxSessionTimeToApplicationSettings < ActiveRecord::Migration + include Gitlab::Database::MigrationHelpers + + # Set this constant to true if this migration requires downtime. + DOWNTIME = false + + # When a migration requires downtime you **must** uncomment the following + # constant and define a short and easy to understand explanation as to why the + # migration requires downtime. + # DOWNTIME_REASON = '' + + # When using the methods "add_concurrent_index" or "add_column_with_default" + # you must disable the use of transactions as these methods can not run in an + # existing transaction. When using "add_concurrent_index" make sure that this + # method is the _only_ method called in the migration, any other changes + # should go in a separate migration. This ensures that upon failure _only_ the + # index creation fails and can be retried or reverted easily. + # + # To disable transactions uncomment the following line and remove these + # comments: + disable_ddl_transaction! + + def up + add_column_with_default :application_settings, :terminal_max_session_time, :integer, default: 0, allow_null: false + end + + def down + remove_column :application_settings, :terminal_max_session_time + end +end diff --git a/db/schema.rb b/db/schema.rb index 92b36218a15..a9f4e865d60 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -109,6 +109,7 @@ ActiveRecord::Schema.define(version: 20170204181513) do t.boolean "html_emails_enabled", default: true t.string "plantuml_url" t.boolean "plantuml_enabled" + t.integer "terminal_max_session_time", default: 0, null: false end create_table "audit_events", force: :cascade do |t| diff --git a/doc/administration/integration/terminal.md b/doc/administration/integration/terminal.md index 3fbb13704aa..11444464537 100644 --- a/doc/administration/integration/terminal.md +++ b/doc/administration/integration/terminal.md @@ -71,5 +71,15 @@ When these headers are not passed through, Workhorse will return a `400 Bad Request` response to users attempting to use a web terminal. In turn, they will receive a `Connection failed` message. +## Limiting WebSocket connection time + +> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8413) +in GitLab 8.17. + +Terminal sessions use long-lived connections; by default, these may last +forever. You can configure a maximum session time in the Admin area of your +GitLab instance if you find this undesirable from a scalability or security +point of view. + [ce-7690]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7690 [kubservice]: ../../user/project/integrations/kubernetes.md) diff --git a/doc/api/settings.md b/doc/api/settings.md index f86c7cc2f94..ca6b9347877 100644 --- a/doc/api/settings.md +++ b/doc/api/settings.md @@ -46,7 +46,8 @@ Example response: "koding_enabled": false, "koding_url": null, "plantuml_enabled": false, - "plantuml_url": null + "plantuml_url": null, + "terminal_max_session_time": 0 } ``` @@ -84,6 +85,7 @@ PUT /application/settings | `disabled_oauth_sign_in_sources` | Array of strings | no | Disabled OAuth sign-in sources | | `plantuml_enabled` | boolean | no | Enable PlantUML integration. Default is `false`. | | `plantuml_url` | string | yes (if `plantuml_enabled` is `true`) | The PlantUML instance URL for integration. | +| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time. | ```bash curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1 @@ -118,6 +120,7 @@ Example response: "koding_enabled": false, "koding_url": null, "plantuml_enabled": false, - "plantuml_url": null + "plantuml_url": null, + "terminal_max_session_time": 0 } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index a07b2a9ca0f..b1ead48caf7 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -575,6 +575,7 @@ module API expose :koding_url expose :plantuml_enabled expose :plantuml_url + expose :terminal_max_session_time end class Release < Grape::Entity diff --git a/lib/api/settings.rb b/lib/api/settings.rb index c5eff16a5de..a1d1c1432d3 100644 --- a/lib/api/settings.rb +++ b/lib/api/settings.rb @@ -107,6 +107,7 @@ module API requires :housekeeping_full_repack_period, type: Integer, desc: "Number of Git pushes after which a full 'git repack' is run." requires :housekeeping_gc_period, type: Integer, desc: "Number of Git pushes after which 'git gc' is run." end + optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.' at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility, :default_group_visibility, :restricted_visibility_levels, :import_sources, :enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit, @@ -120,7 +121,7 @@ module API :akismet_enabled, :admin_notification_email, :sentry_enabled, :repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled, :version_check_enabled, :email_author_in_body, :html_emails_enabled, - :housekeeping_enabled + :housekeeping_enabled, :terminal_max_session_time end put "application/settings" do if current_settings.update_attributes(declared_params(include_missing: false)) diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb index 288771c1c12..3a7af363548 100644 --- a/lib/gitlab/kubernetes.rb +++ b/lib/gitlab/kubernetes.rb @@ -43,10 +43,10 @@ module Gitlab end end - def add_terminal_auth(terminal, token, ca_pem = nil) + def add_terminal_auth(terminal, token:, max_session_time:, ca_pem: nil) terminal[:headers]['Authorization'] << "Bearer #{token}" + terminal[:max_session_time] = max_session_time terminal[:ca_pem] = ca_pem if ca_pem.present? - terminal end def container_exec_url(api_url, namespace, pod_name, container_name) diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb index a3b502ffd6a..c8872df8a93 100644 --- a/lib/gitlab/workhorse.rb +++ b/lib/gitlab/workhorse.rb @@ -107,7 +107,8 @@ module Gitlab 'Terminal' => { 'Subprotocols' => terminal[:subprotocols], 'Url' => terminal[:url], - 'Header' => terminal[:headers] + 'Header' => terminal[:headers], + 'MaxSessionTime' => terminal[:max_session_time], } } details['Terminal']['CAPem'] = terminal[:ca_pem] if terminal.has_key?(:ca_pem) diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb index 7dd4d76d1a3..a32c6131030 100644 --- a/spec/lib/gitlab/workhorse_spec.rb +++ b/spec/lib/gitlab/workhorse_spec.rb @@ -42,7 +42,8 @@ describe Gitlab::Workhorse, lib: true do out = { subprotocols: ['foo'], url: 'wss://example.com/terminal.ws', - headers: { 'Authorization' => ['Token x'] } + headers: { 'Authorization' => ['Token x'] }, + max_session_time: 600 } out[:ca_pem] = ca_pem if ca_pem out @@ -53,7 +54,8 @@ describe Gitlab::Workhorse, lib: true do 'Terminal' => { 'Subprotocols' => ['foo'], 'Url' => 'wss://example.com/terminal.ws', - 'Header' => { 'Authorization' => ['Token x'] } + 'Header' => { 'Authorization' => ['Token x'] }, + 'MaxSessionTime' => 600 } } out['Terminal']['CAPem'] = ca_pem if ca_pem diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb index 4f3cd14e941..9052479d35e 100644 --- a/spec/models/project_services/kubernetes_service_spec.rb +++ b/spec/models/project_services/kubernetes_service_spec.rb @@ -181,11 +181,23 @@ describe KubernetesService, models: true, caching: true do let(:pod) { kube_pod(app: environment.slug) } let(:terminals) { kube_terminals(service, pod) } - it 'returns terminals' do - stub_reactive_cache(service, pods: [ pod, pod, kube_pod(app: "should-be-filtered-out") ]) + before do + stub_reactive_cache( + service, + pods: [ pod, pod, kube_pod(app: "should-be-filtered-out") ] + ) + end + it 'returns terminals' do is_expected.to eq(terminals + terminals) end + + it 'uses max session time from settings' do + stub_application_setting(terminal_max_session_time: 600) + + times = subject.map { |terminal| terminal[:max_session_time] } + expect(times).to eq [600, 600, 600, 600] + end end end diff --git a/spec/support/kubernetes_helpers.rb b/spec/support/kubernetes_helpers.rb index 6c4c246a68b..444612cf871 100644 --- a/spec/support/kubernetes_helpers.rb +++ b/spec/support/kubernetes_helpers.rb @@ -43,7 +43,8 @@ module KubernetesHelpers url: container_exec_url(service.api_url, service.namespace, pod_name, container['name']), subprotocols: ['channel.k8s.io'], headers: { 'Authorization' => ["Bearer #{service.token}"] }, - created_at: DateTime.parse(pod['metadata']['creationTimestamp']) + created_at: DateTime.parse(pod['metadata']['creationTimestamp']), + max_session_time: 0 } terminal[:ca_pem] = service.ca_pem if service.ca_pem.present? terminal |