Commit d1146081 authored by Alan Paruszewski's avatar Alan Paruszewski Committed by Alan (Maciej) Paruszewski

Remove GitLab WAF related models, services and workers

This change removes all code related to WAF (ModSecurity) feature.

Changelog: removed
EE: true
parent 6dfed16d
...@@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController ...@@ -47,7 +47,7 @@ class Clusters::ApplicationsController < Clusters::BaseController
end end
def cluster_application_params def cluster_application_params
params.permit(:application, :hostname, :pages_domain_id, :email, :stack, :modsecurity_enabled, :modsecurity_mode, :host, :port, :protocol, :waf_log_enabled, :cilium_log_enabled) params.permit(:application, :hostname, :pages_domain_id, :email, :stack, :host, :port, :protocol, :cilium_log_enabled)
end end
def cluster_application_destroy_params def cluster_application_destroy_params
......
...@@ -12,11 +12,13 @@ module Clusters ...@@ -12,11 +12,13 @@ module Clusters
include ::Clusters::Concerns::ApplicationStatus include ::Clusters::Concerns::ApplicationStatus
include ::Clusters::Concerns::ApplicationVersion include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData include ::Clusters::Concerns::ApplicationData
include IgnorableColumns
default_value_for :version, VERSION default_value_for :version, VERSION
default_value_for :port, 514 default_value_for :port, 514
default_value_for :protocol, :tcp default_value_for :protocol, :tcp
default_value_for :waf_log_enabled, false
ignore_column :waf_log_enabled, remove_with: '14.2', remove_after: '2021-07-22'
enum protocol: { tcp: 0, udp: 1 } enum protocol: { tcp: 0, udp: 1 }
...@@ -48,9 +50,7 @@ module Clusters ...@@ -48,9 +50,7 @@ module Clusters
private private
def has_at_least_one_log_enabled? def has_at_least_one_log_enabled?
if !waf_log_enabled && !cilium_log_enabled errors.add(:base, _("At least one logging option is required to be enabled")) unless cilium_log_enabled
errors.add(:base, _("At least one logging option is required to be enabled"))
end
end end
def content_values def content_values
...@@ -113,7 +113,6 @@ module Clusters ...@@ -113,7 +113,6 @@ module Clusters
def path_to_logs def path_to_logs
path = [] path = []
path << "/var/log/containers/*#{Ingress::MODSECURITY_LOG_CONTAINER_NAME}*.log" if waf_log_enabled
path << "/var/log/containers/*#{CILIUM_CONTAINER_NAME}*.log" if cilium_log_enabled path << "/var/log/containers/*#{CILIUM_CONTAINER_NAME}*.log" if cilium_log_enabled
path.join(',') path.join(',')
end end
......
...@@ -7,10 +7,6 @@ module Clusters ...@@ -7,10 +7,6 @@ module Clusters
class Ingress < ApplicationRecord class Ingress < ApplicationRecord
VERSION = '1.40.2' VERSION = '1.40.2'
INGRESS_CONTAINER_NAME = 'nginx-ingress-controller' INGRESS_CONTAINER_NAME = 'nginx-ingress-controller'
MODSECURITY_LOG_CONTAINER_NAME = 'modsecurity-log'
MODSECURITY_MODE_LOGGING = "DetectionOnly"
MODSECURITY_MODE_BLOCKING = "On"
MODSECURITY_OWASP_RULES_FILE = "/etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf"
self.table_name = 'clusters_applications_ingress' self.table_name = 'clusters_applications_ingress'
...@@ -20,22 +16,18 @@ module Clusters ...@@ -20,22 +16,18 @@ module Clusters
include ::Clusters::Concerns::ApplicationData include ::Clusters::Concerns::ApplicationData
include AfterCommitQueue include AfterCommitQueue
include UsageStatistics include UsageStatistics
include IgnorableColumns
default_value_for :ingress_type, :nginx default_value_for :ingress_type, :nginx
default_value_for :modsecurity_enabled, true
default_value_for :version, VERSION default_value_for :version, VERSION
default_value_for :modsecurity_mode, :logging
ignore_column :modsecurity_enabled, remove_with: '14.2', remove_after: '2021-07-22'
ignore_column :modsecurity_mode, remove_with: '14.2', remove_after: '2021-07-22'
enum ingress_type: { enum ingress_type: {
nginx: 1 nginx: 1
} }
enum modsecurity_mode: { logging: 0, blocking: 1 }
scope :modsecurity_not_installed, -> { where(modsecurity_enabled: nil) }
scope :modsecurity_enabled, -> { where(modsecurity_enabled: true) }
scope :modsecurity_disabled, -> { where(modsecurity_enabled: false) }
FETCH_IP_ADDRESS_DELAY = 30.seconds FETCH_IP_ADDRESS_DELAY = 30.seconds
state_machine :status do state_machine :status do
...@@ -92,96 +84,13 @@ module Clusters ...@@ -92,96 +84,13 @@ module Clusters
private private
def specification
return {} unless modsecurity_enabled
{
"controller" => {
"config" => {
"enable-modsecurity" => "true",
"enable-owasp-modsecurity-crs" => "false",
"modsecurity-snippet" => modsecurity_snippet_content,
"modsecurity.conf" => modsecurity_config_content
},
"extraContainers" => [
{
"name" => MODSECURITY_LOG_CONTAINER_NAME,
"image" => "busybox",
"args" => [
"/bin/sh",
"-c",
"tail -F /var/log/modsec/audit.log"
],
"volumeMounts" => [
{
"name" => "modsecurity-log-volume",
"mountPath" => "/var/log/modsec",
"readOnly" => true
}
],
"livenessProbe" => {
"exec" => {
"command" => [
"ls",
"/var/log/modsec/audit.log"
]
}
}
}
],
"extraVolumeMounts" => [
{
"name" => "modsecurity-template-volume",
"mountPath" => "/etc/nginx/modsecurity/modsecurity.conf",
"subPath" => "modsecurity.conf"
},
{
"name" => "modsecurity-log-volume",
"mountPath" => "/var/log/modsec"
}
],
"extraVolumes" => [
{
"name" => "modsecurity-template-volume",
"configMap" => {
"name" => "ingress-#{INGRESS_CONTAINER_NAME}",
"items" => [
{
"key" => "modsecurity.conf",
"path" => "modsecurity.conf"
}
]
}
},
{
"name" => "modsecurity-log-volume",
"emptyDir" => {}
}
]
}
}
end
def modsecurity_config_content
File.read(modsecurity_config_file_path)
end
def modsecurity_config_file_path
Rails.root.join('vendor', 'ingress', 'modsecurity.conf')
end
def content_values def content_values
YAML.load_file(chart_values_file).deep_merge!(specification) YAML.load_file(chart_values_file)
end end
def application_jupyter_installed? def application_jupyter_installed?
cluster.application_jupyter&.installed? cluster.application_jupyter&.installed?
end end
def modsecurity_snippet_content
sec_rule_engine = logging? ? MODSECURITY_MODE_LOGGING : MODSECURITY_MODE_BLOCKING
"SecRuleEngine #{sec_rule_engine}\nInclude #{MODSECURITY_OWASP_RULES_FILE}"
end
end end
end end
end end
...@@ -138,7 +138,6 @@ module Clusters ...@@ -138,7 +138,6 @@ module Clusters
scope :gcp_installed, -> { gcp_provided.joins(:provider_gcp).merge(Clusters::Providers::Gcp.with_status(:created)) } scope :gcp_installed, -> { gcp_provided.joins(:provider_gcp).merge(Clusters::Providers::Gcp.with_status(:created)) }
scope :aws_installed, -> { aws_provided.joins(:provider_aws).merge(Clusters::Providers::Aws.with_status(:created)) } scope :aws_installed, -> { aws_provided.joins(:provider_aws).merge(Clusters::Providers::Aws.with_status(:created)) }
scope :with_enabled_modsecurity, -> { joins(:application_ingress).merge(::Clusters::Applications::Ingress.modsecurity_enabled) }
scope :with_available_elasticstack, -> { joins(:application_elastic_stack).merge(::Clusters::Applications::ElasticStack.available) } scope :with_available_elasticstack, -> { joins(:application_elastic_stack).merge(::Clusters::Applications::ElasticStack.available) }
scope :with_available_cilium, -> { joins(:application_cilium).merge(::Clusters::Applications::Cilium.available) } scope :with_available_cilium, -> { joins(:application_cilium).merge(::Clusters::Applications::Cilium.available) }
scope :distinct_with_deployed_environments, -> { joins(:environments).merge(::Deployment.success).distinct } scope :distinct_with_deployed_environments, -> { joins(:environments).merge(::Deployment.success).distinct }
......
...@@ -10,15 +10,12 @@ class ClusterApplicationEntity < Grape::Entity ...@@ -10,15 +10,12 @@ class ClusterApplicationEntity < Grape::Entity
expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) } expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) }
expose :email, if: -> (e, _) { e.respond_to?(:email) } expose :email, if: -> (e, _) { e.respond_to?(:email) }
expose :stack, if: -> (e, _) { e.respond_to?(:stack) } 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 :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }
expose :can_uninstall?, as: :can_uninstall expose :can_uninstall?, as: :can_uninstall
expose :available_domains, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:available_domains) } expose :available_domains, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:available_domains) }
expose :pages_domain, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:pages_domain) } expose :pages_domain, using: Serverless::DomainEntity, if: -> (e, _) { e.respond_to?(:pages_domain) }
expose :modsecurity_mode, if: -> (e, _) { e.respond_to?(:modsecurity_mode) }
expose :host, if: -> (e, _) { e.respond_to?(:host) } expose :host, if: -> (e, _) { e.respond_to?(:host) }
expose :port, if: -> (e, _) { e.respond_to?(:port) } expose :port, if: -> (e, _) { e.respond_to?(:port) }
expose :protocol, if: -> (e, _) { e.respond_to?(:protocol) } expose :protocol, if: -> (e, _) { e.respond_to?(:protocol) }
expose :waf_log_enabled, if: -> (e, _) { e.respond_to?(:waf_log_enabled) }
expose :cilium_log_enabled, if: -> (e, _) { e.respond_to?(:cilium_log_enabled) } expose :cilium_log_enabled, if: -> (e, _) { e.respond_to?(:cilium_log_enabled) }
end end
...@@ -29,14 +29,6 @@ module Clusters ...@@ -29,14 +29,6 @@ module Clusters
application.stack = params[:stack] application.stack = params[:stack]
end end
if application.has_attribute?(:modsecurity_enabled)
application.modsecurity_enabled = params[:modsecurity_enabled] || false
end
if application.has_attribute?(:modsecurity_mode)
application.modsecurity_mode = params[:modsecurity_mode] || 0
end
apply_fluentd_related_attributes(application) apply_fluentd_related_attributes(application)
if application.respond_to?(:oauth_application) if application.respond_to?(:oauth_application)
......
# frozen_string_literal: true
module Projects
module Security
class WafAnomaliesController < Projects::ApplicationController
include SecurityAndCompliancePermissions
POLLING_INTERVAL = 5_000
before_action :authorize_read_waf_anomalies!
before_action :set_polling_interval
feature_category :web_firewall
def summary
return not_found unless anomaly_summary_service.elasticsearch_client
result = anomaly_summary_service.execute
respond_to do |format|
format.json do
status = result[:status] == :success ? :ok : :bad_request
render status: status, json: result
end
end
end
private
def anomaly_summary_service
@anomaly_summary_service ||= ::Security::WafAnomalySummaryService.new(
environment: environment,
**query_params.to_h.symbolize_keys
)
end
def query_params
params.permit(:interval, :from, :to)
end
def set_polling_interval
Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL)
end
def environment
@environment ||= project.environments.find(params.delete("environment_id"))
end
def authorize_read_waf_anomalies!
render_403 unless can?(current_user, :read_threat_monitoring, project)
end
end
end
end
# frozen_string_literal: true
module Security
# Service for fetching summary statistics from ElasticSearch.
# Queries ES and retrieves both total nginx requests & modsec violations
#
class WafAnomalySummaryService < ::BaseService
def initialize(environment:, cluster: environment.deployment_platform&.cluster, interval: 'day', from: 30.days.ago.iso8601, to: Time.zone.now.iso8601, options: {})
@environment = environment
@cluster = cluster
@interval = interval
@from = from
@to = to
@options = options
end
def execute(totals_only: false)
return if elasticsearch_client.nil?
return unless @environment.external_url
# Use multi-search with single query as we'll be adding nginx later
# with https://gitlab.com/gitlab-org/gitlab/issues/14707
aggregate_results = elasticsearch_client.msearch(body: body)
nginx_results, modsec_results = aggregate_results['responses']
if chart_above_v3?
nginx_total_requests = nginx_results.dig('hits', 'total', 'value').to_f
modsec_total_requests = modsec_results.dig('hits', 'total', 'value').to_f
else
nginx_total_requests = nginx_results.dig('hits', 'total').to_f
modsec_total_requests = modsec_results.dig('hits', 'total').to_f
end
return { total_traffic: nginx_total_requests, total_anomalous_traffic: modsec_total_requests } if totals_only
anomalous_traffic_count = nginx_total_requests == 0 ? 0 : (modsec_total_requests / nginx_total_requests).round(2)
{
total_traffic: nginx_total_requests,
anomalous_traffic: anomalous_traffic_count,
history: {
nominal: histogram_from(nginx_results),
anomalous: histogram_from(modsec_results)
},
interval: @interval,
from: @from,
to: @to,
status: :success
}
end
def elasticsearch_client
@elasticsearch_client ||= elastic_stack_adapter&.elasticsearch_client(timeout: @options[:timeout])
end
private
def elastic_stack_adapter
@elastic_stack_adapter ||= @cluster&.elastic_stack_adapter
end
def chart_above_v3?
elastic_stack_adapter.chart_above_v3?
end
def body
[
{ index: indices },
{
query: nginx_requests_query,
aggs: aggregations(@interval),
size: 0 # no docs needed, only counts
},
{ index: indices },
{
query: modsec_requests_query,
aggs: aggregations(@interval),
size: 0 # no docs needed, only counts
}
]
end
# Construct a list of daily indices to be searched. We do this programmatically
# based on the requested timeframe to reduce the load of querying all previous
# indices
def indices
(@from.to_date..@to.to_date).map do |day|
"filebeat-*-#{day.strftime('%Y.%m.%d')}"
end
end
def nginx_requests_query
{
bool: {
must: [
{
range: {
'@timestamp' => {
gte: @from,
lte: @to
}
}
},
{
terms_set: {
message: {
terms: environment_proxy_upstream_name_tokens,
minimum_should_match_script: {
source: 'params.num_terms'
}
}
}
},
{
match_phrase: {
'kubernetes.container.name' => {
query: ::Clusters::Applications::Ingress::INGRESS_CONTAINER_NAME
}
}
},
{
match_phrase: {
'kubernetes.namespace' => {
query: Gitlab::Kubernetes::Helm::NAMESPACE
}
}
},
{
match_phrase: {
stream: {
query: 'stdout'
}
}
}
]
}
}
end
def modsec_requests_query
{
bool: {
must: [
{
range: {
'@timestamp' => {
gte: @from,
lte: @to
}
}
},
{
prefix: {
'transaction.unique_id': application_server_name
}
},
{
match_phrase: {
'kubernetes.container.name' => {
query: ::Clusters::Applications::Ingress::MODSECURITY_LOG_CONTAINER_NAME
}
}
},
{
match_phrase: {
'kubernetes.namespace' => {
query: Gitlab::Kubernetes::Helm::NAMESPACE
}
}
}
]
}
}
end
def aggregations(interval)
{
counts: {
date_histogram: {
field: '@timestamp',
interval: interval,
order: {
'_key': 'asc'
}
}
}
}
end
def histogram_from(results)
buckets = results.dig('aggregations', 'counts', 'buckets') || []
buckets.map { |bucket| [bucket['key_as_string'], bucket['doc_count']] }
end
# Derive server_name to filter modsec audit log by environment
def application_server_name
@environment.formatted_external_url
end
# Derive proxy upstream name to filter nginx log by environment
# See https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/log-format/
def environment_proxy_upstream_name_tokens
[
*@environment.deployment_namespace.split('-'),
@environment.slug # $RELEASE_NAME
]
end
end
end
...@@ -8,9 +8,7 @@ ...@@ -8,9 +8,7 @@
#js-threat-monitoring-app{ data: { documentation_path: 'https://docs.gitlab.com/ee/user/application_security/threat_monitoring/', #js-threat-monitoring-app{ data: { documentation_path: 'https://docs.gitlab.com/ee/user/application_security/threat_monitoring/',
empty_state_svg_path: image_path('illustrations/monitoring/unable_to_connect.svg'), empty_state_svg_path: image_path('illustrations/monitoring/unable_to_connect.svg'),
waf_no_data_svg_path: image_path('illustrations/firewall-not-detected-sm.svg'),
network_policy_no_data_svg_path: image_path('illustrations/network-policies-not-detected-sm.svg'), network_policy_no_data_svg_path: image_path('illustrations/network-policies-not-detected-sm.svg'),
waf_statistics_endpoint: summary_project_security_waf_anomalies_path(@project, format: :json),
network_policy_statistics_endpoint: summary_project_security_network_policies_path(@project, format: :json), network_policy_statistics_endpoint: summary_project_security_network_policies_path(@project, format: :json),
environments_endpoint: project_environments_path(@project), environments_endpoint: project_environments_path(@project),
network_policies_endpoint: project_security_network_policies_path(@project), network_policies_endpoint: project_security_network_policies_path(@project),
......
...@@ -53,10 +53,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -53,10 +53,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resources :audit_events, only: [:index] resources :audit_events, only: [:index]
namespace :security do namespace :security do
resources :waf_anomalies, only: [] do
get :summary, on: :collection
end
resources :network_policies, only: [:index, :create, :update, :destroy] do resources :network_policies, only: [:index, :create, :update, :destroy] do
get :summary, on: :collection get :summary, on: :collection
end end
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::WafAnomaliesController do
let_it_be(:group) { create(:group) }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public, :repository, group: group) }
let_it_be(:environment) { create(:environment, :with_review_app, project: project) }
let_it_be(:cluster) { create(:cluster, :provided_by_gcp, environment_scope: '*', projects: [environment.project]) }
let_it_be(:action_params) { { project_id: project, namespace_id: project.namespace, environment_id: environment } }
let(:es_client) { nil }
describe 'GET #summary' do
subject(:request) { get :summary, params: action_params, format: :json }
before do
stub_licensed_features(threat_monitoring: true)
sign_in(user)
allow_next_instance_of(::Security::WafAnomalySummaryService) do |instance|
allow(instance).to receive(:elasticsearch_client).at_most(3).times { es_client }
allow(instance).to receive(:chart_above_v3?) { true }
end
end
include_context '"Security & Compliance" permissions' do
let(:valid_request) { request }
before_request do
group.add_developer(user)
end
end
context 'with authorized user' do
before do
group.add_developer(user)
end
context 'with elastic_stack' do
let(:es_client) { double(Elasticsearch::Client) }
before do
allow(es_client).to receive(:msearch) { { "responses" => [{}, {}] } }
end
it 'returns anomaly summary' do
subject
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['total_traffic']).to eq(0)
expect(json_response['anomalous_traffic']).to eq(0)
expect(response).to match_response_schema('vulnerabilities/summary', dir: 'ee')
end
end
context 'without elastic_stack' do
it 'returns not found' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
it 'sets a polling interval header' do
subject
expect(response.headers['Poll-Interval']).to eq('5000')
end
end
context 'with unauthorized user' do
it 'returns unauthorized' do
subject
expect(response).to have_gitlab_http_status(:forbidden)
end
end
end
end
...@@ -37,7 +37,7 @@ describe('ThreatMonitoringSection component', () => { ...@@ -37,7 +37,7 @@ describe('ThreatMonitoringSection component', () => {
wrapper = shallowMount(ThreatMonitoringSection, { wrapper = shallowMount(ThreatMonitoringSection, {
propsData: { propsData: {
storeNamespace: 'threatMonitoringNetworkPolicy', storeNamespace: 'threatMonitoringNetworkPolicy',
title: 'Web Application Firewall', title: 'Container Network Policy',
subtitle: 'Requests', subtitle: 'Requests',
nominalTitle: 'Total Requests', nominalTitle: 'Total Requests',
anomalousTitle: 'Anomalous Requests', anomalousTitle: 'Anomalous Requests',
......
...@@ -96,26 +96,7 @@ FactoryBot.define do ...@@ -96,26 +96,7 @@ FactoryBot.define do
end end
factory :clusters_applications_ingress, class: 'Clusters::Applications::Ingress' do factory :clusters_applications_ingress, class: 'Clusters::Applications::Ingress' do
modsecurity_enabled { false }
cluster factory: %i(cluster with_installed_helm provided_by_gcp) cluster factory: %i(cluster with_installed_helm provided_by_gcp)
trait :modsecurity_blocking do
modsecurity_enabled { true }
modsecurity_mode { :blocking }
end
trait :modsecurity_logging do
modsecurity_enabled { true }
modsecurity_mode { :logging }
end
trait :modsecurity_disabled do
modsecurity_enabled { false }
end
trait :modsecurity_not_installed do
modsecurity_enabled { nil }
end
end end
factory :clusters_applications_cert_manager, class: 'Clusters::Applications::CertManager' do factory :clusters_applications_cert_manager, class: 'Clusters::Applications::CertManager' do
...@@ -153,7 +134,6 @@ FactoryBot.define do ...@@ -153,7 +134,6 @@ FactoryBot.define do
factory :clusters_applications_fluentd, class: 'Clusters::Applications::Fluentd' do factory :clusters_applications_fluentd, class: 'Clusters::Applications::Fluentd' do
host { 'example.com' } host { 'example.com' }
waf_log_enabled { true }
cilium_log_enabled { true } cilium_log_enabled { true }
cluster factory: %i(cluster with_installed_helm provided_by_gcp) cluster factory: %i(cluster with_installed_helm provided_by_gcp)
end end
......
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
"host": {"type": ["string", "null"]}, "host": {"type": ["string", "null"]},
"port": {"type": ["integer", "514"]}, "port": {"type": ["integer", "514"]},
"protocol": {"type": ["integer", "0"]}, "protocol": {"type": ["integer", "0"]},
"waf_log_enabled": {"type": ["boolean", "true"]},
"cilium_log_enabled": {"type": ["boolean", "true"]}, "cilium_log_enabled": {"type": ["boolean", "true"]},
"update_available": { "type": ["boolean", "null"] }, "update_available": { "type": ["boolean", "null"] },
"can_uninstall": { "type": "boolean" }, "can_uninstall": { "type": "boolean" },
......
...@@ -38,22 +38,6 @@ RSpec.describe Gitlab::Usage::Metrics::NameSuggestion do ...@@ -38,22 +38,6 @@ RSpec.describe Gitlab::Usage::Metrics::NameSuggestion do
end end
context 'joined relations' do context 'joined relations' do
context 'counted attribute comes from joined relation' do
it_behaves_like 'name suggestion' do
let(:operation) { :distinct_count }
let(:column) { ::Deployment.arel_table[:environment_id] }
let(:relation) do
::Clusters::Applications::Ingress.modsecurity_enabled.logging
.joins(cluster: :deployments)
.merge(::Clusters::Cluster.enabled)
.merge(Deployment.success)
end
let(:constraints) { /'\(clusters_applications_ingress\.modsecurity_enabled = TRUE AND clusters_applications_ingress\.modsecurity_mode = \d+ AND clusters.enabled = TRUE AND deployments.status = \d+\)'/ }
let(:name_suggestion) { /count_distinct_environment_id_from_<adjective describing\: #{constraints}>_deployments_<with>_<adjective describing\: #{constraints}>_clusters_<having>_<adjective describing\: #{constraints}>_clusters_applications_ingress/ }
end
end
context 'counted attribute comes from source relation' do context 'counted attribute comes from source relation' do
it_behaves_like 'name suggestion' do it_behaves_like 'name suggestion' do
# corresponding metric is collected with count(Issue.with_alert_management_alerts.not_authored_by(::User.alert_bot), start: issue_minimum_id, finish: issue_maximum_id) # corresponding metric is collected with count(Issue.with_alert_management_alerts.not_authored_by(::User.alert_bot), start: issue_minimum_id, finish: issue_maximum_id)
......
...@@ -3,9 +3,8 @@ ...@@ -3,9 +3,8 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Clusters::Applications::Fluentd do RSpec.describe Clusters::Applications::Fluentd do
let(:waf_log_enabled) { true }
let(:cilium_log_enabled) { true } let(:cilium_log_enabled) { true }
let(:fluentd) { create(:clusters_applications_fluentd, waf_log_enabled: waf_log_enabled, cilium_log_enabled: cilium_log_enabled) } let(:fluentd) { create(:clusters_applications_fluentd, cilium_log_enabled: cilium_log_enabled) }
include_examples 'cluster application core specs', :clusters_applications_fluentd include_examples 'cluster application core specs', :clusters_applications_fluentd
include_examples 'cluster application status specs', :clusters_applications_fluentd include_examples 'cluster application status specs', :clusters_applications_fluentd
...@@ -51,13 +50,11 @@ RSpec.describe Clusters::Applications::Fluentd do ...@@ -51,13 +50,11 @@ RSpec.describe Clusters::Applications::Fluentd do
end end
describe '#values' do describe '#values' do
let(:modsecurity_log_path) { "/var/log/containers/*#{Clusters::Applications::Ingress::MODSECURITY_LOG_CONTAINER_NAME}*.log" }
let(:cilium_log_path) { "/var/log/containers/*#{described_class::CILIUM_CONTAINER_NAME}*.log" } let(:cilium_log_path) { "/var/log/containers/*#{described_class::CILIUM_CONTAINER_NAME}*.log" }
subject { fluentd.values } subject { fluentd.values }
context 'with both logs variables set to false' do context 'with cilium_log_enabled set to false' do
let(:waf_log_enabled) { false }
let(:cilium_log_enabled) { false } let(:cilium_log_enabled) { false }
it "raises ActiveRecord::RecordInvalid" do it "raises ActiveRecord::RecordInvalid" do
...@@ -65,18 +62,8 @@ RSpec.describe Clusters::Applications::Fluentd do ...@@ -65,18 +62,8 @@ RSpec.describe Clusters::Applications::Fluentd do
end end
end end
context 'with both logs variables set to true' do
it { is_expected.to include("#{modsecurity_log_path},#{cilium_log_path}") }
end
context 'with waf_log_enabled set to true' do
let(:cilium_log_enabled) { false }
it { is_expected.to include(modsecurity_log_path) }
end
context 'with cilium_log_enabled set to true' do context 'with cilium_log_enabled set to true' do
let(:waf_log_enabled) { false } let(:cilium_log_enabled) { true }
it { is_expected.to include(cilium_log_path) } it { is_expected.to include(cilium_log_path) }
end end
......
...@@ -172,94 +172,4 @@ RSpec.describe Clusters::Applications::Ingress do ...@@ -172,94 +172,4 @@ RSpec.describe Clusters::Applications::Ingress do
expect(values).to include('clusterIP') expect(values).to include('clusterIP')
end end
end end
describe '#values' do
subject { ingress }
context 'when modsecurity_enabled is enabled' do
before do
allow(subject).to receive(:modsecurity_enabled).and_return(true)
end
it 'includes modsecurity module enablement' do
expect(subject.values).to include("enable-modsecurity: 'true'")
end
it 'includes modsecurity core ruleset enablement set to false' do
expect(subject.values).to include("enable-owasp-modsecurity-crs: 'false'")
end
it 'includes modsecurity snippet with information related to security rules' do
expect(subject.values).to include("SecRuleEngine DetectionOnly")
expect(subject.values).to include("Include #{described_class::MODSECURITY_OWASP_RULES_FILE}")
end
context 'when modsecurity_mode is set to :blocking' do
before do
subject.blocking!
end
it 'includes modsecurity snippet with information related to security rules' do
expect(subject.values).to include("SecRuleEngine On")
expect(subject.values).to include("Include #{described_class::MODSECURITY_OWASP_RULES_FILE}")
end
end
it 'includes modsecurity.conf content' do
expect(subject.values).to include('modsecurity.conf')
# Includes file content from Ingress#modsecurity_config_content
expect(subject.values).to include('SecAuditLog')
expect(subject.values).to include('extraVolumes')
expect(subject.values).to include('extraVolumeMounts')
end
it 'includes modsecurity sidecar container' do
expect(subject.values).to include('modsecurity-log-volume')
expect(subject.values).to include('extraContainers')
end
it 'executes command to tail modsecurity logs with -F option' do
args = YAML.safe_load(subject.values).dig('controller', 'extraContainers', 0, 'args')
expect(args).to eq(['/bin/sh', '-c', 'tail -F /var/log/modsec/audit.log'])
end
it 'includes livenessProbe for modsecurity sidecar container' do
probe_config = YAML.safe_load(subject.values).dig('controller', 'extraContainers', 0, 'livenessProbe')
expect(probe_config).to eq('exec' => { 'command' => ['ls', '/var/log/modsec/audit.log'] })
end
end
context 'when modsecurity_enabled is disabled' do
before do
allow(subject).to receive(:modsecurity_enabled).and_return(false)
end
it 'excludes modsecurity module enablement' do
expect(subject.values).not_to include('enable-modsecurity')
end
it 'excludes modsecurity core ruleset enablement' do
expect(subject.values).not_to include('enable-owasp-modsecurity-crs')
end
it 'excludes modsecurity.conf content' do
expect(subject.values).not_to include('modsecurity.conf')
# Excludes file content from Ingress#modsecurity_config_content
expect(subject.values).not_to include('SecAuditLog')
expect(subject.values).not_to include('extraVolumes')
expect(subject.values).not_to include('extraVolumeMounts')
end
it 'excludes modsecurity sidecar container' do
expect(subject.values).not_to include('modsecurity-log-volume')
expect(subject.values).not_to include('extraContainers')
end
end
end
end end
...@@ -196,28 +196,6 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do ...@@ -196,28 +196,6 @@ RSpec.describe Clusters::Cluster, :use_clean_rails_memory_store_caching do
end end
end end
describe '.with_enabled_modsecurity' do
subject { described_class.with_enabled_modsecurity }
let_it_be(:cluster) { create(:cluster) }
context 'cluster has ingress application with enabled modsecurity' do
let!(:application) { create(:clusters_applications_ingress, :installed, :modsecurity_logging, cluster: cluster) }
it { is_expected.to include(cluster) }
end
context 'cluster has ingress application with disabled modsecurity' do
let!(:application) { create(:clusters_applications_ingress, :installed, :modsecurity_disabled, cluster: cluster) }
it { is_expected.not_to include(cluster) }
end
context 'cluster does not have ingress application' do
it { is_expected.not_to include(cluster) }
end
end
describe '.with_available_elasticstack' do describe '.with_available_elasticstack' do
subject { described_class.with_available_elasticstack } subject { described_class.with_available_elasticstack }
......
...@@ -85,7 +85,6 @@ RSpec.describe ClusterApplicationEntity do ...@@ -85,7 +85,6 @@ RSpec.describe ClusterApplicationEntity do
expect(subject[:port]).to eq(514) expect(subject[:port]).to eq(514)
expect(subject[:host]).to eq("example.com") expect(subject[:host]).to eq("example.com")
expect(subject[:protocol]).to eq("tcp") expect(subject[:protocol]).to eq("tcp")
expect(subject[:waf_log_enabled]).to be true
expect(subject[:cilium_log_enabled]).to be true expect(subject[:cilium_log_enabled]).to be true
end end
end end
......
...@@ -46,8 +46,7 @@ RSpec.describe Clusters::Applications::CreateService do ...@@ -46,8 +46,7 @@ RSpec.describe Clusters::Applications::CreateService do
context 'ingress application' do context 'ingress application' do
let(:params) do let(:params) do
{ {
application: 'ingress', application: 'ingress'
modsecurity_enabled: true
} }
end end
...@@ -64,10 +63,6 @@ RSpec.describe Clusters::Applications::CreateService do ...@@ -64,10 +63,6 @@ RSpec.describe Clusters::Applications::CreateService do
cluster.reload cluster.reload
end.to change(cluster, :application_ingress) end.to change(cluster, :application_ingress)
end end
it 'sets modsecurity_enabled' do
expect(subject.modsecurity_enabled).to eq(true)
end
end end
context 'cert manager application' do context 'cert manager application' do
......
...@@ -61,12 +61,6 @@ filebeat: ...@@ -61,12 +61,6 @@ filebeat:
target_field: tie_breaker_id target_field: tie_breaker_id
- add_cloud_metadata: ~ - add_cloud_metadata: ~
- add_kubernetes_metadata: ~ - add_kubernetes_metadata: ~
- decode_json_fields:
fields: ["message"]
when:
equals:
kubernetes.container.namespace: "gitlab-managed-apps"
kubernetes.container.name: "modsecurity-log"
kibana: kibana:
enabled: false enabled: false
elasticsearchHosts: "http://elastic-stack-elasticsearch-master:9200" elasticsearchHosts: "http://elastic-stack-elasticsearch-master:9200"
......
This diff is collapsed.
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