Automating Compliance: Surviving Schrems II with OpenSCAP and Ansible on Norwegian Soil
If you are a CTO or Lead Architect operating in Europe right now, you are probably tired of hearing about the CJEU's ruling on July 16. The invalidation of the Privacy Shield (Schrems II) didn't just move the goalposts; it dismantled the stadium. Suddenly, that "easy" deployment on a US-based hyperscaler isn't just a technical decision—it's a massive legal liability regarding third-party data access.
I’ve spent the last six weeks migrating three separate fintech workloads from Frankfurt-based US cloud regions back to strictly Norwegian infrastructure. Why? Because Datatilsynet (The Norwegian Data Protection Authority) isn't known for being lenient when it comes to transfer mechanisms that no longer exist.
But moving data to a local VPS provider creates a new challenge: You lose the lazy comfort of managed proprietary security tools. You have to build the fortress yourself. And if you build it manually, you will fail. Humans forget config flags. Scripts break. Entropy sets in.
Here is how we solve the compliance headache using Compliance as Code—specifically with OpenSCAP and Ansible—deployed on high-performance KVM instances (like the NVMe-backed ones at CoolVDS) to satisfy both the auditors and the performance metrics.
The "War Story": The Audit That Almost Failed
In early 2020, before the Schrems II ruling dropped, I was auditing a Kubernetes cluster for a health-tech client in Oslo. They claimed to be CIS (Center for Internet Security) compliant. They showed me their spreadsheets. They looked green.
I ran a simple automated scan using the SCAP Security Guide.
Result: 43% compliance score. Why? Because someone manually enabled SSH root login to debug a database issue three months prior and forgot to disable it. A firewall rule for port 8080 was left open for a "temporary" test service. The sysctl kernel parameters for ICMP redirects were default (insecure).
Paper compliance is worthless. If you can't verify your infrastructure's state via code, it's not secure.
Step 1: establishing the Baseline with OpenSCAP
Stop guessing. The National Institute of Standards and Technology (NIST) and CIS provide the checklists. OpenSCAP checks them for you.
On a standard Ubuntu 20.04 LTS node (which we use as the base image for most CoolVDS deployments), you don't need expensive proprietary scanners. Install the SSG (SCAP Security Guide):
apt-get install libopenscap8 ssg-base ssg-deb ssg-debian ssg-nonfree ssg-applications
To run a scan against the standard CIS benchmark, you use the oscap binary. This consumes CPU, so do not run this during peak traffic if you are on a noisy-neighbor shared host. (On CoolVDS dedicated core instances, this takes about 45 seconds and won't impact your neighbor).
oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis \
--results scan-results.xml \
--report report.html \
/usr/share/xml/scap/ssg/content/ssg-ubuntu2004-ds.xml
This generates an HTML report telling you exactly where you fail. It’s brutal, but necessary.
Step 2: Remediation with Ansible
Knowing you are vulnerable is half the battle. Fixing it across 50 servers is the other half. Do not write bash scripts for this. Bash scripts are not idempotent; they don't know state. Use Ansible.
Below is a snippet from a hardening role I deploy to every new node. It addresses common failures found in the scan above: SSH configuration and IP stack hardening.
---
- name: Harden SSH Configuration
hosts: all
become: yes
tasks:
- name: Ensure SSH Protocol 2 is used
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^Protocol'
line: 'Protocol 2'
state: present
notify: restart sshd
- name: Disable Root Login
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PermitRootLogin'
line: 'PermitRootLogin no'
state: present
- name: Disable Password Authentication
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^PasswordAuthentication'
line: 'PasswordAuthentication no'
state: present
- name: Set idle timeout interval
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^ClientAliveInterval'
line: 'ClientAliveInterval 300'
state: present
- name: Kernel Hardening
sysctl:
name: "{{ item.key }}"
value: "{{ item.value }}"
state: present
reload: yes
loop:
- { key: 'net.ipv4.conf.all.accept_redirects', value: '0' }
- { key: 'net.ipv4.conf.all.log_martians', value: '1' }
- { key: 'net.ipv4.icmp_echo_ignore_broadcasts', value: '1' }
Pro Tip: When applying kernel hardening viasysctl, be careful withnet.ipv4.tcp_tw_recycle. In modern kernels (Linux 4.12+), this option is removed or behaves differently. Stick totcp_tw_reusefor reducing TIME_WAIT sockets on high-traffic Nginx load balancers.
The Hardware Reality: Why Virtualization Type Matters
Compliance isn't just software. It's isolation. In the context of GDPR and high-security processing, the