From 56a7627af0b4cc9fa9869bff88c8a3c81ca931c6 Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Thu, 13 Feb 2020 03:09:05 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- app/controllers/registrations_controller.rb | 2 - .../update_cluster-applications_v0-7-0.yml | 5 + doc/README.md | 1 + doc/development/sql.md | 2 +- .../testing_guide/best_practices.md | 12 +- doc/development/testing_guide/review_apps.md | 20 +-- .../understanding_explain_plans.md | 64 ++++---- .../img/compliance_dashboard_v12_8.png | Bin 0 -> 66215 bytes .../compliance_dashboard/index.md | 31 ++++ doc/user/application_security/index.md | 143 ++++++++---------- doc/user/clusters/applications.md | 42 +++++ doc/user/gitlab_com/index.md | 3 + ...Managed-Cluster-Applications.gitlab-ci.yml | 3 +- .../bulk_insert_safe_shared_examples.rb | 7 +- 14 files changed, 205 insertions(+), 130 deletions(-) create mode 100644 changelogs/unreleased/update_cluster-applications_v0-7-0.yml create mode 100644 doc/user/application_security/compliance_dashboard/img/compliance_dashboard_v12_8.png create mode 100644 doc/user/application_security/compliance_dashboard/index.md diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index c54687c432c..1c6cbf72cfa 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -55,8 +55,6 @@ class RegistrationsController < Devise::RegistrationsController def welcome return redirect_to new_user_registration_path unless current_user return redirect_to stored_location_or_dashboard(current_user) if current_user.role.present? && !current_user.setup_for_company.nil? - - current_user.name = nil if current_user.name == current_user.username end def update_registration diff --git a/changelogs/unreleased/update_cluster-applications_v0-7-0.yml b/changelogs/unreleased/update_cluster-applications_v0-7-0.yml new file mode 100644 index 00000000000..458a9e69d6e --- /dev/null +++ b/changelogs/unreleased/update_cluster-applications_v0-7-0.yml @@ -0,0 +1,5 @@ +--- +title: Updated cluster-applications to v0.7.0 +merge_request: 24754 +author: +type: changed diff --git a/doc/README.md b/doc/README.md index 59c27d11d99..132351f5353 100644 --- a/doc/README.md +++ b/doc/README.md @@ -359,6 +359,7 @@ The following documentation relates to the DevOps **Secure** stage: | Secure Topics | Description | |:------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------| +| [Compliance Dashboard](user/application_security/compliance_dashboard/index.md) **(ULTIMATE)** | View the most recent Merge Request activity in a group. | | [Container Scanning](user/application_security/container_scanning/index.md) **(ULTIMATE)** | Use Clair to scan docker images for known vulnerabilities. | | [Dependency List](user/application_security/dependency_list/index.md) **(ULTIMATE)** | View your project's dependencies and their known vulnerabilities. | | [Dependency Scanning](user/application_security/dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. | diff --git a/doc/development/sql.md b/doc/development/sql.md index 84ad11effc5..d584a26e455 100644 --- a/doc/development/sql.md +++ b/doc/development/sql.md @@ -82,7 +82,7 @@ on the amount of data indexed). To keep naming of these indexes consistent please use the following naming pattern: -``` +```plaintext index_TABLE_on_COLUMN_trigram ``` diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md index e3355e15455..7eb5bb21be8 100644 --- a/doc/development/testing_guide/best_practices.md +++ b/doc/development/testing_guide/best_practices.md @@ -126,7 +126,7 @@ To resume the test run, press any key. For example: -``` +```shell $ bin/rspec spec/features/auto_deploy_spec.rb:34 Running via Spring preloader in process 8999 Run options: include {:locations=>{"./spec/features/auto_deploy_spec.rb"=>[34]}} @@ -147,7 +147,7 @@ Note: `live_debug` only works on JavaScript enabled specs. Run the spec with `CHROME_HEADLESS=0`, e.g.: -``` +```shell CHROME_HEADLESS=0 bin/rspec some_spec.rb ``` @@ -242,7 +242,7 @@ This can be achieved by using [`before_all`](https://test-prof.evilmartians.io/#/before_all) hook from the [`test-prof` gem](https://rubygems.org/gems/test-prof). -``` +```ruby let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } @@ -260,14 +260,14 @@ Note that if you modify an object defined inside a `let_it_be` block, then you will need to reload the object as needed, or specify the `reload` option to reload for every example. -``` +```ruby let_it_be(:project, reload: true) { create(:project) } ``` You can also specify the `refind` option as well to completely load a new object. -``` +```ruby let_it_be(:project, refind: true) { create(:project) } ``` @@ -411,7 +411,7 @@ cause issues depending on the developer's local network. There are RSpec labels available in `spec/support/dns.rb` which you can apply to tests if you need to bypass the DNS stubbing, e.g.: -``` +```ruby it "really connects to Prometheus", :permit_dns do ``` diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md index 5f0f39cd15b..3307f29a98e 100644 --- a/doc/development/testing_guide/review_apps.md +++ b/doc/development/testing_guide/review_apps.md @@ -230,10 +230,10 @@ Look at a recent `review-deploy` job log, and at the Tiller logs. ```shell # Identify if node spikes are common or load on specific nodes which may get rebalanced by the Kubernetes scheduler -› kubectl top nodes | sort --key 3 --numeric +kubectl top nodes | sort --key 3 --numeric # Identify pods under heavy CPU load -› kubectl top pods | sort --key 2 --numeric +kubectl top pods | sort --key 2 --numeric ``` ### The `logging/user/events/FailedMount` chart is going up @@ -251,21 +251,21 @@ Any secrets or config maps older than 5 days are suspect and should be deleted. **Useful commands:** -``` +```shell # List secrets and config maps ordered by created date -› kubectl get secret,cm --sort-by='{.metadata.creationTimestamp}' | grep 'review-' +kubectl get secret,cm --sort-by='{.metadata.creationTimestamp}' | grep 'review-' # Delete all secrets that are 5 to 9 days old -› kubectl get secret --sort-by='{.metadata.creationTimestamp}' | grep '^review-' | grep '[5-9]d$' | cut -d' ' -f1 | xargs kubectl delete secret +kubectl get secret --sort-by='{.metadata.creationTimestamp}' | grep '^review-' | grep '[5-9]d$' | cut -d' ' -f1 | xargs kubectl delete secret # Delete all secrets that are 10 to 99 days old -› kubectl get secret --sort-by='{.metadata.creationTimestamp}' | grep '^review-' | grep '[1-9][0-9]d$' | cut -d' ' -f1 | xargs kubectl delete secret +kubectl get secret --sort-by='{.metadata.creationTimestamp}' | grep '^review-' | grep '[1-9][0-9]d$' | cut -d' ' -f1 | xargs kubectl delete secret # Delete all config maps that are 5 to 9 days old -› kubectl get cm --sort-by='{.metadata.creationTimestamp}' | grep 'review-' | grep -v 'dns-gitlab-review-app' | grep '[5-9]d$' | cut -d' ' -f1 | xargs kubectl delete cm +kubectl get cm --sort-by='{.metadata.creationTimestamp}' | grep 'review-' | grep -v 'dns-gitlab-review-app' | grep '[5-9]d$' | cut -d' ' -f1 | xargs kubectl delete cm # Delete all config maps that are 10 to 99 days old -› kubectl get cm --sort-by='{.metadata.creationTimestamp}' | grep 'review-' | grep -v 'dns-gitlab-review-app' | grep '[1-9][0-9]d$' | cut -d' ' -f1 | xargs kubectl delete cm +kubectl get cm --sort-by='{.metadata.creationTimestamp}' | grep 'review-' | grep -v 'dns-gitlab-review-app' | grep '[1-9][0-9]d$' | cut -d' ' -f1 | xargs kubectl delete cm ``` ### Using K9s @@ -294,7 +294,7 @@ This in turn prevented other components of the Review App to properly start After some digging, we found that new mounts were failing, when being performed with transient scopes (e.g. pods) of `systemd-mount`: -``` +```plaintext MountVolume.SetUp failed for volume "dns-gitlab-review-app-external-dns-token-sj5jm" : mount failed: exit status 1 Mounting command: systemd-run Mounting arguments: --description=Kubernetes transient mount for /var/lib/kubelet/pods/06add1c3-87b4-11e9-80a9-42010a800107/volumes/kubernetes.io~secret/dns-gitlab-review-app-external-dns-token-sj5jm --scope -- mount -t tmpfs tmpfs /var/lib/kubelet/pods/06add1c3-87b4-11e9-80a9-42010a800107/volumes/kubernetes.io~secret/dns-gitlab-review-app-external-dns-token-sj5jm @@ -342,7 +342,7 @@ clean up the list of non-`Running` pods. Following is a command to delete Review Apps based on their last deployment date (current date was June 6th at the time) with -``` +```shell helm ls -d | grep "Jun 4" | cut -f1 | xargs helm delete --purge ``` diff --git a/doc/development/understanding_explain_plans.md b/doc/development/understanding_explain_plans.md index 53b50b6332c..8f4caf286fe 100644 --- a/doc/development/understanding_explain_plans.md +++ b/doc/development/understanding_explain_plans.md @@ -14,7 +14,7 @@ WHERE visibility_level IN (0, 20); When running this on GitLab.com, we are presented with the following output: -``` +```sql Aggregate (cost=922411.76..922411.77 rows=1 width=8) -> Seq Scan on projects (cost=0.00..908044.47 rows=5746914 width=0) Filter: (visibility_level = ANY ('{0,20}'::integer[])) @@ -35,7 +35,7 @@ WHERE visibility_level IN (0, 20); This will produce: -``` +```sql Aggregate (cost=922420.60..922420.61 rows=1 width=8) (actual time=3428.535..3428.535 rows=1 loops=1) -> Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1) Filter: (visibility_level = ANY ('{0,20}'::integer[])) @@ -69,7 +69,7 @@ WHERE visibility_level IN (0, 20); This will then produce: -``` +```sql Aggregate (cost=922420.60..922420.61 rows=1 width=8) (actual time=3428.535..3428.535 rows=1 loops=1) Buffers: shared hit=208846 -> Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1) @@ -105,7 +105,7 @@ aggregate( Nodes are indicated using a `->` followed by the type of node taken. For example: -``` +```sql Aggregate (cost=922411.76..922411.77 rows=1 width=8) -> Seq Scan on projects (cost=0.00..908044.47 rows=5746914 width=0) Filter: (visibility_level = ANY ('{0,20}'::integer[])) @@ -119,7 +119,7 @@ above it. Nested nodes will look like this: -``` +```sql Aggregate (cost=176.97..176.98 rows=1 width=8) (actual time=0.252..0.252 rows=1 loops=1) Buffers: shared hit=155 -> Nested Loop (cost=0.86..176.75 rows=87 width=0) (actual time=0.035..0.249 rows=36 loops=1) @@ -142,7 +142,7 @@ Here we first perform two separate "Index Only" scans, followed by performing a Each node in a plan has a set of associated statistics, such as the cost, the number of rows produced, the number of loops performed, and more. For example: -``` +```sql Seq Scan on projects (cost=0.00..908044.47 rows=5746914 width=0) ``` @@ -157,7 +157,7 @@ influences the costs depends on a variety of settings, such as `seq_page_cost`, `cpu_tuple_cost`, and various others. The format of the costs field is as follows: -``` +```sql STARTUP COST..TOTAL COST ``` @@ -169,7 +169,7 @@ When using `EXPLAIN ANALYZE`, these statistics will also include the actual time (in milliseconds) spent, and other runtime statistics (e.g. the actual number of produced rows): -``` +```sql Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1) ``` @@ -181,7 +181,7 @@ Using `EXPLAIN (ANALYZE, BUFFERS)` will also give us information about the number of rows removed by a filter, the number of buffers used, and more. For example: -``` +```sql Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1) Filter: (visibility_level = ANY ('{0,20}'::integer[])) Rows Removed by Filter: 65677 @@ -245,7 +245,7 @@ Sorts the input rows as specified using an `ORDER BY` statement. A nested loop will execute its child nodes for every row produced by a node that precedes it. For example: -``` +```sql -> Nested Loop (cost=0.86..176.75 rows=87 width=0) (actual time=0.035..0.249 rows=36 loops=1) Buffers: shared hit=155 -> Index Only Scan using users_pkey on users users_1 (cost=0.43..4.95 rows=87 width=4) (actual time=0.029..0.123 rows=36 loops=1) @@ -287,7 +287,7 @@ WHERE twitter != ''; This will produce the following plan: -``` +```sql Aggregate (cost=845110.21..845110.22 rows=1 width=8) (actual time=1271.157..1271.158 rows=1 loops=1) Buffers: shared hit=202662 -> Seq Scan on users (cost=0.00..844969.99 rows=56087 width=0) (actual time=0.019..1265.883 rows=51833 loops=1) @@ -312,7 +312,7 @@ on the `users` table that we might be able to use. We can obtain this information by running `\d users` in a `psql` console, then scrolling down to the `Indexes:` section: -``` +```sql Indexes: "users_pkey" PRIMARY KEY, btree (id) "users_confirmation_token_key" UNIQUE CONSTRAINT, btree (confirmation_token) @@ -347,7 +347,7 @@ CREATE INDEX CONCURRENTLY twitter_test ON users (twitter); If we now re-run our query using `EXPLAIN (ANALYZE, BUFFERS)` we get the following plan: -``` +```sql Aggregate (cost=61002.82..61002.83 rows=1 width=8) (actual time=297.311..297.312 rows=1 loops=1) Buffers: shared hit=51854 dirtied=19 -> Index Only Scan using twitter_test on users (cost=0.43..60873.13 rows=51877 width=0) (actual time=279.184..293.532 rows=51833 loops=1) @@ -364,7 +364,7 @@ seconds. However, we still use 51,854 buffers, which is about 400 MB of memory. 300 milliseconds is also quite slow for such a simple query. To understand why this query is still expensive, let's take a look at the following: -``` +```sql Index Only Scan using twitter_test on users (cost=0.43..60873.13 rows=51877 width=0) (actual time=279.184..293.532 rows=51833 loops=1) Filter: ((twitter)::text <> ''::text) Rows Removed by Filter: 2487830 @@ -401,7 +401,7 @@ CREATE INDEX CONCURRENTLY twitter_test ON users (twitter) WHERE twitter != ''; Once created, if we run our query again we will be given the following plan: -``` +```sql Aggregate (cost=1608.26..1608.27 rows=1 width=8) (actual time=19.821..19.821 rows=1 loops=1) Buffers: shared hit=44036 -> Index Only Scan using twitter_test on users (cost=0.41..1479.71 rows=51420 width=0) (actual time=0.023..15.514 rows=51833 loops=1) @@ -438,7 +438,7 @@ WHERE visibility_level IN (0, 20); The output of `EXPLAIN (ANALYZE, BUFFERS)` is as follows: -``` +```sql Aggregate (cost=922420.60..922420.61 rows=1 width=8) (actual time=3428.535..3428.535 rows=1 loops=1) Buffers: shared hit=208846 -> Seq Scan on projects (cost=0.00..908053.18 rows=5746969 width=0) (actual time=0.041..2987.606 rows=5746940 loops=1) @@ -451,7 +451,7 @@ Execution time: 3428.596 ms Looking at the output we see the following Filter: -``` +```sql Filter: (visibility_level = ANY ('{0,20}'::integer[])) Rows Removed by Filter: 65677 ``` @@ -481,7 +481,7 @@ ORDER BY visibility_level ASC; For GitLab.com this produces: -``` +```sql visibility_level | amount ------------------+--------- 0 | 5071325 @@ -528,7 +528,7 @@ interacted with somehow? Fortunately, GitLab has an answer for this, and it's a table called `user_interacted_projects`. This table has the following schema: -``` +```sql Table "public.user_interacted_projects" Column | Type | Modifiers ------------+---------+----------- @@ -564,7 +564,7 @@ What we do here is the following: If we run this query we get the following plan: -``` +```sql Aggregate (cost=871.03..871.04 rows=1 width=8) (actual time=9.763..9.763 rows=1 loops=1) -> Nested Loop (cost=0.86..870.52 rows=203 width=0) (actual time=1.072..9.748 rows=143 loops=1) -> Index Scan using index_user_interacted_projects_on_user_id on user_interacted_projects (cost=0.43..160.71 rows=205 width=4) (actual time=0.939..2.508 rows=145 loops=1) @@ -580,7 +580,7 @@ If we run this query we get the following plan: Here it only took us just under 10 milliseconds to get the data. We can also see we're retrieving far fewer projects: -``` +```sql Index Scan using projects_pkey on projects (cost=0.43..3.45 rows=1 width=4) (actual time=0.049..0.050 rows=1 loops=145) Index Cond: (id = user_interacted_projects.project_id) Filter: (visibility_level = ANY ('{0,20}'::integer[])) @@ -592,14 +592,14 @@ Here we see we perform 145 loops (`loops=145`), with every loop producing 1 row If we look at the plan we also see our costs are very low: -``` +```sql Index Scan using projects_pkey on projects (cost=0.43..3.45 rows=1 width=4) (actual time=0.049..0.050 rows=1 loops=145) ``` Here our cost is only 3.45, and it only takes us 0.050 milliseconds to do so. The next index scan is a bit more expensive: -``` +```sql Index Scan using index_user_interacted_projects_on_user_id on user_interacted_projects (cost=0.43..160.71 rows=205 width=4) (actual time=0.939..2.508 rows=145 loops=1) ``` @@ -609,7 +609,7 @@ Here the cost is 160.71 (`cost=0.43..160.71`), taking about 2.5 milliseconds The most expensive part here is the "Nested Loop" that acts upon the result of these two index scans: -``` +```sql Nested Loop (cost=0.86..870.52 rows=203 width=0) (actual time=1.072..9.748 rows=143 loops=1) ``` @@ -687,13 +687,13 @@ Execution time: 0.113 ms `/chatops` slash command](chatops_on_gitlabcom.md). You can use chatops to get a query plan by running the following: -``` +```sql /chatops run explain SELECT COUNT(*) FROM projects WHERE visibility_level IN (0, 20) ``` Visualising the plan using is also supported: -``` +```sql /chatops run explain --visual SELECT COUNT(*) FROM projects WHERE visibility_level IN (0, 20) ``` @@ -701,7 +701,7 @@ Quoting the query is not necessary. For more information about the available options, run: -``` +```sql /chatops run explain --help ``` @@ -714,31 +714,31 @@ For example, in order to test new index you can do the following: Create the index: -``` +```sql exec CREATE INDEX index_projects_marked_for_deletion ON projects (marked_for_deletion_at) WHERE marked_for_deletion_at IS NOT NULL ``` Analyze the table to update its statistics: -``` +```sql exec ANALYZE projects ``` Get the query plan: -``` +```sql explain SELECT * FROM projects WHERE marked_for_deletion_at < CURRENT_DATE ``` Once done you can rollback your changes: -``` +```sql reset ``` For more information about the available options, run: -``` +```sql help ``` diff --git a/doc/user/application_security/compliance_dashboard/img/compliance_dashboard_v12_8.png b/doc/user/application_security/compliance_dashboard/img/compliance_dashboard_v12_8.png new file mode 100644 index 0000000000000000000000000000000000000000..5fc54927de7c9925be17dbea3442a10ab83d9237 GIT binary patch literal 66215 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 Gp$P!|G#G^d literal 0 HcmV?d00001 diff --git a/doc/user/application_security/compliance_dashboard/index.md b/doc/user/application_security/compliance_dashboard/index.md new file mode 100644 index 00000000000..afe3ce185e6 --- /dev/null +++ b/doc/user/application_security/compliance_dashboard/index.md @@ -0,0 +1,31 @@ +--- +type: reference, howto +--- + +# Compliance Dashboard **(ULTIMATE)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/36524) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.8. + +The Compliance Dashboard gives you the ability to see a group's Merge Request activity +by providing a high-level view for all projects in the group. For example, code approved +for merging into production. + +## Overview + +To access the Compliance Dashboard for a group, navigate to **{shield}** **Security & Compliance > Compliance** on the group's menu. + +![Compliance Dashboard](img/compliance_dashboard_v12_8.png) + +## Use cases + +This feature is for people who care about the compliance status of projects within their group. + +You can use the dashboard to: + +- Get an overview of the latest Merge Request for each project. +- See if Merge Requests were approved and by whom. + +## Permissions + +- On [GitLab Ultimate](https://about.gitlab.com/pricing/) tier. +- By **Administrators** and **Group Owners**. diff --git a/doc/user/application_security/index.md b/doc/user/application_security/index.md index e1bd9ca742b..a48152c2aab 100644 --- a/doc/user/application_security/index.md +++ b/doc/user/application_security/index.md @@ -4,26 +4,22 @@ type: reference, howto # GitLab Secure **(ULTIMATE)** -Check your application for security vulnerabilities that may lead to -unauthorized access, data leaks, and denial of services. +GitLab can check your application for security vulnerabilities that may lead to unauthorized access, +data leaks, denial of services, and more. GitLab reports vulnerabilities in the merge request so you +can fix them before merging. The [Security Dashboard](security_dashboard/index.md) provides a +high-level view of vulnerabilities detected in your projects, pipeline, and groups. With the +information provided, you can immediately begin risk analysis and remediation. -GitLab will perform static and dynamic tests on the code of your application, -looking for known flaws and report them in the merge request so you can fix -them before merging. - -Security teams can use dashboards to get a high-level view on projects and -groups, and start remediation processes when needed. - - For an overview of application security with GitLab, see [Security Deep Dive](https://www.youtube.com/watch?v=k4vEJnGYy84). ## Security scanning tools -GitLab can scan and report any vulnerabilities found in your project. +GitLab uses the following tools to scan and report known vulnerabilities found in your project. | Secure scanning tool | Description | |:-----------------------------------------------------------------------------|:-----------------------------------------------------------------------| +| [Compliance Dashboard](compliance_dashboard/index.md) **(ULTIMATE)** | View the most recent Merge Request activity in a group. | | [Container Scanning](container_scanning/index.md) **(ULTIMATE)** | Scan Docker containers for known vulnerabilities. | | [Dependency List](dependency_list/index.md) **(ULTIMATE)** | View your project's dependencies and their known vulnerabilities. | | [Dependency Scanning](dependency_scanning/index.md) **(ULTIMATE)** | Analyze your dependencies for known vulnerabilities. | @@ -34,26 +30,22 @@ GitLab can scan and report any vulnerabilities found in your project. ## Maintenance and update of the vulnerabilities database -The various scanning tools and the vulnerabilities database are updated regularly. +The scanning tools and vulnerabilities database are updated regularly. | Secure scanning tool | Vulnerabilities database updates | |:-------------------------------------------------------------|-------------------------------------------| -| [Container Scanning](container_scanning/index.md) | Uses `clair` underneath and the latest `clair-db` version is used for each job run by running the [`latest` docker image tag](https://gitlab.com/gitlab-org/gitlab/blob/438a0a56dc0882f22bdd82e700554525f552d91b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L37). The `clair-db` database [is updated daily according to the author](https://github.com/arminc/clair-local-scan#clair-server-or-local). | -| [Dependency Scanning](dependency_scanning/index.md) | Relies on `bundler-audit` (for Rubygems), `retire.js` (for NPM packages) and `gemnasium` (GitLab's own tool for all libraries). `bundler-audit` and `retire.js` both fetch their vulnerabilities data from GitHub repositories, so vulnerabilities added to `ruby-advisory-db` and `retire.js` are immediately available. The tools themselves are updated once per month if there's a new version. The [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is updated at least once a week. | -| [Dynamic Application Security Testing (DAST)](dast/index.md) | The scanning engine is updated on a periodic basis. See the [version of the underlying tool `zaproxy`](https://gitlab.com/gitlab-org/security-products/dast/blob/master/Dockerfile#L1). The scanning rules are downloaded at the runtime of the scan. | -| [Static Application Security Testing (SAST)](sast/index.md) | Relies exclusively on [the tools GitLab is wrapping](sast/index.md#supported-languages-and-frameworks). The underlying analyzers are updated at least once per month if a relevant update is available. The vulnerabilities database is updated by the upstream tools. | - -You don't have to update GitLab to benefit from the latest vulnerabilities definitions, -but you may have to in the future. - -The security tools are released as Docker images, and the vendored job definitions -to enable them are using the `x-y-stable` image tags that get overridden each time a new -release of the tools is pushed. The Docker images are updated to match the -previous GitLab releases, so they automatically get the latest versions of the -scanning tools without the user having to do anything. - -This workflow comes with some drawbacks and there's a -[plan to change this](https://gitlab.com/gitlab-org/gitlab/issues/9725). +| [Container Scanning](container_scanning/index.md) | Uses `clair`. The latest `clair-db` version is used for each job by running the [`latest` docker image tag](https://gitlab.com/gitlab-org/gitlab/blob/438a0a56dc0882f22bdd82e700554525f552d91b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml#L37). The `clair-db` database [is updated daily according to the author](https://github.com/arminc/clair-local-scan#clair-server-or-local). | +| [Dependency Scanning](dependency_scanning/index.md) | Relies on `bundler-audit` (for Rubygems), `retire.js` (for NPM packages), and `gemnasium` (GitLab's own tool for all libraries). Both `bundler-audit` and `retire.js` fetch their vulnerabilities data from GitHub repositories, so vulnerabilities added to `ruby-advisory-db` and `retire.js` are immediately available. The tools themselves are updated once per month if there's a new version. The [Gemnasium DB](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is updated at least once a week. | +| [Dynamic Application Security Testing (DAST)](dast/index.md) | The scanning engine is updated on a periodic basis. See the [version of the underlying tool `zaproxy`](https://gitlab.com/gitlab-org/security-products/dast/blob/master/Dockerfile#L1). The scanning rules are downloaded at scan runtime. | +| [Static Application Security Testing (SAST)](sast/index.md) | Relies exclusively on [the tools GitLab wraps](sast/index.md#supported-languages-and-frameworks). The underlying analyzers are updated at least once per month if a relevant update is available. The vulnerabilities database is updated by the upstream tools. | + +Currently, you do not have to update GitLab to benefit from the latest vulnerabilities definitions. +The security tools are released as Docker images. The vendored job definitions to enable them use +the `x-y-stable` image tags that get overridden each time a new release of the tools is pushed. The +Docker images are updated to match the previous GitLab releases, so users automatically get the +latest versions of the scanning tools without having to do anything. There are some known issues +with this approach, however, and there is a +[plan to resolve them](https://gitlab.com/gitlab-org/gitlab/issues/9725). ## Interacting with the vulnerabilities @@ -63,14 +55,14 @@ CAUTION: **Warning:** This feature is currently [Alpha](https://about.gitlab.com/handbook/product/#alpha-beta-ga) and while you can start using it, it may receive important changes in the future. Each security vulnerability in the merge request report or the -[Security Dashboard](security_dashboard/index.md) is actionable. Clicking on an -entry, a detailed information will pop up with different possible options: - -- [Dismiss vulnerability](#dismissing-a-vulnerability): Dismissing a vulnerability - will place a ~~strikethrough~~ styling on it. -- [Create issue](#creating-an-issue-for-a-vulnerability): The new issue will - have the title and description pre-populated with the information from the - vulnerability report and will be created as [confidential](../project/issues/confidential_issues.md) by default. +[Security Dashboard](security_dashboard/index.md) is actionable. Click an entry to view detailed +information with several options: + +- [Dismiss vulnerability](#dismissing-a-vulnerability): Dismissing a vulnerability styles it in + strikethrough. +- [Create issue](#creating-an-issue-for-a-vulnerability): Create a new issue with the title and + description prepopulated with information from the vulnerability report. By default, such issues + are [confidential](../project/issues/confidential_issues.md). - [Solution](#solutions-for-vulnerabilities-auto-remediation): For some vulnerabilities, a solution is provided for how to fix the vulnerability. @@ -88,8 +80,8 @@ If you wish to undo this dismissal, you can click the **Undo dismiss** button. When dismissing a vulnerability, it's often helpful to provide a reason for doing so. If you press the comment button next to **Dismiss vulnerability** in the modal, -a text box will appear, allowing you to add a comment with your dismissal. -Once added, you can edit it or delete it. This allows you to add and update +a text box appears for you to add a comment with your dismissal. +Once added, you can edit or delete it. This allows you to add and update context for a vulnerability as you learn more over time. ![Dismissed vulnerability comment](img/dismissed_info_v12_3.png) @@ -97,16 +89,16 @@ context for a vulnerability as you learn more over time. ### Creating an issue for a vulnerability You can create an issue for a vulnerability by selecting the **Create issue** -button from within the vulnerability modal or using the action buttons to the right of -a vulnerability row when in the group security dashboard. +button from within the vulnerability modal, or by using the action buttons to the right of +a vulnerability row in the group security dashboard. -This will create a [confidential issue](../project/issues/confidential_issues.md) -on the project this vulnerability came from and pre-fill it with some useful -information taken from the vulnerability report. Once the issue is created, you -will be redirected to it so you can edit, assign, or comment on it. +This creates a [confidential issue](../project/issues/confidential_issues.md) in the project the +vulnerability came from, and prepopulates it with some useful information taken from the vulnerability +report. Once the issue is created, you are redirected to it so you can edit, assign, or comment on +it. -Upon returning to the group security dashboard, you'll see that -the vulnerability will now have an associated issue next to the name. +Upon returning to the group security dashboard, the vulnerability now has an associated issue next +to the name. ![Linked issue in the group security dashboard](img/issue.png) @@ -126,7 +118,7 @@ automatically generates. The following scanners are supported: Some vulnerabilities can be fixed by applying a patch that is automatically generated by GitLab. To apply the fix: -1. Click on the vulnerability. +1. Click the vulnerability. 1. Download and review the patch file `remediation.patch`. 1. Ensure your local project has the same commit checked out that was used to generate the patch. 1. Run `git apply remediation.patch`. @@ -138,13 +130,13 @@ generated by GitLab. To apply the fix: > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/9224) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 11.9. -In certain cases, GitLab will allow you to create a merge request that will -automatically remediate the vulnerability. Any vulnerability that has a +In certain cases, GitLab allows you to create a merge request that automatically remediates the +vulnerability. Any vulnerability that has a [solution](#solutions-for-vulnerabilities-auto-remediation) can have a merge request created to automatically solve the issue. -If this action is available there will be a **Create merge request** button in the vulnerability modal. -Clicking on this button will create a merge request to apply the solution onto the source branch. +If this action is available, the vulnerability modal contains a **Create merge request** button. +Click this button to create a merge request to apply the solution onto the source branch. ![Create merge request from vulnerability](img/create_issue_with_list_hover.png) @@ -155,30 +147,29 @@ Clicking on this button will create a merge request to apply the solution onto t Merge Request Approvals can be configured to require approval from a member of your security team when a merge request would introduce one of the following security issues: -- a security vulnerability -- a software license compliance violation +- A security vulnerability +- A software license compliance violation -This threshold is defined as `high`, `critical`, or `unknown` -severity. When any vulnerabilities are present within a merge request, an -approval will be required from the `Vulnerability-Check` approver group. +This threshold is defined as `high`, `critical`, or `unknown` severity. When any vulnerabilities are +present within a merge request, an approval is required from the `Vulnerability-Check` approver +group. ### Enabling Security Approvals within a project To enable Security Approvals, a [project approval rule](../project/merge_requests/merge_request_approvals.md#multiple-approval-rules-premium) -must be created with the case-sensitive name `Vulnerability-Check`. This approval -group must be set with an "Approvals required" count greater than zero. +must be created with the case-sensitive name `Vulnerability-Check`. This approval group must be set +with the number of approvals required greater than zero. -Once this group has been added to your project, the approval rule will be enabled -for all Merge Requests. +Once this group is added to your project, the approval rule is enabled for all merge requests. -Any code changes made will cause the count of approvals required to reset. +Any code changes cause the approvals required to reset. -An approval will be required when a security report: +An approval is required when a security report: - Contains a new vulnerability of `high`, `critical`, or `unknown` severity. - Is not generated during pipeline execution. -An approval will be optional when a security report: +An approval is optional when a security report: - Contains no new vulnerabilities. - Contains only new vulnerabilities of `low` or `medium` severity. @@ -186,22 +177,22 @@ An approval will be optional when a security report: ### Enabling License Approvals within a project To enable License Approvals, a [project approval rule](../project/merge_requests/merge_request_approvals.md#multiple-approval-rules-premium) -must be created with the case-sensitive name `License-Check`. This approval -group must be set with an "Approvals required" count greater than zero. +must be created with the case-sensitive name `License-Check`. This approval group must be set +with the number of approvals required greater than zero. -Once this group has been added to your project, the approval rule will be enabled -for all Merge Requests. To configure how this rule behaves, you can choose which -licenses to `approve` or `blacklist` in the -[project policies for License Compliance](license_compliance/index.md#project-policies-for-license-compliance) section. +Once this group is added to your project, the approval rule is enabled for all Merge Requests. To +configure how this rule behaves, you can choose which licenses to `approve` or `blacklist` in the +[project policies for License Compliance](license_compliance/index.md#project-policies-for-license-compliance) +section. -Any code changes made will cause the count of approvals required to reset. +Any code changes cause the approvals required to reset. -An approval will be required when a license report: +An approval is required when a license report: - Contains a dependency that includes a software license that is `blacklisted`. - Is not generated during pipeline execution. -An approval will be optional when a license report: +An approval is optional when a license report: - Contains no software license violations. - Contains only new licenses that are `approved` or unknown. @@ -211,7 +202,7 @@ An approval will be optional when a license report: ### Getting error message `sast job: stage parameter should be [some stage name here]` When including a security job template like [`SAST`](sast/index.md#configuration), -the following error can be raised, depending on your GitLab CI/CD configuration: +the following error may occur, depending on your GitLab CI/CD configuration: ```plaintext Found errors in your .gitlab-ci.yml: @@ -219,8 +210,7 @@ Found errors in your .gitlab-ci.yml: * sast job: stage parameter should be unit-tests ``` -This error appears when the stage (nammed `test`) of the included job isn't declared -in `.gitlab-ci.yml`. +This error appears when the included job's stage (named `test`) isn't declared in `.gitlab-ci.yml`. To fix this issue, you can either: - Add a `test` stage in your `.gitlab-ci.yml`. @@ -235,5 +225,4 @@ To fix this issue, you can either: ``` [Learn more on overriding the SAST template](sast/index.md#overriding-the-sast-template). -All the security scanning tools define their stage, so this error can occur with -all of them. +All the security scanning tools define their stage, so this error can occur with all of them. diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md index 60ce03c2fc4..55c51ef5fb6 100644 --- a/doc/user/clusters/applications.md +++ b/doc/user/clusters/applications.md @@ -487,6 +487,7 @@ Supported applications: - [Sentry](#install-sentry-using-gitlab-ci) - [GitLab Runner](#install-gitlab-runner-using-gitlab-ci) - [Cilium](#install-cilium-using-gitlab-ci) +- [JupyterHub](#install-jupyterhub-using-gitlab-ci) ### Usage @@ -749,6 +750,47 @@ agent: enabled: false ``` +### Install JupyterHub using GitLab CI + +> [Introduced](https://gitlab.com/gitlab-org/cluster-integration/cluster-applications/-/merge_requests/40) in GitLab 12.8. + +Enable JupyterHub in the `.gitlab/managed-apps/config.yaml` file to install it: + +```yaml +jupyterhub: + installed: true + gitlabProjectIdWhitelist: [] + gitlabGroupWhitelist: [] +``` + +`gitlabProjectIdWhitelist` restricts GitLab authentication to only members of the specified projects. `gitlabGroupWhitelist` restricts GitLab authentication to only members of the specified groups. Specifying an empty array for both will allow any user on the GitLab instance to log in. + +JupyterHub is installed into the `gitlab-managed-apps` namespace of your +cluster. + +In order for JupyterHub to function, you must setup an [OAuth Application](../../integration/oauth_provider.md). Using the following values: + +- "Redirect URI" to `http:///hub/oauth_callback` +- "Scope" to `api read_repository write_repository` + +In addition the following variables must be specified using [CI variables](../../ci/variables/README.md): + +- `JUPYTERHUB_PROXY_SECRET_TOKEN` will set [`proxy.secretToken`](https://zero-to-jupyterhub.readthedocs.io/en/stable/reference.html#proxy-secrettoken). Generate this using `openssl rand -hex 32`. +- `JUPYTERHUB_COOKIE_SECRET` will set [`hub.cookieSecret`](https://zero-to-jupyterhub.readthedocs.io/en/stable/reference.html#hub-cookiesecret). Generate this using `openssl rand -hex 32`. +- `JUPYTERHUB_HOST` is the hostname used for the installation (e.g., `jupyter.example.gitlab.com`). +- `JUPYTERHUB_GITLAB_HOST` is the hostname of the GitLab instance used for authentication (e.g., `example.gitlab.com`). +- `JUPYTERHUB_AUTH_CRYPTO_KEY` will set [`auth.state.cryptoKey`](https://zero-to-jupyterhub.readthedocs.io/en/stable/reference.html#auth-state-cryptokey). Generate this using `openssl rand -hex 32`. +- `JUPYTERHUB_AUTH_GITLAB_CLIENT_ID` the "Application ID" for the OAuth Application. +- `JUPYTERHUB_AUTH_GITLAB_CLIENT_SECRET` the "Secret" for the OAuth Application. + +By default JupyterHub will be installed using a +[default values file](https://gitlab.com/gitlab-org/cluster-integration/cluster-applications/-/blob/master/src/default-data/jupyterhub/values.yaml.gotmpl). +You can customize the installation of JupyterHub by defining +`.gitlab/managed-apps/jupyterhub/values.yaml` file in your cluster management +project. Refer to the +[chart reference](https://zero-to-jupyterhub.readthedocs.io/en/stable/reference.html) +for the available configuration options. + ## Upgrading applications > [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24789) in GitLab 11.8. diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md index dbba71fc17b..03e4fbb3b3f 100644 --- a/doc/user/gitlab_com/index.md +++ b/doc/user/gitlab_com/index.md @@ -131,6 +131,9 @@ Below are the shared Runners settings. The full contents of our `config.toml` are: +NOTE: **Note:** +Settings that are not public are shown as `X`. + **Google Cloud Platform** ```toml diff --git a/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml b/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml index 5d8125f47a9..a133c5e0485 100644 --- a/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Managed-Cluster-Applications.gitlab-ci.yml @@ -1,6 +1,6 @@ apply: stage: deploy - image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.6.0" + image: "registry.gitlab.com/gitlab-org/cluster-integration/cluster-applications:v0.7.0" environment: name: production variables: @@ -11,6 +11,7 @@ apply: SENTRY_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/sentry/values.yaml GITLAB_RUNNER_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/gitlab-runner/values.yaml CILIUM_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/cilium/values.yaml + JUPYTERHUB_VALUES_FILE: $CI_PROJECT_DIR/.gitlab/managed-apps/jupyterhub/values.yaml script: - gitlab-managed-apps /usr/local/share/gitlab-managed-apps/helmfile.yaml only: diff --git a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb index 0a2b6616bb4..78d0945ea63 100644 --- a/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb +++ b/spec/support/shared_examples/models/concerns/bulk_insert_safe_shared_examples.rb @@ -1,6 +1,11 @@ # frozen_string_literal: true -RSpec.shared_examples 'a BulkInsertSafe model' do |target_class| +RSpec.shared_examples 'a BulkInsertSafe model' do |klass| + # Call `.dup` on the class passed in, as a test in this set of examples + # calls `belongs_to` on the class, thereby adding a new belongs_to + # relationship to the model that can break remaining specs in the test suite. + let(:target_class) { klass.dup } + # We consider all callbacks unsafe for bulk insertions unless we have explicitly # whitelisted them (esp. anything related to :save, :create, :commit etc.) let(:callback_method_blacklist) do -- GitLab