# Security Configuration Assessment (SCA) in Wazuh

> Wazuh 4.14 SCA module - CIS Benchmark policies, YAML policy format, check types, custom policies, compliance mapping, and troubleshooting guide

Source: https://opennix.org/en/docs/wazuh/capabilities/wazuh-sca/


The Security Configuration Assessment (SCA) module in Wazuh evaluates endpoint configurations against established security standards. It compares current system settings with reference policies and produces a report of passed, failed, and not-applicable checks. The built-in policies align with CIS Benchmarks and can be extended with custom rules tailored to organizational needs.

## How It Works

SCA evaluates endpoint configuration through a three-stage process:

1. **Policy loading** - the agent loads YAML policy files from its local directory or receives them centrally from the server
2. **Check execution** - each check in the policy tests a specific configuration aspect (file, process, registry, command)
3. **Result aggregation** - results are sent to the server and indexed for visualization in the dashboard

```
Agent loads policy -> Executes checks ->
-> Results (passed/failed/not applicable) ->
-> Sent to server -> Indexer -> Dashboard
```

## Configuring SCA in ossec.conf

The SCA module is configured in the `<sca>` block of `/var/ossec/etc/ossec.conf`:

```xml
<sca>
  <enabled>yes</enabled>
  <scan_on_start>yes</scan_on_start>
  <interval>12h</interval>
  <skip_nfs>yes</skip_nfs>

  <policies>
    <policy>etc/shared/cis_ubuntu22-04.yml</policy>
    <policy>etc/shared/cis_apache_24.yml</policy>
    <policy enabled="no">etc/shared/cis_win2022.yml</policy>
  </policies>
</sca>
```

### Configuration Parameters

| Parameter | Description | Default |
|-----------|-------------|---------|
| `enabled` | Enable the SCA module | yes |
| `scan_on_start` | Run scan when the agent starts | yes |
| `interval` | Interval between scans | 12h |
| `skip_nfs` | Skip network filesystems | yes |
| `policies` | List of policy files to apply | - |

## Built-in CIS Benchmark Policies

Wazuh ships with policies based on CIS (Center for Internet Security) standards. These policies cover operating systems and services.

### Linux

| Policy | File |
|--------|------|
| CIS Ubuntu 22.04 | `cis_ubuntu22-04.yml` |
| CIS Ubuntu 20.04 | `cis_ubuntu20-04.yml` |
| CIS Debian 11 | `cis_debian11.yml` |
| CIS Debian 10 | `cis_debian10.yml` |
| CIS CentOS 8 | `cis_centos8.yml` |
| CIS CentOS 7 | `cis_centos7.yml` |
| CIS RHEL 9 | `cis_rhel9.yml` |
| CIS RHEL 8 | `cis_rhel8.yml` |
| CIS RHEL 7 | `cis_rhel7.yml` |
| CIS Amazon Linux 2 | `cis_amazonlinux2.yml` |
| CIS SUSE Linux 15 | `cis_sles15.yml` |

### Windows

| Policy | File |
|--------|------|
| CIS Windows Server 2022 | `cis_win2022.yml` |
| CIS Windows Server 2019 | `cis_win2019.yml` |
| CIS Windows Server 2016 | `cis_win2016.yml` |
| CIS Windows 11 Enterprise | `cis_win11_enterprise.yml` |
| CIS Windows 10 Enterprise | `cis_win10_enterprise.yml` |

### Applications and Services

| Policy | File |
|--------|------|
| CIS Apache HTTP Server 2.4 | `cis_apache_24.yml` |
| CIS MySQL 8.0 | `cis_mysql80.yml` |
| CIS PostgreSQL 13 | `cis_postgresql13.yml` |
| CIS Docker | `cis_docker.yml` |
| CIS Kubernetes | `cis_kubernetes.yml` |
| CIS Nginx | `cis_nginx.yml` |

## Policy File Format (YAML)

Each SCA policy file consists of four sections: `policy`, `requirements`, `variables`, and `checks`.

### The policy Section

Contains policy metadata:

