DEV Community

Cover image for NIS 2 for Non-EU Vendors: 60-Day Remediation Playbook
Pentest Testing Corp
Pentest Testing Corp

Posted on

NIS 2 for Non-EU Vendors: 60-Day Remediation Playbook

Why this matters now

Even if you’re outside the EU, your EU customers are pushing NIS 2 obligations downstream via MSAs, DPAs, and security schedules. To keep deals moving (and renewals clean), you need a fast, evidence-driven plan that proves reasonable security, incident readiness, and continuous improvement—without boiling the ocean.

This playbook gives Non-EU vendors a 60-day, engineering-first plan to reach a credible baseline, complete with code, KPIs, ISO 27001 alignment, and a customer-ready attestation packet. For quick gap scans to seed your backlog, you can use our Free Website Vulnerability Scanner and attach before/after evidence to tickets.

NIS 2 for Non-EU Vendors: 60-Day Remediation Playbook

Need help compressing timelines? Our team runs focused Risk Assessment Services and hands-on Remediation Services to accelerate this 60-day track with auditor-ready artifacts.


Who’s in scope via supply chains and contracts

If you process, host, monitor, or support systems for EU customers in NIS 2 sectors—or you’re a material supplier to those who do—you’ll likely see NIS 2 clauses appear in:

  • Master Service Agreements and Security Addenda
  • Data Processing Agreements and Sub-processor terms
  • RFPs/RFIs and vendor risk questionnaires

Practical test: if an EU customer asks for “NIS 2 evidence,” “incident reporting readiness,” “patch SLAs,” or “risk register excerpts,” you’re functionally in scope—treat it like a contractual requirement.


The 60-Day Plan (four 2-week sprints)

Sprint 1 (Days 1–14): Asset inventory & baseline controls

Objectives

  • Centralize asset & service inventory (cloud + endpoints + SaaS)
  • Fix internet-facing quick wins (TLS, headers, auth exposures)
  • Stand up logging & evidence storage

Code & configs

Cloud asset snapshot (multi-cloud, Python)

# /ops/asset_snapshot.py
import json, subprocess, datetime, pathlib
ts = datetime.datetime.utcnow().strftime("%Y-%m-%dT%H%M%SZ")

def sh(cmd): return subprocess.check_output(cmd, shell=True, text=True).strip()

inventory = {"ts": ts, "aws":{}, "gcp":{}, "azure":{}}

# AWS EC2 + ELB + S3 (requires AWS CLI + creds)
inventory["aws"]["ec2"] = json.loads(sh("aws ec2 describe-instances --output json"))
inventory["aws"]["elb"] = json.loads(sh("aws elbv2 describe-load-balancers --output json"))
inventory["aws"]["s3"]  = json.loads(sh("aws s3api list-buckets --output json"))

# GCP instances
# gcloud auth configure first
inventory["gcp"]["compute"] = json.loads(sh("gcloud compute instances list --format=json"))

# Azure VMs
inventory["azure"]["vms"] = json.loads(sh("az vm list --show-details -o json"))

pathlib.Path("evidence/assets").mkdir(parents=True, exist_ok=True)
open(f"evidence/assets/inventory_{ts}.json","w").write(json.dumps(inventory, indent=2))
print("Saved asset inventory", ts)
Enter fullscreen mode Exit fullscreen mode

Linux web surface quick-hardening (NGINX)

# /etc/nginx/conf.d/ssl.conf
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
Enter fullscreen mode Exit fullscreen mode

Evidence binder structure

mkdir -p evidence/{assets,scans,configs,logs}/$(date +%F)
Enter fullscreen mode Exit fullscreen mode

Seed your backlog with pre-fix evidence (Free Scanner)

  • Run a quick scan against public URLs; export results as PDF/CSV.
  • Attach the pre-fix file to the ticket; retest and attach post-fix. Use: Website Vulnerability Scanner Online free. For context, see our SOC 2 evidence article showing how to capture scanner screenshots/reports as artifacts.

Screenshot of the free tools webpage where you can access security assessment tools.Screenshot of the free tools webpage where you can access security assessment tools.

