Skip to content

Commit e216202

Browse files
committed
Fix NFS export security and consolidate firewall rules
Replace no_root_squash with root_squash in NFS export options. With no_root_squash, any root user on K8s nodes (or root-running pods) had full root access to all NFS shares. Now squashed root maps to configurable anonuid/anongid (default 65534/nobody). Add anonuid and anongid to the exports template so the mapping is explicit in /etc/exports. Consolidate UFW firewall configuration and validation into a single block so all rules, enable, and verification run as one atomic unit gated on UFW availability.
1 parent 88df047 commit e216202

File tree

3 files changed

+22
-35
lines changed

3 files changed

+22
-35
lines changed

metal/roles/storage/defaults/main.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
homelab_net_cidr: "10.10.10.0/24"
44

55
# Default NFS export options
6-
default_nfs_options: "rw,sync,no_subtree_check,no_root_squash,insecure"
6+
default_nfs_options: "rw,sync,no_subtree_check,root_squash,insecure"
7+
8+
# Anonymous uid/gid for root_squash mapping (squashed root becomes this user)
9+
nfs_anon_uid: 65534 # nobody
10+
nfs_anon_gid: 65534 # nogroup
711

812
# Firewall configuration
913
manage_firewall: true # Set to false to skip firewall configuration

metal/roles/storage/tasks/nfs_firewall.yml

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22
# NFS Firewall Configuration
33
# Configures UFW firewall rules for NFS services
44

5-
# =================================================================
6-
# FIREWALL DETECTION AND SETUP
7-
# =================================================================
8-
95
- name: Check if UFW is installed
106
command: which ufw
117
register: ufw_check
@@ -30,11 +26,7 @@
3026
debug:
3127
msg: "UFW is {{ 'available' if ufw_available.rc == 0 else 'not available' }}"
3228

33-
# =================================================================
34-
# UFW CONFIGURATION
35-
# =================================================================
36-
37-
- name: Configure UFW firewall rules
29+
- name: Configure and validate UFW firewall
3830
when: ufw_available.rc == 0
3931
block:
4032
- name: Check UFW status
@@ -53,18 +45,18 @@
5345
direction: outgoing
5446
policy: allow
5547

56-
- name: Configure TCP ports in firewall
48+
- name: Allow SSH access
5749
ufw:
5850
rule: allow
59-
src: "{{ item.0 }}"
60-
port: "{{ item.1 }}"
51+
src: "{{ item }}"
52+
port: "22"
6153
proto: tcp
62-
comment: "{{ item.1 }}/tcp"
63-
loop: "{{ firewall_allowed_networks | product(['22']) | list }}"
54+
comment: "SSH 22/tcp"
55+
loop: "{{ firewall_allowed_networks }}"
6456
loop_control:
65-
label: "{{ item.0 }} -> {{ item.1 }}/tcp"
57+
label: "{{ item }} -> 22/tcp"
6658

67-
- name: Configure NFS TCP ports in firewall
59+
- name: Allow NFS TCP ports
6860
ufw:
6961
rule: allow
7062
src: "{{ item.0 }}"
@@ -75,18 +67,22 @@
7567
loop_control:
7668
label: "{{ item.0 }} -> {{ item.1 }}/tcp"
7769

78-
- name: Configure NFS UDP ports in firewall
70+
- name: Allow NFS UDP ports
7971
ufw:
8072
rule: allow
8173
src: "{{ item.0 }}"
8274
port: "{{ item.1 }}"
8375
proto: udp
8476
comment: "NFS {{ item.1 }}/udp"
85-
# UDP 2049 to support potential NFSv3 clients
8677
loop: "{{ firewall_allowed_networks | product(['111', '2049']) | list }}"
8778
loop_control:
8879
label: "{{ item.0 }} -> {{ item.1 }}/udp"
8980

81+
- name: Enable UFW if not active
82+
ufw:
83+
state: enabled
84+
when: "'Status: active' not in ufw_status.stdout"
85+
9086
- name: Display firewall rules
9187
command: ufw status numbered
9288
register: ufw_rules
@@ -97,28 +93,15 @@
9793
msg: "{{ ufw_rules.stdout_lines }}"
9894
when: ufw_rules.stdout_lines is defined
9995

100-
- name: Enable UFW if not active
101-
ufw:
102-
state: enabled
103-
when: "'Status: active' not in ufw_status.stdout"
104-
register: ufw_enable
105-
106-
# =================================================================
107-
# FIREWALL VALIDATION
108-
# =================================================================
109-
110-
- name: Validate firewall allows NFS traffic
111-
when: ufw_available.rc == 0
112-
block:
113-
- name: Check if NFS ports are allowed
96+
- name: Verify NFS firewall rules
11497
shell: |
11598
set -o pipefail
11699
ufw status | grep -E '(111|2049|20048)'
117100
register: nfs_ports_check
118101
changed_when: false
119102
failed_when: false
120103

121-
- name: Verify NFS firewall rules
104+
- name: Assert NFS firewall rules are present
122105
assert:
123106
that:
124107
- nfs_ports_check.rc == 0

metal/roles/storage/templates/exports.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
# Default NFS exports for Kubernetes
77
{% for dir in storage_dirs %}
8-
/mnt/storage/{{ dir }} {{ homelab_net_cidr | default('10.10.10.0/24') }}({{ default_nfs_options }},fsid={{ loop.index }})
8+
/mnt/storage/{{ dir }} {{ homelab_net_cidr | default('10.10.10.0/24') }}({{ default_nfs_options }},anonuid={{ nfs_anon_uid }},anongid={{ nfs_anon_gid }},fsid={{ loop.index }})
99
{% endfor %}
1010

1111
# Custom exports (if defined in inventory)

0 commit comments

Comments
 (0)