```yaml
policy:
  id: "custom_ssh_hardening"
  file: "ssh_hardening.yml"
  name: "SSH Server Hardening Policy"
  description: "Security checks for OpenSSH server configuration"
  references:
    - https://www.cisecurity.org/benchmark/distribution_independent_linux
```

### The requirements Section

Defines prerequisites for policy execution:

```yaml
requirements:
  title: "Check SSH server is installed"
  description: "Requirements for running the SSH hardening policy"
  condition: any
  rules:
    - 'f:/etc/ssh/sshd_config'
    - 'p:sshd'
```

If the requirements are not met, the policy is skipped and all checks receive a "Not applicable" status.

### The variables Section

Defines reusable variables for checks:

```yaml
variables:
  $sshd_config: /etc/ssh/sshd_config
  $pam_dir: /etc/pam.d
  $ssh_service: sshd
  $audit_log: /var/log/auth.log,/var/log/secure
```

Variables are prefixed with `$` and can contain multiple paths separated by commas.

### The checks Section

Contains the set of checks. Each check includes metadata and test rules.

```yaml
checks:
  - id: 5001
    title: "Ensure SSH root login is disabled"
    description: "The PermitRootLogin parameter specifies if root can log in using SSH"
    rationale: "Disabling root login forces administrators to use personal accounts"
    remediation: "Set PermitRootLogin to no in /etc/ssh/sshd_config"
    compliance:
      - cis: ["5.2.10"]
      - pci_dss: ["2.2.4"]
      - nist_800_53: ["AC.6"]
      - mitre_attack: ["T1078"]
    condition: all
    rules:
      - 'f:$sshd_config -> !r:^# && r:PermitRootLogin\s+no'
```

## Check Types

### File Checks (f:)

Verify file existence and content:

```yaml
# Check file exists
- 'f:/etc/ssh/sshd_config'

# Negation - file must NOT exist
- 'not f:/etc/hosts.equiv'

# Content match (literal)
- 'f:/etc/ssh/sshd_config -> PermitRootLogin no'

# Content match (regex)
- 'f:/etc/ssh/sshd_config -> r:^PermitRootLogin\s+no'

# Numeric comparison
- 'f:/etc/ssh/sshd_config -> n:MaxAuthTries\s+(\d+) compare <= 4'

# Compound condition (AND)
- 'f:/etc/ssh/sshd_config -> !r:^# && r:PermitRootLogin && r:no'
```

### Directory Checks (d:)

Verify directory existence and file contents:

```yaml
# Check directory exists
- 'd:/etc/ssh'

# Find files matching pattern in directory
- 'd:/etc/cron.d -> r:\.cron$'

# Check file content within directory
- 'd:/etc/sudoers.d -> r:NOPASSWD'
```

### Process Checks (p:)

Verify the presence or absence of running processes:

```yaml
# Process must be running
- 'p:sshd'
- 'p:auditd'

# Process must NOT be running
- 'not p:telnetd'
- 'not p:rshd'
```

### Command Checks (c:)

Execute a command and verify the output:

```yaml
# Check command output
- 'c:systemctl is-enabled sshd -> r:enabled'

# Numeric comparison of output
- 'c:sshd -T -> n:maxauthtries\s+(\d+) compare <= 4'

# Check service status
- 'c:systemctl status firewalld -> r:active'

# Check file permissions
- 'c:stat -c %a /etc/shadow -> r:^0?600$'
```

### Registry Checks (r:) - Windows Only

Verify Windows Registry keys and values:

```yaml
# Check key exists
- 'r:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa'

# Check value
- 'r:HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa -> LimitBlankPasswordUse -> 1'

# Numeric comparison
- 'r:HKEY_LOCAL_MACHINE\...\Lsa -> n:LmCompatibilityLevel compare >= 3'
```

## Evaluation Logic

### The condition Field

The `condition` field determines how individual rule results are aggregated into the final check result:

| Value | Description |
|-------|-------------|
| `all` | Check passes if ALL rules pass |
| `any` | Check passes if AT LEAST ONE rule passes |
| `none` | Check passes if NO rules pass |

### Check Results

| Status | Description |
|--------|-------------|
| **Passed** | Configuration meets policy requirements |
| **Failed** | Configuration does not meet requirements |
| **Not applicable** | Check cannot be executed (target does not exist) |