Related reading: 21 Essential SOC 2 Type II Evidence Artifacts—includes examples of storing scanner screenshots/reports as proof.


Sprint 2 (Days 15–28): Incident readiness & reporting drill

Objectives

  • Define SEV ladder, on-call, and T+24h customer notify flow
  • Practice a tabletop: detection → triage → comms → forensics → lessons
  • Capture runbooks and IR metrics as evidence

Runbook (YAML)

# /runbooks/incident_webapp.yaml
severity: SEV-2
triggers:
  - multiple 5xx spikes > 3x baseline 10m
  - WAF blocks > 10k/min or auth failures > 2% users
first_response:
  - page: "oncall-web@pagerduty"
  - gather: "nginx access/error logs, WAF events, APM traces"
  - contain: "disable new signups, enable read-only if needed"
communications:
  internal: "slack #inc-webapp, exec-brief 60m"
  customer: "draft T+8h, send T+24h if incident confirmed"
evidence:
  - attach: "cloudtrail exports, kibana searches, timeline.md"
closure:
  - lessons: "PRs linked, playbook updated, KPI captured"
Enter fullscreen mode Exit fullscreen mode

Detection queries

Splunk (admin logins outside expected geo)

index=auth action=login role=admin NOT src_country="expected"
| stats count by user, src_ip, src_country
Enter fullscreen mode Exit fullscreen mode

Nginx error spike alert (bash + grep)

grep "$(date -u +%d/%b/%Y:%H)" /var/log/nginx/error.log | wc -l
# hook into cron + alerting if > threshold
Enter fullscreen mode Exit fullscreen mode

Sprint 3 (Days 29–42): Patch SLAs & vulnerability closure

Objectives

  • Define Patch SLAs (e.g., Critical 72h, High 7d, Medium 30d)
  • Enforce auto-updates where safe; ticket exceptions
  • Track closure KPIs (exposure age, % within SLA)

Windows patch export (PowerShell)

Get-HotFix | Select HotFixID,InstalledOn |
 Sort-Object InstalledOn -Descending |
 Export-Csv evidence/patches_win_$(Get-Date -Format yyyy-MM-dd).csv -NoType
Enter fullscreen mode Exit fullscreen mode

Linux unattended security updates (Debian/Ubuntu)

apt-get update && apt-get install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
Enter fullscreen mode Exit fullscreen mode

AWS SSM Patch Baseline (CLI snippet)

aws ssm create-patch-baseline \
 --name "ProdSecurity" --approval-rules '...
 {"PatchRules":[{"PatchFilterGroup":{"PatchFilters":[
 {"Key":"PATCHELIGIBILITY","Values":["SECURITY"]}]},
 "ApproveAfterDays":2,"ComplianceLevel":"CRITICAL"}]}'
Enter fullscreen mode Exit fullscreen mode

Vuln closure KPI (SQL on your ticket DB)

-- % findings closed within SLA by severity (last 30 days)
SELECT severity,
  100.0*SUM(CASE WHEN closed_at <= due_at THEN 1 ELSE 0 END)/COUNT(*) AS pct_within_sla
FROM findings
WHERE created_at >= NOW() - INTERVAL '30 day'
GROUP BY severity;
Enter fullscreen mode Exit fullscreen mode

When you’re ready to formalize this, our Remediation Services team can pair with your engineers to close high-risk items and produce clear before/after evidence for customers.


Sprint 4 (Days 43–60): DDoS posture, KPIs & “living” risk register

Objectives

  • Add rate-limit/WAF controls; verify metrics and fallbacks
  • Publish board-ready KPIs (rolling)
  • Maintain a living risk register mapped to ISO 27001

NGINX basic rate limiting

# /etc/nginx/conf.d/ratelimit.conf
limit_req_zone $binary_remote_addr zone=reqzone:10m rate=10r/s;
server {
  location / {
    limit_req zone=reqzone burst=20 nodelay;
  }
}
Enter fullscreen mode Exit fullscreen mode

Cloudflare (Terraform sketch)

resource "cloudflare_rate_limit" "login" {
  zone_id  = var.zone_id
  threshold = 100
  period    = 60
  action { mode = "managed_challenge" }
  match { request { url = "/login" methods = ["POST"] } }
  enabled = true
}
Enter fullscreen mode Exit fullscreen mode

