Commit fc2fc73b authored by Achilleas Pipinellis's avatar Achilleas Pipinellis

Merge branch '39140-scope-modsec-feature-flag-to-groups' into 'master'

Add enable_modsecurity toggle to Ingress Managed App

See merge request gitlab-org/gitlab!21966
parents 554cce8b 4ececb6d
......@@ -53,6 +53,7 @@ export default class Clusters {
helpPath,
ingressHelpPath,
ingressDnsHelpPath,
ingressModSecurityHelpPath,
environmentsHelpPath,
clustersHelpPath,
deployBoardsHelpPath,
......@@ -69,6 +70,7 @@ export default class Clusters {
helpPath,
ingressHelpPath,
ingressDnsHelpPath,
ingressModSecurityHelpPath,
environmentsHelpPath,
clustersHelpPath,
deployBoardsHelpPath,
......@@ -169,6 +171,7 @@ export default class Clusters {
ingressHelpPath: this.state.ingressHelpPath,
managePrometheusPath: this.state.managePrometheusPath,
ingressDnsHelpPath: this.state.ingressDnsHelpPath,
ingressModSecurityHelpPath: this.state.ingressModSecurityHelpPath,
cloudRunHelpPath: this.state.cloudRunHelpPath,
providerType: this.state.providerType,
preInstalledKnative: this.state.preInstalledKnative,
......
......@@ -56,6 +56,11 @@ export default {
required: false,
default: '',
},
ingressModSecurityHelpPath: {
type: String,
required: false,
default: '',
},
cloudRunHelpPath: {
type: String,
required: false,
......@@ -112,6 +117,9 @@ export default {
ingressInstalled() {
return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED;
},
ingressEnableModsecurity() {
return this.applications.ingress.modsecurity_enabled;
},
ingressExternalEndpoint() {
return this.applications.ingress.externalIp || this.applications.ingress.externalHostname;
},
......@@ -127,6 +135,18 @@ export default {
enableClusterApplicationElasticStack() {
return gon.features && gon.features.enableClusterApplicationElasticStack;
},
ingressModSecurityDescription() {
const escapedUrl = _.escape(this.ingressModSecurityHelpPath);
return sprintf(
s__('ClusterIntegration|Learn more about %{startLink}ModSecurity%{endLink}'),
{
startLink: `<a href="${escapedUrl}" target="_blank" rel="noopener noreferrer">`,
endLink: '</a>',
},
false,
);
},
ingressDescription() {
return sprintf(
_.escape(
......@@ -135,9 +155,9 @@ export default {
),
),
{
pricingLink: `<strong><a href="https://cloud.google.com/compute/pricing#lb"
pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb"
target="_blank" rel="noopener noreferrer">
${_.escape(s__('ClusterIntegration|pricing'))}</a></strong>`,
${_.escape(s__('ClusterIntegration|pricing'))}</a>`,
},
false,
);
......@@ -311,6 +331,9 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
:request-reason="applications.ingress.requestReason"
:installed="applications.ingress.installed"
:install-failed="applications.ingress.installFailed"
:install-application-request-params="{
modsecurity_enabled: applications.ingress.modsecurity_enabled,
}"
:uninstallable="applications.ingress.uninstallable"
:uninstall-successful="applications.ingress.uninstallSuccessful"
:uninstall-failed="applications.ingress.uninstallFailed"
......@@ -326,6 +349,26 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
}}
</p>
<template>
<div class="form-group">
<div class="form-check form-check-inline">
<input
v-model="applications.ingress.modsecurity_enabled"
:disabled="ingressInstalled"
type="checkbox"
autocomplete="off"
class="form-check-input"
/>
<label class="form-check-label label-bold" for="ingress-enable-modsecurity">
{{ s__('ClusterIntegration|Enable Web Application Firewall') }}
</label>
</div>
<p class="form-text text-muted">
<strong v-html="ingressModSecurityDescription"></strong>
</p>
</div>
</template>
<template v-if="ingressInstalled">
<div class="form-group">
<label for="ingress-endpoint">{{ s__('ClusterIntegration|Ingress Endpoint') }}</label>
......@@ -375,7 +418,9 @@ Crossplane runs inside your Kubernetes cluster and supports secure connectivity
</p>
</template>
<template v-if="!ingressInstalled">
<div class="bs-callout bs-callout-info" v-html="ingressDescription"></div>
<div class="bs-callout bs-callout-info">
<strong v-html="ingressDescription"></strong>
</div>
</template>
</div>
</application-row>
......
......@@ -52,6 +52,7 @@ export default class ClusterStore {
ingress: {
...applicationInitialState,
title: s__('ClusterIntegration|Ingress'),
modsecurity_enabled: false,
externalIp: null,
externalHostname: null,
},
......@@ -108,6 +109,7 @@ export default class ClusterStore {
helpPath,
ingressHelpPath,
ingressDnsHelpPath,
ingressModSecurityHelpPath,
environmentsHelpPath,
clustersHelpPath,
deployBoardsHelpPath,
......@@ -116,6 +118,7 @@ export default class ClusterStore {
this.state.helpPath = helpPath;
this.state.ingressHelpPath = ingressHelpPath;
this.state.ingressDnsHelpPath = ingressDnsHelpPath;
this.state.ingressModSecurityHelpPath = ingressModSecurityHelpPath;
this.state.environmentsHelpPath = environmentsHelpPath;
this.state.clustersHelpPath = clustersHelpPath;
this.state.deployBoardsHelpPath = deployBoardsHelpPath;
......@@ -207,6 +210,8 @@ export default class ClusterStore {
if (appId === INGRESS) {
this.state.applications.ingress.externalIp = serverAppEntry.external_ip;
this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname;
this.state.applications.ingress.modsecurity_enabled =
serverAppEntry.modsecurity_enabled || this.state.applications.ingress.modsecurity_enabled;
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
this.state.applications.cert_manager.email || serverAppEntry.email;
......
......@@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController
end
def cluster_application_params
params.permit(:application, :hostname, :kibana_hostname, :email, :stack)
params.permit(:application, :hostname, :kibana_hostname, :email, :stack, :modsecurity_enabled)
end
def cluster_application_destroy_params
......
......@@ -14,6 +14,7 @@ module Clusters
include AfterCommitQueue
default_value_for :ingress_type, :nginx
default_value_for :modsecurity_enabled, false
default_value_for :version, VERSION
enum ingress_type: {
......@@ -73,7 +74,7 @@ module Clusters
private
def specification
return {} unless Feature.enabled?(:ingress_modsecurity)
return {} unless modsecurity_enabled
{
"controller" => {
......
......@@ -11,6 +11,7 @@ class ClusterApplicationEntity < Grape::Entity
expose :kibana_hostname, if: -> (e, _) { e.respond_to?(:kibana_hostname) }
expose :email, if: -> (e, _) { e.respond_to?(:email) }
expose :stack, if: -> (e, _) { e.respond_to?(:stack) }
expose :modsecurity_enabled, if: -> (e, _) { e.respond_to?(:modsecurity_enabled) }
expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }
expose :can_uninstall?, as: :can_uninstall
end
......@@ -31,6 +31,10 @@ module Clusters
application.stack = params[:stack]
end
if application.has_attribute?(:modsecurity_enabled)
application.modsecurity_enabled = params[:modsecurity_enabled] || false
end
if application.respond_to?(:oauth_application)
application.oauth_application = create_oauth_application(application, request)
end
......
......@@ -30,6 +30,7 @@
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'),
ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'),
ingress_mod_security_help_path: help_page_path('user/clusters/applications.md', anchor: 'web-application-firewall-modsecurity'),
environments_help_path: help_page_path('ci/environments', anchor: 'defining-environments'),
clusters_help_path: help_page_path('user/project/clusters/index.md', anchor: 'deploying-to-a-kubernetes-cluster'),
deploy_boards_help_path: help_page_path('user/project/deploy_boards.html', anchor: 'enabling-deploy-boards'),
......
---
title: Add enable_modsecurity setting to managed ingress
merge_request: 21966
author:
type: added
......@@ -248,10 +248,10 @@ use an A record. If your external endpoint is a hostname, use a CNAME record.
#### Web Application Firewall (ModSecurity)
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/issues/65192) in GitLab 12.3 (enabled using `ingress_modsecurity` [feature flag](../../development/feature_flags/development.md#enabling-a-feature-flag-in-development)).
> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/21966) in GitLab 12.7.
Out of the box, GitLab provides you real-time security monitoring with
[`modsecurity`](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#modsecurity)
[ModSecurity](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#modsecurity).
Modsecurity is a toolkit for real-time web application monitoring, logging,
and access control. With GitLab's offering, the [OWASP's Core Rule Set](https://www.modsecurity.org/CRS/Documentation/), which provides generic attack detection capabilities,
......@@ -267,22 +267,18 @@ This feature:
kubectl -n gitlab-managed-apps exec -it $(kubectl get pods -n gitlab-managed-apps | grep 'ingress-controller' | awk '{print $1}') -- tail -f /var/log/modsec/audit.log
```
There is a small performance overhead by enabling `modsecurity`. If this is
considered significant for your application, you can either:
To enable ModSecurity, check the **Enable Web Application Firewall** checkbox
when installing your [Ingress application](#ingress).
- Disable ModSecurity's rule engine for your deployed application by setting
[the deployment variable](../../topics/autodevops/index.md)
`AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` to `Off`. This will prevent ModSecurity from
processing any requests for the given application or environment.
- Toggle the feature flag to false by running the following command within your
instance's Rails console:
There is a small performance overhead by enabling ModSecurity. If this is
considered significant for your application, you can disable ModSecurity's
rule engine for your deployed application by setting
[the deployment variable](../../topics/autodevops/index.md)
`AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE` to `Off`. This will prevent ModSecurity
from processing any requests for the given application or environment.
```ruby
Feature.disable(:ingress_modsecurity)
```
Once disabled, you must [uninstall](#uninstalling-applications) and reinstall your Ingress
application for the changes to take effect.
To permanently disable it, you must [uninstall](#uninstalling-applications) and
reinstall your Ingress application for the changes to take effect.
### JupyterHub
......
......@@ -3851,6 +3851,9 @@ msgstr ""
msgid "ClusterIntegration|Enable Cloud Run on GKE (beta)"
msgstr ""
msgid "ClusterIntegration|Enable Web Application Firewall"
msgstr ""
msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
msgstr ""
......@@ -4037,6 +4040,9 @@ msgstr ""
msgid "ClusterIntegration|Learn more about %{help_link_start}zones%{help_link_end}."
msgstr ""
msgid "ClusterIntegration|Learn more about %{startLink}ModSecurity%{endLink}"
msgstr ""
msgid "ClusterIntegration|Learn more about %{startLink}Regions %{externalLinkIcon}%{endLink}."
msgstr ""
......
......@@ -71,6 +71,7 @@ FactoryBot.define do
end
factory :clusters_applications_ingress, class: Clusters::Applications::Ingress do
modsecurity_enabled { false }
cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end
......
......@@ -38,6 +38,7 @@
"kibana_hostname": { "type": ["string", "null"] },
"email": { "type": ["string", "null"] },
"stack": { "type": ["string", "null"] },
"modsecurity_enabled": { "type": ["boolean", "null"] },
"update_available": { "type": ["boolean", "null"] },
"can_uninstall": { "type": "boolean" }
},
......
......@@ -190,6 +190,7 @@ describe('Applications', () => {
title: 'Ingress',
status: 'installed',
externalHostname: 'localhost.localdomain',
modsecurity_enabled: false,
},
helm: { title: 'Helm Tiller' },
cert_manager: { title: 'Cert-Manager' },
......@@ -473,7 +474,12 @@ describe('Applications', () => {
vm = mountComponent(Applications, {
applications: {
...APPLICATIONS_MOCK_STATE,
ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
ingress: {
title: 'Ingress',
status: 'installed',
externalIp: '1.1.1.1',
modsecurity_enabled: false,
},
elastic_stack: { title: 'Elastic Stack', status: 'installed', kibana_hostname: '' },
},
});
......
......@@ -150,7 +150,7 @@ const DEFAULT_APPLICATION_STATE = {
const APPLICATIONS_MOCK_STATE = {
helm: { title: 'Helm Tiller', status: 'installable' },
ingress: { title: 'Ingress', status: 'installable' },
ingress: { title: 'Ingress', status: 'installable', modsecurity_enabled: false },
crossplane: { title: 'Crossplane', status: 'installable', stack: '' },
cert_manager: { title: 'Cert-Manager', status: 'installable' },
runner: { title: 'GitLab Runner' },
......
......@@ -86,6 +86,7 @@ describe('Clusters Store', () => {
uninstallSuccessful: false,
uninstallFailed: false,
validationError: null,
modsecurity_enabled: false,
},
runner: {
title: 'GitLab Runner',
......
......@@ -142,11 +142,11 @@ describe Clusters::Applications::Ingress do
let(:project) { build(:project) }
let(:cluster) { build(:cluster, projects: [project]) }
context 'when ingress_modsecurity is enabled' do
context 'when modsecurity_enabled is enabled' do
before do
stub_feature_flags(ingress_modsecurity: true)
allow(subject).to receive(:cluster).and_return(cluster)
allow(subject).to receive(:modsecurity_enabled).and_return(true)
end
it 'includes modsecurity module enablement' do
......@@ -173,10 +173,8 @@ describe Clusters::Applications::Ingress do
end
end
context 'when ingress_modsecurity is disabled' do
context 'when modsecurity_enabled is disabled' do
before do
stub_feature_flags(ingress_modsecurity: false)
allow(subject).to receive(:cluster).and_return(cluster)
end
......
......@@ -47,6 +47,33 @@ describe Clusters::Applications::CreateService do
create(:clusters_applications_helm, :installed, cluster: cluster)
end
context 'ingress application' do
let(:params) do
{
application: 'ingress',
modsecurity_enabled: true
}
end
before do
expect_any_instance_of(Clusters::Applications::Ingress)
.to receive(:make_scheduled!)
.and_call_original
end
it 'creates the application' do
expect do
subject
cluster.reload
end.to change(cluster, :application_ingress)
end
it 'sets modsecurity_enabled' do
expect(subject.modsecurity_enabled).to eq(true)
end
end
context 'cert manager application' do
let(:params) 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