--- - name: Set hostname to logger hostname: name: logger become: yes - name: Create a directory if it does not exist file: path: /vagrant state: directory mode: '0755' become: yes - name: Copy vagrant resources synchronize: src: ../../Vagrant/resources dest: /vagrant/ become: yes - name: Copy bootstrap file copy: src: ../../Vagrant/bootstrap.sh dest: /home/vagrant owner: vagrant group: vagrant mode: u+x,g+x - name: Copy netplan file copy: src: ../resources/01-netcfg.yaml dest: /tmp owner: vagrant group: vagrant - name: Copy the netplan file over as root shell: "mv /tmp/01-netcfg.yaml /etc/netplan/01-netcfg.yaml" become: yes - name: Apply the new netplan shell: netplan apply become: yes - name: Add APT Repositories apt_repository: repo: "{{ item }}" with_items: - "ppa:apt-fast/stable" - "ppa:rmescandon/yq" - "ppa:oisf/suricata-stable" become: yes - name: Install apt-fast become: yes apt: name: apt-fast update_cache: yes - name: Install tools via apt-fast args: executable: /bin/bash become: yes shell: | echo "[$(date +%H:%M:%S)]: Running apt-fast install..." apt-fast -qq install -y jq whois build-essential git docker docker-compose unzip htop yq register: apt_install_prerequisites failed_when: "'error' in apt_install_prerequisites.stderr" - name: Update the MOTD args: executable: /bin/bash become: yes shell: | echo "[$(date +%H:%M:%S)]: Updating the MOTD..." # Force color terminal sed -i 's/#force_color_prompt=yes/force_color_prompt=yes/g' /root/.bashrc sed -i 's/#force_color_prompt=yes/force_color_prompt=yes/g' /home/vagrant/.bashrc # Remove some stock Ubuntu MOTD content chmod -x /etc/update-motd.d/10-help-text # Copy the DetectionLab MOTD cp /vagrant/resources/logger/20-detectionlab /etc/update-motd.d/ chmod +x /etc/update-motd.d/20-detectionlab register: modify_motd failed_when: "'error' in modify_motd.stderr" - name: Test Prerequisites args: executable: /bin/bash become: yes shell: | for package in jq whois build-essential git docker docker-compose unzip yq; do echo "[$(date +%H:%M:%S)]: [TEST] Validating that $package is correctly installed..." # Loop through each package using dpkg if ! dpkg -S $package >/dev/null; then # If which returns a non-zero return code, try to re-install the package echo "[-] $package was not found. Attempting to reinstall." apt-get -qq update && apt-get install -y $package if ! which $package >/dev/null; then # If the reinstall fails, give up echo "[X] Unable to install $package even after a retry. Exiting." exit 1 fi else echo "[+] $package was successfully installed!" fi done register: test_prerequisites failed_when: "'error' in test_prerequisites.stderr" - name: Fix Static IPs args: executable: /bin/bash become: yes shell: | USING_KVM=$(sudo lsmod | grep kvm) if [ ! -z "$USING_KVM" ]; then echo "[*] Using KVM, no need to fix DHCP for eth1 iface" exit 0 fi if [ -f /sys/class/net/eth2/address ]; then if [ "$(cat /sys/class/net/eth2/address)" == "00:50:56:a3:b1:c4" ]; then echo "[*] Using ESXi, no need to change anything" exit 0 fi fi # There's a fun issue where dhclient keeps messing with eth1 despite the fact # that eth1 has a static IP set. We workaround this by setting a static DHCP lease. echo -e 'interface "eth1" { send host-name = gethostname(); send dhcp-requested-address 192.168.38.105; }' >>/etc/dhcp/dhclient.conf netplan apply # Fix eth1 if the IP isn't set correctly ETH1_IP=$(ip -4 addr show eth1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}') if [ "$ETH1_IP" != "192.168.38.105" ]; then echo "Incorrect IP Address settings detected. Attempting to fix." ifdown eth1 ip addr flush dev eth1 ifup eth1 ETH1_IP=$(ifconfig eth1 | grep 'inet addr' | cut -d ':' -f 2 | cut -d ' ' -f 1) if [ "$ETH1_IP" == "192.168.38.105" ]; then echo "[$(date +%H:%M:%S)]: The static IP has been fixed and set to 192.168.38.105" else echo "[$(date +%H:%M:%S)]: Failed to fix the broken static IP for eth1. Exiting because this will cause problems with other VMs." exit 1 fi fi # Make sure we do have a DNS resolution while true; do if [ "$(dig +short @8.8.8.8 github.com)" ]; then break; fi sleep 1 done register: fix_eth1_static_ip failed_when: "'error' in fix_eth1_static_ip.stderr" changed_when: "'Using ESXi' in fix_eth1_static_ip.stdout" - name: Install Splunk args: executable: /bin/bash become: yes shell: | # Check if Splunk is already installed if [ -f "/opt/splunk/bin/splunk" ]; then echo "[$(date +%H:%M:%S)]: Splunk is already installed" else echo "[$(date +%H:%M:%S)]: Installing Splunk..." # Get download.splunk.com into the DNS cache. Sometimes resolution randomly fails during wget below dig @8.8.8.8 download.splunk.com >/dev/null dig @8.8.8.8 splunk.com >/dev/null dig @8.8.8.8 www.splunk.com >/dev/null # Try to resolve the latest version of Splunk by parsing the HTML on the downloads page echo "[$(date +%H:%M:%S)]: Attempting to autoresolve the latest version of Splunk..." LATEST_SPLUNK=$(curl https://www.splunk.com/en_us/download/splunk-enterprise.html | grep -i deb | grep -Eo "data-link=\"................................................................................................................................" | cut -d '"' -f 2) # Sanity check what was returned from the auto-parse attempt if [[ "$(echo $LATEST_SPLUNK | grep -c "^https:")" -eq 1 ]] && [[ "$(echo $LATEST_SPLUNK | grep -c "\.deb$")" -eq 1 ]]; then echo "[$(date +%H:%M:%S)]: The URL to the latest Splunk version was automatically resolved as: $LATEST_SPLUNK" echo "[$(date +%H:%M:%S)]: Attempting to download..." wget --progress=bar:force -P /opt "$LATEST_SPLUNK" else echo "[$(date +%H:%M:%S)]: Unable to auto-resolve the latest Splunk version. Falling back to hardcoded URL..." # Download Hardcoded Splunk wget --progress=bar:force -O /opt/splunk-8.0.2-a7f645ddaf91-linux-2.6-amd64.deb 'https://download.splunk.com/products/splunk/releases/8.0.2/linux/splunk-8.0.2-a7f645ddaf91-linux-2.6-amd64.deb&wget=true' fi dpkg -i /opt/splunk*.deb /opt/splunk/bin/splunk start --accept-license --answer-yes --no-prompt --seed-passwd changeme /opt/splunk/bin/splunk add index wineventlog -auth 'admin:changeme' /opt/splunk/bin/splunk add index osquery -auth 'admin:changeme' /opt/splunk/bin/splunk add index osquery-status -auth 'admin:changeme' /opt/splunk/bin/splunk add index sysmon -auth 'admin:changeme' /opt/splunk/bin/splunk add index powershell -auth 'admin:changeme' /opt/splunk/bin/splunk add index zeek -auth 'admin:changeme' /opt/splunk/bin/splunk add index suricata -auth 'admin:changeme' /opt/splunk/bin/splunk add index threathunting -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_forwarder/splunk-add-on-for-microsoft-windows_500.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/add-on-for-microsoft-sysmon_800.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/asn-lookup-generator_101.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/lookup-file-editor_331.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/splunk-add-on-for-zeek-aka-bro_400.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/force-directed-app-for-splunk_200.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/punchcard-custom-visualization_130.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/sankey-diagram-custom-visualization_130.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/link-analysis-app-for-splunk_161.tgz -auth 'admin:changeme' /opt/splunk/bin/splunk install app /vagrant/resources/splunk_server/threathunting_141.tgz -auth 'admin:changeme' # Add custom Macro definitions for ThreatHunting App cp /vagrant/resources/splunk_server/macros.conf /opt/splunk/etc/apps/ThreatHunting/default/macros.conf # Fix Windows TA macros mkdir /opt/splunk/etc/apps/Splunk_TA_windows/local cp /opt/splunk/etc/apps/Splunk_TA_windows/default/macros.conf /opt/splunk/etc/apps/Splunk_TA_windows/local sed -i 's/wineventlog_windows/wineventlog/g' /opt/splunk/etc/apps/Splunk_TA_windows/local/macros.conf # Fix Force Directed App until 2.0.1 is released (https://answers.splunk.com/answers/668959/invalid-key-in-stanza-default-value-light.html#answer-669418) rm /opt/splunk/etc/apps/force_directed_viz/default/savedsearches.conf # Add a Splunk TCP input on port 9997 echo -e "[splunktcp://9997]\nconnection_host = ip" >/opt/splunk/etc/apps/search/local/inputs.conf # Add props.conf and transforms.conf cp /vagrant/resources/splunk_server/props.conf /opt/splunk/etc/apps/search/local/ cp /vagrant/resources/splunk_server/transforms.conf /opt/splunk/etc/apps/search/local/ cp /opt/splunk/etc/system/default/limits.conf /opt/splunk/etc/system/local/limits.conf # Bump the memtable limits to allow for the ASN lookup table sed -i.bak 's/max_memtable_bytes = 10000000/max_memtable_bytes = 30000000/g' /opt/splunk/etc/system/local/limits.conf # Skip Splunk Tour and Change Password Dialog echo "[$(date +%H:%M:%S)]: Disabling the Splunk tour prompt..." touch /opt/splunk/etc/.ui_login mkdir -p /opt/splunk/etc/users/admin/search/local echo -e "[search-tour]\nviewed = 1" >/opt/splunk/etc/system/local/ui-tour.conf # Source: https://answers.splunk.com/answers/660728/how-to-disable-the-modal-pop-up-help-us-to-improve.html if [ ! -d "/opt/splunk/etc/users/admin/user-prefs/local" ]; then mkdir -p "/opt/splunk/etc/users/admin/user-prefs/local" fi echo '[general] render_version_messages = 1 dismissedInstrumentationOptInVersion = 4 notification_python_3_impact = false display.page.home.dashboardId = /servicesNS/nobody/search/data/ui/views/logger_dashboard' > /opt/splunk/etc/users/admin/user-prefs/local/user-prefs.conf # Disable the instrumentation popup echo -e "showOptInModal = 0\noptInVersionAcknowledged = 4" >>/opt/splunk/etc/apps/splunk_instrumentation/local/telemetry.conf # Enable SSL Login for Splunk echo -e "[settings]\nenableSplunkWebSSL = true" >/opt/splunk/etc/system/local/web.conf # Copy over the Logger Dashboard if [ ! -d "/opt/splunk/etc/apps/search/local/data/ui/views" ]; then mkdir -p "/opt/splunk/etc/apps/search/local/data/ui/views" fi cp /vagrant/resources/splunk_server/logger_dashboard.xml /opt/splunk/etc/apps/search/local/data/ui/views || echo "Unable to find dashboard" # Reboot Splunk to make changes take effect /opt/splunk/bin/splunk restart /opt/splunk/bin/splunk enable boot-start # Generate the ASN lookup table /opt/splunk/bin/splunk search "|asngen | outputlookup asn" -auth 'admin:changeme' fi register: install_splunk changed_when: "'The Splunk web interface is at https://logger:8000' in install_splunk.stdout" - name: Install Fleet args: executable: /bin/bash become: yes shell: | # Install Fleet if [ -f "/opt/kolide-quickstart" ]; then echo "[$(date +%H:%M:%S)]: Fleet is already installed" else echo "[$(date +%H:%M:%S)]: Installing Fleet..." echo -e "\n127.0.0.1 kolide" >>/etc/hosts echo -e "\n127.0.0.1 logger" >>/etc/hosts cd /opt && git clone https://github.com/kolide/kolide-quickstart.git cd /opt/kolide-quickstart || echo "Something went wrong while trying to clone the kolide-quickstart repository" cp /vagrant/resources/fleet/server.* . sed -i 's/ -it//g' demo.sh ./demo.sh up simple # Set the enrollment secret to match what we deploy to Windows hosts docker run --rm --network=kolidequickstart_default mysql:5.7 mysql -h mysql -u kolide --password=kolide -e 'update app_configs set osquery_enroll_secret = "enrollmentsecret" where id=1;' --batch kolide # Set snapshot events to be split into multiple events docker run --rm --network=kolidequickstart_default mysql:5.7 mysql -h mysql -u kolide --password=kolide -e 'insert into options (name, type, value) values ("logger_snapshot_event_type", 2, "true");' --batch kolide echo "Updated enrollment secret" fi register: install_fleet changed_when: "'Updated enrollment secret' in install_fleet.stdout" failed_when: "'error' in install_splunk.stderr" - name: Download Palantir Osquery Config args: executable: /bin/bash become: yes shell: | if [ -f /opt/osquery-configuration ]; then echo "[$(date +%H:%M:%S)]: osquery configs have already been downloaded" else # Import Palantir osquery configs into Fleet echo "[$(date +%H:%M:%S)]: Downloading Palantir osquery configs..." cd /opt && git clone https://github.com/palantir/osquery-configuration.git fi register: download_palantir_osquery failed_when: "'error' in download_palantir_osquery.stderr" - name: Import osquery Config into Fleet args: executable: /bin/bash become: yes shell: | cd /opt wget --progress=bar:force https://github.com/kolide/fleet/releases/download/2.4.0/fleet.zip unzip fleet.zip -d fleet cp fleet/linux/fleetctl /usr/local/bin/fleetctl && chmod +x /usr/local/bin/fleetctl fleetctl config set --address https://192.168.38.105:8412 fleetctl config set --tls-skip-verify true fleetctl setup --email admin@detectionlab.network --username admin --password 'admin123#' --org-name DetectionLab fleetctl login --email admin@detectionlab.network --password 'admin123#' # Change the query invervals to reflect a lab environment # Every hour -> Every 3 minutes # Every 24 hours -> Every 15 minutes sed -i 's/interval: 3600/interval: 180/g' osquery-configuration/Fleet/Endpoints/MacOS/osquery.yaml sed -i 's/interval: 3600/interval: 180/g' osquery-configuration/Fleet/Endpoints/Windows/osquery.yaml sed -i 's/interval: 28800/interval: 900/g' osquery-configuration/Fleet/Endpoints/MacOS/osquery.yaml sed -i 's/interval: 28800/interval: 900/g' osquery-configuration/Fleet/Endpoints/Windows/osquery.yaml # Dont log osquery INFO messages fleetctl get options > /tmp/options.yaml /usr/bin/yq w -i /tmp/options.yaml 'spec.config.options.logger_min_status' '1' fleetctl apply -f /tmp/options.yaml # Use fleetctl to import YAML files fleetctl apply -f osquery-configuration/Fleet/Endpoints/MacOS/osquery.yaml fleetctl apply -f osquery-configuration/Fleet/Endpoints/Windows/osquery.yaml for pack in osquery-configuration/Fleet/Endpoints/packs/*.yaml; do fleetctl apply -f "$pack" done # Add Splunk monitors for Fleet /opt/splunk/bin/splunk add monitor "/opt/kolide-quickstart/osquery_result" -index osquery -sourcetype 'osquery:json' -auth 'admin:changeme' /opt/splunk/bin/splunk add monitor "/opt/kolide-quickstart/osquery_status" -index osquery-status -sourcetype 'osquery:status' -auth 'admin:changeme' register: fleet_osquery_config failed_when: "'error' in fleet_osquery_config.stderr" changed_when: "'Fleet login successful and context configured!' in fleet_osquery_config.stdout" - name: Install Suricata args: executable: /bin/bash become: yes shell: | # Run iwr -Uri testmyids.com -UserAgent "BlackSun" in Powershell to generate test alerts from Windows echo "[$(date +%H:%M:%S)]: Installing Suricata..." # Install suricata apt-get -qq -y install suricata crudini # Install suricata-update cd /opt || exit 1 git clone https://github.com/OISF/suricata-update.git cd /opt/suricata-update || exit 1 python setup.py install cp /vagrant/resources/suricata/suricata.yaml /etc/suricata/suricata.yaml crudini --set --format=sh /etc/default/suricata '' iface eth1 # update suricata signature sources suricata-update update-sources # disable protocol decode as it is duplicative of Zeek echo re:protocol-command-decode >>/etc/suricata/disable.conf # enable et-open and attackdetection sources suricata-update enable-source et/open suricata-update enable-source ptresearch/attackdetection # Update suricata and restart suricata-update service suricata stop service suricata start sleep 3 # Verify that Suricata is running if ! pgrep -f suricata >/dev/null; then echo "Suricata attempted to start but is not running. Exiting" exit 1 fi register: install_suricata failed_when: "'error' in install_suricata.stderr" - name: Install Zeek args: executable: /bin/bash become: yes shell: | echo "[$(date +%H:%M:%S)]: Installing Zeek..." sh -c "echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_18.04/ /' > /etc/apt/sources.list.d/security:zeek.list" wget -nv https://download.opensuse.org/repositories/security:zeek/xUbuntu_18.04/Release.key -O /tmp/Release.key apt-key add - >/opt/zeek/share/zeek/site/local.zeek # Configure Zeek crudini --del $NODECFG zeek crudini --set $NODECFG manager type manager crudini --set $NODECFG manager host localhost crudini --set $NODECFG proxy type proxy crudini --set $NODECFG proxy host localhost # Setup $CPUS numbers of Zeek workers crudini --set $NODECFG worker-eth1 type worker crudini --set $NODECFG worker-eth1 host localhost crudini --set $NODECFG worker-eth1 interface eth1 crudini --set $NODECFG worker-eth1 lb_method pf_ring crudini --set $NODECFG worker-eth1 lb_procs "$(nproc)" # Setup Zeek to run at boot cp /vagrant/resources/zeek/zeek.service /lib/systemd/system/zeek.service systemctl enable zeek systemctl start zeek mkdir -p $SPLUNK_ZEEK_JSON/local cp $SPLUNK_ZEEK_JSON/default/inputs.conf $SPLUNK_ZEEK_JSON/local/inputs.conf crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_ZEEK_MONITOR index zeek crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_ZEEK_MONITOR sourcetype bro:json crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_ZEEK_MONITOR whitelist '.*\.log$' crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_ZEEK_MONITOR blacklist '.*(communication|stderr)\.log$' crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_ZEEK_MONITOR disabled 0 crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_SURICATA_MONITOR index suricata crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_SURICATA_MONITOR sourcetype suricata:json crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_SURICATA_MONITOR whitelist 'eve.json' crudini --set $SPLUNK_ZEEK_JSON/local/inputs.conf $SPLUNK_SURICATA_MONITOR disabled 0 crudini --set $SPLUNK_ZEEK_JSON/local/props.conf $SPLUNK_SURICATA_SOURCETYPE TRUNCATE 0 # Ensure permissions are correct and restart splunk chown -R splunk $SPLUNK_ZEEK_JSON /opt/splunk/bin/splunk restart # Verify that Zeek is running if ! pgrep -f zeek >/dev/null; then echo "Zeek attempted to start but is not running. Exiting" exit 1 fi register: install_zeek - name: Install Guacamole args: executable: /bin/bash become: yes shell: | echo "[$(date +%H:%M:%S)]: Installing Guacamole..." cd /opt apt-get -qq install -y libcairo2-dev libjpeg62-dev libpng-dev libossp-uuid-dev libfreerdp-dev libpango1.0-dev libssh2-1-dev libssh-dev tomcat8 tomcat8-admin tomcat8-user wget --progress=bar:force "http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/1.0.0/source/guacamole-server-1.0.0.tar.gz" -O guacamole-server-1.0.0.tar.gz tar -xvf guacamole-server-1.0.0.tar.gz && cd guacamole-server-1.0.0 ./configure &>/dev/null && make --quiet &>/dev/null && make --quiet install &>/dev/null || echo "[-] An error occurred while installing Guacamole." ldconfig cd /var/lib/tomcat8/webapps wget --progress=bar:force "http://apache.org/dyn/closer.cgi?action=download&filename=guacamole/1.0.0/binary/guacamole-1.0.0.war" -O guacamole.war mkdir /etc/guacamole mkdir /usr/share/tomcat8/.guacamole cp /vagrant/resources/guacamole/user-mapping.xml /etc/guacamole/ cp /vagrant/resources/guacamole/guacamole.properties /etc/guacamole/ cp /vagrant/resources/guacamole/guacd.service /lib/systemd/system sudo ln -s /etc/guacamole/guacamole.properties /usr/share/tomcat8/.guacamole/ sudo ln -s /etc/guacamole/user-mapping.xml /usr/share/tomcat8/.guacamole/ systemctl enable guacd systemctl enable tomcat8 systemctl start guacd systemctl start tomcat8 register: install_guacamole failed_when: "'error' in install_guacamole.stderr" - name: Postinstall Tasks args: executable: /bin/bash become: yes shell: | # Include Splunk and Zeek in the PATH echo export PATH="$PATH:/opt/splunk/bin:/opt/zeek/bin" >>~/.bashrc # Ping DetectionLab server for usage statistics curl -A "DetectionLab-logger" "https://detectionlab.network/logger"