From 136b205e01f15f5fbe705a67ddb8e4050b2d5ca0 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Mon, 10 Nov 2025 20:48:31 +0800 Subject: [PATCH] firewall role: add UFW mail server firewall configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add comprehensive UFW firewall rules for mail server - Opens essential ports: SSH (22), HTTPS (443), HTTP (80) - Opens mail ports: SMTP (25), Submission (587), SMTPS (465), IMAPS (993) - Blocks plaintext ports: POP3 (110), IMAP (143), POP3S (995) - Allows LMTP (24) from private networks only - Provides verification output with visual status display - Default deny all incoming, allow all outgoing - Security warnings included in output Features: - Idempotent UFW configuration - Configurable via variables - Clean visual output of all rules - SSH added first to prevent lockout - Documentation in defaults/main.yml 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../roles/vhosts/firewall/defaults/main.yml | 31 ++++++ .../roles/vhosts/firewall/handlers/main.yml | 2 + .../roles/vhosts/firewall/tasks/main.yml | 98 +++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 playbooks/roles/vhosts/firewall/defaults/main.yml create mode 100644 playbooks/roles/vhosts/firewall/handlers/main.yml create mode 100644 playbooks/roles/vhosts/firewall/tasks/main.yml diff --git a/playbooks/roles/vhosts/firewall/defaults/main.yml b/playbooks/roles/vhosts/firewall/defaults/main.yml new file mode 100644 index 0000000..f374ede --- /dev/null +++ b/playbooks/roles/vhosts/firewall/defaults/main.yml @@ -0,0 +1,31 @@ +--- +# Mail server firewall configuration + +# SSH port (always allow - most important!) +# Note: SSH is allowed FIRST to ensure you don't lock yourself out +# This should typically be customized to your actual SSH port (e.g., 2222) +ssh_port: 22 + +# Private networks allowed to access LMTP (port 24) +lmtp_private_networks: + - 127.0.0.1 + - 10.0.0.0/8 + +# Essential ports to open (SSH first!) +essential_ports: + - { port: "{{ ssh_port }}", protocol: 'tcp', comment: 'SSH (Secure Shell) - Critical!' } + - { port: 443, protocol: 'tcp', comment: 'HTTPS - Web SSL' } + - { port: 80, protocol: 'tcp', comment: 'HTTP - Web (optional)' } + +# Mail ports to open +mail_ports: + - { port: 25, protocol: 'tcp', comment: 'SMTP MX inbound' } + - { port: 587, protocol: 'tcp', comment: 'SMTP Submission (AUTH)' } + - { port: 465, protocol: 'tcp', comment: 'SMTPS (optional)' } + - { port: 993, protocol: 'tcp', comment: 'IMAPS SSL' } + +# Plaintext ports to deny +denied_ports: + - { port: 110, protocol: 'tcp', comment: 'Deny POP3 plaintext' } + - { port: 143, protocol: 'tcp', comment: 'Deny IMAP plaintext' } + - { port: 995, protocol: 'tcp', comment: 'Deny POP3S plaintext' } diff --git a/playbooks/roles/vhosts/firewall/handlers/main.yml b/playbooks/roles/vhosts/firewall/handlers/main.yml new file mode 100644 index 0000000..7c78e0e --- /dev/null +++ b/playbooks/roles/vhosts/firewall/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# Firewall handlers (reserved for future use) diff --git a/playbooks/roles/vhosts/firewall/tasks/main.yml b/playbooks/roles/vhosts/firewall/tasks/main.yml new file mode 100644 index 0000000..4384063 --- /dev/null +++ b/playbooks/roles/vhosts/firewall/tasks/main.yml @@ -0,0 +1,98 @@ +--- +- name: Install UFW + apt: + name: ufw + state: present + +- name: Set default policies + ufw: + policy: "{{ item.policy }}" + direction: "{{ item.direction }}" + loop: + - { policy: 'deny', direction: 'incoming' } + - { policy: 'allow', direction: 'outgoing' } + +- name: Allow essential ports (SSH first!) + ufw: + port: "{{ item.port }}" + protocol: "{{ item.protocol }}" + rule: allow + comment: "{{ item.comment }}" + loop: "{{ essential_ports }}" + +- name: Allow necessary mail ports + ufw: + port: "{{ item.port }}" + protocol: "{{ item.protocol }}" + rule: allow + comment: "{{ item.comment }}" + loop: "{{ mail_ports }}" + +- name: Allow LMTP from private networks + ufw: + port: 24 + protocol: tcp + rule: allow + from_ip: "{{ item }}" + comment: 'LMTP private' + loop: "{{ lmtp_private_networks }}" + +- name: Deny plaintext ports + ufw: + port: "{{ item.port }}" + protocol: "{{ item.protocol }}" + rule: deny + comment: "{{ item.comment }}" + loop: "{{ denied_ports }}" + +- name: Enable UFW + ufw: + state: enabled + +- name: Get UFW numbered status + command: ufw status numbered + register: ufw_numbered + changed_when: false + +- name: Get UFW status verbose + command: ufw status verbose + register: ufw_status + changed_when: false + +- name: Display UFW status + debug: + msg: | + 🔥 UFW Firewall Status + ==================== + {{ ufw_status.stdout }} + +- name: Display configured mail rules + debug: + msg: | + 🔥 Mail Server Firewall Rules: + ============================= + + ESSENTIAL PORTS (Open - Applied First!): + {% for port in essential_ports %} + ✅ {{ port.port }}/{{ port.protocol }} - {{ port.comment }} + {% endfor %} + + MAIL PORTS (Open): + {% for port in mail_ports %} + ✅ {{ port.port }}/{{ port.protocol }} - {{ port.comment }} + {% endfor %} + + DENIED PORTS (Blocked): + {% for port in denied_ports %} + ❌ {{ port.port }}/{{ port.protocol }} - {{ port.comment }} + {% endfor %} + + LMTP ACCESS (Private Networks): + {% for network in lmtp_private_networks %} + ✅ From {{ network }} to port 24/tcp - Local mail delivery + {% endfor %} + + ⚠️ SECURITY NOTES: + - SSH port {{ ssh_port }} is open - ensure you use key-based authentication + - Default policy: deny all incoming, allow all outgoing + - Only open ports that are absolutely necessary