mirror of
https://github.com/morten-olsen/homelab-nuclei-operator.git
synced 2026-02-08 02:16:23 +01:00
init
This commit is contained in:
514
docs/api.md
Normal file
514
docs/api.md
Normal file
@@ -0,0 +1,514 @@
|
||||
# API Reference
|
||||
|
||||
This document provides a complete reference for the Nuclei Operator Custom Resource Definitions (CRDs).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [NucleiScan](#nucleiscan)
|
||||
- [Metadata](#metadata)
|
||||
- [Spec](#spec)
|
||||
- [Status](#status)
|
||||
- [Type Definitions](#type-definitions)
|
||||
- [SourceReference](#sourcereference)
|
||||
- [Finding](#finding)
|
||||
- [ScanSummary](#scansummary)
|
||||
- [ScanPhase](#scanphase)
|
||||
- [Examples](#examples)
|
||||
|
||||
---
|
||||
|
||||
## NucleiScan
|
||||
|
||||
`NucleiScan` is the primary custom resource for the Nuclei Operator. It represents a security scan configuration and stores the scan results.
|
||||
|
||||
**API Group:** `nuclei.homelab.mortenolsen.pro`
|
||||
**API Version:** `v1alpha1`
|
||||
**Kind:** `NucleiScan`
|
||||
**Short Names:** `ns`, `nscan`
|
||||
|
||||
### Metadata
|
||||
|
||||
Standard Kubernetes metadata fields apply. The operator automatically sets owner references when creating NucleiScan resources from Ingress or VirtualService resources.
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `name` | string | Unique name within the namespace |
|
||||
| `namespace` | string | Namespace where the resource resides |
|
||||
| `labels` | map[string]string | Labels for organizing and selecting resources |
|
||||
| `annotations` | map[string]string | Annotations for storing additional metadata |
|
||||
| `ownerReferences` | []OwnerReference | References to owner resources (set automatically) |
|
||||
|
||||
### Spec
|
||||
|
||||
The `spec` field defines the desired state of the NucleiScan.
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
name: my-ingress
|
||||
namespace: default
|
||||
uid: "abc123-def456"
|
||||
targets:
|
||||
- https://example.com
|
||||
- https://api.example.com
|
||||
templates:
|
||||
- cves/
|
||||
- vulnerabilities/
|
||||
severity:
|
||||
- medium
|
||||
- high
|
||||
- critical
|
||||
schedule: "@every 24h"
|
||||
suspend: false
|
||||
```
|
||||
|
||||
#### Spec Fields
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `sourceRef` | [SourceReference](#sourcereference) | Yes | Reference to the source Ingress or VirtualService |
|
||||
| `targets` | []string | Yes | List of URLs to scan (minimum 1) |
|
||||
| `templates` | []string | No | Nuclei templates to use. If empty, uses default templates |
|
||||
| `severity` | []string | No | Severity filter. Valid values: `info`, `low`, `medium`, `high`, `critical` |
|
||||
| `schedule` | string | No | Cron schedule for periodic rescanning |
|
||||
| `suspend` | bool | No | When true, suspends scheduled scans |
|
||||
|
||||
#### Schedule Format
|
||||
|
||||
The `schedule` field supports two formats:
|
||||
|
||||
1. **Simplified interval format:**
|
||||
- `@every <duration>` - e.g., `@every 24h`, `@every 6h`, `@every 30m`
|
||||
|
||||
2. **Standard cron format:**
|
||||
- `* * * * *` - minute, hour, day of month, month, day of week
|
||||
- Examples:
|
||||
- `0 2 * * *` - Daily at 2:00 AM
|
||||
- `0 */6 * * *` - Every 6 hours
|
||||
- `0 3 * * 0` - Weekly on Sunday at 3:00 AM
|
||||
|
||||
### Status
|
||||
|
||||
The `status` field contains the observed state of the NucleiScan, including scan results.
|
||||
|
||||
```yaml
|
||||
status:
|
||||
phase: Completed
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: "True"
|
||||
reason: ScanCompleted
|
||||
message: "Scan completed with 3 findings"
|
||||
lastTransitionTime: "2024-01-15T10:35:00Z"
|
||||
- type: ScanActive
|
||||
status: "False"
|
||||
reason: ScanCompleted
|
||||
message: "Scan completed successfully"
|
||||
lastTransitionTime: "2024-01-15T10:35:00Z"
|
||||
lastScanTime: "2024-01-15T10:30:00Z"
|
||||
completionTime: "2024-01-15T10:35:00Z"
|
||||
nextScheduledTime: "2024-01-16T10:30:00Z"
|
||||
summary:
|
||||
totalFindings: 3
|
||||
findingsBySeverity:
|
||||
medium: 2
|
||||
high: 1
|
||||
targetsScanned: 2
|
||||
durationSeconds: 300
|
||||
findings:
|
||||
- templateId: CVE-2021-44228
|
||||
templateName: Apache Log4j RCE
|
||||
severity: critical
|
||||
type: http
|
||||
host: https://example.com
|
||||
matchedAt: https://example.com/api/login
|
||||
timestamp: "2024-01-15T10:32:00Z"
|
||||
lastError: ""
|
||||
observedGeneration: 1
|
||||
```
|
||||
|
||||
#### Status Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `phase` | [ScanPhase](#scanphase) | Current phase of the scan |
|
||||
| `conditions` | []Condition | Standard Kubernetes conditions |
|
||||
| `lastScanTime` | *Time | When the last scan was initiated |
|
||||
| `completionTime` | *Time | When the last scan completed |
|
||||
| `nextScheduledTime` | *Time | When the next scheduled scan will run |
|
||||
| `summary` | *[ScanSummary](#scansummary) | Aggregated scan statistics |
|
||||
| `findings` | [][Finding](#finding) | Array of scan results |
|
||||
| `lastError` | string | Error message if the scan failed |
|
||||
| `observedGeneration` | int64 | Generation observed by the controller |
|
||||
|
||||
#### Conditions
|
||||
|
||||
The operator maintains the following condition types:
|
||||
|
||||
| Type | Description |
|
||||
|------|-------------|
|
||||
| `Ready` | Indicates whether the scan has completed successfully |
|
||||
| `ScanActive` | Indicates whether a scan is currently running |
|
||||
|
||||
**Condition Reasons:**
|
||||
|
||||
| Reason | Description |
|
||||
|--------|-------------|
|
||||
| `ScanPending` | Scan is waiting to start |
|
||||
| `ScanRunning` | Scan is currently in progress |
|
||||
| `ScanCompleted` | Scan completed successfully |
|
||||
| `ScanFailed` | Scan failed with an error |
|
||||
| `ScanSuspended` | Scan is suspended |
|
||||
|
||||
---
|
||||
|
||||
## Type Definitions
|
||||
|
||||
### SourceReference
|
||||
|
||||
`SourceReference` identifies the Ingress or VirtualService that triggered the scan.
|
||||
|
||||
```go
|
||||
type SourceReference struct {
|
||||
APIVersion string `json:"apiVersion"`
|
||||
Kind string `json:"kind"`
|
||||
Name string `json:"name"`
|
||||
Namespace string `json:"namespace"`
|
||||
UID string `json:"uid"`
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `apiVersion` | string | Yes | API version of the source resource (e.g., `networking.k8s.io/v1`) |
|
||||
| `kind` | string | Yes | Kind of the source resource. Valid values: `Ingress`, `VirtualService` |
|
||||
| `name` | string | Yes | Name of the source resource |
|
||||
| `namespace` | string | Yes | Namespace of the source resource |
|
||||
| `uid` | string | Yes | UID of the source resource |
|
||||
|
||||
### Finding
|
||||
|
||||
`Finding` represents a single vulnerability or issue discovered during a scan.
|
||||
|
||||
```go
|
||||
type Finding struct {
|
||||
TemplateID string `json:"templateId"`
|
||||
TemplateName string `json:"templateName,omitempty"`
|
||||
Severity string `json:"severity"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Host string `json:"host"`
|
||||
MatchedAt string `json:"matchedAt,omitempty"`
|
||||
ExtractedResults []string `json:"extractedResults,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Reference []string `json:"reference,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Timestamp metav1.Time `json:"timestamp"`
|
||||
Metadata *runtime.RawExtension `json:"metadata,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `templateId` | string | Yes | Nuclei template identifier (e.g., `CVE-2021-44228`) |
|
||||
| `templateName` | string | No | Human-readable template name |
|
||||
| `severity` | string | Yes | Severity level: `info`, `low`, `medium`, `high`, `critical` |
|
||||
| `type` | string | No | Finding type: `http`, `dns`, `ssl`, `tcp`, etc. |
|
||||
| `host` | string | Yes | Target host that was scanned |
|
||||
| `matchedAt` | string | No | Specific URL or endpoint where the issue was found |
|
||||
| `extractedResults` | []string | No | Data extracted by the template |
|
||||
| `description` | string | No | Detailed description of the finding |
|
||||
| `reference` | []string | No | URLs to additional information |
|
||||
| `tags` | []string | No | Tags associated with the finding |
|
||||
| `timestamp` | Time | Yes | When the finding was discovered |
|
||||
| `metadata` | RawExtension | No | Additional template metadata (preserved as JSON) |
|
||||
|
||||
### ScanSummary
|
||||
|
||||
`ScanSummary` provides aggregated statistics about the scan.
|
||||
|
||||
```go
|
||||
type ScanSummary struct {
|
||||
TotalFindings int `json:"totalFindings"`
|
||||
FindingsBySeverity map[string]int `json:"findingsBySeverity,omitempty"`
|
||||
TargetsScanned int `json:"targetsScanned"`
|
||||
DurationSeconds int64 `json:"durationSeconds,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `totalFindings` | int | Total number of findings |
|
||||
| `findingsBySeverity` | map[string]int | Breakdown of findings by severity level |
|
||||
| `targetsScanned` | int | Number of targets that were scanned |
|
||||
| `durationSeconds` | int64 | Duration of the scan in seconds |
|
||||
|
||||
### ScanPhase
|
||||
|
||||
`ScanPhase` represents the current phase of the scan lifecycle.
|
||||
|
||||
```go
|
||||
type ScanPhase string
|
||||
|
||||
const (
|
||||
ScanPhasePending ScanPhase = "Pending"
|
||||
ScanPhaseRunning ScanPhase = "Running"
|
||||
ScanPhaseCompleted ScanPhase = "Completed"
|
||||
ScanPhaseFailed ScanPhase = "Failed"
|
||||
)
|
||||
```
|
||||
|
||||
| Phase | Description |
|
||||
|-------|-------------|
|
||||
| `Pending` | Scan is waiting to be executed |
|
||||
| `Running` | Scan is currently in progress |
|
||||
| `Completed` | Scan finished successfully |
|
||||
| `Failed` | Scan failed with an error |
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Basic NucleiScan
|
||||
|
||||
A minimal NucleiScan configuration:
|
||||
|
||||
```yaml
|
||||
apiVersion: nuclei.homelab.mortenolsen.pro/v1alpha1
|
||||
kind: NucleiScan
|
||||
metadata:
|
||||
name: basic-scan
|
||||
namespace: default
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
name: my-ingress
|
||||
namespace: default
|
||||
uid: "12345678-1234-1234-1234-123456789012"
|
||||
targets:
|
||||
- https://example.com
|
||||
```
|
||||
|
||||
### NucleiScan with Severity Filter
|
||||
|
||||
Scan only for medium, high, and critical vulnerabilities:
|
||||
|
||||
```yaml
|
||||
apiVersion: nuclei.homelab.mortenolsen.pro/v1alpha1
|
||||
kind: NucleiScan
|
||||
metadata:
|
||||
name: severity-filtered-scan
|
||||
namespace: default
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
name: production-ingress
|
||||
namespace: production
|
||||
uid: "abcdef12-3456-7890-abcd-ef1234567890"
|
||||
targets:
|
||||
- https://api.example.com
|
||||
- https://www.example.com
|
||||
severity:
|
||||
- medium
|
||||
- high
|
||||
- critical
|
||||
```
|
||||
|
||||
### NucleiScan with Specific Templates
|
||||
|
||||
Use specific Nuclei template categories:
|
||||
|
||||
```yaml
|
||||
apiVersion: nuclei.homelab.mortenolsen.pro/v1alpha1
|
||||
kind: NucleiScan
|
||||
metadata:
|
||||
name: cve-scan
|
||||
namespace: default
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
name: app-ingress
|
||||
namespace: default
|
||||
uid: "fedcba98-7654-3210-fedc-ba9876543210"
|
||||
targets:
|
||||
- https://app.example.com
|
||||
templates:
|
||||
- cves/
|
||||
- vulnerabilities/
|
||||
- exposures/
|
||||
severity:
|
||||
- high
|
||||
- critical
|
||||
```
|
||||
|
||||
### Scheduled NucleiScan
|
||||
|
||||
Run a scan daily at 2:00 AM:
|
||||
|
||||
```yaml
|
||||
apiVersion: nuclei.homelab.mortenolsen.pro/v1alpha1
|
||||
kind: NucleiScan
|
||||
metadata:
|
||||
name: daily-security-scan
|
||||
namespace: default
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
name: main-ingress
|
||||
namespace: default
|
||||
uid: "11111111-2222-3333-4444-555555555555"
|
||||
targets:
|
||||
- https://example.com
|
||||
- https://api.example.com
|
||||
severity:
|
||||
- medium
|
||||
- high
|
||||
- critical
|
||||
schedule: "0 2 * * *"
|
||||
suspend: false
|
||||
```
|
||||
|
||||
### NucleiScan for VirtualService
|
||||
|
||||
Scan an Istio VirtualService:
|
||||
|
||||
```yaml
|
||||
apiVersion: nuclei.homelab.mortenolsen.pro/v1alpha1
|
||||
kind: NucleiScan
|
||||
metadata:
|
||||
name: istio-app-scan
|
||||
namespace: istio-apps
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.istio.io/v1beta1
|
||||
kind: VirtualService
|
||||
name: my-virtualservice
|
||||
namespace: istio-apps
|
||||
uid: "vs-uid-12345"
|
||||
targets:
|
||||
- https://istio-app.example.com
|
||||
severity:
|
||||
- low
|
||||
- medium
|
||||
- high
|
||||
- critical
|
||||
```
|
||||
|
||||
### Comprehensive Security Audit
|
||||
|
||||
Full security audit with all severity levels and template categories:
|
||||
|
||||
```yaml
|
||||
apiVersion: nuclei.homelab.mortenolsen.pro/v1alpha1
|
||||
kind: NucleiScan
|
||||
metadata:
|
||||
name: comprehensive-audit
|
||||
namespace: security
|
||||
labels:
|
||||
audit-type: comprehensive
|
||||
compliance: required
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
name: production-ingress
|
||||
namespace: production
|
||||
uid: "prod-uid-67890"
|
||||
targets:
|
||||
- https://www.example.com
|
||||
- https://api.example.com
|
||||
- https://admin.example.com
|
||||
templates:
|
||||
- cves/
|
||||
- vulnerabilities/
|
||||
- exposures/
|
||||
- misconfiguration/
|
||||
- default-logins/
|
||||
- takeovers/
|
||||
severity:
|
||||
- info
|
||||
- low
|
||||
- medium
|
||||
- high
|
||||
- critical
|
||||
schedule: "0 3 * * 0" # Weekly on Sunday at 3 AM
|
||||
suspend: false
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Print Columns
|
||||
|
||||
When listing NucleiScan resources with `kubectl get nucleiscans`, the following columns are displayed:
|
||||
|
||||
| Column | JSONPath | Description |
|
||||
|--------|----------|-------------|
|
||||
| NAME | `.metadata.name` | Resource name |
|
||||
| PHASE | `.status.phase` | Current scan phase |
|
||||
| FINDINGS | `.status.summary.totalFindings` | Total number of findings |
|
||||
| SOURCE | `.spec.sourceRef.kind` | Source resource kind |
|
||||
| AGE | `.metadata.creationTimestamp` | Resource age |
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
NAME PHASE FINDINGS SOURCE AGE
|
||||
my-app-scan Completed 5 Ingress 2d
|
||||
api-scan Running 0 Ingress 1h
|
||||
istio-app-scan Completed 2 VirtualService 5d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Validation
|
||||
|
||||
The CRD includes validation rules enforced by the Kubernetes API server:
|
||||
|
||||
### Spec Validation
|
||||
|
||||
- `sourceRef.kind` must be either `Ingress` or `VirtualService`
|
||||
- `targets` must contain at least one item
|
||||
- `severity` values must be one of: `info`, `low`, `medium`, `high`, `critical`
|
||||
|
||||
### Status Validation
|
||||
|
||||
- `phase` must be one of: `Pending`, `Running`, `Completed`, `Failed`
|
||||
|
||||
---
|
||||
|
||||
## RBAC Requirements
|
||||
|
||||
To interact with NucleiScan resources, the following RBAC permissions are needed:
|
||||
|
||||
### Read-only Access
|
||||
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: nucleiscan-viewer
|
||||
rules:
|
||||
- apiGroups: ["nuclei.homelab.mortenolsen.pro"]
|
||||
resources: ["nucleiscans"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
```
|
||||
|
||||
### Full Access
|
||||
|
||||
```yaml
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: nucleiscan-editor
|
||||
rules:
|
||||
- apiGroups: ["nuclei.homelab.mortenolsen.pro"]
|
||||
resources: ["nucleiscans"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["nuclei.homelab.mortenolsen.pro"]
|
||||
resources: ["nucleiscans/status"]
|
||||
verbs: ["get"]
|
||||
762
docs/user-guide.md
Normal file
762
docs/user-guide.md
Normal file
@@ -0,0 +1,762 @@
|
||||
# User Guide
|
||||
|
||||
This guide provides detailed instructions for using the Nuclei Operator to automate security scanning of your Kubernetes applications.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [Installation](#installation)
|
||||
- [Basic Usage](#basic-usage)
|
||||
- [Configuration Options](#configuration-options)
|
||||
- [Working with Ingress Resources](#working-with-ingress-resources)
|
||||
- [Working with VirtualService Resources](#working-with-virtualservice-resources)
|
||||
- [Scheduled Scans](#scheduled-scans)
|
||||
- [Viewing Scan Results](#viewing-scan-results)
|
||||
- [Best Practices](#best-practices)
|
||||
- [Security Considerations](#security-considerations)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
The Nuclei Operator automates security scanning by watching for Kubernetes Ingress and Istio VirtualService resources. When a new resource is created or updated, the operator automatically:
|
||||
|
||||
1. Extracts target URLs from the resource
|
||||
2. Creates a NucleiScan custom resource
|
||||
3. Executes a Nuclei security scan
|
||||
4. Stores the results in the NucleiScan status
|
||||
|
||||
This enables continuous security monitoring of your web applications without manual intervention.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before installing the Nuclei Operator, ensure you have:
|
||||
|
||||
- A Kubernetes cluster (v1.26 or later)
|
||||
- `kubectl` configured to access your cluster
|
||||
- Cluster admin permissions (for CRD installation)
|
||||
|
||||
### Quick Installation
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/mortenolsen/nuclei-operator.git
|
||||
cd nuclei-operator
|
||||
|
||||
# Install CRDs
|
||||
make install
|
||||
|
||||
# Deploy the operator
|
||||
make deploy IMG=ghcr.io/mortenolsen/nuclei-operator:latest
|
||||
```
|
||||
|
||||
### Verify Installation
|
||||
|
||||
```bash
|
||||
# Check that the operator is running
|
||||
kubectl get pods -n nuclei-operator-system
|
||||
|
||||
# Verify CRDs are installed
|
||||
kubectl get crd nucleiscans.nuclei.homelab.mortenolsen.pro
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
NAME CREATED AT
|
||||
nucleiscans.nuclei.homelab.mortenolsen.pro 2024-01-15T10:00:00Z
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Automatic Scanning via Ingress
|
||||
|
||||
The simplest way to use the operator is to create an Ingress resource. The operator will automatically create a NucleiScan.
|
||||
|
||||
**Step 1: Create an Ingress**
|
||||
|
||||
```yaml
|
||||
# my-app-ingress.yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: my-app
|
||||
namespace: default
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- myapp.example.com
|
||||
secretName: myapp-tls
|
||||
rules:
|
||||
- host: myapp.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: my-app
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
```bash
|
||||
kubectl apply -f my-app-ingress.yaml
|
||||
```
|
||||
|
||||
**Step 2: View the Created NucleiScan**
|
||||
|
||||
```bash
|
||||
# List NucleiScans
|
||||
kubectl get nucleiscans
|
||||
|
||||
# View details
|
||||
kubectl describe nucleiscan my-app-scan
|
||||
```
|
||||
|
||||
### Manual NucleiScan Creation
|
||||
|
||||
You can also create NucleiScan resources manually for more control:
|
||||
|
||||
```yaml
|
||||
# manual-scan.yaml
|
||||
apiVersion: nuclei.homelab.mortenolsen.pro/v1alpha1
|
||||
kind: NucleiScan
|
||||
metadata:
|
||||
name: manual-security-scan
|
||||
namespace: default
|
||||
spec:
|
||||
sourceRef:
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
name: my-app
|
||||
namespace: default
|
||||
uid: "your-ingress-uid" # Get with: kubectl get ingress my-app -o jsonpath='{.metadata.uid}'
|
||||
targets:
|
||||
- https://myapp.example.com
|
||||
severity:
|
||||
- high
|
||||
- critical
|
||||
```
|
||||
|
||||
```bash
|
||||
kubectl apply -f manual-scan.yaml
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Severity Filtering
|
||||
|
||||
Filter scan results by severity level:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
severity:
|
||||
- info # Informational findings
|
||||
- low # Low severity
|
||||
- medium # Medium severity
|
||||
- high # High severity
|
||||
- critical # Critical severity
|
||||
```
|
||||
|
||||
**Recommended configurations:**
|
||||
|
||||
| Use Case | Severity Levels |
|
||||
|----------|-----------------|
|
||||
| Production monitoring | `medium`, `high`, `critical` |
|
||||
| Security audit | `info`, `low`, `medium`, `high`, `critical` |
|
||||
| Quick check | `high`, `critical` |
|
||||
|
||||
### Template Selection
|
||||
|
||||
Specify which Nuclei templates to use:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
templates:
|
||||
- cves/ # CVE checks
|
||||
- vulnerabilities/ # General vulnerabilities
|
||||
- exposures/ # Exposed services/files
|
||||
- misconfiguration/ # Misconfigurations
|
||||
- default-logins/ # Default credentials
|
||||
- takeovers/ # Subdomain takeovers
|
||||
```
|
||||
|
||||
**Template categories:**
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| `cves/` | Known CVE vulnerabilities |
|
||||
| `vulnerabilities/` | General vulnerability checks |
|
||||
| `exposures/` | Exposed sensitive files and services |
|
||||
| `misconfiguration/` | Security misconfigurations |
|
||||
| `default-logins/` | Default credential checks |
|
||||
| `takeovers/` | Subdomain takeover vulnerabilities |
|
||||
| `technologies/` | Technology detection |
|
||||
| `ssl/` | SSL/TLS issues |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Configure the operator using environment variables in the deployment:
|
||||
|
||||
```yaml
|
||||
# In config/manager/manager.yaml
|
||||
env:
|
||||
- name: NUCLEI_BINARY_PATH
|
||||
value: "/usr/local/bin/nuclei"
|
||||
- name: NUCLEI_TEMPLATES_PATH
|
||||
value: "/nuclei-templates"
|
||||
- name: NUCLEI_TIMEOUT
|
||||
value: "30m"
|
||||
```
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `NUCLEI_BINARY_PATH` | Path to Nuclei binary | `nuclei` |
|
||||
| `NUCLEI_TEMPLATES_PATH` | Custom templates directory | (Nuclei default) |
|
||||
| `NUCLEI_TIMEOUT` | Scan timeout duration | `30m` |
|
||||
|
||||
---
|
||||
|
||||
## Working with Ingress Resources
|
||||
|
||||
### URL Extraction
|
||||
|
||||
The operator extracts URLs from Ingress resources based on:
|
||||
|
||||
1. **TLS configuration**: Hosts in `spec.tls[].hosts` are scanned with HTTPS
|
||||
2. **Rules**: Hosts in `spec.rules[].host` are scanned
|
||||
3. **Paths**: Individual paths from `spec.rules[].http.paths[]` are included
|
||||
|
||||
**Example Ingress:**
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: multi-path-app
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- secure.example.com
|
||||
secretName: secure-tls
|
||||
rules:
|
||||
- host: secure.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: api-service
|
||||
port:
|
||||
number: 8080
|
||||
- path: /admin
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: admin-service
|
||||
port:
|
||||
number: 8081
|
||||
- host: public.example.com
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: public-service
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
**Extracted URLs:**
|
||||
- `https://secure.example.com/api`
|
||||
- `https://secure.example.com/admin`
|
||||
- `http://public.example.com/`
|
||||
|
||||
### Naming Convention
|
||||
|
||||
NucleiScan resources are named based on the Ingress:
|
||||
|
||||
```
|
||||
<ingress-name>-scan
|
||||
```
|
||||
|
||||
For example, an Ingress named `my-app` creates a NucleiScan named `my-app-scan`.
|
||||
|
||||
### Owner References
|
||||
|
||||
The operator sets owner references on NucleiScan resources, enabling:
|
||||
|
||||
- **Automatic cleanup**: When an Ingress is deleted, its NucleiScan is also deleted
|
||||
- **Relationship tracking**: Easy identification of which Ingress created which scan
|
||||
|
||||
---
|
||||
|
||||
## Working with VirtualService Resources
|
||||
|
||||
### Prerequisites
|
||||
|
||||
VirtualService support requires Istio to be installed in your cluster.
|
||||
|
||||
### URL Extraction
|
||||
|
||||
The operator extracts URLs from VirtualService resources based on:
|
||||
|
||||
1. **Hosts**: All hosts in `spec.hosts[]`
|
||||
2. **HTTP routes**: Paths from `spec.http[].match[].uri`
|
||||
|
||||
**Example VirtualService:**
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.istio.io/v1beta1
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: my-istio-app
|
||||
namespace: default
|
||||
spec:
|
||||
hosts:
|
||||
- myapp.example.com
|
||||
gateways:
|
||||
- my-gateway
|
||||
http:
|
||||
- match:
|
||||
- uri:
|
||||
prefix: /api
|
||||
route:
|
||||
- destination:
|
||||
host: api-service
|
||||
port:
|
||||
number: 8080
|
||||
- match:
|
||||
- uri:
|
||||
prefix: /web
|
||||
route:
|
||||
- destination:
|
||||
host: web-service
|
||||
port:
|
||||
number: 80
|
||||
```
|
||||
|
||||
**Extracted URLs:**
|
||||
- `https://myapp.example.com/api`
|
||||
- `https://myapp.example.com/web`
|
||||
|
||||
### Naming Convention
|
||||
|
||||
NucleiScan resources for VirtualServices follow the same pattern:
|
||||
|
||||
```
|
||||
<virtualservice-name>-scan
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scheduled Scans
|
||||
|
||||
### Enabling Scheduled Scans
|
||||
|
||||
Add a `schedule` field to run scans periodically:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
schedule: "@every 24h"
|
||||
```
|
||||
|
||||
### Schedule Formats
|
||||
|
||||
**Simplified interval format:**
|
||||
|
||||
| Format | Description |
|
||||
|--------|-------------|
|
||||
| `@every 1h` | Every hour |
|
||||
| `@every 6h` | Every 6 hours |
|
||||
| `@every 24h` | Every 24 hours |
|
||||
| `@every 168h` | Every week |
|
||||
|
||||
**Standard cron format:**
|
||||
|
||||
```
|
||||
┌───────────── minute (0 - 59)
|
||||
│ ┌───────────── hour (0 - 23)
|
||||
│ │ ┌───────────── day of month (1 - 31)
|
||||
│ │ │ ┌───────────── month (1 - 12)
|
||||
│ │ │ │ ┌───────────── day of week (0 - 6) (Sunday = 0)
|
||||
│ │ │ │ │
|
||||
* * * * *
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
|
||||
| Schedule | Description |
|
||||
|----------|-------------|
|
||||
| `0 2 * * *` | Daily at 2:00 AM |
|
||||
| `0 */6 * * *` | Every 6 hours |
|
||||
| `0 3 * * 0` | Weekly on Sunday at 3:00 AM |
|
||||
| `0 0 1 * *` | Monthly on the 1st at midnight |
|
||||
|
||||
### Suspending Scheduled Scans
|
||||
|
||||
Temporarily pause scheduled scans without deleting the resource:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
schedule: "@every 24h"
|
||||
suspend: true # Scans are paused
|
||||
```
|
||||
|
||||
To resume:
|
||||
|
||||
```bash
|
||||
kubectl patch nucleiscan my-scan -p '{"spec":{"suspend":false}}'
|
||||
```
|
||||
|
||||
### Viewing Next Scheduled Time
|
||||
|
||||
```bash
|
||||
kubectl get nucleiscan my-scan -o jsonpath='{.status.nextScheduledTime}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Viewing Scan Results
|
||||
|
||||
### List All Scans
|
||||
|
||||
```bash
|
||||
# Basic listing
|
||||
kubectl get nucleiscans
|
||||
|
||||
# With additional details
|
||||
kubectl get nucleiscans -o wide
|
||||
|
||||
# In all namespaces
|
||||
kubectl get nucleiscans -A
|
||||
```
|
||||
|
||||
### View Scan Details
|
||||
|
||||
```bash
|
||||
# Full details
|
||||
kubectl describe nucleiscan my-app-scan
|
||||
|
||||
# JSON output
|
||||
kubectl get nucleiscan my-app-scan -o json
|
||||
|
||||
# YAML output
|
||||
kubectl get nucleiscan my-app-scan -o yaml
|
||||
```
|
||||
|
||||
### Extract Specific Information
|
||||
|
||||
```bash
|
||||
# Get scan phase
|
||||
kubectl get nucleiscan my-app-scan -o jsonpath='{.status.phase}'
|
||||
|
||||
# Get total findings count
|
||||
kubectl get nucleiscan my-app-scan -o jsonpath='{.status.summary.totalFindings}'
|
||||
|
||||
# Get findings by severity
|
||||
kubectl get nucleiscan my-app-scan -o jsonpath='{.status.summary.findingsBySeverity}'
|
||||
|
||||
# Get all findings
|
||||
kubectl get nucleiscan my-app-scan -o jsonpath='{.status.findings}' | jq .
|
||||
|
||||
# Get critical findings only
|
||||
kubectl get nucleiscan my-app-scan -o json | jq '.status.findings[] | select(.severity == "critical")'
|
||||
```
|
||||
|
||||
### Export Results
|
||||
|
||||
```bash
|
||||
# Export to JSON file
|
||||
kubectl get nucleiscan my-app-scan -o json > scan-results.json
|
||||
|
||||
# Export findings only
|
||||
kubectl get nucleiscan my-app-scan -o jsonpath='{.status.findings}' > findings.json
|
||||
|
||||
# Export as CSV (using jq)
|
||||
kubectl get nucleiscan my-app-scan -o json | jq -r '.status.findings[] | [.templateId, .severity, .host, .matchedAt] | @csv' > findings.csv
|
||||
```
|
||||
|
||||
### Watch Scan Progress
|
||||
|
||||
```bash
|
||||
# Watch scan status changes
|
||||
kubectl get nucleiscans -w
|
||||
|
||||
# Watch specific scan
|
||||
watch kubectl get nucleiscan my-app-scan
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Severity Filters in Production
|
||||
|
||||
Avoid scanning for `info` level findings in production to reduce noise:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
severity:
|
||||
- medium
|
||||
- high
|
||||
- critical
|
||||
```
|
||||
|
||||
### 2. Schedule Scans During Off-Peak Hours
|
||||
|
||||
Run scheduled scans during low-traffic periods:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
schedule: "0 3 * * *" # 3 AM daily
|
||||
```
|
||||
|
||||
### 3. Use Namespaces for Organization
|
||||
|
||||
Organize scans by environment or team:
|
||||
|
||||
```bash
|
||||
# Development scans
|
||||
kubectl get nucleiscans -n development
|
||||
|
||||
# Production scans
|
||||
kubectl get nucleiscans -n production
|
||||
```
|
||||
|
||||
### 4. Label Your Resources
|
||||
|
||||
Add labels for better organization and filtering:
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
labels:
|
||||
environment: production
|
||||
team: security
|
||||
compliance: pci-dss
|
||||
```
|
||||
|
||||
```bash
|
||||
# Filter by label
|
||||
kubectl get nucleiscans -l environment=production
|
||||
```
|
||||
|
||||
### 5. Monitor Scan Failures
|
||||
|
||||
Set up alerts for failed scans:
|
||||
|
||||
```bash
|
||||
# Find failed scans
|
||||
kubectl get nucleiscans --field-selector status.phase=Failed
|
||||
```
|
||||
|
||||
### 6. Regular Template Updates
|
||||
|
||||
Keep Nuclei templates updated for the latest vulnerability checks. The operator uses the templates bundled in the container image.
|
||||
|
||||
### 7. Resource Limits
|
||||
|
||||
Ensure the operator has appropriate resource limits:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Network Access
|
||||
|
||||
The operator needs network access to scan targets. Consider:
|
||||
|
||||
1. **Network Policies**: Ensure the operator can reach scan targets
|
||||
2. **Egress Rules**: Allow outbound traffic to target hosts
|
||||
3. **Internal vs External**: Be aware of scanning internal vs external endpoints
|
||||
|
||||
### RBAC Permissions
|
||||
|
||||
The operator requires specific permissions:
|
||||
|
||||
- **Read** Ingress and VirtualService resources
|
||||
- **Full control** over NucleiScan resources
|
||||
- **Create** events for logging
|
||||
|
||||
Review the RBAC configuration in `config/rbac/role.yaml`.
|
||||
|
||||
### Scan Impact
|
||||
|
||||
Consider the impact of security scans:
|
||||
|
||||
1. **Rate Limiting**: Nuclei respects rate limits, but be aware of target capacity
|
||||
2. **WAF/IDS Alerts**: Scans may trigger security alerts on targets
|
||||
3. **Logging**: Scan traffic will appear in target access logs
|
||||
|
||||
### Sensitive Data
|
||||
|
||||
Scan results may contain sensitive information:
|
||||
|
||||
1. **Access Control**: Restrict access to NucleiScan resources
|
||||
2. **Data Retention**: Consider cleanup policies for old scan results
|
||||
3. **Audit Logging**: Enable Kubernetes audit logging for compliance
|
||||
|
||||
### Container Security
|
||||
|
||||
The operator container includes the Nuclei binary:
|
||||
|
||||
1. **Image Updates**: Regularly update the operator image
|
||||
2. **Vulnerability Scanning**: Scan the operator image itself
|
||||
3. **Non-root User**: The operator runs as a non-root user
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Scan Stuck in Pending
|
||||
|
||||
**Symptoms:** NucleiScan remains in `Pending` phase
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. Check operator logs:
|
||||
```bash
|
||||
kubectl logs -n nuclei-operator-system deployment/nuclei-operator-controller-manager
|
||||
```
|
||||
|
||||
2. Verify the operator is running:
|
||||
```bash
|
||||
kubectl get pods -n nuclei-operator-system
|
||||
```
|
||||
|
||||
3. Check for resource constraints:
|
||||
```bash
|
||||
kubectl describe pod -n nuclei-operator-system -l control-plane=controller-manager
|
||||
```
|
||||
|
||||
### Scan Failed
|
||||
|
||||
**Symptoms:** NucleiScan shows `Failed` phase
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. Check the error message:
|
||||
```bash
|
||||
kubectl get nucleiscan my-scan -o jsonpath='{.status.lastError}'
|
||||
```
|
||||
|
||||
2. Common errors:
|
||||
- **Timeout**: Increase timeout or reduce targets
|
||||
- **Network error**: Check connectivity to targets
|
||||
- **Binary not found**: Verify Nuclei is installed in the container
|
||||
|
||||
3. Retry the scan:
|
||||
```bash
|
||||
# Trigger a new scan by updating the spec
|
||||
kubectl patch nucleiscan my-scan -p '{"spec":{"targets":["https://example.com"]}}'
|
||||
```
|
||||
|
||||
### No NucleiScan Created for Ingress
|
||||
|
||||
**Symptoms:** Ingress exists but no NucleiScan is created
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. Verify the Ingress has hosts defined:
|
||||
```bash
|
||||
kubectl get ingress my-ingress -o jsonpath='{.spec.rules[*].host}'
|
||||
```
|
||||
|
||||
2. Check operator RBAC:
|
||||
```bash
|
||||
kubectl auth can-i list ingresses --as=system:serviceaccount:nuclei-operator-system:nuclei-operator-controller-manager
|
||||
```
|
||||
|
||||
3. Check operator logs for errors:
|
||||
```bash
|
||||
kubectl logs -n nuclei-operator-system deployment/nuclei-operator-controller-manager | grep -i error
|
||||
```
|
||||
|
||||
### Empty Scan Results
|
||||
|
||||
**Symptoms:** Scan completes but has no findings
|
||||
|
||||
**Possible causes:**
|
||||
|
||||
1. **Targets not accessible**: Verify targets are reachable from the operator pod
|
||||
2. **Severity filter too strict**: Try including more severity levels
|
||||
3. **Templates not matching**: Ensure templates are appropriate for the targets
|
||||
|
||||
**Verification:**
|
||||
|
||||
```bash
|
||||
# Test connectivity from operator pod
|
||||
kubectl exec -n nuclei-operator-system deployment/nuclei-operator-controller-manager -- curl -I https://your-target.com
|
||||
```
|
||||
|
||||
### High Resource Usage
|
||||
|
||||
**Symptoms:** Operator consuming excessive CPU/memory
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. Reduce concurrent scans by adjusting controller concurrency
|
||||
2. Increase resource limits:
|
||||
```yaml
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 1Gi
|
||||
```
|
||||
3. Reduce scan scope (fewer targets or templates)
|
||||
|
||||
### Scheduled Scans Not Running
|
||||
|
||||
**Symptoms:** Scheduled scan time passes but scan doesn't start
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. Verify scan is not suspended:
|
||||
```bash
|
||||
kubectl get nucleiscan my-scan -o jsonpath='{.spec.suspend}'
|
||||
```
|
||||
|
||||
2. Check the schedule format:
|
||||
```bash
|
||||
kubectl get nucleiscan my-scan -o jsonpath='{.spec.schedule}'
|
||||
```
|
||||
|
||||
3. Verify next scheduled time:
|
||||
```bash
|
||||
kubectl get nucleiscan my-scan -o jsonpath='{.status.nextScheduledTime}'
|
||||
```
|
||||
|
||||
### Getting Help
|
||||
|
||||
If you're still experiencing issues:
|
||||
|
||||
1. Check the [GitHub Issues](https://github.com/mortenolsen/nuclei-operator/issues)
|
||||
2. Review the [Architecture documentation](../ARCHITECTURE.md)
|
||||
3. Enable debug logging and collect logs
|
||||
4. Open a new issue with:
|
||||
- Kubernetes version
|
||||
- Operator version
|
||||
- Relevant resource YAML (sanitized)
|
||||
- Operator logs
|
||||
- Steps to reproduce
|
||||
Reference in New Issue
Block a user