Living risk register (YAML)

# /risk/risk_register.yaml
- id: R-001
  desc: Internet-exposed login susceptible to credential stuffing
  inherent: High
  controls: [WAF-01, RL-01, MFA-01]
  iso27001: [A.5.15, A.8.16, A.8.20]
  owner: eng-appsec
  due: 2025-12-15
  treatment: Reduce
  evidence: ["evidence/waf/waf_rules_2025-11-11.json","kpis/kpi_ddos_2025-11.csv"]
  status: Open
Enter fullscreen mode Exit fullscreen mode

Board-ready KPIs (CSV emitted weekly)

# /ops/kpis.sh
now=$(date -u +%F)
mkdir -p kpis
echo "name,value,date" > kpis/$now.csv
echo "patch_within_sla,$(psql -d sec -At -f queries/pct_within_sla.sql),$now" >> kpis/$now.csv
echo "mfa_coverage,$(python ops/mfa_coverage.py),$now" >> kpis/$now.csv
echo "waf_blocks_last_7d,$(jq '.events|length' logs/waf_last7d.json),$now" >> kpis/$now.csv
Enter fullscreen mode Exit fullscreen mode

Aligning with ISO 27001 (reuse evidence, cut time)

Rather than inventing new artifacts, map what you already produce to Annex A controls:

  • Access control & MFA → user/admin inventory, SSO enforcement (A.5.15, A.8.2, A.8.3)
  • Vulnerability mgmt & patching → scanner exports, SLA KPIs, change tickets (A.8.8, A.8.9)
  • Logging & monitoring → SIEM queries, CloudTrail retention, tamper-evident chains (A.8.15, A.8.16)
  • Business continuity → backup restore tests, DR targets & actuals (A.5.29, A.5.30)

If you need ready-to-reuse templates and binders, our Risk Assessment Services package includes ISO 27001 mappings that align with the evidence types above.


What to show customers: the attestation packet

Bundle the following into a single PDF/ZIP and share through your customer’s secure channel:

  1. Executive attestation (1 page)
We, <Company>, attest that we maintain reasonable security controls aligned to NIS 2 expectations for suppliers. Evidence enclosed:
assets, patch SLAs & KPIs, IR runbooks, tabletop report, scanner pre/post-fix, WAF/rate-limits, and a current risk register excerpt.
Signed: CISO/CTO, Date
Enter fullscreen mode Exit fullscreen mode
  1. Evidence index (machine-readable)
{
  "version": "2025-11-11",
  "artifacts": [
    {"name":"asset_inventory","path":"evidence/assets/inventory_2025-11-11.json"},
    {"name":"scanner_before","path":"evidence/scans/pre_fix_2025-11.pdf"},
    {"name":"scanner_after","path":"evidence/scans/post_fix_2025-11.pdf"},
    {"name":"ir_runbook","path":"runbooks/incident_webapp.yaml"},
    {"name":"ddos_controls","path":"configs/waf/ratelimit.conf"},
    {"name":"kpis","path":"kpis/2025-11-11.csv"},
    {"name":"risk_register","path":"risk/risk_register.yaml"}
  ]
}
Enter fullscreen mode Exit fullscreen mode
  1. Test & runbook excerpts Include sanitized tabletop notes and a short restoration drill log (timestamped).

See how we capture scans and artifacts in SOC 2 Evidence Artifacts; the same pattern satisfies NIS 2 request-lists from customers.


Sample Report to check Website Vulnerability

Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.


DEV-oriented quick wins you can ship in two sprints

  • Guardrails: branch protection + required reviews (gh api ... > branch_protection.json)
  • Evals: integration tests that fail on missing headers/TLS regressions
  • Logging: CloudTrail + 365-day retention (IaC), hash-chained daily exports
  • Kill-switches: feature flags for read-only mode, signup throttle, or webhooks pause

(We show copy-pasteable examples in the article above; save outputs in /evidence/... for auditors and customers.)


Related internal resources (keep learning)


Move faster with guided help

Top comments (0)