Restructure in-progress
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# tasks/cleanup_confirmation.yml - Common task file for cleanup confirmation
|
||||
- name: Show cleanup information
|
||||
debug:
|
||||
msg: |
|
||||
************************************************
|
||||
* CLEANUP OPERATION *
|
||||
************************************************
|
||||
The following resources will be DELETED PERMANENTLY:
|
||||
{% if cleanup_redirector and redirector_name is defined %}
|
||||
- Redirector: {{ redirector_name }} ({{ redirector_ip | default('IP unknown') }})
|
||||
{% endif %}
|
||||
{% if cleanup_c2 and c2_name is defined %}
|
||||
- C2 Server: {{ c2_name }} ({{ c2_ip | default('IP unknown') }})
|
||||
{% endif %}
|
||||
when: confirm_cleanup | bool
|
||||
|
||||
- name: Confirm cleanup operation
|
||||
pause:
|
||||
prompt: "\n>>> Type 'yes' to confirm deletion or press Ctrl+C to abort <<<"
|
||||
register: confirmation
|
||||
when: confirm_cleanup | bool
|
||||
|
||||
- name: Skip cleanup if not confirmed
|
||||
meta: end_play
|
||||
when: confirm_cleanup | bool and confirmation.user_input != 'yes'
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
# Common task for configuring mail server
|
||||
# Shared across all providers
|
||||
|
||||
- name: Configure Postfix main.cf
|
||||
lineinfile:
|
||||
path: /etc/postfix/main.cf
|
||||
regexp: "{{ item.regexp }}"
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
- { regexp: '^myhostname', line: "myhostname = mail.{{ domain }}" }
|
||||
- { regexp: '^mydomain', line: "mydomain = {{ domain }}" }
|
||||
- { regexp: '^myorigin', line: "myorigin = $mydomain" }
|
||||
- { regexp: '^inet_interfaces', line: "inet_interfaces = all" }
|
||||
- { regexp: '^inet_protocols', line: "inet_protocols = ipv4" }
|
||||
- { regexp: '^smtpd_banner', line: "smtpd_banner = $myhostname ESMTP $mail_name" }
|
||||
- { regexp: '^mynetworks', line: "mynetworks = 127.0.0.0/8 [::1]/128" }
|
||||
- { regexp: '^relay_domains', line: "relay_domains = $mydestination" }
|
||||
- { regexp: '^smtpd_tls_cert_file', line: "smtpd_tls_cert_file = /etc/letsencrypt/live/{{ domain }}/fullchain.pem" }
|
||||
- { regexp: '^smtpd_tls_key_file', line: "smtpd_tls_key_file = /etc/letsencrypt/live/{{ domain }}/privkey.pem" }
|
||||
- { regexp: '^smtpd_tls_security_level', line: "smtpd_tls_security_level = encrypt" }
|
||||
- { regexp: '^smtpd_tls_session_cache_database', line: "smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache" }
|
||||
- { regexp: '^smtp_tls_session_cache_database', line: "smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache" }
|
||||
- { regexp: '^smtpd_use_tls', line: "smtpd_use_tls = yes" }
|
||||
- { regexp: '^smtpd_tls_auth_only', line: "smtpd_tls_auth_only = yes" }
|
||||
- { regexp: '^milter_default_action', line: "milter_default_action = accept" }
|
||||
- { regexp: '^milter_protocol', line: "milter_protocol = 6" }
|
||||
- { regexp: '^smtpd_milters', line: "smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock" }
|
||||
- { regexp: '^non_smtpd_milters', line: "non_smtpd_milters = unix:/var/spool/postfix/opendkim/opendkim.sock" }
|
||||
|
||||
- name: Configure OpenDKIM
|
||||
lineinfile:
|
||||
path: /etc/opendkim.conf
|
||||
regexp: "{{ item.regexp }}"
|
||||
line: "{{ item.line }}"
|
||||
with_items:
|
||||
- { regexp: '^Domain', line: "Domain {{ domain }}" }
|
||||
- { regexp: '^KeyFile', line: "KeyFile /etc/opendkim/keys/{{ domain }}/mail.private" }
|
||||
- { regexp: '^Selector', line: "Selector mail" }
|
||||
- { regexp: '^Socket', line: "Socket local:/var/spool/postfix/opendkim/opendkim.sock" }
|
||||
- { regexp: '^Syslog', line: "Syslog yes" }
|
||||
- { regexp: '^UMask', line: "UMask 002" }
|
||||
- { regexp: '^Mode', line: "Mode sv" }
|
||||
|
||||
- name: Create DKIM directory
|
||||
file:
|
||||
path: /etc/opendkim/keys/{{ domain }}
|
||||
state: directory
|
||||
owner: opendkim
|
||||
group: opendkim
|
||||
mode: 0700
|
||||
|
||||
- name: Generate DKIM keys
|
||||
command: >
|
||||
opendkim-genkey -D /etc/opendkim/keys/{{ domain }} -d {{ domain }} -s mail
|
||||
args:
|
||||
creates: /etc/opendkim/keys/{{ domain }}/mail.private
|
||||
|
||||
- name: Set permissions for DKIM keys
|
||||
file:
|
||||
path: /etc/opendkim/keys/{{ domain }}/mail.private
|
||||
owner: opendkim
|
||||
group: opendkim
|
||||
mode: 0600
|
||||
|
||||
- name: Configure OpenDKIM TrustedHosts
|
||||
copy:
|
||||
content: |
|
||||
127.0.0.1
|
||||
::1
|
||||
localhost
|
||||
{{ domain }}
|
||||
dest: /etc/opendkim/TrustedHosts
|
||||
owner: opendkim
|
||||
group: opendkim
|
||||
mode: 0644
|
||||
|
||||
- name: Enable submission port (587) in master.cf
|
||||
blockinfile:
|
||||
path: /etc/postfix/master.cf
|
||||
insertafter: '^#submission'
|
||||
block: |
|
||||
submission inet n - y - - smtpd
|
||||
-o syslog_name=postfix/submission
|
||||
-o smtpd_tls_security_level=encrypt
|
||||
-o smtpd_sasl_auth_enable=yes
|
||||
-o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
|
||||
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
|
||||
|
||||
- name: Configure Dovecot for Postfix SASL
|
||||
blockinfile:
|
||||
path: /etc/dovecot/conf.d/10-master.conf
|
||||
insertafter: '^service auth {'
|
||||
block: |
|
||||
# Postfix smtp-auth
|
||||
unix_listener /var/spool/postfix/private/auth {
|
||||
mode = 0660
|
||||
user = postfix
|
||||
group = postfix
|
||||
}
|
||||
|
||||
- name: Set Dovecot auth_mechanisms
|
||||
lineinfile:
|
||||
path: /etc/dovecot/conf.d/10-auth.conf
|
||||
regexp: '^auth_mechanisms'
|
||||
line: 'auth_mechanisms = plain login'
|
||||
|
||||
- name: Create Dovecot password file for SASL authentication
|
||||
file:
|
||||
path: /etc/dovecot/passwd
|
||||
state: touch
|
||||
mode: '0600'
|
||||
owner: dovecot
|
||||
group: dovecot
|
||||
|
||||
- name: Add SMTP auth user to Dovecot
|
||||
lineinfile:
|
||||
path: /etc/dovecot/passwd
|
||||
line: "{{ smtp_auth_user }}:{{ smtp_auth_pass | password_hash('sha512_crypt') }}"
|
||||
|
||||
- name: Disable system auth and use passwd-file
|
||||
lineinfile:
|
||||
path: /etc/dovecot/conf.d/10-auth.conf
|
||||
regexp: '^!include auth-system.conf.ext'
|
||||
line: '#!include auth-system.conf.ext'
|
||||
|
||||
- name: Add auth-passwdfile configuration
|
||||
blockinfile:
|
||||
path: /etc/dovecot/conf.d/10-auth.conf
|
||||
insertafter: '^auth_mechanisms ='
|
||||
block: |
|
||||
passdb {
|
||||
driver = passwd-file
|
||||
args = scheme=sha512_crypt /etc/dovecot/passwd
|
||||
}
|
||||
userdb {
|
||||
driver = static
|
||||
args = uid=vmail gid=vmail home=/var/vmail/%u
|
||||
}
|
||||
|
||||
- name: Create vmail group
|
||||
group:
|
||||
name: vmail
|
||||
gid: 5000
|
||||
state: present
|
||||
|
||||
- name: Create vmail user
|
||||
user:
|
||||
name: vmail
|
||||
uid: 5000
|
||||
group: vmail
|
||||
create_home: no
|
||||
|
||||
- name: Create vmail directory structure
|
||||
file:
|
||||
path: /var/vmail
|
||||
state: directory
|
||||
owner: vmail
|
||||
group: vmail
|
||||
mode: 0700
|
||||
|
||||
- name: Restart Postfix
|
||||
service:
|
||||
name: postfix
|
||||
state: restarted
|
||||
|
||||
- name: Restart Dovecot
|
||||
service:
|
||||
name: dovecot
|
||||
state: restarted
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
# Linode/initial-infrastructure.yml
|
||||
# This playbook only creates the Linode instances without trying to configure them
|
||||
# This separation makes the deployment more reliable
|
||||
|
||||
- name: Create Linode infrastructure
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
connection: local
|
||||
vars_files:
|
||||
- vars.yaml
|
||||
vars:
|
||||
# Default values if not provided
|
||||
ssh_user: "{{ ssh_user | default('root') }}"
|
||||
linode_region: "{{ linode_region | default(region_choices | random) }}"
|
||||
plan: "{{ plan | default('g6-standard-2') }}"
|
||||
image: "{{ image | default('linode/kali') }}"
|
||||
|
||||
# Determine what to deploy based on configuration
|
||||
deploy_redirector: "{{ not (c2_only | default(false)) }}"
|
||||
deploy_c2: "{{ not (redirector_only | default(false)) }}"
|
||||
|
||||
# Generate random names if not provided
|
||||
redirector_name: "{{ redirector_name | default('srv-' + 100000000 | random | to_uuid | hash('md5') | truncate(8, True, '')) }}"
|
||||
c2_name: "{{ c2_name | default('node-' + 100000000 | random | to_uuid | hash('md5') | truncate(8, True, '')) }}"
|
||||
|
||||
tasks:
|
||||
- name: Validate required Linode token
|
||||
assert:
|
||||
that:
|
||||
- linode_token is defined and linode_token != ""
|
||||
fail_msg: "Linode API token is required. Set linode_token in vars.yaml or via --linode-token."
|
||||
|
||||
- name: Create redirector Linode instance
|
||||
community.general.linode_v4:
|
||||
access_token: "{{ linode_token }}"
|
||||
label: "{{ redirector_name }}"
|
||||
type: "{{ plan }}"
|
||||
region: "{{ linode_region }}"
|
||||
image: "{{ image }}"
|
||||
root_pass: "{{ lookup('password', '/dev/null length=24 chars=ascii_letters,digits') }}"
|
||||
authorized_keys:
|
||||
- "{{ lookup('file', ssh_key_path) }}"
|
||||
state: present
|
||||
register: redirector_instance
|
||||
when: deploy_redirector
|
||||
|
||||
- name: Set redirector_ip for later use
|
||||
set_fact:
|
||||
redirector_instance_id: "{{ redirector_instance.instance.id }}"
|
||||
redirector_ip: "{{ redirector_instance.instance.ipv4[0] }}"
|
||||
when: deploy_redirector and redirector_instance is defined
|
||||
|
||||
- name: Create C2 Linode instance
|
||||
community.general.linode_v4:
|
||||
access_token: "{{ linode_token }}"
|
||||
label: "{{ c2_name }}"
|
||||
type: "{{ plan }}"
|
||||
region: "{{ linode_region }}"
|
||||
image: "{{ image }}"
|
||||
root_pass: "{{ lookup('password', '/dev/null length=24 chars=ascii_letters,digits') }}"
|
||||
authorized_keys:
|
||||
- "{{ lookup('file', ssh_key_path) }}"
|
||||
state: present
|
||||
register: c2_instance
|
||||
when: deploy_c2
|
||||
|
||||
- name: Set c2_ip for later use
|
||||
set_fact:
|
||||
c2_instance_id: "{{ c2_instance.instance.id }}"
|
||||
c2_ip: "{{ c2_instance.instance.ipv4[0] }}"
|
||||
when: deploy_c2 and c2_instance is defined
|
||||
|
||||
- name: Display instance information
|
||||
debug:
|
||||
msg:
|
||||
- "Linode instances created successfully!"
|
||||
- "Waiting for instances to initialize..."
|
||||
- "{{ 'Redirector IP: ' + redirector_ip if redirector_ip is defined else 'No redirector deployed' }}"
|
||||
- "{{ 'C2 Server IP: ' + c2_ip if c2_ip is defined else 'No C2 server deployed' }}"
|
||||
|
||||
- name: Wait for instances to initialize (30 seconds)
|
||||
pause:
|
||||
seconds: 30
|
||||
when: (deploy_redirector and redirector_instance is defined) or (deploy_c2 and c2_instance is defined)
|
||||
@@ -0,0 +1,179 @@
|
||||
---
|
||||
# Common task for installing offensive security tools
|
||||
# Shared across all providers
|
||||
|
||||
- name: Force tools directory to root regardless of user
|
||||
set_fact:
|
||||
home_dir: "/root"
|
||||
tools_dir: "/root/Tools"
|
||||
when: provider == "aws"
|
||||
|
||||
- name: Determine home directory path
|
||||
set_fact:
|
||||
home_dir: "{{ (ansible_user == 'root') | ternary('/root', '/home/' + ansible_user) }}"
|
||||
tools_dir: "{{ (ansible_user == 'root') | ternary('/root/Tools', '/home/' + ansible_user + '/Tools') }}"
|
||||
|
||||
- name: Create Tools directory
|
||||
file:
|
||||
path: "{{ tools_dir }}"
|
||||
state: directory
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0755'
|
||||
|
||||
- name: Install base dependencies
|
||||
apt:
|
||||
name:
|
||||
- python3-pip
|
||||
- python3-venv
|
||||
- pipx
|
||||
- curl
|
||||
- wget
|
||||
- git
|
||||
- jq
|
||||
- unzip
|
||||
- tmux
|
||||
state: present
|
||||
update_cache: yes
|
||||
|
||||
- name: Check if pipx is installed
|
||||
command: which pipx
|
||||
register: pipx_check
|
||||
ignore_errors: true
|
||||
changed_when: false
|
||||
|
||||
- name: Configure pipx path
|
||||
shell: |
|
||||
export PATH="$PATH:{{ home_dir }}/.local/bin"
|
||||
pipx ensurepath
|
||||
args:
|
||||
executable: /bin/bash
|
||||
register: pipx_path_result
|
||||
until: pipx_path_result is success
|
||||
retries: 3
|
||||
delay: 5
|
||||
when: pipx_check.rc == 0
|
||||
|
||||
- name: Set PATH for subsequent operations
|
||||
set_fact:
|
||||
custom_path: "{{ home_dir }}/.local/bin:{{ ansible_env.PATH }}"
|
||||
|
||||
- name: Install tools via pipx
|
||||
shell: |
|
||||
export PATH="{{ custom_path }}"
|
||||
pipx install git+https://github.com/Pennyw0rth/NetExec
|
||||
pipx install git+https://github.com/blacklanternsecurity/TREVORspray
|
||||
pipx install impacket
|
||||
environment:
|
||||
PATH: "{{ custom_path }}"
|
||||
register: pipx_install_result
|
||||
until: pipx_install_result is success
|
||||
retries: 3
|
||||
delay: 5
|
||||
|
||||
- name: Install offensive security tools
|
||||
apt:
|
||||
name:
|
||||
- nmap
|
||||
- tcpdump
|
||||
- hydra
|
||||
- john
|
||||
- hashcat
|
||||
- sqlmap
|
||||
- gobuster
|
||||
- dirb
|
||||
- enum4linux
|
||||
- dnsenum
|
||||
- seclists
|
||||
- responder
|
||||
- golang
|
||||
- proxychains
|
||||
- tor
|
||||
- crackmapexec
|
||||
state: present
|
||||
|
||||
- name: Download Kerbrute
|
||||
shell: |
|
||||
mkdir -p {{ tools_dir }}/Kerbrute
|
||||
wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_linux_amd64 -O {{ tools_dir }}/Kerbrute/kerbrute
|
||||
chmod +x {{ tools_dir }}/Kerbrute/kerbrute
|
||||
args:
|
||||
executable: /bin/bash
|
||||
creates: "{{ tools_dir }}/Kerbrute/kerbrute"
|
||||
|
||||
- name: Clone SharpCollection nightly builds
|
||||
git:
|
||||
repo: https://github.com/Flangvik/SharpCollection.git
|
||||
dest: "{{ tools_dir }}/SharpCollection"
|
||||
version: master
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Clone PEASS-ng
|
||||
git:
|
||||
repo: https://github.com/carlospolop/PEASS-ng.git
|
||||
dest: "{{ tools_dir }}/PEASS-ng"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Clone MailSniper
|
||||
git:
|
||||
repo: https://github.com/dafthack/MailSniper.git
|
||||
dest: "{{ tools_dir }}/MailSniper"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Clone Inveigh
|
||||
git:
|
||||
repo: https://github.com/Kevin-Robertson/Inveigh.git
|
||||
dest: "{{ tools_dir }}/Inveigh"
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Install Metasploit Framework (Nightly Build)
|
||||
shell: |
|
||||
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > /tmp/msfinstall
|
||||
chmod 755 /tmp/msfinstall
|
||||
/tmp/msfinstall
|
||||
rm -f /tmp/msfinstall
|
||||
args:
|
||||
executable: /bin/bash
|
||||
creates: /usr/bin/msfconsole
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Ensure GoPhish directory exists
|
||||
file:
|
||||
path: "{{ tools_dir }}/gophish"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
|
||||
- name: Grab GoPhish latest release
|
||||
shell: |
|
||||
curl -s https://api.github.com/repos/gophish/gophish/releases/latest | jq -r '.assets[] | select(.browser_download_url | contains("linux-64bit.zip")) | .browser_download_url'
|
||||
register: gophish_url
|
||||
failed_when: gophish_url.stdout == ""
|
||||
changed_when: false
|
||||
|
||||
- name: Download and install GoPhish
|
||||
shell: |
|
||||
curl -L "{{ gophish_url.stdout }}" -o {{ tools_dir }}/gophish.zip
|
||||
unzip {{ tools_dir }}/gophish.zip -d {{ tools_dir }}/gophish
|
||||
rm -f {{ tools_dir }}/gophish.zip
|
||||
chmod +x {{ tools_dir }}/gophish/gophish
|
||||
args:
|
||||
creates: "{{ tools_dir }}/gophish/gophish"
|
||||
|
||||
- name: Deploy Gophish config.json with custom admin port
|
||||
template:
|
||||
src: "../templates/gophish-config.j2"
|
||||
dest: "{{ tools_dir }}/gophish/config.json"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
mode: '0644'
|
||||
vars:
|
||||
gophish_admin_port: "8090"
|
||||
domain: "{{ domain }}"
|
||||
|
||||
- name: Set proper ownership for Tools directory
|
||||
file:
|
||||
path: "{{ tools_dir }}"
|
||||
owner: "{{ ansible_user }}"
|
||||
group: "{{ ansible_user }}"
|
||||
recurse: true
|
||||
mode: '0755'
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
# tasks/port_randomization.yml
|
||||
# Task file to randomize ports for C2 infrastructure with improved host-based service management
|
||||
|
||||
- name: Create port randomization script
|
||||
copy:
|
||||
src: "../files/randomize_ports.sh"
|
||||
dest: "/root/Tools/randomize_ports.sh"
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
|
||||
- name: Execute port randomization script
|
||||
shell: |
|
||||
cd /root/Tools && ./randomize_ports.sh
|
||||
register: randomize_result
|
||||
when: randomize_ports | default(true) | bool
|
||||
|
||||
- name: Display port randomization results
|
||||
debug:
|
||||
msg: "{{ randomize_result.stdout_lines }}"
|
||||
when: randomize_ports | default(true) | bool
|
||||
|
||||
- name: Store randomized ports in variables
|
||||
shell: |
|
||||
PORT_CONFIG="/root/Tools/port_config.json"
|
||||
if [ -f "$PORT_CONFIG" ]; then
|
||||
cat "$PORT_CONFIG"
|
||||
else
|
||||
echo '{"error": "Port configuration not found"}'
|
||||
fi
|
||||
register: port_config_result
|
||||
when: randomize_ports | default(true) | bool
|
||||
|
||||
- name: Set port fact variables
|
||||
set_fact:
|
||||
randomized_http_port: "{{ (port_config_result.stdout | from_json).http_c2_port | default(8888) }}"
|
||||
randomized_https_port: "{{ (port_config_result.stdout | from_json).https_c2_port | default(443) }}"
|
||||
randomized_mtls_port: "{{ (port_config_result.stdout | from_json).mtls_c2_port | default(31337) }}"
|
||||
randomized_shell_handler_port: "{{ (port_config_result.stdout | from_json).shell_handler_port | default(shell_handler_port) }}"
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0
|
||||
|
||||
- name: Update shell handler configuration with randomized port
|
||||
replace:
|
||||
path: "/root/Tools/shell-handler/persistent-listener.sh"
|
||||
regexp: "LISTEN_PORT=.*"
|
||||
replace: "LISTEN_PORT={{ randomized_shell_handler_port | default(shell_handler_port) }}"
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0
|
||||
|
||||
- name: Update shell handler service with randomized port
|
||||
lineinfile:
|
||||
path: "/etc/systemd/system/shell-handler.service"
|
||||
regexp: 'Environment="LISTEN_PORT='
|
||||
line: 'Environment="LISTEN_PORT={{ randomized_shell_handler_port | default(shell_handler_port) }}"'
|
||||
insertafter: '^\\[Service\\]'
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0
|
||||
|
||||
# Improved service management - checks for service existence before restarting
|
||||
- name: Determine services to restart based on host type
|
||||
set_fact:
|
||||
services_to_restart: []
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0
|
||||
|
||||
- name: Check if Havoc service exists
|
||||
stat:
|
||||
path: "/etc/systemd/system/havoc.service"
|
||||
register: havoc_service_stat
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0
|
||||
|
||||
- name: Add Havoc to services list if it exists
|
||||
set_fact:
|
||||
services_to_restart: "{{ services_to_restart + ['havoc'] }}"
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0 and havoc_service_stat.stat.exists | default(false)
|
||||
|
||||
- name: Check if shell-handler service exists
|
||||
stat:
|
||||
path: "/etc/systemd/system/shell-handler.service"
|
||||
register: shell_handler_service_stat
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0
|
||||
|
||||
- name: Add shell-handler to services list if it exists
|
||||
set_fact:
|
||||
services_to_restart: "{{ services_to_restart + ['shell-handler'] }}"
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0 and shell_handler_service_stat.stat.exists | default(false)
|
||||
|
||||
- name: Reload and restart services
|
||||
systemd:
|
||||
daemon_reload: yes
|
||||
name: "{{ item }}"
|
||||
state: restarted
|
||||
with_items: "{{ services_to_restart }}"
|
||||
when: randomize_ports | default(true) | bool and port_config_result.rc == 0 and services_to_restart | length > 0
|
||||
@@ -0,0 +1,402 @@
|
||||
---
|
||||
# Security hardening tasks for C2 server - Fixed AWS collection issues
|
||||
|
||||
- name: Check if system is updated
|
||||
apt:
|
||||
update_cache: yes
|
||||
register: apt_update_result
|
||||
until: apt_update_result is success
|
||||
retries: 5
|
||||
delay: 5
|
||||
|
||||
- name: Install security packages (non-UFW)
|
||||
apt:
|
||||
name:
|
||||
- fail2ban
|
||||
- unattended-upgrades
|
||||
- debsums
|
||||
- aide
|
||||
- rkhunter
|
||||
- logrotate
|
||||
state: present
|
||||
register: package_install
|
||||
until: package_install is success
|
||||
retries: 3
|
||||
delay: 5
|
||||
|
||||
# Provider and role detection - DRY principle
|
||||
- name: Determine deployment configuration
|
||||
set_fact:
|
||||
is_aws_provider: "{{ provider | default('unknown') == 'aws' }}"
|
||||
is_c2_server: "{{ 'c2servers' in group_names }}"
|
||||
is_redirector: "{{ 'redirectors' in group_names }}"
|
||||
|
||||
# UFW Configuration Block - Non-AWS only
|
||||
- name: UFW detection and installation block
|
||||
block:
|
||||
- name: Check if UFW is installed
|
||||
command: which ufw
|
||||
register: ufw_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Install UFW if not present (non-AWS)
|
||||
apt:
|
||||
name: ufw
|
||||
state: present
|
||||
update_cache: yes
|
||||
register: ufw_install
|
||||
until: ufw_install is success
|
||||
retries: 3
|
||||
delay: 5
|
||||
when: ufw_check.rc != 0
|
||||
when: not is_aws_provider
|
||||
|
||||
- name: Configure UFW for non-AWS deployments
|
||||
block:
|
||||
- name: Configure UFW default policies
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
policy: deny
|
||||
direction: incoming
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Configure UFW for C2 server
|
||||
block:
|
||||
- name: Reset UFW to default deny
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
policy: deny
|
||||
direction: incoming
|
||||
|
||||
- name: Allow SSH only from operator IP
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "22"
|
||||
src: "{{ operator_ip }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow Havoc Teamserver only from operator IP
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ havoc_teamserver_port | default('40056') }}"
|
||||
src: "{{ operator_ip }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow traffic only from redirector
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item }}"
|
||||
src: "{{ redirector_ip }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- "80"
|
||||
- "443"
|
||||
- "{{ havoc_http_port | default('8080') }}"
|
||||
- "{{ havoc_https_port | default('9443') }}"
|
||||
- "{{ havoc_payload_port | default('8443') }}"
|
||||
- "{{ gophish_admin_port }}"
|
||||
- "{{ gophish_phish_port | default('8081') }}"
|
||||
- "{{ tracker_port | default('5000') }}"
|
||||
when: is_c2_server
|
||||
|
||||
- name: Configure UFW for redirector
|
||||
block:
|
||||
- name: Reset UFW to default deny
|
||||
community.general.ufw:
|
||||
state: enabled
|
||||
policy: deny
|
||||
direction: incoming
|
||||
|
||||
- name: Allow SSH only from operator IP
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "22"
|
||||
src: "{{ operator_ip }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow public services from anywhere
|
||||
community.general.ufw:
|
||||
rule: allow
|
||||
port: "{{ item }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- "80"
|
||||
- "443"
|
||||
- "{{ shell_handler_port | default('4488') }}"
|
||||
when: is_redirector
|
||||
when: not is_aws_provider and (ufw_check.rc == 0 or ufw_install is success)
|
||||
|
||||
# Iptables fallback configuration - Non-AWS only
|
||||
- name: Configure basic iptables rules if UFW unavailable
|
||||
block:
|
||||
- name: Set up basic iptables rules for C2 server
|
||||
shell: |
|
||||
iptables -F
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables -P OUTPUT ACCEPT
|
||||
# Allow established connections
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
# Allow loopback
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
# Allow SSH from operator
|
||||
iptables -A INPUT -p tcp --dport 22 -s {{ operator_ip }} -j ACCEPT
|
||||
# Allow Havoc teamserver from operator
|
||||
iptables -A INPUT -p tcp --dport {{ havoc_teamserver_port | default(40056) }} -s {{ operator_ip }} -j ACCEPT
|
||||
# Allow traffic from redirector
|
||||
iptables -A INPUT -p tcp --dport 80 -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 443 -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ havoc_http_port | default(8080) }} -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ havoc_https_port | default(9443) }} -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ havoc_payload_port | default(8443) }} -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ gophish_admin_port }} -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ gophish_phish_port | default(8081) }} -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ tracker_port | default(5000) }} -s {{ redirector_ip }} -j ACCEPT
|
||||
when: is_c2_server
|
||||
|
||||
- name: Set up basic iptables rules for redirector
|
||||
shell: |
|
||||
iptables -F
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables -P OUTPUT ACCEPT
|
||||
# Allow established connections
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
# Allow loopback
|
||||
iptables -A INPUT -i lo -j ACCEPT
|
||||
# Allow SSH from operator
|
||||
iptables -A INPUT -p tcp --dport 22 -s {{ operator_ip }} -j ACCEPT
|
||||
# Allow public services
|
||||
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ shell_handler_port | default(4488) }} -j ACCEPT
|
||||
when: is_redirector
|
||||
|
||||
- name: Save iptables rules
|
||||
shell: |
|
||||
iptables-save > /etc/iptables/rules.v4
|
||||
ignore_errors: yes
|
||||
when: not is_aws_provider and (ufw_check.rc != 0 and (ufw_install is undefined or ufw_install is failed))
|
||||
|
||||
# SSH Hardening - Universal application
|
||||
- name: Harden SSH configuration
|
||||
lineinfile:
|
||||
path: /etc/ssh/sshd_config
|
||||
regexp: "{{ item.regexp }}"
|
||||
line: "{{ item.line }}"
|
||||
state: present
|
||||
backup: yes
|
||||
loop:
|
||||
- { regexp: '^#?PermitRootLogin', line: 'PermitRootLogin no' }
|
||||
- { regexp: '^#?PasswordAuthentication', line: 'PasswordAuthentication no' }
|
||||
- { regexp: '^#?X11Forwarding', line: 'X11Forwarding no' }
|
||||
- { regexp: '^#?MaxAuthTries', line: 'MaxAuthTries 3' }
|
||||
- { regexp: '^#?AllowTcpForwarding', line: 'AllowTcpForwarding yes' }
|
||||
- { regexp: '^#?ClientAliveInterval', line: 'ClientAliveInterval 300' }
|
||||
- { regexp: '^#?ClientAliveCountMax', line: 'ClientAliveCountMax 2' }
|
||||
- { regexp: '^#?Protocol', line: 'Protocol 2' }
|
||||
- { regexp: '^#?MaxStartups', line: 'MaxStartups 10:30:100' }
|
||||
- { regexp: '^#?LoginGraceTime', line: 'LoginGraceTime 60' }
|
||||
register: ssh_config_updated
|
||||
|
||||
# System resource limits configuration
|
||||
- name: Configure system resource limits
|
||||
community.general.pam_limits:
|
||||
domain: "*"
|
||||
limit_type: "{{ item.limit_type }}"
|
||||
limit_item: "{{ item.limit_item }}"
|
||||
value: "{{ item.value }}"
|
||||
loop:
|
||||
- { limit_type: soft, limit_item: nofile, value: 65535 }
|
||||
- { limit_type: hard, limit_item: nofile, value: 65535 }
|
||||
- { limit_type: soft, limit_item: nproc, value: 4096 }
|
||||
- { limit_type: hard, limit_item: nproc, value: 4096 }
|
||||
|
||||
# Fail2ban configuration - Universal
|
||||
- name: Set up fail2ban SSH jail
|
||||
copy:
|
||||
dest: /etc/fail2ban/jail.d/sshd.conf
|
||||
content: |
|
||||
[sshd]
|
||||
enabled = true
|
||||
port = ssh
|
||||
filter = sshd
|
||||
logpath = /var/log/auth.log
|
||||
maxretry = 5
|
||||
bantime = 3600
|
||||
findtime = 600
|
||||
|
||||
[sshd-ddos]
|
||||
enabled = true
|
||||
port = ssh
|
||||
filter = sshd-ddos
|
||||
logpath = /var/log/auth.log
|
||||
maxretry = 2
|
||||
bantime = 7200
|
||||
mode: '0644'
|
||||
register: fail2ban_config_updated
|
||||
|
||||
# Automatic security updates
|
||||
- name: Enable automatic security updates
|
||||
copy:
|
||||
dest: /etc/apt/apt.conf.d/20auto-upgrades
|
||||
content: |
|
||||
APT::Periodic::Update-Package-Lists "1";
|
||||
APT::Periodic::Unattended-Upgrade "1";
|
||||
APT::Periodic::AutocleanInterval "7";
|
||||
APT::Periodic::Download-Upgradeable-Packages "1";
|
||||
mode: '0644'
|
||||
|
||||
# Log cleaning functionality
|
||||
- name: Create Tools directory if it doesn't exist
|
||||
file:
|
||||
path: /root/Tools
|
||||
state: directory
|
||||
mode: '0700'
|
||||
when: zero_logs | default(false) | bool
|
||||
|
||||
- name: Create log cleaning script if zero-logs is enabled
|
||||
copy:
|
||||
dest: /root/Tools/clean-logs.sh
|
||||
content: |
|
||||
#!/bin/bash
|
||||
# C2ingRed Log cleaning script for operational security
|
||||
echo "Starting log cleaning at $(date)" >> /tmp/clean-logs.log
|
||||
|
||||
# Clear authentication logs
|
||||
echo "" > /var/log/auth.log
|
||||
echo "" > /var/log/auth.log.1
|
||||
|
||||
# Clear system logs
|
||||
echo "" > /var/log/syslog
|
||||
echo "" > /var/log/syslog.1
|
||||
|
||||
# Clear kernel logs
|
||||
echo "" > /var/log/kern.log
|
||||
echo "" > /var/log/kern.log.1
|
||||
|
||||
# Clear application specific logs
|
||||
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
|
||||
find /var/log -type f -name "*.log.*" -exec truncate -s 0 {} \;
|
||||
|
||||
# Clear journal logs
|
||||
journalctl --vacuum-time=1s 2>/dev/null || true
|
||||
|
||||
# Clear bash history for all users
|
||||
for user_home in /home/*; do
|
||||
if [ -d "$user_home" ]; then
|
||||
echo "" > "$user_home/.bash_history" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Clear root bash history
|
||||
echo "" > /root/.bash_history 2>/dev/null || true
|
||||
history -c 2>/dev/null || true
|
||||
|
||||
# Clear tmp files
|
||||
find /tmp -type f -mtime +1 -delete 2>/dev/null || true
|
||||
|
||||
echo "Log cleaning completed at $(date)" >> /tmp/clean-logs.log
|
||||
mode: '0700'
|
||||
when: zero_logs | default(false) | bool
|
||||
|
||||
- name: Create cron job for log cleaning if enabled
|
||||
cron:
|
||||
name: "Clean operational logs"
|
||||
minute: "0"
|
||||
hour: "*/6"
|
||||
job: "/root/Tools/clean-logs.sh"
|
||||
user: root
|
||||
when: zero_logs | default(false) | bool
|
||||
|
||||
# Service restart handlers
|
||||
- name: Restart SSH service if configuration changed
|
||||
service:
|
||||
name: ssh
|
||||
state: restarted
|
||||
when: ssh_config_updated.changed
|
||||
|
||||
- name: Check if fail2ban service exists
|
||||
stat:
|
||||
path: "/etc/init.d/fail2ban"
|
||||
register: fail2ban_service_stat
|
||||
|
||||
- name: Restart fail2ban service if installed and configuration changed
|
||||
service:
|
||||
name: fail2ban
|
||||
state: restarted
|
||||
enabled: yes
|
||||
when: fail2ban_service_stat.stat.exists and fail2ban_config_updated.changed
|
||||
|
||||
# AWS-specific security updates - NO MODULES REQUIRED
|
||||
- name: Check if we're running on AWS provider
|
||||
set_fact:
|
||||
is_aws_provider: "{{ hostvars['localhost']['provider'] | default('') == 'aws' }}"
|
||||
|
||||
- name: AWS-specific security updates
|
||||
block:
|
||||
- name: Get redirector security group information
|
||||
block:
|
||||
- name: Check if infrastructure state file exists
|
||||
stat:
|
||||
path: "{{ playbook_dir }}/infrastructure_state_{{ hostvars['localhost']['deployment_id'] }}.json"
|
||||
register: redirector_state_file
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Load redirector state if available
|
||||
include_vars:
|
||||
file: "{{ playbook_dir }}/infrastructure_state_{{ hostvars['localhost']['deployment_id'] }}.json"
|
||||
name: redirector_state
|
||||
when: redirector_state_file.stat.exists
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Update redirector security group via raw AWS CLI
|
||||
delegate_to: localhost
|
||||
shell: |
|
||||
export AWS_ACCESS_KEY_ID="{{ hostvars['localhost']['aws_access_key'] }}"
|
||||
export AWS_SECRET_ACCESS_KEY="{{ hostvars['localhost']['aws_secret_key'] }}"
|
||||
export AWS_DEFAULT_REGION="{{ redirector_state.region | default(aws_region) }}"
|
||||
|
||||
# Install AWS CLI if not present
|
||||
if ! command -v aws &> /dev/null; then
|
||||
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
|
||||
unzip -q awscliv2.zip
|
||||
sudo ./aws/install --update
|
||||
rm -rf aws awscliv2.zip
|
||||
fi
|
||||
|
||||
# Add security group rule (ignore if exists)
|
||||
aws ec2 authorize-security-group-ingress \
|
||||
--group-id {{ redirector_state.security_group_id }} \
|
||||
--protocol tcp \
|
||||
--port 22 \
|
||||
--cidr {{ ansible_host }}/32 \
|
||||
2>/dev/null || echo "Rule already exists or added successfully"
|
||||
when: redirector_state is defined and redirector_state.security_group_id is defined
|
||||
register: sg_update_result
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Display security group update result
|
||||
debug:
|
||||
msg: |
|
||||
AWS Security Group Update: {{ 'SUCCESS' if sg_update_result.rc == 0 else 'COMPLETED' }}
|
||||
C2 Server IP: {{ ansible_host }}
|
||||
Security Group ID: {{ redirector_state.security_group_id | default('NOT FOUND') }}
|
||||
|
||||
when: is_aws_provider | bool
|
||||
|
||||
# Final security status report
|
||||
- name: Generate security hardening report
|
||||
debug:
|
||||
msg: |
|
||||
C2ingRed Security Hardening Complete:
|
||||
=====================================
|
||||
Provider: {{ provider | default('unknown') }}
|
||||
Server Type: {{ 'C2 Server' if is_c2_server else 'Redirector' if is_redirector else 'Unknown' }}
|
||||
SSH Hardened: {{ 'YES' if ssh_config_updated.changed else 'ALREADY CONFIGURED' }}
|
||||
Fail2ban Configured: {{ 'YES' if fail2ban_config_updated.changed else 'ALREADY CONFIGURED' }}
|
||||
Firewall: {{ 'AWS Security Groups' if is_aws_provider else 'UFW/iptables' }}
|
||||
Log Cleaning: {{ 'ENABLED' if zero_logs | default(false) | bool else 'DISABLED' }}
|
||||
Auto Updates: ENABLED
|
||||
System Limits: CONFIGURED
|
||||
@@ -0,0 +1,223 @@
|
||||
---
|
||||
# Common task for configuring proper traffic flow between infrastructure components
|
||||
|
||||
- name: Determine server role in infrastructure
|
||||
set_fact:
|
||||
server_role: >-
|
||||
{% if inventory_hostname in groups['c2servers'] | default([]) %}c2{%
|
||||
elif inventory_hostname in groups['redirectors'] | default([]) %}redirector{%
|
||||
elif inventory_hostname in groups['logservers'] | default([]) %}logserver{%
|
||||
elif inventory_hostname in groups['payloadservers'] | default([]) %}payloadserver{%
|
||||
elif inventory_hostname in groups['phishingservers'] | default([]) %}phishingserver{%
|
||||
elif inventory_hostname in groups['sharedrives'] | default([]) %}sharedrive{%
|
||||
else %}unknown{% endif %}
|
||||
|
||||
# Determine if we're using AWS provider
|
||||
- name: Determine if using AWS provider
|
||||
set_fact:
|
||||
is_aws_provider: "{{ provider | default('unknown') == 'aws' }}"
|
||||
|
||||
# Skip firewall configuration for AWS instances
|
||||
- name: Skip firewall configuration for AWS instances
|
||||
debug:
|
||||
msg: "Skipping host-based firewall configuration for AWS instance. Security Groups are handling this at the infrastructure level."
|
||||
when: is_aws_provider
|
||||
|
||||
# Check if UFW is installed or can be installed
|
||||
- name: Check if UFW is installed
|
||||
command: which ufw
|
||||
register: ufw_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
when: not is_aws_provider
|
||||
|
||||
# Try to install UFW if not found and we're not on AWS
|
||||
- name: Install UFW if not present
|
||||
apt:
|
||||
name: ufw
|
||||
state: present
|
||||
update_cache: yes
|
||||
register: ufw_install
|
||||
until: ufw_install is success or ufw_install is failed
|
||||
retries: 3
|
||||
delay: 5
|
||||
ignore_errors: yes
|
||||
when: not is_aws_provider and ufw_check.rc != 0
|
||||
|
||||
# Set a fact to track if UFW is available
|
||||
- name: Determine if UFW is available
|
||||
set_fact:
|
||||
ufw_available: "{{ (ufw_check.rc == 0) or (ufw_install is defined and ufw_install is success) }}"
|
||||
when: not is_aws_provider
|
||||
|
||||
# UFW Configuration Block (non-AWS only)
|
||||
- name: Configure C2 server routing with UFW
|
||||
block:
|
||||
- name: Allow SSH only from operator IP
|
||||
ufw:
|
||||
rule: allow
|
||||
port: 22
|
||||
src: "{{ operator_ip }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow teamserver access only from operator IP
|
||||
ufw:
|
||||
rule: allow
|
||||
port: "{{ havoc_teamserver_port | default('40056') }}"
|
||||
src: "{{ operator_ip }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Configure required flows for each connected component
|
||||
ufw:
|
||||
rule: allow
|
||||
port: "{{ item.port }}"
|
||||
src: "{{ item.ip }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- { ip: "{{ redirector_ip }}", port: "80" }
|
||||
- { ip: "{{ redirector_ip }}", port: "443" }
|
||||
- { ip: "{{ redirector_ip }}", port: "{{ havoc_http_port | default('8080') }}" }
|
||||
- { ip: "{{ redirector_ip }}", port: "{{ havoc_https_port | default('9443') }}" }
|
||||
- { ip: "{{ logserver_ip | default(omit) }}", port: "5144" }
|
||||
- { ip: "{{ payloadserver_ip | default(omit) }}", port: "8888" }
|
||||
when: item.ip != omit
|
||||
when:
|
||||
- server_role == 'c2'
|
||||
- not is_aws_provider
|
||||
- ufw_available | default(false) | bool
|
||||
|
||||
# Fallback to iptables if UFW is not available
|
||||
- name: Configure C2 server routing with iptables (fallback)
|
||||
block:
|
||||
- name: Set up basic iptables rules for C2 server
|
||||
shell: |
|
||||
iptables -F
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables -P OUTPUT ACCEPT
|
||||
# Allow established connections
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
# Allow SSH from operator
|
||||
iptables -A INPUT -p tcp --dport 22 -s {{ operator_ip }} -j ACCEPT
|
||||
# Allow Havoc teamserver from operator
|
||||
iptables -A INPUT -p tcp --dport {{ havoc_teamserver_port | default(40056) }} -s {{ operator_ip }} -j ACCEPT
|
||||
# Allow traffic from redirector
|
||||
iptables -A INPUT -p tcp --dport 80 -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 443 -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ havoc_http_port | default(8080) }} -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ havoc_https_port | default(9443) }} -s {{ redirector_ip }} -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport {{ gophish_admin_port }} -s {{ redirector_ip }} -j ACCEPT
|
||||
when: redirector_ip is defined
|
||||
when:
|
||||
- server_role == 'c2'
|
||||
- not is_aws_provider
|
||||
- not (ufw_available | default(false) | bool)
|
||||
|
||||
# Configure redirector routing with UFW when available
|
||||
- name: Configure redirector routing with UFW
|
||||
block:
|
||||
- name: Allow SSH only from operator IP
|
||||
ufw:
|
||||
rule: allow
|
||||
port: 22
|
||||
src: "{{ operator_ip }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow public web access
|
||||
ufw:
|
||||
rule: allow
|
||||
port: "{{ item }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- 80
|
||||
- 443
|
||||
|
||||
- name: Allow shell handler access
|
||||
ufw:
|
||||
rule: allow
|
||||
port: "{{ shell_handler_port | default('4444') }}"
|
||||
proto: tcp
|
||||
when:
|
||||
- server_role == 'redirector'
|
||||
- not is_aws_provider
|
||||
- ufw_available | default(false) | bool
|
||||
|
||||
# Fallback to iptables for redirector if UFW is not available
|
||||
- name: Configure redirector routing with iptables (fallback)
|
||||
block:
|
||||
- name: Set up basic iptables rules for redirector
|
||||
shell: |
|
||||
iptables -F
|
||||
iptables -P INPUT DROP
|
||||
iptables -P FORWARD DROP
|
||||
iptables -P OUTPUT ACCEPT
|
||||
# Allow established connections
|
||||
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
# Allow SSH from operator
|
||||
iptables -A INPUT -p tcp --dport 22 -s {{ operator_ip }} -j ACCEPT
|
||||
# Allow web traffic from anywhere
|
||||
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
|
||||
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
|
||||
# Allow shell handler port
|
||||
iptables -A INPUT -p tcp --dport {{ shell_handler_port | default('4444') }} -j ACCEPT
|
||||
when:
|
||||
- server_role == 'redirector'
|
||||
- not is_aws_provider
|
||||
- not (ufw_available | default(false) | bool)
|
||||
|
||||
# The rest of your tasks remain unchanged...
|
||||
- name: Configure logging server routing
|
||||
block:
|
||||
- name: Allow SSH only from operator IP
|
||||
ufw:
|
||||
rule: allow
|
||||
port: 22
|
||||
src: "{{ operator_ip }}"
|
||||
proto: tcp
|
||||
|
||||
- name: Allow log ingestion from infrastructure
|
||||
ufw:
|
||||
rule: allow
|
||||
port: 5144 # Logstash port
|
||||
src: "{{ item }}"
|
||||
proto: tcp
|
||||
loop:
|
||||
- "{{ c2_ip }}"
|
||||
- "{{ redirector_ip }}"
|
||||
- "{{ payloadserver_ip | default(omit) }}"
|
||||
- "{{ phishingserver_ip | default(omit) }}"
|
||||
when: item != omit
|
||||
when:
|
||||
- server_role == 'logserver'
|
||||
- not is_aws_provider
|
||||
- ufw_available | default(false) | bool
|
||||
|
||||
- name: Update security group to allow SSH from C2 to redirector
|
||||
block:
|
||||
- name: Allow SSH from C2 to redirector (AWS)
|
||||
amazon.aws.ec2_security_group:
|
||||
name: "{{ redirector_name }}-sg"
|
||||
description: "Security group for redirector {{ redirector_name }}"
|
||||
vpc_id: "{{ vpc_id }}"
|
||||
region: "{{ aws_redirector_region }}"
|
||||
rules:
|
||||
- proto: tcp
|
||||
ports: 22
|
||||
cidr_ip: "{{ c2_ip }}/32"
|
||||
state: present
|
||||
when: provider == "aws"
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Allow SSH from C2 to redirector (UFW)
|
||||
ufw:
|
||||
rule: allow
|
||||
port: 22
|
||||
src: "{{ c2_ip }}"
|
||||
proto: tcp
|
||||
when: provider != "aws" and not is_aws_provider and ufw_available | default(false) | bool
|
||||
|
||||
- name: Allow SSH from C2 to redirector (iptables fallback)
|
||||
shell: |
|
||||
iptables -A INPUT -p tcp --dport 22 -s {{ c2_ip }} -j ACCEPT
|
||||
when: provider != "aws" and not is_aws_provider and not (ufw_available | default(false) | bool)
|
||||
when: server_role == 'redirector' and c2_ip is defined and redirector_ip is defined
|
||||
Reference in New Issue
Block a user