### Comparison Operators

| Operator | Description |
|----------|-------------|
| `r:` | Regex match |
| `!r:` | Negated regex match |
| `n:` | Numeric comparison |
| `&&` | Logical AND (combine conditions) |

Numeric operators: `<`, `<=`, `==`, `!=`, `>=`, `>`

## Writing Custom Policies

### Complete Policy Example

```yaml
policy:
  id: "web_server_hardening"
  file: "web_server_hardening.yml"
  name: "Web Server Security Hardening"
  description: "Custom security checks for Apache/Nginx web servers"
  references:
    - https://httpd.apache.org/docs/2.4/misc/security_tips.html

requirements:
  title: "Check web server is installed"
  description: "At least one web server must be present"
  condition: any
  rules:
    - 'p:apache2'
    - 'p:httpd'
    - 'p:nginx'

variables:
  $apache_conf: /etc/apache2/apache2.conf,/etc/httpd/conf/httpd.conf
  $nginx_conf: /etc/nginx/nginx.conf

checks:
  - id: 10001
    title: "Ensure ServerTokens is set to Prod"
    description: "ServerTokens controls the information returned in HTTP headers"
    rationale: "Detailed server information aids attackers in reconnaissance"
    remediation: "Set ServerTokens Prod in the Apache configuration"
    compliance:
      - cis: ["5.13"]
      - pci_dss: ["2.2.4"]
    condition: all
    rules:
      - 'f:$apache_conf -> !r:^# && r:ServerTokens\s+Prod'

  - id: 10002
    title: "Ensure directory listing is disabled"
    description: "Options -Indexes prevents directory listing"
    rationale: "Directory listing exposes file structure to potential attackers"
    remediation: "Add Options -Indexes to directory configuration"
    compliance:
      - cis: ["5.7"]
    condition: all
    rules:
      - 'f:$apache_conf -> !r:^# && r:Options.*-Indexes'

  - id: 10003
    title: "Ensure TRACE method is disabled"
    description: "TraceEnable Off disables HTTP TRACE method"
    rationale: "TRACE method can be used for cross-site tracing attacks"
    remediation: "Set TraceEnable Off in the Apache configuration"
    compliance:
      - cis: ["5.8"]
      - mitre_attack: ["T1190"]
    condition: all
    rules:
      - 'f:$apache_conf -> !r:^# && r:TraceEnable\s+Off'

  - id: 10004
    title: "Ensure Nginx server_tokens is off"
    description: "server_tokens off hides Nginx version in HTTP headers"
    rationale: "Version disclosure assists in targeted exploit selection"
    remediation: "Set server_tokens off in nginx.conf"
    condition: all
    rules:
      - 'f:$nginx_conf -> !r:^# && r:server_tokens\s+off'

  - id: 10005
    title: "Ensure SSL/TLS is configured"
    description: "Web server should use TLS for encrypted communications"
    rationale: "Unencrypted traffic exposes data to interception"
    remediation: "Configure SSL certificate and enable TLS"
    compliance:
      - pci_dss: ["4.1"]
      - nist_800_53: ["SC.8"]
    condition: any
    rules:
      - 'f:$apache_conf -> r:SSLEngine\s+on'
      - 'f:$nginx_conf -> r:ssl_certificate'
```

### Deploying Custom Policies

Place custom policy files in `/var/ossec/etc/shared/` and register them in the SCA configuration:

```xml
<sca>
  <policies>
    <policy>etc/shared/web_server_hardening.yml</policy>
  </policies>
</sca>
```

### Centralized Distribution

To distribute policies centrally through the Wazuh server:

1. Place the policy file in `/var/ossec/etc/shared/default/` on the server
2. The policy will be delivered automatically to all agents in the `default` group
3. For agent groups, use `/var/ossec/etc/shared/<group_name>/`

## Compliance Mapping

SCA policies support mapping to the following standards:

| Standard | Compliance Key |
|----------|---------------|
| CIS Benchmarks | `cis` |
| PCI DSS | `pci_dss` |
| NIST 800-53 | `nist_800_53` |
| HIPAA | `hipaa` |
| GDPR | `gdpr` |
| TSC (SOC 2) | `tsc` |
| MITRE ATT&CK | `mitre_attack` |

## SCA Dashboard

The SCA dashboard in Wazuh Dashboard provides:

- **Policy overview** - list of policies with pass rate percentages
- **Per-agent detail** - SCA results for a specific agent
- **Change history** - result trends over time
- **Failed checks** - list of non-compliant findings with remediation guidance
- **Filtering** - by policy, agent, check status, and compliance standard

Access the dashboard at: Wazuh Dashboard - Modules - Configuration Assessment.

## Remediation Guidance

Each SCA check includes a `remediation` field with instructions for resolving non-compliance. A typical workflow:

1. Review failed checks in the SCA dashboard
2. Sort by criticality (CIS Level 1 - essential, Level 2 - defense-in-depth)
3. Apply the guidance from the `remediation` field
4. Wait for the next scan cycle or trigger a manual rescan

To trigger a manual rescan:

```bash
/var/ossec/bin/agent_control -r -u <agent_id>
```

## Comparison with Alternative Solutions

| Feature | Wazuh SCA | OpenSCAP | Lynis | Nessus Compliance |
|---------|-----------|----------|-------|-------------------|
| Policy format | YAML | XCCDF/OVAL | Shell | Proprietary |
| CIS Benchmarks | Yes (built-in) | Yes (SCAP) | Yes (partial) | Yes (paid) |
| Custom policies | Yes | Yes (complex) | Yes | Limited |
| Centralized management | Yes (agent.conf) | No | No | Yes |
| Real-time monitoring | Periodic | On-demand | On-demand | Periodic |
| SIEM integration | Built-in | Requires export | Requires export | Via API |
| License | Open Source (GPLv2) | Open Source | GPL | Commercial |
| Windows support | Yes | Limited | No | Yes |
| Results API | REST API | CLI | CLI | REST API |

Wazuh SCA stands out through its straightforward YAML policy format, centralized distribution via the server, and native integration with the indexer for long-term result storage.

## Troubleshooting

### Policy Not Loading

1. Validate the YAML file syntax:
```bash
python3 -c "import yaml; yaml.safe_load(open('/var/ossec/etc/shared/policy.yml'))"
```

2. Confirm the policy file is listed in the configuration:
```bash
grep -A5 '<sca>' /var/ossec/etc/ossec.conf
```

3. Check the agent logs:
```bash
grep sca /var/ossec/logs/ossec.log | tail -20
```

### All Checks Show "Not applicable"

1. Verify that the `requirements` section conditions are met. If prerequisites are not satisfied, all checks receive "Not applicable" status.

2. Check that the paths in variables are correct:
```bash
ls -la /etc/ssh/sshd_config
```

3. Confirm the policy matches the agent's operating system.

### SCA Results Not Appearing in Dashboard

1. Verify agent-to-server connectivity:
```bash
/var/ossec/bin/agent_control -i <agent_id>
```

2. Confirm the indexer is accessible:
```bash
curl -sk -u admin:password https://localhost:9200/_cat/indices | grep sca
```

3. Check for SCA data in the index:
```bash
curl -sk -u admin:password \
  "https://localhost:9200/wazuh-states-vulnerabilities-*/_search?size=1" \
  -H "Content-Type: application/json"
```

### Check Not Recognizing Configuration

1. Verify the regex pattern is correct:
```bash
grep -P 'PermitRootLogin\s+no' /etc/ssh/sshd_config
```

2. Note that `f:` checks match the pattern against each line of the file individually.

3. For compound conditions (`&&`), all parts must match within a single line.

## Related Sections

- [File Integrity Monitoring](/docs/wazuh/capabilities/wazuh-file-integrity-monitoring/) - FIM tracks file changes while SCA verifies configuration correctness
- [Vulnerability Detection](/docs/wazuh/capabilities/wazuh-vulnerability-detection/) - combining SCA with vulnerability detection for comprehensive assessment
- [Use Cases](/docs/wazuh/getting-started/wazuh-use-cases/) - practical SCA deployment scenarios

