"
3 |
4 | # Install required packages
5 | ARG DEBIAN_FRONTEND=noninteractive
6 | RUN apt-get -qq update && apt-get -qq install -y \
7 | zip \
8 | git \
9 | curl \
10 | wget \
11 | openvpn \
12 | vim \
13 | dnsutils \
14 | tcpdump \
15 | netcat \
16 | dnsutils \
17 | ipset \
18 | iputils-ping \
19 | syslog-ng \
20 | python3 \
21 | net-tools \
22 | curl \
23 | nano \
24 | bridge-utils \
25 | python3-pip \
26 | > /dev/null
27 |
28 | RUN python3 -m pip install --upgrade requests
29 |
30 | # Set and create the gophish installation directory
31 | ARG GOPHISHDIR
32 | ARG GOPHISHVERSION
33 | ARG CONTACT_EMAIL
34 |
35 | ENV GOPHISHDIR=${GOPHISHDIR}
36 | ENV GOPHISHVERSION=${GOPHISHVERSION}
37 | ENV CONTACT_EMAIL=${CONTACT_EMAIL}
38 |
39 | RUN mkdir -p ${GOPHISHDIR}/tmp 2>/dev/null
40 |
41 | # Run the installation shell script
42 | COPY files/gophish_goget.sh /tmp/goget.sh
43 | RUN /bin/sh /tmp/goget.sh $GOPHISHVERSION $GOPHISHDIR
44 |
45 | # Fix bugs with rsyslog and Docker
46 | RUN echo "#!/bin/sh\nexit 0" > /usr/sbin/policy-rc.d
47 |
48 | # Copy the OpenVPN config and change the remote to the relay VPS
49 | # Also ensure $vpn is available inside the container
50 | ARG VPN
51 | ENV VPN=${VPN}
52 |
53 |
54 | COPY $VPN /root/$VPN
55 | # Create a GoPhish service
56 | # COPY files/gophish.service /etc/init.d/gophish
57 | # RUN chmod +x /etc/init.d/gophish
58 |
59 | # Set-up the GoPhish config
60 | COPY files/gophish-config.json $GOPHISHDIR/config.json
61 | RUN sed -i "s/\"contact_address\": \"\"/\"contact_address\": \"$CONTACT_EMAIL\"/g" $GOPHISHDIR/config.json
62 | COPY files/gophish_api.py $GOPHISHDIR/gophish_api.py
63 |
64 | # Copy the entrypoint and execute the container
65 | COPY docker-entrypoint.sh /docker-entrypoint.sh
66 | RUN chmod +x /docker-entrypoint.sh
67 | ENTRYPOINT ["/docker-entrypoint.sh"]
68 |
--------------------------------------------------------------------------------
/roles/relay-nginx/meta/main.yml:
--------------------------------------------------------------------------------
1 | galaxy_info:
2 | author: your name
3 | description: your description
4 | company: your company (optional)
5 |
6 | # If the issue tracker for your role is not on github, uncomment the
7 | # next line and provide a value
8 | # issue_tracker_url: http://example.com/issue/tracker
9 |
10 | # Some suggested licenses:
11 | # - BSD (default)
12 | # - MIT
13 | # - GPLv2
14 | # - GPLv3
15 | # - Apache
16 | # - CC-BY
17 | license: license (GPLv2, CC-BY, etc)
18 |
19 | min_ansible_version: 1.2
20 |
21 | # If this a Container Enabled role, provide the minimum Ansible Container version.
22 | # min_ansible_container_version:
23 |
24 | # Optionally specify the branch Galaxy will use when accessing the GitHub
25 | # repo for this role. During role install, if no tags are available,
26 | # Galaxy will use this branch. During import Galaxy will access files on
27 | # this branch. If Travis integration is configured, only notifications for this
28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
29 | # (usually master) will be used.
30 | #github_branch:
31 |
32 | #
33 | # platforms is a list of platforms, and each platform has a name and a list of versions.
34 | #
35 | # platforms:
36 | # - name: Fedora
37 | # versions:
38 | # - all
39 | # - 25
40 | # - name: SomePlatform
41 | # versions:
42 | # - all
43 | # - 1.0
44 | # - 7
45 | # - 99.99
46 |
47 | galaxy_tags: []
48 | # List tags for your role here, one per line. A tag is a keyword that describes
49 | # and categorizes the role. Users find roles by searching for tags. Be sure to
50 | # remove the '[]' above, if you add tags to this list.
51 | #
52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
53 | # Maximum 20 tags per role.
54 |
55 | dependencies: []
56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
57 | # if you add dependencies to this list.
58 |
--------------------------------------------------------------------------------
/roles/backend-manual-phish/files/manual-phish/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | red=$(tput -T xterm-256color setaf 1)
4 | green=$(tput -T xterm-256color setaf 2)
5 | orange=$(tput -T xterm-256color setaf 3)
6 | blue=$(tput -T xterm-256color setaf 4)
7 | darkgreen=$(tput -T xterm-256color setaf 6)
8 | white=$(tput -T xterm-256color setaf 7)
9 | reset=$(tput -T xterm-256color sgr0)
10 |
11 | # Start the VPN (if applicable) and renew the certificate
12 | if [ -n ${VPN} ]; then
13 | mkdir -p /var/log/openvpn
14 | openvpn --config "/root/${VPN}" --log /var/log/openvpn/connection.log &
15 | sleep 4
16 |
17 | # Display the IP address
18 | IPADDR=$(ip addr show | grep "global tun0" | cut -f6 -d " ")
19 | cat <<-EOF
20 | ----------------
21 | ${blue}[*]${reset} Starting the VPN
22 | ${blue}[*]${reset} Connecting to: ${green}${RELAY_HOST}${reset} - Project ${green}${CODENAME}${reset}
23 | ${blue}[*]${reset} IP address of VPN interface: ${green}${IPADDR}${reset}
24 | ----------------
25 | EOF
26 | else
27 | ipaddr=$(ip addr show | grep "global eth0" | cut -f6 -d " ")
28 | cat <<-EOF
29 | --------------------------
30 | ${orange}[!]${reset} ${red}DOCKER STARTED WITHOUT VPN${reset}
31 | ${orange}[!]${reset} To change this, set the VPN_CONFIG variable in the .env file accordingly
32 | ${orange}[!]${reset} IP address: ${IPADDR}
33 | --------------------------
34 | EOF
35 | fi
36 |
37 | echo "Configuring access"
38 | touch /var/log/phish-log.log
39 | mkdir -p /home/mutt/Mail/INBOX
40 | chown mutt:mutt /var/log/phish-log.log
41 | chown -R mutt:mutt /home/mutt/
42 | echo "su mutt" >> /root/.bashrc
43 |
44 | su mutt
45 | echo "Configuring Mutt"
46 |
47 | cd /home/mutt;
48 | chmod +x change-sender-name.sh
49 | ./change-sender-name.sh $REAL_NAME $USER_NAME
50 | sed -i 's/PHISH_DOMAIN/'"$DOMAIN_NAME"/ /home/mutt/.muttrc
51 | sed -i 's/PHISH_DOMAIN/'"$DOMAIN_NAME/" /home/mutt/.msmtprc
52 | echo 'echo "' >>/home/mutt/.bashrc
53 | cat bash-instructions >>/home/mutt/.bashrc
54 | echo '"' >>/home/mutt/.bashrc
55 |
56 | tail -f /root/$VPN
57 |
--------------------------------------------------------------------------------
/roles/relay-cobalt-strike/meta/main.yml:
--------------------------------------------------------------------------------
1 | galaxy_info:
2 | author: your name
3 | description: your description
4 | company: your company (optional)
5 |
6 | # If the issue tracker for your role is not on github, uncomment the
7 | # next line and provide a value
8 | # issue_tracker_url: http://example.com/issue/tracker
9 |
10 | # Some suggested licenses:
11 | # - BSD (default)
12 | # - MIT
13 | # - GPLv2
14 | # - GPLv3
15 | # - Apache
16 | # - CC-BY
17 | license: license (GPLv2, CC-BY, etc)
18 |
19 | min_ansible_version: 1.2
20 |
21 | # If this a Container Enabled role, provide the minimum Ansible Container version.
22 | # min_ansible_container_version:
23 |
24 | # Optionally specify the branch Galaxy will use when accessing the GitHub
25 | # repo for this role. During role install, if no tags are available,
26 | # Galaxy will use this branch. During import Galaxy will access files on
27 | # this branch. If Travis integration is configured, only notifications for this
28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch
29 | # (usually master) will be used.
30 | #github_branch:
31 |
32 | #
33 | # platforms is a list of platforms, and each platform has a name and a list of versions.
34 | #
35 | # platforms:
36 | # - name: Fedora
37 | # versions:
38 | # - all
39 | # - 25
40 | # - name: SomePlatform
41 | # versions:
42 | # - all
43 | # - 1.0
44 | # - 7
45 | # - 99.99
46 |
47 | galaxy_tags: []
48 | # List tags for your role here, one per line. A tag is a keyword that describes
49 | # and categorizes the role. Users find roles by searching for tags. Be sure to
50 | # remove the '[]' above, if you add tags to this list.
51 | #
52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters.
53 | # Maximum 20 tags per role.
54 |
55 | dependencies: []
56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above,
57 | # if you add dependencies to this list.
58 |
--------------------------------------------------------------------------------
/roles/backend-gophish/files/gophish/docker-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | touch /var/log/gophish-log.log
4 |
5 | red=$(tput -T xterm-256color setaf 1)
6 | green=$(tput -T xterm-256color setaf 2)
7 | orange=$(tput -T xterm-256color setaf 3)
8 | blue=$(tput -T xterm-256color setaf 4)
9 | darkgreen=$(tput -T xterm-256color setaf 6)
10 | white=$(tput -T xterm-256color setaf 7)
11 | reset=$(tput -T xterm-256color sgr0)
12 |
13 | # Start the VPN (if applicable) and renew the certificate
14 | if [ -n ${VPN} ]; then
15 | mkdir -p /var/log/openvpn
16 | openvpn --config "/root/${VPN}" --log /var/log/openvpn/connection.log &
17 | sleep 4
18 |
19 | # Display the IP address
20 | IPADDR=$(ip addr show | grep "global tun0" | cut -f6 -d " ")
21 | cat <<-EOF
22 | ----------------
23 | ${blue}[*]${reset} Starting the VPN
24 | ${blue}[*]${reset} Connecting to: ${green}${RELAY_HOST}${reset} - Project ${green}${CODENAME}${reset}
25 | ${blue}[*]${reset} IP address of VPN interface: ${green}${IPADDR}${reset}
26 | ----------------
27 | EOF
28 | else
29 | ipaddr=$(ip addr show | grep "global eth0" | cut -f6 -d " ")
30 | cat <<-EOF
31 | --------------------------
32 | ${orange}[!]${reset} ${red}DOCKER STARTED WITHOUT VPN${reset}
33 | ${orange}[!]${reset} To change this, set the VPN_CONFIG variable in the .env file accordingly
34 | ${orange}[!]${reset} IP address: ${IPADDR}
35 | --------------------------
36 | EOF
37 | fi
38 |
39 | echo "Starting goPhish version ${GOPHISHVERSION}"
40 |
41 | export gf_add="python3 ${GOPHISHDIR}/gophish_api.py -H ${SMTP_HOST}"
42 |
43 | if [ -n ${SMTP_USER} ]; then
44 | gf_add="$gf_add -u ${SMTP_USER} -p ${SMTP_PASSWORD}"
45 | fi
46 |
47 | if [ -n ${SMTP_PORT} ]; then
48 | gf_add="$gf_add -P ${SMTP_PORT}"
49 | fi
50 |
51 | if [ -n ${SMTP_FROMS} ]; then
52 | gf_add="$gf_add -f ${SMTP_FROMS}"
53 | fi
54 |
55 | if [ -n ${GOPHISH_INITIAL_ADMIN_API_TOKEN} ]; then
56 | gf_add="$gf_add -k ${GOPHISH_INITIAL_ADMIN_API_TOKEN}"
57 | fi
58 |
59 | echo "Create users command: $gf_add"
60 | bash -c "sleep 10 ; $gf_add" &>/dev/null &
61 |
62 | exec sh -c "cd ${GOPHISHDIR} && ./gophish"
63 |
--------------------------------------------------------------------------------
/roles/backend-osint/tasks/osint-setup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "SpiderFoot: Clone the git repository"
3 | ansible.builtin.git:
4 | repo: 'https://github.com/smicallef/spiderfoot.git'
5 | dest: "{{ toolkit_directory }}/spiderfoot"
6 |
7 | - name: "SpiderFoot: Build Docker image (This can take 5+ Minutes, do not despair)"
8 | become: yes
9 | community.docker.docker_image:
10 | build:
11 | dockerfile: Dockerfile
12 | path: "{{ toolkit_directory }}/spiderfoot"
13 | rm: yes
14 | debug: yes
15 | name: spiderfoot
16 | push: no
17 | source: build
18 | state: present
19 | tag: latest
20 |
21 | - name: "SpiderFoot: Create Docker volume for storing data"
22 | become: yes
23 | community.docker.docker_volume:
24 | volume_name: spiderfoot
25 | state: present
26 | recreate: never
27 |
28 | - name: "SpiderFoot: Start Docker container"
29 | become: yes
30 | community.docker.docker_container:
31 | container_default_behavior: no_defaults
32 | # TODO: Set a limit on CPU and memory to prevent max resources
33 | #cpu_period:
34 | #cpu_quota:
35 | #cpu_shares:
36 | cpus: 0.75
37 | #cpuset_cpus:
38 | #cpuset_mems:
39 | debug: yes
40 | hostname: spiderfoot
41 | image: spiderfoot:latest
42 | log_driver: journald
43 | memory: 2G # out of 4G
44 | name: spiderfoot
45 | network_mode: bridge
46 | output_logs: yes
47 | privileged: no
48 | published_ports:
49 | - 127.0.0.1:5001:5001
50 | pull: no # Image should be built locally and will not be pushed
51 | restart_policy: unless-stopped
52 | state: started
53 | volumes:
54 | # Store userdata in a named volume defined above
55 | # Required to be able to save spiderfoot.db in case of recreated container
56 | - "spiderfoot:/home/spiderfoot"
57 |
58 | - name: "SpiderFoot: Create symbolic link to Docker volume in {{ toolkit_directory }}/spiderfoot-userdata"
59 | become: yes
60 | ansible.builtin.file:
61 | src: "/var/lib/docker/volumes/spiderfoot/_data"
62 | dest: "{{ toolkit_directory }}/spiderfoot-userdata"
63 | state: link
64 |
--------------------------------------------------------------------------------
/roles/backend-cobalt-strike/files/docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 |
3 | services:
4 | teamserver:
5 | build:
6 | context: .
7 | args:
8 | LICENSE_KEY: ${CS_LICENSE}
9 | image: cobaltstrike:latest
10 | container_name: teamserver
11 | hostname: teamserver
12 | environment:
13 | - PASSWORD=${TEAMSERVER_PASSWORD}
14 | - RELAYVPS=${RELAY_HOST}
15 | - VPN=${VPN_CONFIG}
16 | - LICENSE=${CS_LICENSE}
17 | - C2_PROFILE=${MALLEABLE_PROFILE}
18 | - CODENAME=${CODE_NAME}
19 | ports:
20 | - "50050:50050"
21 | - "51000-51020:51000-51020"
22 | - "22"
23 | - "53"
24 | - "80"
25 | - "135"
26 | - "138"
27 | - "443"
28 | - "445"
29 | - "8080"
30 | - "8443"
31 | volumes:
32 | - profiles:/opt/cobaltstrike/c2-profiles
33 | - datamodel:/opt/cobaltstrike/data
34 | - logs:/opt/cobaltstrike/logs
35 | cap_add:
36 | - NET_ADMIN
37 | devices:
38 | - /dev/net/tun
39 | networks:
40 | cobaltstrike:
41 | restart: unless-stopped
42 | command: ["./teamserver", "${RELAY_HOST}", "${TEAMSERVER_PASSWORD}", "/opt/cobaltstrike/c2-profiles/${MALLEABLE_PROFILE}"]
43 | healthcheck: # Search for C382 (hex of 50050) in the listening ports list
44 | test: ["CMD", "grep", "C382", "/proc/net/tcp"]
45 | interval: 30s
46 | timeout: 1s
47 | retries: 15
48 | start_period: 30s
49 |
50 | bot:
51 | image: cobaltstrike:latest
52 | container_name: bot
53 | hostname: bot
54 | environment:
55 | - PASSWORD=${TEAMSERVER_PASSWORD}
56 | - LICENSE=${CS_LICENSE}
57 | - DISABLE_VPN=true
58 | volumes:
59 | - scripts:/opt/cobaltstrike/aggressor-scripts
60 | depends_on:
61 | - teamserver
62 | networks:
63 | cobaltstrike:
64 | restart: unless-stopped
65 | command: ["./agscript", "teamserver", "50050", "bot", "${TEAMSERVER_PASSWORD}", "/opt/cobaltstrike/aggressor-scripts/listener.service.cna"]
66 |
67 | volumes:
68 | scripts:
69 | profiles:
70 | datamodel:
71 | logs:
72 |
73 | networks:
74 | cobaltstrike:
75 | name: cobaltstrike
76 | driver: bridge
77 |
--------------------------------------------------------------------------------
/roles/backend-gophish/tasks/gophish-setup.yml:
--------------------------------------------------------------------------------
1 |
2 | - name: "GoPhish: Creating {{ gophish_docker_path }}{{ relay_host }}/ directory"
3 | become: yes
4 | ansible.builtin.file:
5 | path: "{{ gophish_docker_path }}{{ relay_host }}/"
6 | state: directory
7 | mode: 0755
8 |
9 | - name: "GoPhish: Copy the GoPhish docker container"
10 | become: yes
11 | ansible.builtin.copy:
12 | src: files/gophish/
13 | dest: "{{ gophish_docker_path }}{{ relay_host }}/"
14 |
15 | - name: "GoPhish selecting OVPN config based on pattern {{ gophish_ovpn_pattern }}"
16 | become: yes
17 | ansible.builtin.copy:
18 | src: "{{ openvpn_client_profile_path }}{{ relay_host }}/{{ gophish_ovpn_pattern }}"
19 | dest: "{{ gophish_docker_path }}{{ relay_host }}/gophish.ovpn"
20 | remote_src: yes
21 |
22 | - name: Add Codename
23 | ansible.builtin.lineinfile:
24 | dest: "{{ gophish_docker_path }}{{ relay_host }}/.env"
25 | regexp: "^CODENAME=.*$"
26 | line: "CODENAME={{ codename }}"
27 | backrefs: yes
28 | become: yes
29 |
30 | - name: "Creating Sender addresses"
31 | ansible.builtin.set_fact:
32 | from_addresses: "{{ send_from|map('regex_replace', '$', domain_name)|list|join(',') }}"
33 | changed_when: false # Done always, so no change notification
34 |
35 | - name: "Setting Remote addresses"
36 | ansible.builtin.lineinfile:
37 | dest: "{{ gophish_docker_path }}{{ relay_host }}/.env"
38 | regexp: "^RELAY_HOST=.*$"
39 | line: "RELAY_HOST={{ relay_host_ip }}"
40 | backrefs: yes
41 | become: yes
42 |
43 | - name: "GoPhish setting environment variables"
44 | become: yes
45 | ansible.builtin.lineinfile:
46 | dest: "{{ gophish_docker_path }}{{ relay_host }}/.env"
47 | regexp: "^{{ item.key }}=.*$"
48 | line: "{{ item.key }}={{ item.value }}"
49 | backrefs: yes
50 | with_dict: "{{ gophish_config }}"
51 |
52 | - name: "GoPhish Building image (This can take 5+ minutes, do not despair)"
53 | become: yes
54 | ansible.builtin.shell: "cd {{ gophish_docker_path }}{{ relay_host }}/; docker-compose build"
55 |
56 | - name: "GoPhish Starting GoPhish"
57 | become: yes
58 | ansible.builtin.shell: "cd {{ gophish_docker_path }}{{ relay_host }}/; docker-compose up --detach"
59 |
--------------------------------------------------------------------------------
/roles/relay-phishing/tasks/os-prep.yml:
--------------------------------------------------------------------------------
1 | #- name: Ensure localisation files for '{{ config_system_locale }}' are available
2 | # locale_gen:
3 | # name: "{{ config_system_locale }}"
4 | # state: present
5 | #
6 | #- name: Ensure language files for '{{ config_system_language }}' are available
7 | # locale_gen:
8 | # name: "{{ config_system_language }}"
9 | # state: present
10 | #
11 | #- name: Get current locale and language configuration
12 | # command: localectl status
13 | # register: locale_status
14 | # changed_when: false
15 | #
16 | #- name: Parse 'LANG' from current locale and language configuration
17 | # set_fact:
18 | # locale_lang: "{{ locale_status.stdout | regex_search('LANG=([^\n]+)', '\\1') | first }}"
19 | #
20 | #- name: Parse 'LANGUAGE' from current locale and language configuration
21 | # set_fact:
22 | # locale_language: "{{ locale_status.stdout | regex_search('LANGUAGE=([^\n]+)', '\\1') | default([locale_lang], true) | first }}"
23 | #
24 | #- name: Configure locale to '{{ config_system_locale }}' and language to '{{ config_system_language }}'
25 | # become: yes
26 | # command: localectl set-locale LANG={{ config_system_locale }} LANGUAGE={{ config_system_language }}
27 | # changed_when: locale_lang != config_system_locale or locale_language != config_system_language
28 |
29 | - name: "APT: Uninstall exim if present on the system"
30 | become: yes
31 | ansible.builtin.apt:
32 | name:
33 | - exim4
34 | - exim4-base
35 | - exim4-config
36 | - exim4-daemon-light
37 | state: absent
38 | update_cache: true
39 |
40 | - name: "APT: Install Git and mailserver packages"
41 | become: yes
42 | ansible.builtin.apt:
43 | name:
44 | - python3
45 | - git
46 | - dovecot-imapd
47 | - dovecot-lmtpd
48 | - postfix
49 | - postgrey
50 | - postfix-policyd-spf-python
51 | - opendkim
52 | - opendkim-tools
53 | - opendmarc
54 | - mailutils
55 | - certbot
56 | state: present
57 | update_cache: true
58 |
59 | - name: "Hostname: Add {{ domain_name }} to /etc/hosts"
60 | become: yes
61 | ansible.builtin.lineinfile:
62 | dest: /etc/hosts
63 | line: "127.0.0.1 localhost {{ domain_name }}"
64 | state: present
65 |
--------------------------------------------------------------------------------
/roles/backend-webcatcher/files/web-catcher/src/logger.php:
--------------------------------------------------------------------------------
1 | $value)
28 | echo "$header: $value\r\n";
29 |
30 | // If you wanted raw request data vs. parsed POST data:
31 | $postdata = file_get_contents('php://input');
32 | if(strlen($postdata)) echo $postdata."\r\n";
33 |
34 | // Post data / Cookies / Files
35 | if(count($_POST) || count($_COOKIE)) {
36 | ob_start();
37 |
38 | echo "POST\n";
39 | var_dump($_POST);
40 |
41 | echo "COOKIES\n";
42 | var_dump($_COOKIE);
43 |
44 | echo "FILES\n";
45 | var_dump($_FILES);
46 |
47 | $postdata = ob_get_clean();
48 | echo str_replace("\n","\r\n",$postdata);
49 | }
50 | echo "\r\n";
51 |
52 | // usage of random character string discourages guessing
53 | // the url if the directory is web-accessible; but, if at
54 | // all possible, make it inaccessible:
55 | file_put_contents('/var/log/requests.log',ob_get_clean(),FILE_APPEND);
56 | }
57 |
58 | // then, a simple maintenance page:
59 | ?>
60 |
61 |
62 |
63 | Some Title
64 |
65 |
66 |
67 | Add some styling here
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/roles/backend-common/tasks/backend-hardening.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Allow all inbound loopback traffic"
3 | become: yes
4 | ansible.builtin.iptables:
5 | action: append
6 | chain: INPUT
7 | in_interface: lo
8 | jump: ACCEPT
9 | wait: 60
10 | notify: saveiptables
11 | register: iptables_action
12 | retries: 10
13 | until: iptables_action is success or 'Another app is currently holding the xtables lock' not in iptables_action.msg
14 |
15 | - name: "Allow already established inbound connections"
16 | become: yes
17 | ansible.builtin.iptables:
18 | chain: INPUT
19 | ctstate: ESTABLISHED,RELATED
20 | jump: ACCEPT
21 | wait: 60
22 | notify: saveiptables
23 | register: iptables_action
24 | retries: 10
25 | until: iptables_action is success or 'Another app is currently holding the xtables lock' not in iptables_action.msg
26 |
27 | - name: "Allow inbound ICMP traffic"
28 | become: yes
29 | ansible.builtin.iptables:
30 | chain: INPUT
31 | jump: ACCEPT
32 | protocol: icmp
33 | wait: 60
34 | notify: saveiptables
35 | register: iptables_action
36 | retries: 10
37 | until: iptables_action is success or 'Another app is currently holding the xtables lock' not in iptables_action.msg
38 |
39 | - name: "Allow inbound SSH traffic from Company IP space"
40 | become: yes
41 | ansible.builtin.iptables:
42 | chain: INPUT
43 | destination_port: "22"
44 | jump: ACCEPT
45 | protocol: tcp
46 | source: "{{ item }}"
47 | wait: 60
48 | with_items: "{{ company_ip_space }}"
49 | notify: saveiptables
50 | register: iptables_action
51 | retries: 10
52 | until: iptables_action is success or 'Another app is currently holding the xtables lock' not in iptables_action.msg
53 |
54 | - name: "Drop all other inbound traffic"
55 | become: yes
56 | ansible.builtin.iptables:
57 | chain: INPUT
58 | policy: DROP
59 | wait: 60
60 | notify: saveiptables
61 | register: iptables_action
62 | retries: 10
63 | until: iptables_action is success or 'Another app is currently holding the xtables lock' not in iptables_action.msg
64 |
65 | - name: "Deploy SSH server configuration from template"
66 | become: yes
67 | ansible.builtin.template:
68 | src: sshd_config.j2
69 | dest: /etc/ssh/sshd_config
70 | notify:
71 | - restart sshd
72 |
--------------------------------------------------------------------------------
/roles/common-tasks/ovpn-create-client.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "OpenVPN clients: Check if ovpn configuration exists in {{ ovpn_config_directory }}"
3 | ansible.builtin.stat:
4 | path: "{{ ovpn_config_directory }}/config.conf"
5 | register: ovpn_config
6 |
7 | - name: "Missing OpenVPN configuration. Can't create client profiles. Exiting."
8 | block:
9 | - ansible.builtin.debug:
10 | msg: "Missing OpenVPN configuration. Can't create client profiles. Exiting."
11 |
12 | - meta: end_play
13 | when: ovpn_config.stat.exists == False
14 |
15 | - name: "OpenVPN clients: Check to see if client profiles already exist in {{ ovpn_config_directory }}/clients/"
16 | ansible.builtin.stat:
17 | path: "{{ ovpn_config_directory }}/clients/{{ item }}.visz"
18 | register: existing_client_profiles
19 | with_items: "{{ client_profiles }}"
20 | ignore_errors: True
21 |
22 | - name: "OpenVPN clients: Create ovpn client profile"
23 | become: yes
24 | ansible.builtin.expect:
25 | command: "openvpn-generate client --path {{ ovpn_config_directory }}"
26 | responses:
27 | "Common Name": "{{ item.item }}"
28 | echo: yes
29 | with_items: "{{ existing_client_profiles.results }}"
30 | when: item.stat.exists == False
31 |
32 | - name: "OpenVPN clients: Convert the client profiles from visc to ovpn"
33 | become: yes
34 | ansible.builtin.command: "python3 {{ ovpn_config_directory }}/python3-convert-visc-to-ovpn.py {{ ovpn_config_directory }}/clients/{{ item.item }}.visz"
35 | with_items: "{{ existing_client_profiles.results }}"
36 | when: item.stat.exists == False
37 |
38 | - name: Create local OpenVPN client profiles directory
39 | ansible.builtin.file:
40 | path: "{{ export_path }}/client_profiles/{{ inventory_hostname }}/"
41 | state: directory
42 | recurse: yes
43 | delegate_to: localhost
44 |
45 | - name: Map remote client profiles
46 | ansible.builtin.find:
47 | paths: "{{ ovpn_config_directory }}/clients/"
48 | recurse: no
49 | patterns: "*.ovpn"
50 | register: ovpn_client_files
51 |
52 | - name: "Download the client profiles to {{ export_path }} directory"
53 | ansible.builtin.fetch:
54 | src: "{{ item.path }}"
55 | dest: "{{ export_path }}/client_profiles/{{ inventory_hostname }}/"
56 | flat: yes
57 | with_items: "{{ ovpn_client_files.files }}"
58 |
--------------------------------------------------------------------------------
/tools/util/templates/configuration.yml:
--------------------------------------------------------------------------------
1 | ---
2 | # #############################################################################
3 | # IP Space Configuration
4 | # #############################################################################
5 |
6 | # IP Space for allowing incoming SSH connections to the deployed infrastructure
7 | # Please set this to for example your companies outbound IP ranges
8 | \VAR{company_ip_space}
9 |
10 |
11 | # IP Space for your private C2 machines. These ranges determine what systems
12 | # are allowed to use the redirectors.
13 | \VAR{company_c2_space}
14 |
15 | # #############################################################################
16 | # Credentials
17 | # #############################################################################
18 |
19 | # User / ssh-key for all hosts
20 | # Note that if you need to configure individual keys
21 | # please do so in the hosts file for the respective hosts
22 | \VAR{ansible_user}
23 |
24 | # #############################################################################
25 | # RT Campaign
26 | # #############################################################################
27 |
28 | # The Codename for this RT assessment
29 | \VAR{codename}
30 |
31 | # Return emails for phishing campaigns will be forwarded to these addresses
32 | \VAR{phishing_reply_forwards} # space separated
33 |
34 | # Directory where RT tooling and docker-containers are stored
35 | # Usually no need to change this
36 | \VAR{toolkit_directory}
37 |
38 | # Address needed for Certbot to request Let's Encrypt certificates
39 | \VAR{certbot_mail_address}
40 |
41 | # #############################################################################
42 | # User accounts fot the RT backend servers.
43 | # #############################################################################
44 |
45 | # Please configure these values for your teammembers
46 | \VAR{users}
47 |
48 | # #############################################################################
49 | # Other configurations (Usually no change is needed here)
50 | # #############################################################################
51 |
52 | # Locales to use within the infrastructure
53 | \VAR{config_system_locale}
54 | \VAR{config_system_language}
55 |
56 | \VAR{ovpn_config_directory}
57 |
58 | \VAR{ansible_python_interpreter}
59 |
60 | \VAR{readme_location}
61 |
62 | # The internal IP space created by the openvpn servers on your redirectors
63 | # Usually there is no need to change this
64 | \VAR{internal_vpn_ip_space}
65 | \VAR{internal_vpn_ip_gateway}
66 |
67 |
--------------------------------------------------------------------------------
/roles/backend-cobalt-strike/tasks/cobalt-strike-setup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: "Cobalt Strike: Copy Docker build context"
3 | become: yes
4 | ansible.builtin.copy:
5 | src: docker/
6 | dest: "{{ cs_docker_path }}/{{ relay_host }}"
7 | directory_mode: 0755
8 |
9 | - name: "Cobalt Strike: Select OpenVPN configuration based on pattern {{ cs_ovpn_pattern }}"
10 | become: yes
11 | ansible.builtin.copy:
12 | src: "{{ openvpn_client_profile_path }}/{{ relay_host }}/{{ cs_ovpn_pattern }}"
13 | dest: "{{ cs_docker_path }}/{{ relay_host }}/cobaltstrike.ovpn"
14 | remote_src: yes
15 |
16 | - name: "Cobalt Strike: Copy .env file for docker-compose"
17 | become: yes
18 | ansible.builtin.template:
19 | src: environment.j2
20 | dest: "{{ cs_docker_path }}/{{ relay_host }}/.env"
21 |
22 | - name: "Cobalt Strike: Create Malleable C2 profiles folder"
23 | become: yes
24 | ansible.builtin.file:
25 | path: "{{ cs_docker_path }}/{{ relay_host }}/c2-profiles/"
26 | state: directory
27 | mode: '0755'
28 |
29 | - name: "Cobalt Strike: Initialising cobaltstrike variables"
30 | become: yes
31 | set_fact:
32 | c2_tcp_port: "{{ 65535 | random(1024) }}"
33 | run_once: yes
34 |
35 | - name: "Cobalt Strike: Create aggressor scripts folder"
36 | become: yes
37 | ansible.builtin.file:
38 | path: "{{ cs_docker_path }}/{{ relay_host }}/aggressor-scripts/"
39 | state: directory
40 | mode: '0755'
41 |
42 | - name: "Cobalt Strike: Install Malleable C2 profile {{ malleable_profile }}"
43 | become: yes
44 | ansible.builtin.template:
45 | src: "{{ malleable_profile }}.j2"
46 | dest: "{{ cs_docker_path }}/{{ relay_host }}/c2-profiles/{{ malleable_profile }}"
47 | mode: 0755
48 | force: yes
49 |
50 | - name: "Cobalt Strike: Install default HTTPS listener"
51 | become: yes
52 | ansible.builtin.template:
53 | src: listener.service.cna.j2
54 | dest: "{{ cs_docker_path }}/{{ relay_host }}/aggressor-scripts/listener.service.cna"
55 | mode: 0755
56 | force: yes
57 |
58 | - name: "Cobalt Strike: Build Docker image and create container (This can take 5+ minutes, do not depair)"
59 | become: yes
60 | community.docker.docker_compose:
61 | build: yes
62 | debug: yes
63 | files:
64 | - docker-compose.yml
65 | project_src: "{{ cs_docker_path }}/{{ relay_host }}"
66 | pull: no
67 | recreate: smart
68 | state: present
69 | stopped: no
70 | timeout: 10
71 | register: result
72 | failed_when: "not result.services.teamserver.teamserver.state.running"
73 | changed_when: "result.actions in ['restart', 'create']"
74 |
75 | - name: "Cobalt Strike: Create symbolic link to Docker volumes in {{ toolkit_directory }}"
76 | become: yes
77 | ansible.builtin.file:
78 | src: "/var/lib/docker/volumes"
79 | dest: "{{ toolkit_directory }}/docker-volumes"
80 | state: link
81 |
--------------------------------------------------------------------------------
/roles/backend-common/tasks/full-system-logging.yml:
--------------------------------------------------------------------------------
1 |
2 | - name: "Logging: Copy the informative text for the bash shell to /tmp/bash_config"
3 | become: yes
4 | ansible.builtin.copy:
5 | src: bash_config
6 | dest: /tmp/bash_config
7 |
8 | - name: "Logging: Check if /etc/rsyslog.d/bash.conf exists"
9 | ansible.builtin.stat:
10 | path: /etc/rsyslog.d/bash.conf
11 | register: bash_conf
12 |
13 | - name: "Logging: Create empty /etc/rsyslog.d/bash.conf if it does not yet exist"
14 | become: yes
15 | ansible.builtin.file:
16 | path: /etc/rsyslog.d/bash.conf
17 | state: touch
18 | owner: root
19 | group: root
20 | mode: 0644
21 | when: bash_conf.stat.exists == 0
22 |
23 | - name: "Logging: Add local6 logging to /etc/rsyslog.d/bash.conf configuration"
24 | become: yes
25 | ansible.builtin.lineinfile:
26 | dest: "/etc/rsyslog.d/bash.conf"
27 | line: "local6.* /var/log/commands/commands.log"
28 | state: present
29 |
30 | - name: "Logging: Add command logging to the /etc/logrotate.d/rsyslog"
31 | become: yes
32 | ansible.builtin.lineinfile:
33 | dest: "/etc/logrotate.d/rsyslog"
34 | line: "/var/log/commands/commands.log"
35 | state: present
36 |
37 | - name: "Logging: Restart rsyslog"
38 | become: yes
39 | ansible.builtin.service:
40 | name: rsyslog
41 | state: restarted
42 | changed_when: false # Always restart just to be sure. No need to mention changed message
43 |
44 | - name: "Logging: Create asciinema log directory /var/log/ascii"
45 | become: yes
46 | ansible.builtin.file:
47 | path: /var/log/ascii
48 | owner: root
49 | group: rtspec
50 | mode: 0773
51 | state: directory
52 |
53 | - name: "Logging: Write informative text to /etc/bash.bashrc"
54 | become: yes
55 | ansible.builtin.blockinfile:
56 | path: /etc/bash.bashrc
57 | block: "{{ lookup('file', 'bash_config') }}"
58 | create: yes
59 | state: present
60 |
61 | - name: "Logging: Create the /var/log/tcpdump directory"
62 | become: yes
63 | ansible.builtin.file:
64 | path: /var/log/tcpdump
65 | state: directory
66 | owner: root
67 | group: rtspec
68 | mode: 0775
69 |
70 | - name: "Logging: Add AppArmor in complain mode to circumvent tcpdump permission issues"
71 | become: yes
72 | ansible.builtin.command: aa-complain /sbin/tcpdump
73 | changed_when: false # Done always, so no change notification
74 |
75 | - name: "Logging: Copy the tcpdumper script to /etc/systemd/system/tcpdumper.service"
76 | become: yes
77 | ansible.builtin.template:
78 | src: tcpdumper.j2
79 | dest: /etc/systemd/system/tcpdumper.service
80 |
81 | - name: "Logging: Force systemd to reread configs"
82 | become: yes
83 | ansible.builtin.systemd:
84 | daemon_reload: yes
85 |
86 | - name: "Logging: Enable service for tcpdumper"
87 | ansible.builtin.systemd:
88 | name: tcpdumper
89 | enabled: yes
90 | state: started
91 | become: yes
92 |
93 |
--------------------------------------------------------------------------------
/roles/backend-common/tasks/os-prep.yml:
--------------------------------------------------------------------------------
1 | - name: "Change Ubuntu mirror because of hash mismatches on ESXi"
2 | become: yes
3 | ansible.builtin.replace:
4 | path: /etc/apt/sources.list
5 | regexp: 'http://[^\s]*archive\.ubuntu\.com/ubuntu'
6 | replace: 'http://archive.ubuntu.com/ubuntu'
7 |
8 | - name: "Change Ubuntu mirror because of hash mismatches on ESXi"
9 | become: true
10 | replace:
11 | path: /etc/apt/sources.list
12 | regexp: 'http://[^\s]*archive\.ubuntu\.com/ubuntu'
13 | replace: 'http://archive.ubuntu.com/ubuntu'
14 |
15 |
16 | - name: "Install basic packages for backend server use"
17 | become: yes
18 | ansible.builtin.apt:
19 | name:
20 | - docker.io
21 | - docker-compose
22 | - python3
23 | - pwgen
24 | - vim
25 | - openvpn
26 | - iptables
27 | - iptables-persistent
28 | - apparmor-utils
29 | - tcpdump
30 | - python3-pexpect
31 | - python3-click
32 | - netcat
33 | - dnsutils
34 | - python3-pip
35 | - ipset
36 | - zip
37 | - screen
38 | - apparmor-utils
39 | - net-tools
40 | - curl
41 | - nano
42 | - bridge-utils
43 | - asciinema
44 | state: present
45 | update_cache: yes
46 | cache_valid_time: 0
47 | register: apt_action
48 | retries: 10
49 | until: apt_action is success or ('Failed to lock apt for exclusive operation' not in apt_action.msg and '/var/lib/dpkg/lock' not in apt_action.msg)
50 | # https://github.com/ansible/ansible/issues/51663#issuecomment-752286191
51 |
52 | - name: "Remove UFW so it can't interfere with our iptables rules"
53 | become: yes
54 | ansible.builtin.apt:
55 | name:
56 | - ufw
57 | state: absent
58 | register: apt_action
59 | retries: 10
60 | until: apt_action is success or ('Failed to lock apt for exclusive operation' not in apt_action.msg and '/var/lib/dpkg/lock' not in apt_action.msg)
61 | # https://github.com/ansible/ansible/issues/51663#issuecomment-752286191
62 |
63 | - name: "Autoremove unused packages"
64 | become: yes
65 | ansible.builtin.command:
66 | cmd: apt -y autoremove
67 | register: apt_result
68 | changed_when: "'packages will be REMOVED' in apt_result.stdout"
69 |
70 | - name: "Purge residual kernel packages"
71 | become: yes
72 | ansible.builtin.shell:
73 | cmd: apt remove -y --purge $(dpkg -l | grep "^rc\s*linux-image-" | awk '{print $2}' | tr '\n' ' ')
74 | register: apt_result
75 | changed_when: "'packages will be REMOVED' in apt_result.stdout"
76 |
77 | - name: "Set the system's hostname to {{ codename }}-{{ inventory_hostname }}"
78 | become: yes
79 | ansible.builtin.hostname:
80 | name: "{{ codename }}-{{ inventory_hostname }}"
81 |
82 | - name: "Add {{ codename }}-{{ inventory_hostname }} to /etc/hosts"
83 | become: yes
84 | ansible.builtin.lineinfile:
85 | dest: /etc/hosts
86 | regexp: '^127\.0\.0\.1[ \t]+localhost'
87 | line: "127.0.0.1 localhost {{ codename }}-{{ inventory_hostname }}"
88 | state: present
89 |
90 |
--------------------------------------------------------------------------------
/roles/backend-osint/templates/startvpn.j2:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | trap ctrl_c INT
4 | VPN_FILE=$1
5 | TUNNEL=tun0
6 |
7 | function check_running() {
8 | if [[ $(ip link | grep $TUNNEL | grep ",UP,") == *$TUNNEL* ]]; then
9 | echo "VPN Already up, exiting"
10 | exit 0
11 | fi
12 | }
13 |
14 | function check_permission() {
15 | if test " `id -u`" != " 0"
16 | then
17 | echo "permission denied (use sudo)"
18 | exit 1
19 | fi
20 | }
21 |
22 | function check_argument() {
23 | if [ -z $VPN_FILE ]
24 | then
25 | echo "First argument must be VPN file(.ovpn)"
26 | exit 1
27 | fi
28 | }
29 |
30 | function ensure_ssh_connectivity() {
31 | {% for companyip in company_ip_space %}
32 | ip route add {{ companyip }} via {{ ansible_default_ipv4.gateway }}
33 | {% endfor %}
34 | }
35 |
36 | function config() {
37 | #Get the default network interface
38 | echo "Detecting your default network interface..."
39 | INTERFACE=`ip addr | grep "state UP" | cut -d ":" -f 2 | head -n 1`
40 | echo "Using "$INTERFACE
41 |
42 | echo "Using interface "$TUNNEL " for VPN, change the script if you need another one."
43 |
44 | #Get the VPN IP, PORT and PROTOCOL from the VPN file
45 | echo "Detecting your VPN server address..."
46 | IP=`cat $VPN_FILE | grep "remote " | awk '{print $2}'`
47 | echo "Using IP "$IP
48 |
49 | echo "Detecting your VPN port..."
50 | PORT=`cat $VPN_FILE | grep "remote " | awk '{print $3}'`
51 | echo "Using port "$PORT
52 |
53 | echo "Detecting your VPN protocol..."
54 | PROTOCOL=`cat $VPN_FILE | grep "remote " | awk '{print $4}'`
55 | echo "Using protocol "$PROTOCOL
56 | }
57 |
58 | function ctrl_c() {
59 | echo "Flushing iptables and exiting"
60 | exit 1
61 | }
62 |
63 | function set_firewall_rules() {
64 | echo "Setting firewall rules"
65 | ##Allow connection with the VPN IP
66 | iptables -A OUTPUT -p $PROTOCOL -d $IP --dport $PORT -j ACCEPT
67 | iptables -A INPUT -p $PROTOCOL -s $IP --sport $PORT -j ACCEPT
68 | ##Allow connection through the tunnel
69 | iptables -A OUTPUT -o $TUNNEL -j ACCEPT
70 | iptables -A INPUT -i $TUNNEL -j ACCEPT
71 | ##Block all connection through the main interface, but allow outgoing for SSH
72 | {% for companyip in company_ip_space %}
73 | iptables -A OUTPUT -o $INTERFACE -j ACCEPT -d {{ companyip }}
74 | {% endfor %}
75 | iptables -A OUTPUT -o $INTERFACE -j DROP
76 | iptables -A INPUT -i $INTERFACE -j DROP
77 | }
78 |
79 | function reconnect() {
80 | echo "Reconnecting VPN"
81 | #Kill older openvpn processes to avoid creating new tunnels
82 | kill `ps -ef | grep "openvpn $VPN_FILE" | grep -v "grep" | awk '{print $2}'` > /dev/null 2>&1
83 | #Start openvpn
84 | /usr/sbin/openvpn $VPN_FILE &
85 | }
86 |
87 | #Check every 20 seconds if VPN goes down, then reconnect it
88 | function check_connection() {
89 | echo "Check VPN connection"
90 | while [ true ]
91 | do
92 | if [[ $(ip link | grep $TUNNEL | grep ",UP,") != *$TUNNEL* ]]; then
93 | reconnect
94 | fi
95 | sleep 20
96 | done
97 | }
98 |
99 | check_running
100 | check_permission
101 | check_argument
102 | ensure_ssh_connectivity
103 | config
104 | set_firewall_rules
105 | check_connection
106 |
--------------------------------------------------------------------------------
/roles/relay-common/tasks/os-prep.yml:
--------------------------------------------------------------------------------
1 | #- name: Ensure localisation files for '{{ config_system_locale }}' are available
2 | # locale_gen:
3 | # name: "{{ config_system_locale }}"
4 | # state: present
5 | #
6 | #- name: Ensure language files for '{{ config_system_language }}' are available
7 | # locale_gen:
8 | # name: "{{ config_system_language }}"
9 | # state: present
10 | #
11 | #- name: Get current locale and language configuration
12 | # command: localectl status
13 | # register: locale_status
14 | # changed_when: false
15 | #
16 | #- name: Parse 'LANG' from current locale and language configuration
17 | # set_fact:
18 | # locale_lang: "{{ locale_status.stdout | regex_search('LANG=([^\n]+)', '\\1') | first }}"
19 | #
20 | #- name: Parse 'LANGUAGE' from current locale and language configuration
21 | # set_fact:
22 | # locale_language: "{{ locale_status.stdout | regex_search('LANGUAGE=([^\n]+)', '\\1') | default([locale_lang], true) | first }}"
23 | #
24 | #- name: Configure locale to '{{ config_system_locale }}' and language to '{{ config_system_language }}'
25 | # become: yes
26 | # command: localectl set-locale LANG={{ config_system_locale }} LANGUAGE={{ config_system_language }}
27 | # changed_when: locale_lang != config_system_locale or locale_language != config_system_language
28 |
29 | - name: "Install packages for the basic function of the relay"
30 | become: yes
31 | ansible.builtin.apt:
32 | name:
33 | - python3
34 | - python3-click
35 | - python3-pexpect
36 | - python3-pip
37 | - pwgen
38 | - vim
39 | - iptables
40 | - iptables-persistent
41 | - tcpdump
42 | - netcat
43 | - dnsutils
44 | - ipset
45 | - net-tools
46 | - curl
47 | - nano
48 | - bridge-utils
49 | state: present
50 | update_cache: yes
51 | cache_valid_time: 3600
52 | register: apt_action
53 | retries: 10
54 | until: apt_action is success or ('Failed to lock apt for exclusive operation' not in apt_action.msg and '/var/lib/dpkg/lock' not in apt_action.msg)
55 | # https://github.com/ansible/ansible/issues/51663#issuecomment-752286191
56 |
57 | - name: "Remove ufw so it can't interfere with our iptables setup"
58 | become: yes
59 | ansible.builtin.apt:
60 | name:
61 | - ufw
62 | state: absent
63 |
64 | - name: "Autoremove unused packages"
65 | become: yes
66 | ansible.builtin.command:
67 | cmd: apt -y autoremove
68 | register: apt_result
69 | changed_when: "'packages will be REMOVED' in apt_result.stdout"
70 |
71 | - name: "Purge residual kernel packages"
72 | become: yes
73 | ansible.builtin.shell:
74 | cmd: apt remove -y --purge $(dpkg -l | grep "^rc\s*linux-image-" | awk '{print $2}' | tr '\n' ' ')
75 | register: apt_result
76 | changed_when: "'packages will be REMOVED' in apt_result.stdout"
77 |
78 | - name: "Set the hostname to {{ codename }}-{{ inventory_hostname }}"
79 | become: yes
80 | ansible.builtin.hostname:
81 | name: "{{ codename }}-{{ inventory_hostname }}"
82 |
83 | - name: "Add {{ codename }}-{{ inventory_hostname }} to /etc/hosts"
84 | become: yes
85 | ansible.builtin.lineinfile:
86 | dest: /etc/hosts
87 | line: "127.0.0.1 localhost {{ codename }}-{{ inventory_hostname }}"
88 | state: present
89 |
--------------------------------------------------------------------------------
/roles/relay-nginx/tasks/nginx-config.yml:
--------------------------------------------------------------------------------
1 | - name: "RELAY - NGINX: Disable NGINX Default Virtual Host"
2 | become: yes
3 | ansible.builtin.file:
4 | path: /etc/nginx/sites-enabled/default
5 | state: absent
6 | changed_when: false # Done always, so no change notification
7 |
8 | - name: "RELAY - NGINX: Copy NGINX template file (Also saving a backup)"
9 | become: yes
10 | ansible.builtin.template:
11 | src: "revproxy.j2"
12 | dest: "/etc/nginx/sites-available/{{nginx_relay_config_name}}.conf"
13 | force: no
14 |
15 | - name: "RELAY - NGINX: Add upstream proxy to nginx config"
16 | become: yes
17 | ansible.builtin.blockinfile:
18 | path: "/etc/nginx/sites-available/{{nginx_relay_config_name}}.conf"
19 | marker: "#### {mark} UPSTREAM FOR {{ relay_to_client_profile }} ####"
20 | insertafter: "#ANSIBLE_MARKER_UPSTREAM"
21 | block: |
22 | upstream backend-{{ relay_to_client_profile }} {
23 | zone backend 64k;
24 | # The backend address is supposed to be automatically updated with the
25 | # update-rev-proxy-to-{{ relay_to_client_profile }}.sh script running in a cronjob
26 | server 10.8.0.2:{{ backend_port }}; # {{ relay_to_client_profile }}
27 | }
28 |
29 | - name: "RELAY - NGINX: Add forward proxy rules for secret strings"
30 | become: yes
31 | ansible.builtin.blockinfile:
32 | path: "/etc/nginx/sites-available/{{nginx_relay_config_name}}.conf"
33 | marker: "#### {mark} FORWARD FOR {{ relay_to_client_profile }} ####"
34 | insertafter: "#ANSIBLE_MARKER_PROXY"
35 | block: |
36 | {% for secret in secret_strings %}
37 | location /{{secret.string}} {
38 | proxy_pass {{ secret.connection_method }}://backend-{{ relay_to_client_profile }}{{ secret.forward_path }};
39 | }
40 | {% endfor %}
41 |
42 | - name: "RELAY - NGINX: Copy script to dynamically update the target for reverse connections"
43 | become: yes
44 | ansible.builtin.template:
45 | src: "update-rev-proxy-ip.j2"
46 | dest: "{{ toolkit_directory }}/update-rev-proxy-to-{{ relay_to_client_profile }}.sh"
47 | mode: "0744"
48 |
49 | - name: "RELAY - NGINX: Add cronjob entry for dynamically updating IP address"
50 | become: yes
51 | ansible.builtin.cron:
52 | name: "Update Backend IP address"
53 | user: root
54 | minute: 0,3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48,51,54,57
55 | hour: "*"
56 | day: "*"
57 | month: "*"
58 | weekday: "*"
59 | job: "{{ toolkit_directory }}/update-rev-proxy-to-{{ relay_to_client_profile }}.sh"
60 | cron_file: "update-backend-ip-{{ relay_to_client_profile }}"
61 |
62 | - name: "RELAY - NGINX: Link NGINX Relayhost Reverse Proxy"
63 | become: yes
64 | ansible.builtin.file:
65 | src: /etc/nginx/sites-available/{{nginx_relay_config_name}}.conf
66 | dest: /etc/nginx/sites-enabled/{{nginx_relay_config_name}}.conf
67 | state: link
68 |
69 | - name: "RELAY - NGINX: Set Logrotate to keep logs for a year"
70 | become: yes
71 | ansible.builtin.copy:
72 | src: "nginx.logrotate"
73 | dest: "/etc/logrotate.d/nginx"
74 | force: yes
75 |
76 | - name: "RELAY - NGINX: Make Sure NGINX Service Is Running"
77 | become: yes
78 | ansible.builtin.service:
79 | name: nginx
80 | state: restarted
81 | enabled: yes
82 | changed_when: false # Done always, so no change notification
83 |
84 |
--------------------------------------------------------------------------------
/step-3-deploy-infra.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | orange=$(printf '\033[0;33m')
4 | green=$(printf '\033[0;32m')
5 | reset=$(printf '\033[0;00m')
6 | blue=$(printf '\033[0;34m')
7 | red=$(printf '\033[0;31m')
8 | yellow=$(printf '\033[0;93m')
9 | purple=$(printf '\033[0;95m')
10 | cyan=$(printf '\033[1;36m')
11 |
12 | bold=$(printf '\033[1m')
13 | underline=$(printf '\033[4m')
14 | reversed=$(printf '\033[7m')
15 |
16 | if [[ $# -eq 0 ]] ; then
17 | cat <<-EOF
18 | Usage: $0 []
19 |
20 | Tags are optional and allow you to deploy only certain components
21 | Run ./tools/list-tags.py to see all available tags
22 |
23 | WARNING: Tags assume that the "backend-common" or "relay-common" and "users"
24 | roles are already deployed to the server, if this is not the case
25 | add "backend-common" or "relay-common" and "users" as a tag
26 | EOF
27 | exit 1
28 | fi
29 |
30 | CONFIG_PATH="${PWD}/$1"
31 | HOST_FILE="${CONFIG_PATH}/hosts.yml"
32 | CONFIG_FILE="${CONFIG_PATH}/configuration.yml"
33 | EXPORT_PATH="${CONFIG_PATH}/Exports_Autogenerated/"
34 | VAULT_FILE="${CONFIG_PATH}/vaulted_vars.yml"
35 |
36 | echo
37 | echo "__________ .___ __ __.__ .___"
38 | echo "\______ \ ____ __| _/ / \ / \__|____________ _______ __| _/"
39 | echo " | _// __ \ / __ | \ \/\/ / \___ /\__ \\_ __ \/ __ |"
40 | echo " | | \ ___// /_/ | \ /| |/ / / __ \| | \/ /_/ |"
41 | echo " |____|_ /\___ >____ | \__/\ / |__/_____ \(____ /__| \____ |"
42 | echo " \/ \/ \/ \/ \/ \/ \/"
43 | echo
44 | echo " --- Deploying Infrastructure --- "
45 | echo
46 | echo
47 |
48 | cat <<-EOF
49 | Have you checked all requirements? If they are not met, this will fail.
50 | Check the requirements with ${green}python3 tools/check-prerequisites.py ${CONFIG_PATH}${reset}
51 | EOF
52 | read -p "Do you want to continue? [y/N] " -n 1 -r
53 | echo
54 |
55 | if [[ ! ${REPLY} =~ ^[Yy]$ ]]; then
56 | exit 1
57 | fi
58 |
59 | if [ ! -f "${HOST_FILE}" ]; then
60 | echo "Hosts file (hosts) does not exist in ${CONFIG_PATH}."
61 | exit 1
62 | fi
63 |
64 | if [ ! -f "${CONFIG_FILE}" ]; then
65 | echo "Config file (configuration.yml) file does not exist in ${CONFIG_PATH}."
66 | exit 1
67 | fi
68 |
69 | mkdir -p ${EXPORT_PATH}
70 |
71 | if [[ $# -eq 1 ]] ; then
72 | ansible-playbook -i "${HOST_FILE}" -e "@${CONFIG_FILE}" -e "@${VAULT_FILE}" --ask-vault-pass --extra-vars "export_path=${EXPORT_PATH} config_path=${CONFIG_PATH}" playbook.yml
73 | fi
74 |
75 | if [[ $# -eq 2 ]] ; then
76 | TAGS="$2"
77 | cat <<-EOF
78 |
79 | WARNING: Tags assume that the "backend-common" or "relay-common" and "users"
80 | roles are already deployed to the server, if this is not the case
81 | add "backend-common" or "relay-common" and "users" as a tag or your deployment
82 | will fail
83 |
84 | EOF
85 | ansible-playbook -i "${HOST_FILE}" -e "@${CONFIG_FILE}" -e "@${VAULT_FILE}" --tags "${TAGS}" --ask-vault-pass --extra-vars "export_path=${EXPORT_PATH} config_path=${CONFIG_PATH}" playbook.yml
86 | fi
87 | cat <<-EOF
88 | ------------------------------------- DONE -------------------------------------
89 | - Consider running ${green}./tools/create-ssh-config.py ${CONFIG_PATH}${reset} to create your SSH Config
90 | - Consider running ${green}./tools/diagram.py ${CONFIG_PATH}${reset} to create a diagram of your infra
91 | - Configure your DNS settings for phishing and CobaltStrike campaigns. Consider running ${green}cat ${CONFIG_PATH}/Exports_Autogenerated/dns_configs/*${reset}
92 | EOF
93 |
--------------------------------------------------------------------------------
/playbook.yml:
--------------------------------------------------------------------------------
1 | # RELAY CONFIGURATIONS
2 |
3 | - name: "***** CONFIGURING ALL RELAYS *****"
4 | hosts: relays
5 | gather_facts: yes
6 | roles:
7 | - relay-common
8 | tags:
9 | - relay
10 | - relay-common
11 |
12 | - name: "***** CONFIGURING USERS FOR RELAYS *****"
13 | hosts: relays
14 | gather_facts: yes
15 | roles:
16 | - users
17 | tags:
18 | - relay
19 | - users
20 | - users-relay
21 |
22 | - name: "***** CONFIGURING DROPBOX RELAYS *****"
23 | hosts: relays_dropbox
24 | gather_facts: yes
25 | roles:
26 | - relay-dropbox
27 | tags:
28 | - relay
29 | - relay-dropbox
30 | - dropbox
31 |
32 | - name: "***** CONFIGURING PHISHING RELAYS *****"
33 | hosts: relays_phishing
34 | gather_facts: yes
35 | roles:
36 | - relay-phishing
37 | tags:
38 | - relay
39 | - relay-phishing
40 | - phishing
41 |
42 | # The relays_nginx needs to have serial: 1 set so multiple nginx roles do not interfere
43 | - name: "***** CONFIGURING NGINX RELAYS *****"
44 | hosts: relays_nginx
45 | serial: 1
46 | gather_facts: yes
47 | roles:
48 | - relay-nginx
49 | tags:
50 | - relay
51 | - relay-nginx
52 | - nginx
53 | - stage-one
54 | - cobalt-strike
55 |
56 | # Configure the CobaltStrike DNS relays
57 | - name: "***** CONFIGURING Cobaltstrike RELAYS *****"
58 | hosts: relays_cobalt_strike
59 | gather_facts: yes
60 | roles:
61 | - relay-cobalt-strike
62 | tags:
63 | - relay
64 | - relay-cobalt-strike
65 | - cobalt-strike
66 |
67 | - name: "***** CONFIGURING OSINT RELAYS *****"
68 | hosts: relays_osint
69 | gather_facts: yes
70 | roles:
71 | - relay-osint
72 | tags:
73 | - relay
74 | - relay-osint
75 | - osint
76 |
77 | # BACKEND CONFIGURATIONS
78 |
79 | - name: "***** CONFIGURING BACKENDS *****"
80 | hosts: backends
81 | gather_facts: yes
82 | roles:
83 | - backend-common
84 | tags:
85 | - backend
86 | - backend-common
87 |
88 | - name: "***** CONFIGURING USERS FOR BACKENDS *****"
89 | hosts: backends
90 | gather_facts: yes
91 | roles:
92 | - users
93 | tags:
94 | - users
95 | - backend
96 | - users-backends
97 |
98 | - name: "***** CONFIGURING BACKEND GOPHISH *****"
99 | hosts: backends_gophish
100 | gather_facts: yes
101 | roles:
102 | - backend-gophish
103 | tags:
104 | - backend
105 | - backend-gophish
106 | - phishing
107 |
108 | - name: "***** CONFIGURING BACKEND MANUAL PHISH *****"
109 | hosts: backends_manual_phish
110 | gather_facts: yes
111 | roles:
112 | - backend-manual-phish
113 | tags:
114 | - backend
115 | - backend-manual-phish
116 | - phishing
117 |
118 | - name: "***** CONFIGURING BACKEND COBALT STRIKE *****"
119 | hosts: backends_cobalt_strike
120 | gather_facts: yes
121 | roles:
122 | - backend-cobalt-strike
123 | tags:
124 | - backend
125 | - backend-cobalt-strike
126 | - cobalt-strike
127 |
128 | - name: "***** CONFIGURING BACKEND DROPBOX *****"
129 | hosts: backends_dropbox
130 | gather_facts: yes
131 | roles:
132 | - backend-dropbox
133 | tags:
134 | - backend
135 | - backend-dropbox
136 | - dropbox
137 |
138 | - name: "***** CONFIGURING BACKEND OSINT *****"
139 | hosts: backends_osint
140 | gather_facts: yes
141 | roles:
142 | - backend-osint
143 | tags:
144 | - backend
145 | - backend-osint
146 | - osint
147 |
148 | - name: "***** CONFIGURING BACKEND WEB CATCHER *****"
149 | hosts: backends_web_catcher
150 | gather_facts: yes
151 | roles:
152 | - backend-webcatcher
153 | tags:
154 | - backend
155 | - backend-webcatcher
156 | - webcatcher
157 |
--------------------------------------------------------------------------------
/roles/backend-common/templates/sshd_config.j2:
--------------------------------------------------------------------------------
1 | # $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $
2 |
3 | # This is the sshd server system-wide configuration file. See
4 | # sshd_config(5) for more information.
5 |
6 | # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
7 |
8 | # The strategy used for options in the default sshd_config shipped with
9 | # OpenSSH is to specify options with their default value where
10 | # possible, but leave them commented. Uncommented options override the
11 | # default value.
12 |
13 | Include /etc/ssh/sshd_config.d/*.conf
14 |
15 | #Port 22
16 | #AddressFamily any
17 | #ListenAddress 0.0.0.0
18 | #ListenAddress ::
19 |
20 | #HostKey /etc/ssh/ssh_host_rsa_key
21 | #HostKey /etc/ssh/ssh_host_ecdsa_key
22 | #HostKey /etc/ssh/ssh_host_ed25519_key
23 |
24 | # Ciphers and keying
25 | #RekeyLimit default none
26 |
27 | # Logging
28 | #SyslogFacility AUTH
29 | #LogLevel INFO
30 |
31 | # Authentication:
32 |
33 | #LoginGraceTime 2m
34 | PermitRootLogin no
35 | #StrictModes yes
36 | #MaxAuthTries 6
37 | #MaxSessions 10
38 |
39 | #PubkeyAuthentication yes
40 |
41 | # Expect .ssh/authorized_keys2 to be disregarded by default in future.
42 | #AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
43 |
44 | #AuthorizedPrincipalsFile none
45 |
46 | #AuthorizedKeysCommand none
47 | #AuthorizedKeysCommandUser nobody
48 |
49 | # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
50 | #HostbasedAuthentication no
51 | # Change to yes if you don't trust ~/.ssh/known_hosts for
52 | # HostbasedAuthentication
53 | #IgnoreUserKnownHosts no
54 | # Don't read the user's ~/.rhosts and ~/.shosts files
55 | #IgnoreRhosts yes
56 |
57 | # To disable tunneled clear text passwords, change to no here!
58 | PasswordAuthentication no
59 | #PermitEmptyPasswords no
60 |
61 | # Change to yes to enable challenge-response passwords (beware issues with
62 | # some PAM modules and threads)
63 | ChallengeResponseAuthentication no
64 |
65 | # Kerberos options
66 | #KerberosAuthentication no
67 | #KerberosOrLocalPasswd yes
68 | #KerberosTicketCleanup yes
69 | #KerberosGetAFSToken no
70 |
71 | # GSSAPI options
72 | #GSSAPIAuthentication no
73 | #GSSAPICleanupCredentials yes
74 | #GSSAPIStrictAcceptorCheck yes
75 | #GSSAPIKeyExchange no
76 |
77 | # Set this to 'yes' to enable PAM authentication, account processing,
78 | # and session processing. If this is enabled, PAM authentication will
79 | # be allowed through the ChallengeResponseAuthentication and
80 | # PasswordAuthentication. Depending on your PAM configuration,
81 | # PAM authentication via ChallengeResponseAuthentication may bypass
82 | # the setting of "PermitRootLogin without-password".
83 | # If you just want the PAM account and session checks to run without
84 | # PAM authentication, then enable this but set PasswordAuthentication
85 | # and ChallengeResponseAuthentication to 'no'.
86 | UsePAM yes
87 |
88 | #AllowAgentForwarding yes
89 | #AllowTcpForwarding yes
90 | #GatewayPorts no
91 | X11Forwarding yes
92 | #X11DisplayOffset 10
93 | #X11UseLocalhost yes
94 | #PermitTTY yes
95 | PrintMotd no
96 | #PrintLastLog yes
97 | #TCPKeepAlive yes
98 | #PermitUserEnvironment no
99 | #Compression delayed
100 | #ClientAliveInterval 0
101 | #ClientAliveCountMax 3
102 | #UseDNS no
103 | #PidFile /var/run/sshd.pid
104 | #MaxStartups 10:30:100
105 | #PermitTunnel no
106 | #ChrootDirectory none
107 | #VersionAddendum none
108 |
109 | # no default banner path
110 | #Banner none
111 |
112 | # Allow client to pass locale environment variables
113 | AcceptEnv LANG LC_*
114 |
115 | # override default of no subsystems
116 | Subsystem sftp /usr/lib/openssh/sftp-server
117 |
118 | # Example of overriding settings on a per-user basis
119 | #Match User anoncvs
120 | # X11Forwarding no
121 | # AllowTcpForwarding no
122 | # PermitTTY no
123 | # ForceCommand cvs server
124 |
--------------------------------------------------------------------------------
/roles/relay-common/templates/sshd_config.j2:
--------------------------------------------------------------------------------
1 | # $OpenBSD: sshd_config,v 1.103 2018/04/09 20:41:22 tj Exp $
2 |
3 | # This is the sshd server system-wide configuration file. See
4 | # sshd_config(5) for more information.
5 |
6 | # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin
7 |
8 | # The strategy used for options in the default sshd_config shipped with
9 | # OpenSSH is to specify options with their default value where
10 | # possible, but leave them commented. Uncommented options override the
11 | # default value.
12 |
13 | Include /etc/ssh/sshd_config.d/*.conf
14 |
15 | #Port 22
16 | #AddressFamily any
17 | #ListenAddress 0.0.0.0
18 | #ListenAddress ::
19 |
20 | #HostKey /etc/ssh/ssh_host_rsa_key
21 | #HostKey /etc/ssh/ssh_host_ecdsa_key
22 | #HostKey /etc/ssh/ssh_host_ed25519_key
23 |
24 | # Ciphers and keying
25 | #RekeyLimit default none
26 |
27 | # Logging
28 | #SyslogFacility AUTH
29 | #LogLevel INFO
30 |
31 | # Authentication:
32 |
33 | #LoginGraceTime 2m
34 | PermitRootLogin no
35 | #StrictModes yes
36 | #MaxAuthTries 6
37 | #MaxSessions 10
38 |
39 | #PubkeyAuthentication yes
40 |
41 | # Expect .ssh/authorized_keys2 to be disregarded by default in future.
42 | #AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
43 |
44 | #AuthorizedPrincipalsFile none
45 |
46 | #AuthorizedKeysCommand none
47 | #AuthorizedKeysCommandUser nobody
48 |
49 | # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
50 | #HostbasedAuthentication no
51 | # Change to yes if you don't trust ~/.ssh/known_hosts for
52 | # HostbasedAuthentication
53 | #IgnoreUserKnownHosts no
54 | # Don't read the user's ~/.rhosts and ~/.shosts files
55 | #IgnoreRhosts yes
56 |
57 | # To disable tunneled clear text passwords, change to no here!
58 | PasswordAuthentication no
59 | #PermitEmptyPasswords no
60 |
61 | # Change to yes to enable challenge-response passwords (beware issues with
62 | # some PAM modules and threads)
63 | ChallengeResponseAuthentication no
64 |
65 | # Kerberos options
66 | #KerberosAuthentication no
67 | #KerberosOrLocalPasswd yes
68 | #KerberosTicketCleanup yes
69 | #KerberosGetAFSToken no
70 |
71 | # GSSAPI options
72 | #GSSAPIAuthentication no
73 | #GSSAPICleanupCredentials yes
74 | #GSSAPIStrictAcceptorCheck yes
75 | #GSSAPIKeyExchange no
76 |
77 | # Set this to 'yes' to enable PAM authentication, account processing,
78 | # and session processing. If this is enabled, PAM authentication will
79 | # be allowed through the ChallengeResponseAuthentication and
80 | # PasswordAuthentication. Depending on your PAM configuration,
81 | # PAM authentication via ChallengeResponseAuthentication may bypass
82 | # the setting of "PermitRootLogin without-password".
83 | # If you just want the PAM account and session checks to run without
84 | # PAM authentication, then enable this but set PasswordAuthentication
85 | # and ChallengeResponseAuthentication to 'no'.
86 | UsePAM yes
87 |
88 | #AllowAgentForwarding yes
89 | #AllowTcpForwarding yes
90 | #GatewayPorts no
91 | X11Forwarding yes
92 | #X11DisplayOffset 10
93 | #X11UseLocalhost yes
94 | #PermitTTY yes
95 | PrintMotd no
96 | #PrintLastLog yes
97 | #TCPKeepAlive yes
98 | #PermitUserEnvironment no
99 | #Compression delayed
100 | #ClientAliveInterval 0
101 | #ClientAliveCountMax 3
102 | #UseDNS no
103 | #PidFile /var/run/sshd.pid
104 | #MaxStartups 10:30:100
105 | #PermitTunnel no
106 | #ChrootDirectory none
107 | #VersionAddendum none
108 |
109 | # no default banner path
110 | #Banner none
111 |
112 | # Allow client to pass locale environment variables
113 | AcceptEnv LANG LC_*
114 |
115 | # override default of no subsystems
116 | Subsystem sftp /usr/lib/openssh/sftp-server
117 |
118 | # Example of overriding settings on a per-user basis
119 | #Match User anoncvs
120 | # X11Forwarding no
121 | # AllowTcpForwarding no
122 | # PermitTTY no
123 | # ForceCommand cvs server
124 |
--------------------------------------------------------------------------------
/roles/relay-common/tasks/ovpn-setup.yml:
--------------------------------------------------------------------------------
1 | - name: "OpenVPN Server: Create {{ ovpn_config_directory }} if it does not exist"
2 | become: yes
3 | ansible.builtin.file:
4 | path: "{{ ovpn_config_directory }}"
5 | state: directory
6 | mode: "0755"
7 |
8 | - name: "OpenVPN Server: Install the SparkLabs configuration generator"
9 | become: yes
10 | ansible.builtin.apt:
11 | deb: https://github.com/thesparklabs/openvpn-configuration-generator/releases/download/1.0.8/openvpn-configuration-generator_20.04_1.0.8.deb
12 | register: apt_action
13 | retries: 10
14 | until: apt_action is success or ('Failed to lock apt for exclusive operation' not in apt_action.msg and '/var/lib/dpkg/lock' not in apt_action.msg)
15 | # https://github.com/ansible/ansible/issues/51663#issuecomment-752286191
16 |
17 | - name: "OpenVPN Server: Copy visc to ovpn conversion script to {{ ovpn_config_directory }}/python3-convert-visc-to-ovpn.py"
18 | become: yes
19 | ansible.builtin.copy:
20 | src: python3-convert-visc-to-ovpn.py
21 | dest: "{{ ovpn_config_directory }}/python3-convert-visc-to-ovpn.py"
22 |
23 | - name: "OpenVPN Server: Install openvpn-server"
24 | become: yes
25 | ansible.builtin.apt:
26 | name: openvpn
27 | update_cache: yes
28 | cache_valid_time: 3600
29 | state: latest
30 | register: apt_action
31 | retries: 10
32 | until: apt_action is success or ('Failed to lock apt for exclusive operation' not in apt_action.msg and '/var/lib/dpkg/lock' not in apt_action.msg)
33 | # https://github.com/ansible/ansible/issues/51663#issuecomment-752286191
34 |
35 | - name: "OpenVPN Server: Check if ovpn configuration exists in {{ ovpn_config_directory }}"
36 | become: yes
37 | stat:
38 | path: "{{ ovpn_config_directory }}/config.conf"
39 | register: vpn_exists
40 |
41 | - name: "OpenVPN Server: Create ovpn profile (Generating keys take up to 240 seconds, since randomness is low on new installs)"
42 | become: yes
43 | ansible.builtin.expect:
44 | command: "openvpn-generate init --path {{ ovpn_config_directory }}"
45 | responses:
46 | (?i)Server address: "{{ ansible_host }}"
47 | (?i)Server Port: 1194
48 | (?i)Protocol: 1 # UDP
49 | (?i)Redirect all traffic through VPN: Y
50 | (?i)Please select an option: 6 # Don't use DNS for VPN connections
51 | (?i)Would you like to use anonymous defaults for certificate details: Y
52 | echo: yes
53 | timeout: 240
54 | when: vpn_exists.stat.exists == False
55 |
56 | - name: "OpenVPN Server: Delete default ovpn configuration"
57 | become: true
58 | file:
59 | path: "{{ item }}"
60 | state: absent
61 | with_items:
62 | - /etc/openvpn/config.conf
63 | - /etc/openvpn/server
64 | - /etc/openvpn/pki
65 | when: vpn_exists.stat.exists == False
66 |
67 | # Note that we do not make a symlink here, as this will cause the openvpn service to fail
68 | - name: "OpenVPN Server: Copy configuration file {{ ovpn_config_directory }}/server/server.conf to /etc/openvpn"
69 | become: yes
70 | ansible.builtin.copy:
71 | src: "{{ ovpn_config_directory }}/server/{{ item }}"
72 | dest: "/etc/openvpn/{{ item }}"
73 | owner: root
74 | group: root
75 | mode: "0600"
76 | remote_src: yes # The config is already on the target, just not in the right spot
77 | with_items:
78 | - ca.crt
79 | - dh.pem
80 | - server.conf
81 | - server.crt
82 | - server.key
83 |
84 | # TODO: Convert this into a handler that gets called when needed
85 | - name: "OpenVPN Server: Start the systemd service now and on (re)boot"
86 | become: yes
87 | ansible.builtin.systemd:
88 | name: openvpn@server
89 | enabled: yes
90 | state: started
91 |
92 | # Set ip forwarding on in /proc and in the sysctl file and reload if necessary
93 | - name: "Enable IP forwarding for relay functionality"
94 | become: yes
95 | ansible.builtin.sysctl:
96 | name: net.ipv4.ip_forward
97 | value: 1
98 | sysctl_set: yes
99 | state: present
100 | reload: yes
101 |
--------------------------------------------------------------------------------
/roles/relay-phishing/files/opendkim.conf:
--------------------------------------------------------------------------------
1 | #Shource: https://github.com/mantvydasb/Red-Team-Infrastructure-Automation/blob/master/configs/opendkim.conf
2 | # This is a basic configuration that can easily be adapted to suit a standard
3 | # installation. For more advanced options, see opendkim.conf(5) and/or
4 | # /usr/share/doc/opendkim/examples/opendkim.conf.sample.
5 |
6 | # Log to syslog
7 | Syslog yes
8 | # Required to use local socket with MTAs that access the socket as a non-
9 | # privileged user (e.g. Postfix)
10 | UMask 007
11 |
12 | # Sign for example.com with key in /etc/dkimkeys/dkim.key using
13 | # selector '2007' (e.g. 2007._domainkey.example.com)
14 | #Domain example.com
15 | #KeyFile /etc/dkimkeys/dkim.key
16 | #Selector 2007
17 |
18 | # Commonly-used options; the commented-out versions show the defaults.
19 | #Canonicalization simple
20 | #Mode sv
21 | #SubDomains no
22 |
23 | # Socket smtp://localhost
24 | #
25 | # ## Socket socketspec
26 | # ##
27 | # ## Names the socket where this filter should listen for milter connections
28 | # ## from the MTA. Required. Should be in one of these forms:
29 | # ##
30 | # ## inet:port@address to listen on a specific interface
31 | # ## inet:port to listen on all interfaces
32 | # ## local:/path/to/socket to listen on a UNIX domain socket
33 | #
34 | #Socket inet:8892@localhost
35 | Socket local:/var/run/opendkim/opendkim.sock
36 |
37 | ## PidFile filename
38 | ### default (none)
39 | ###
40 | ### Name of the file where the filter should write its pid before beginning
41 | ### normal operations.
42 | #
43 | PidFile /var/run/opendkim/opendkim.pid
44 |
45 |
46 | # Always oversign From (sign using actual From and a null From to prevent
47 | # malicious signatures header fields (From and/or others) between the signer
48 | # and the verifier. From is oversigned by default in the Debian pacakge
49 | # because it is often the identity key used by reputation systems and thus
50 | # somewhat security sensitive.
51 | OversignHeaders From
52 |
53 | ## ResolverConfiguration filename
54 | ## default (none)
55 | ##
56 | ## Specifies a configuration file to be passed to the Unbound library that
57 | ## performs DNS queries applying the DNSSEC protocol. See the Unbound
58 | ## documentation at http://unbound.net for the expected content of this file.
59 | ## The results of using this and the TrustAnchorFile setting at the same
60 | ## time are undefined.
61 | ## In Debian, /etc/unbound/unbound.conf is shipped as part of the Suggested
62 | ## unbound package
63 |
64 | # ResolverConfiguration /etc/unbound/unbound.conf
65 |
66 | ## TrustAnchorFile filename
67 | ## default (none)
68 | ##
69 | ## Specifies a file from which trust anchor data should be read when doing
70 | ## DNS queries and applying the DNSSEC protocol. See the Unbound documentation
71 | ## at http://unbound.net for the expected format of this file.
72 |
73 | TrustAnchorFile /usr/share/dns/root.key
74 |
75 | ## Userid userid
76 | ### default (none)
77 | ###
78 | ### Change to user "userid" before starting normal operation? May include
79 | ### a group ID as well, separated from the userid by a colon.
80 | #
81 | UserID opendkim
82 |
83 | AutoRestart Yes
84 | AutoRestartRate 10/1h
85 | UMask 002
86 | Syslog yes
87 | SyslogSuccess Yes
88 | LogWhy Yes
89 |
90 | Canonicalization relaxed/simple
91 |
92 | ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
93 | InternalHosts refile:/etc/opendkim/TrustedHosts
94 | KeyTable refile:/etc/opendkim/KeyTable
95 | SigningTable refile:/etc/opendkim/SigningTable
96 |
97 | Mode sv
98 | PidFile /var/run/opendkim/opendkim.pid
99 | SignatureAlgorithm rsa-sha256
100 |
101 | UserID opendkim:opendkim
102 |
103 | Socket inet:12345@localhost
104 |
--------------------------------------------------------------------------------