Skip to content

Commit

Permalink
feat: 🎸 write audit logs to file (#65)
Browse files Browse the repository at this point in the history
* feat: 🎸 write audit logs to file

* terraform-docs: automated action

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
jaskaransarkaria and github-actions[bot] authored Sep 1, 2023
1 parent d14ef16 commit 2999f1d
Show file tree
Hide file tree
Showing 7 changed files with 347 additions and 35 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ No modules.
|------|------|
| [helm_release.nginx_ingress](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [kubectl_manifest.nginx_ingress_default_certificate](https://registry.terraform.io/providers/gavinbunney/kubectl/latest/docs/resources/manifest) | resource |
| [kubernetes_config_map.fluent-bit-config](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource |
| [kubernetes_config_map.fluent_bit_lua_script](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource |
| [kubernetes_config_map.modsecurity_nginx_config](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map) | resource |
| [kubernetes_cron_job_v1.restart_modsec_containers](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/cron_job_v1) | resource |
| [kubernetes_namespace.ingress_controllers](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace) | resource |
| [kubernetes_role_binding_v1.restart_modsec_containers](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_binding_v1) | resource |
| [kubernetes_role_v1.restart_modsec_containers](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/role_v1) | resource |
| [kubernetes_service_account_v1.restart_modsec_containers](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account_v1) | resource |
| [template_file.nginx_ingress_default_certificate](https://registry.terraform.io/providers/hashicorp/template/latest/docs/data-sources/file) | data source |

## Inputs
Expand All @@ -48,6 +54,7 @@ No modules.
|------|-------------|------|---------|:--------:|
| <a name="input_backend_repo"></a> [backend\_repo](#input\_backend\_repo) | repository for the default backend app | `string` | `"ministryofjustice/cloud-platform-custom-error-pages"` | no |
| <a name="input_backend_tag"></a> [backend\_tag](#input\_backend\_tag) | tag of the default backend app | `string` | `"0.6"` | no |
| <a name="input_cluster"></a> [cluster](#input\_cluster) | cluster name used for opensearch indicies | `string` | `""` | no |
| <a name="input_cluster_domain_name"></a> [cluster\_domain\_name](#input\_cluster\_domain\_name) | The cluster domain used for externalDNS annotations and certmanager | `any` | n/a | yes |
| <a name="input_controller_name"></a> [controller\_name](#input\_controller\_name) | Will be used as the ingress controller name and the class annotation | `string` | n/a | yes |
| <a name="input_default_cert"></a> [default\_cert](#input\_default\_cert) | Useful if you want to use a default certificate for your ingress controller. Format: namespace/secretName | `string` | `"ingress-controllers/default-certificate"` | no |
Expand All @@ -56,9 +63,11 @@ No modules.
| <a name="input_enable_latest_tls"></a> [enable\_latest\_tls](#input\_enable\_latest\_tls) | Provide support to tlsv1.3 along with tlsv1.2 | `bool` | `false` | no |
| <a name="input_enable_modsec"></a> [enable\_modsec](#input\_enable\_modsec) | Enable https://github.com/SpiderLabs/ModSecurity-nginx | `bool` | `false` | no |
| <a name="input_enable_owasp"></a> [enable\_owasp](#input\_enable\_owasp) | Use default ruleset from https://github.com/SpiderLabs/owasp-modsecurity-crs/ | `bool` | `false` | no |
| <a name="input_fluent_bit_version"></a> [fluent\_bit\_version](#input\_fluent\_bit\_version) | fluent bit container version used to exrtact modsec audit logs | `string` | `"2.1.8-amd64"` | no |
| <a name="input_is_live_cluster"></a> [is\_live\_cluster](#input\_is\_live\_cluster) | For live clusters externalDNS annotation will have var.live\_domain (default *.cloud-platform.service.justice.gov.uk) | `bool` | `false` | no |
| <a name="input_live1_cert_dns_name"></a> [live1\_cert\_dns\_name](#input\_live1\_cert\_dns\_name) | This is to add the live-1 dns name for eks-live cluster default certificate | `string` | `""` | no |
| <a name="input_live_domain"></a> [live\_domain](#input\_live\_domain) | The live domain used for externalDNS annotation | `string` | `"cloud-platform.service.justice.gov.uk"` | no |
| <a name="input_opensearch_modsec_audit_host"></a> [opensearch\_modsec\_audit\_host](#input\_opensearch\_modsec\_audit\_host) | domain endpoint for the opensearch cluster | `string` | `""` | no |
| <a name="input_replica_count"></a> [replica\_count](#input\_replica\_count) | Number of replicas set in deployment | `string` | n/a | yes |

## Outputs
Expand Down
201 changes: 201 additions & 0 deletions configmap.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
resource "kubernetes_config_map" "fluent-bit-config" {
count = var.enable_modsec ? 1 : 0

metadata {
name = "fluent-bit-config"
namespace = "ingress-controllers"
labels = {
"k8s-app" = var.controller_name
}
}
data = {
"fluent-bit.conf" = <<-EOT
[SERVICE]
Flush 1
Log_Level info
Daemon Off
Grace 30
Parsers_File parsers.conf
Parsers_File custom_parsers.conf
HTTP_Server On
HTTP_Listen 0.0.0.0
HTTP_Port 2020
Storage.path /var/log/flb-storage/
Storage.max_chunks_up 64
Storage.backlog.mem_limit 5MB
[INPUT]
Name tail
Alias modsec_nginx_ingress_audit_index
Tag cp-ingress-modsec-index-audit.*
Path /var/log/audit/*.log
Parser modsec-audit-log-index
Refresh_Interval 5
Buffer_Max_Size 5MB
Buffer_Chunk_Size 1M
Offset_Key pause_position_modsec-audit-index
DB cp-ingress-modsec-audit-index.db
DB.locking true
Storage.type filesystem
Storage.pause_on_chunks_overlimit True
[INPUT]
Name tail
Alias modsec_nginx_ingress_audit
Tag cp-ingress-modsec-audit.*
Path /var/log/audit/**/**/*
Parser docker
Refresh_Interval 5
Buffer_Max_Size 5MB
Buffer_Chunk_Size 1M
Offset_Key pause_position_modsec-audit
DB cp-ingress-modsec-audit.db
DB.locking true
Storage.type filesystem
Storage.pause_on_chunks_overlimit True
[FILTER]
Name lua
Match cp-ingress-modsec-audit.*
script /fluent-bit/scripts/cb_extract_tag_value.lua
call cb_extract_tag_value
[FILTER]
Name parser
Parser generic-json
Match cp-ingress-modsec-audit.*
Key_Name log
Reserve_Data On
Preserve_Key On
[OUTPUT]
Name opensearch
Alias modsec_nginx_ingress_audit
Match *
Host ${var.opensearch_modsec_audit_host}
Port 443
Type _doc
Time_Key @timestamp
Logstash_Prefix ${var.cluster}_k8s_modsec_ingress
tls On
Logstash_Format On
Replace_Dots On
Generate_ID On
Retry_Limit False
AWS_AUTH On
AWS_REGION eu-west-2
Suppress_Type_Name On
Buffer_Size False
EOT

"custom_parsers.conf" = <<-EOT
[PARSER]
Name modsec-audit-log-index
Format regex
Regex ^(?<url>[^ ]+) (?<client_ip>[^ ]+) (?<log>.*)$
Time_Key time
Time_Format %d/%m/%Y:T%H:%M:%S.%z
[PARSER]
Name initial-json
Format json
Time_Key time
Time_Keep On
[PARSER]
Name generic-json
Format json
Time_Key time
Time_Format %Y-%b-%dT%H:%M:%S
Time_Keep On
# Command | Decoder | Field | Optional Action
# =============|==================|=================
Decode_Field_As escaped_utf8 log do_next
Decode_Field_As json log
EOT
}

depends_on = [
kubernetes_namespace.ingress_controllers,
]

lifecycle {
ignore_changes = [metadata[0].annotations]
}
}

resource "kubernetes_config_map" "fluent_bit_lua_script" {
count = var.enable_modsec ? 1 : 0

metadata {
name = "fluent-bit-luascripts"
namespace = "ingress-controllers"
labels = {
"k8s-app" = var.controller_name
}
}
data = {
"cb_extract_tag_value.lua" = <<-EOT
function cb_extract_tag_value(tag, timestamp, record)
local github_team = string.gmatch(record["log"], '%[tag "github_team=([%a+|%-]*)"%]')
local github_team_from_json = string.gmatch(record["log"], '"tags":%[.*"github_team=([%a+|%-]*)".*%]')
local new_record = record
local team_matches = {}
local json_matches = {}
for team in github_team do
table.insert(team_matches, team)
end
for team in github_team_from_json do
table.insert(json_matches, team)
end
if #team_matches > 0 then
new_record["github_teams"] = team_matches
return 1, timestamp, new_record
elseif #json_matches > 0 then
new_record["github_teams"] = json_matches
return 1, timestamp, new_record
else
return 0, timestamp, record
end
end
EOT
}

depends_on = [
kubernetes_namespace.ingress_controllers,
]

lifecycle {
ignore_changes = [metadata[0].annotations]
}
}

resource "kubernetes_config_map" "modsecurity_nginx_config" {
count = var.enable_modsec ? 1 : 0

metadata {
name = "modsecurity-nginx-config"
namespace = "ingress-controllers"
labels = {
"k8s-app" = var.controller_name
}
}
data = {
"modsecurity.conf" = file("${path.module}/templates/modsecurity.conf"),
}

depends_on = [
kubernetes_namespace.ingress_controllers,
]

lifecycle {
ignore_changes = [metadata[0].annotations]
}
}

69 changes: 69 additions & 0 deletions cron.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
resource "kubernetes_service_account_v1" "restart_modsec_containers" {
metadata {
name = "restart-modsec-containers"
namespace = "ingress-controllers"
}
}

resource "kubernetes_role_v1" "restart_modsec_containers" {
metadata {
name = "restart-modsec-containers"
namespace = "ingress-controllers"
}

rule {
api_groups = ["apps", "applications"]
resources = ["deployments"]
verbs = ["get", "list", "patch"]
}
}

resource "kubernetes_role_binding_v1" "restart_modsec_containers" {
metadata {
name = "restart-modsec-containers"
namespace = "ingress-controllers"
}
role_ref {
api_group = "rbac.authorization.k8s.io"
kind = "Role"
name = "restart-modsec-containers"
}
subject {
kind = "ServiceAccount"
name = "restart-modsec-containers"
namespace = "ingress-controllers"
}
}

resource "kubernetes_cron_job_v1" "restart_modsec_containers" {
metadata {
name = "restart-modsec-containers-nightly"
namespace = "ingress-controllers"
}
spec {
concurrency_policy = "Forbid"
failed_jobs_history_limit = 2
schedule = "00 23 * * *"
starting_deadline_seconds = 10
successful_jobs_history_limit = 0
job_template {
metadata {}
spec {
backoff_limit = 2
active_deadline_seconds = 600
ttl_seconds_after_finished = 10
template {
metadata {}
spec {
service_account_name = "restart-modsec-containers"
container {
name = "kubectl"
image = "bitnami/kubectl"
command = ["kubectl", "rollout", "restart", "deployment/nginx-ingress-modsec-controller"]
}
}
}
}
}
}
}
24 changes: 1 addition & 23 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ resource "helm_release" "nginx_ingress" {
enable_external_dns_annotation = var.enable_external_dns_annotation
backend_repo = var.backend_repo
backend_tag = var.backend_tag
fluent_bit_version = var.fluent_bit_version
})]

depends_on = [
Expand Down Expand Up @@ -98,26 +99,3 @@ resource "kubectl_manifest" "nginx_ingress_default_certificate" {
var.dependence_certmanager
]
}

resource "kubernetes_config_map" "modsecurity_nginx_config" {
count = var.enable_modsec ? 1 : 0

metadata {
name = "modsecurity-nginx-config"
namespace = "ingress-controllers"
labels = {
"k8s-app" = var.controller_name
}
}
data = {
"modsecurity.conf" = file("${path.module}/templates/modsecurity.conf"),
}

depends_on = [
kubernetes_namespace.ingress_controllers,
]

lifecycle {
ignore_changes = [metadata[0].annotations]
}
}
15 changes: 10 additions & 5 deletions templates/modsecurity.conf
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,15 @@ SecAuditLogParts AEFHKZ
# Use a single file for logging. This is much easier to look at, but
# assumes that you will use the audit log only ocassionally.
#
SecAuditLogType Serial
SecAuditLog /dev/stdout
SecAuditLogType Concurrent

SecAuditLogDirMode 0777
SecAuditLogFileMode 0777

SecAuditLog /var/log/audit/index.log

SecAuditLog2 /var/log/audit/index2.log
SecAuditLogStorageDir /var/log/audit/
SecAuditLogFormat JSON
SecRuleRemoveById 920350

Expand Down Expand Up @@ -268,7 +275,5 @@ SecUnicodeMapFile unicode.mapping 20127
# The following information will be shared: ModSecurity version,
# Web Server version, APR version, PCRE version, Lua version, Libxml2
# version, Anonymous unique id for host.
SecStatusEngine On

SecAuditLogStorageDir /var/log/audit/
SecStatusEngine Off

Loading

0 comments on commit 2999f1d

Please sign in to comment.