Merge pull request #462 from clong/azure_dev

Add Support for Azure
This commit is contained in:
Chris Long
2020-06-14 18:57:55 -07:00
committed by GitHub
35 changed files with 1239 additions and 17 deletions

18
.gitignore vendored
View File

@@ -4,17 +4,7 @@ Packer/packer_cache/*
Packer/packer_build.log
Boxes/*
.DS_Store
Terraform/*/*.tfstate
Terraform/*/.terraform
Terraform/*/*.tfvars
Terraform/*/*.lock.info
Terraform/*/*.backup
Terraform/*.tfstate
Terraform/*.terraform
Terraform/*.tfvars
Terraform/*.lock.info
Terraform/*.backup
ESXi/.terraform/*
ESXi/*.tfstate
ESXi/*.backup
ESXi/*.tfvars
**/.terraform/*
*.tfstate
*.tfstate.*
*.tfvars

View File

@@ -0,0 +1,3 @@
[defaults]
inventory = inventory.yml
host_key_checking = False

View File

@@ -0,0 +1,18 @@
---
- hosts: dc
roles:
- dc
- common
tags: dc
- hosts: wef
roles:
- wef
- common
tags: wef
- hosts: win10
roles:
- win10
- common
tags: win10

View File

@@ -0,0 +1,6 @@
ansible_user: vagrant
ansible_password: Vagrant123
ansible_port: 5985
ansible_connection: winrm
ansible_winrm_transport: basic
ansible_winrm_server_cert_validation: ignore

View File

@@ -0,0 +1,4 @@
ansible_user: vagrant
ansible_password: vagrant
ansible_port: 22
ansible_connection: ssh

View File

@@ -0,0 +1,14 @@
---
# Replace the x's with the IP addresses from "terrafrom output"
dc:
hosts:
x.x.x.x:
wef:
hosts:
x.x.x.x:
win10:
hosts:
x.x.x.x:

View File

@@ -0,0 +1,71 @@
---
- name: Downloading the Palantir WEF Configuration
win_shell: ".\\download_palantir_wef.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: palantir_wef
failed_when: "'Exception' in palantir_wef.stdout"
- debug: msg="{{ palantir_wef.stdout_lines }}"
- name: Downloading the Palantir osquery Configuration
win_shell: ".\\download_palantir_osquery.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: palantir_osquery
failed_when: "'Exception' in palantir_osquery.stdout"
- debug: msg="{{ palantir_osquery.stdout_lines }}"
- name: Installing osquery
win_shell: ".\\install-osquery.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: install_osquery
failed_when: "'Exception' in install_osquery.stdout"
- debug: msg="{{ install_osquery.stdout_lines }}"
- name: Installing SysInternals Tools
win_shell: ".\\install-sysinternals.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: sysinternals
failed_when: "'Exception' in sysinternals.stdout"
- debug: msg="{{ sysinternals.stdout_lines }}"
- name: Installing AutorunsToWineventlog
win_shell: ".\\install-autorunstowineventlog.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: autorunstowineventlog
failed_when: "'Exception' in autorunstowineventlog.stdout"
- debug: msg="{{ autorunstowineventlog.stdout_lines }}"
- name: Installing Red Team Tooling
win_shell: ".\\install-redteam.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: redteam
failed_when: "'Exception' in redteam.stdout"
- debug: msg="{{ redteam.stdout_lines }}"
- name: Install Utilities
win_chocolatey:
name:
- NotepadPlusPlus
- GoogleChrome
- WinRar
- wireshark
- winpcap
state: present

View File

@@ -0,0 +1,144 @@
---
- name: Set DNS Address
win_shell: "Set-DnsClientServerAddress -InterfaceAlias Ethernet -ServerAddresses 127.0.0.1,8.8.8.8"
- name: Install git
win_chocolatey:
name: git
state: present
- name: Check if existing DetectionLab directory
win_stat:
path: 'c:\DetectionLab'
register: dir
- name: Git clone Detectionlab
win_shell: git clone https://github.com/clong/DetectionLab.git
args:
chdir: 'c:\'
when: not dir.stat.exists
- name: Copy scripts to c:\vagrant
win_shell: Copy-Item -Recurse c:\DetectionLab\Vagrant c:\vagrant
- name: Create an Administrator user
win_user:
name: Administrator
password: Vagrant123
state: present
groups:
- Users,Administrators
password_never_expires: yes
- name: Create the Domain
win_shell: .\\provision.ps1
args:
chdir: 'c:\vagrant\scripts'
register: domain_creation
changed_when: "'Status : Success' in domain_creation.stdout"
- name: Reboot Afer Domain Creation
win_reboot:
msg: "Installing AD. Rebooting..."
pre_reboot_delay: 15
reboot_timeout: 600
post_reboot_delay: 60
- name: Configure OU
win_shell: .\\configure-ou.ps1
args:
chdir: 'c:\vagrant\scripts'
register: ou_creation
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: windomain.local\vagrant
ansible_become_password: vagrant
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
failed_when: "'Exception' in ou_creation.stderr"
- debug: msg="{{ ou_creation.stdout_lines }}"
- name: Configure WEF GPO
win_shell: .\\configure-wef-gpo.ps1
args:
chdir: 'c:\vagrant\scripts'
register: wef_gpo
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: windomain.local\vagrant
ansible_become_password: vagrant
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
failed_when: "'Exception' in wef_gpo.stderr"
- debug: msg="{{ wef_gpo.stdout_lines }}"
- name: Configure Powershell Logging GPO
win_shell: .\\configure-powershelllogging.ps1
args:
chdir: 'c:\vagrant\scripts'
register: powershell_gpo
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: windomain.local\vagrant
ansible_become_password: vagrant
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
failed_when: "'Exception' in powershell_gpo.stderr"
- debug: msg="{{ powershell_gpo.stdout_lines }}"
- name: Configure Auditing Policy GPO
win_shell: .\\configure-AuditingPolicyGPOs.ps1
args:
chdir: 'c:\vagrant\scripts'
register: audit_policy
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: windomain.local\vagrant
ansible_become_password: vagrant
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
failed_when: "'Exception' in audit_policy.stderr"
- debug: msg="{{ audit_policy.stdout_lines }}"
- name: Disable Windows Defender GPO
win_shell: .\\configure-disable-windows-defender-gpo.ps1
args:
chdir: 'c:\vagrant\scripts'
register: disable_win_def
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: windomain.local\vagrant
ansible_become_password: vagrant
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
failed_when: "'Exception' in disable_win_def.stderr"
- debug: msg="{{ disable_win_def.stdout_lines }}"
- name: Configure RDP Permissions GPO
win_shell: .\\configure-rdp-user-gpo.ps1
args:
chdir: 'c:\vagrant\scripts'
register: rdp_gpo
vars:
ansible_become: yes
ansible_become_method: runas
ansible_become_user: windomain.local\vagrant
ansible_become_password: vagrant
ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only
failed_when: "'Exception' in rdp_gpo.stderr"
- debug: msg="{{ rdp_gpo.stdout_lines }}"
- name: Configure DC with raw Commands
win_shell: "{{ item }}"
with_items:
- "wevtutil el | Select-String -notmatch \"Microsoft-Windows-LiveId\" | Foreach-Object {wevtutil cl \"$_\"}"
- "Set-SmbServerConfiguration -AuditSmb1Access $true -Force"

View File

@@ -0,0 +1,136 @@
---
# This needs to be made idempodent
- name: Set HostOnly DNS Address
win_shell: "Set-DnsClientServerAddress -InterfaceAlias Ethernet -ServerAddresses 192.168.38.102,8.8.8.8"
- name: Install git
win_chocolatey:
name: git
state: present
- name: Check if existing DetectionLab directory
win_stat:
path: 'c:\DetectionLab'
register: dir
- name: Git clone Detectionlab
win_shell: git clone https://github.com/clong/DetectionLab.git
args:
chdir: 'c:\'
when: not dir.stat.exists
- name: Copy scripts to c:\vagrant
win_shell: Copy-Item -Recurse c:\DetectionLab\Vagrant c:\vagrant
- name: Join the Domain
win_shell: .\\provision.ps1
args:
chdir: 'c:\vagrant\scripts'
register: wef_join_domain
changed_when: "'HasSucceeded : True' in wef_join_domain.stdout"
- debug: msg="{{ wef_join_domain.stdout_lines }}"
- name: Reboot After Joining the Domain
win_reboot:
msg: "Joining the domain. Rebooting..."
pre_reboot_delay: 15
reboot_timeout: 600
post_reboot_delay: 60
- name: Download Microsoft ATA
win_get_url:
url: http://download.microsoft.com/download/4/9/1/491394D1-3F28-4261-ABC6-C836A301290E/ATA1.9.iso
dest: "C:\\Users\\vagrant\\AppData\\Local\\Temp\\Microsoft ATA 1.9.iso"
timeout: 3600
- name: Clear Event Logs
win_shell: "wevtutil el | Select-String -notmatch \"Microsoft-Windows-LiveId\" | Foreach-Object {wevtutil cl \"$_\"}"
- name: Downloading the Palantir WEF Configuration
win_shell: ".\\download_palantir_wef.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: palantir_wef
failed_when: "'Exception' in palantir_wef.stdout"
- debug: msg="{{ palantir_wef.stdout_lines }}"
- name: Installing WEF Subscriptions
win_shell: ".\\install-wefsubscriptions.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: wef_subscriptions
failed_when: "'Exception' in wef_subscriptions.stdout"
- debug: msg="{{ wef_subscriptions.stdout_lines }}"
- name: Installing the Splunk Universal Forwarder
win_shell: ".\\install-splunkuf.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: splunkuf
failed_when: "'Exception' in splunkuf.stdout"
- debug: msg="{{ splunkuf.stdout_lines }}"
- name: Install Splunk Windows TA
win_shell: ".\\install-windows_ta.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: windowsta
failed_when: "'Exception' in windowsta.stdout"
- debug: msg="{{ windowsta.stdout_lines }}"
- name: Installing the Powershell Log Transcription Share
win_shell: ".\\configure-pslogstranscriptsshare.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: pstranscriptshare
failed_when: "'Exception' in pstranscriptshare.stdout"
- debug: msg="{{ pstranscriptshare.stdout_lines }}"
- name: Installing Microsoft Advanced Threat Analytics
win_shell: ".\\install-microsoft-ata.ps1"
args:
chdir: 'c:\vagrant\scripts'
register: windowsata
failed_when: "'Exception' in windowsata.stdout"
- debug: msg="{{ windowsata.stdout_lines }}"
- name: Configure WEF with raw Commands
win_shell: "{{ item }}"
with_items:
- "wevtutil el | Select-String -notmatch \"Microsoft-Windows-LiveId\" | Foreach-Object {wevtutil cl \"$_\"}"
- "Set-SmbServerConfiguration -AuditSmb1Access $true -Force"
- name: Disable password complexity policy
win_shell: |
secedit /export /cfg C:\secpol.cfg
(gc C:\secpol.cfg).replace("PasswordComplexity = 1", "PasswordComplexity = 0") | Out-File C:\secpol.cfg
secedit /configure /db C:\Windows\security\local.sdb /cfg C:\secpol.cfg /areas SECURITYPOLICY
rm -force C:\secpol.cfg -confirm:$false
become: yes
become_method: enable
- name: Add Ansible user
win_user:
name: ansible
password: Ansible123
groups:
- Administrators
- name: Switch to Ansible user
set_fact:
ansible_user: ansible
ansible_password: Ansible123
- name: Change password for vagrant account back to vagrant
win_user:
name: vagrant
password: vagrant

View File

@@ -0,0 +1,87 @@
---
- name: Set HostOnly DNS Address
win_shell: "Set-DnsClientServerAddress -InterfaceAlias Ethernet -ServerAddresses 192.168.38.102,8.8.8.8"
- name: Install git
win_chocolatey:
name: git
state: present
- name: Check if existing DetectionLab directory
win_stat:
path: 'c:\DetectionLab'
register: dir
- name: Git clone Detectionlab
win_shell: git clone https://github.com/clong/DetectionLab.git
args:
chdir: 'c:\'
when: not dir.stat.exists
- name: Copy scripts to c:\vagrant
win_shell: Copy-Item -Recurse c:\DetectionLab\Vagrant c:\vagrant
- name: Making Windows10 Great Again
win_shell: .\\MakeWindows10GreatAgain.ps1
args:
chdir: 'c:\vagrant\scripts'
- name: Join the Domain
win_shell: .\\provision.ps1
args:
chdir: 'c:\vagrant\scripts'
- name: Update group policy
win_shell: "gpupdate /force"
- name: Reboot Server
win_reboot:
msg: "Joined the domain. Rebooting..."
pre_reboot_delay: 15
reboot_timeout: 600
post_reboot_delay: 60
- name: Clear Event Logs
win_shell: "wevtutil el | Select-String -notmatch \"Microsoft-Windows-LiveId\" | Foreach-Object {wevtutil cl \"$_\"}"
- name: Install Classic Shell with Chocolatey
win_chocolatey:
name:
- classic-shell
state: present
install_args: "ADDLOCAL=ClassicStartMenu"
- name: DetectionLab Menu
win_shell: |
"\"C:\\Program Files\\Classic Shell\\ClassicStartMenu.exe -xml c:\\vagrant\\resources\\windows\\MenuSettings.xml\""
regedit /s c:\vagrant\resources\windows\MenuStyle_Default_Win7.reg
- name: Disable password complexity policy
win_shell: |
secedit /export /cfg C:\secpol.cfg
(gc C:\secpol.cfg).replace("PasswordComplexity = 1", "PasswordComplexity = 0") | Out-File C:\secpol.cfg
secedit /configure /db C:\Windows\security\local.sdb /cfg C:\secpol.cfg /areas SECURITYPOLICY
rm -force C:\secpol.cfg -confirm:$false
become: yes
become_method: enable
- name: Add Ansible user
win_user:
name: ansible
password: Ansible123
groups:
- Administrators
- name: Switch to Ansible user
set_fact:
ansible_user: ansible
ansible_password: Ansible123
- name: Change password for vagrant account back to vagrant
win_user:
name: vagrant
password: vagrant

101
Azure/README.md Normal file
View File

@@ -0,0 +1,101 @@
# Building DetectionLab on Azure
NOTE: This is an early release and it's possible that certain features may not work perfectly for everyone yet
## Prereqs (~30-60 minutes)
0. Have an active [Azure account](https://azure.microsoft.com/en-us/free/). If you don't have one yet, they give you $200 worth of credit to use within the first 30 days!
1. [Install Terraform](https://www.terraform.io/downloads.html) and move it to a directory included in your system's PATH.
2. [Install Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html). This will be used to provision the instances. Some users may prefer to install Ansible inside of a virtual environment; that's fine too.
3. [Install the Azure command line client](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) and make sure it's included in your system's PATH.
4. Either create or re-use an existing SSH keypair that you'll use to authenticate to the logger host
5. Make sure you've pulled down the most recent changes from the DetectionLab git repo
6. Please note that the default credentials before provisioning are `vagrant:Vagrant123` due to the windows SKU/AMI password complexity requirements!
## Steps
### Terraform
1. **(5 Minutes)** - Configure the `terraform.tfvars` file
1. Copy the file at `/DetectionLab/Azure/Terraform/terraform.tfvars.example` to `/DetectionLab/Azure/Terraform/terraform.tfvars`
2. In the newly copied terraform.tfvars, provide a value for each variable.
2. **(5 Minutes)** - Authenticate to Azure using `az`
1. Run `az login`. This should bring up a browser that asks you to sign into your Azure account.
2. Sign in and the window should say "You have logged into Microsoft Azure!"
3. **(3-20 Minutes)** - Bring up the VM's using Terraform
1. `cd` to `Azure/Terraform` and run `terraform init` to initialize the working directory
2. Make sure you followed the pre-reqs and have a `terraform.tfvars` file present with your public IP address whitelisted
3. Run `terraform apply` to check the Terraform plan or `terraform apply --auto-approve` to bypass the check
4. It will take ~20 minutes for logger to come online and finish provisioning, but **you can move onto the next step once you see that DC, WEF, and WIN10 have fininshed creation** (usually around 2 minutes):
```
azurerm_virtual_machine.dc: Creation complete after 1m55s
azurerm_virtual_machine.wef: Creation complete after 1m54s
azurerm_virtual_machine.win10: Creation complete after 1m55s
```
At this point in time, we're at this state:
* Logger VM has been brought up and is provisioning
* DC VM has been brought up but is unprovisioned
* DC VM has been brought up but is unprovisioned
* DC VM has been brought up but is unprovisioned
At this point in time, you should be able to open a new terminal window, navigate to `DetectionLab/Azure/Terraform` and run `terraform output`. You should see something like the following:
```
ata_url = https://52.191.136.x
dc_public_ip = 52.183.119.x
fleet_url = https://52.191.170.x:8412
guacamole_url = https://52.191.136.x:8080/guacamole
logger_public_ip = 52.191.170.x
region = West US 2
splunk_url = https://52.191.170.x:8000
wef_public_ip = 52.191.136.x
win10_public_ip = 52.229.34.x
```
![](../img/azure_terraform1.png)
We're going to use this output in the next step.
---
### Ansible
We're going to use Ansible to fininsh provisioning the rest of the Windows hosts.
1. **(5 Minutes)** - Configure the `inventory.yml` file
1. Navigate to `Azure/Ansible` and open `inventory.yml` for editing.
2. Take the `public_ip` values from `terraform output` and replace the `x.x.x.x` values with the public IP of each host
Now that Ansible is configured for provisioning, there are two ways to go about this:
* Provision each host one at a time (e.g. DC, then WEF, then WIN10). This is slower, but requires less steps.
* Provision the DC, then provision WEF and WIN10 simultaneously. This is faster, but you'll have to open multiple terminals and do a bit of manual work.
For the provisioning to be successful, the DC has to spin up Active Directory before provisioning of the WEF and WIN10 hosts can begin, otherwise they will fail to join the domain and provisioning will fail.
If you'd like to take the slower but easier route, ensure you're in `DetectionLab/Azure/Ansible` and run `ansible-playbook -v detectionlab.yml`. This will provision the hosts one at a time (DC, WEF, then WIN10). However, if you'd like to go the faster route, follow the directions below.
If you'd like to take the faster route, I recommend opening 3 terminal windows to `DetectionLab/Azure/Ansible` and following the following steps:
1. In the first window, run `ansible-playbook -v detectionlab.yml --tags "dc"`
2. Once the DC has passed the `Reboot Afer Domain Creation` Ansible step, you can begin provisioning WEF and WIN10
3. In the second window, run `ansible-playbook -v detectionlab.yml --tags "wef"`
4. In the third window, run `ansible-playbook -v detectionlab.yml --tags "win10"`
![](../img/azure_ansible1.png)
If you run into any issues along the way, please open an issue on Github and I'll do my best to find a solution.
## Debugging / Troubleshooting / Known Issues
* If an Ansible playbook fails (and they often do), you can pick up where it left off with `ansible-playbook -vvv detectionlab.yml --tags="hostname-goes-here" --start-at-task="taskname"`
* The "Configure OU" Ansible step often fails because the cmdlet can't find AD. Re-running it often fixes the issue (for unknown reasons): `ansible-playbook -vvv detectionlab.yml --tags="dc" --start-at-task="Configure OU"`
* "Installing Red Team Tooling" hangs if AV isn't disabled successfully
* It seems like sometimes the logger provisioning "errors" somewhere and causes the box to become tained, despite the provision being successful. Work around this by untainting it: `terraform untaint azurerm_virtual_machine.dc`
## Future work required
* It probably makes sense to abstract all of the logic in `bootstrap.sh` into individual Ansible tasks
* I imagine there's a streamlined way to get the results of `terraform output` into the `inventory.yml` for Ansible.
* I'm guessing there's a way to parallelize some of this execution or make some of it asynchronous: https://medium.com/developer-space/parallel-playbook-execution-in-ansible-30799ccda4e0 and https://docs.ansible.com/ansible/latest/user_guide/playbooks_async.html
## Credits
As usual, this work is based off the heavy lifting that others have done. My primary sources for this work were:
* [The DetectionLab work that juju4 has been doing on Azure and Ansible.](https://github.com/juju4/DetectionLab/tree/devel-azureansible/Ansible) At least 90% of this code was borrowed from their work.
* [Automate Windows VM Creation and Configuration in vSphere Using Packer, Terraform and Ansible - Dmitry Teslya](https://dteslya.engineer/automation/2019-02-19-configuring_vms_with_ansible/#setting-up-ansible)
Thank you to all of the sponsors who made this possible!

View File

@@ -0,0 +1,17 @@
<FirstLogonCommands>
<SynchronousCommand>
<CommandLine>cmd /c "mkdir C:\terraform"</CommandLine>
<Description>Create the Terraform working directory</Description>
<Order>11</Order>
</SynchronousCommand>
<SynchronousCommand>
<CommandLine>cmd /c "copy C:\AzureData\CustomData.bin C:\terraform\winrm.ps1"</CommandLine>
<Description>Move the CustomData file to the working directory</Description>
<Order>12</Order>
</SynchronousCommand>
<SynchronousCommand>
<CommandLine>powershell.exe -sta -ExecutionPolicy Unrestricted -file C:\terraform\winrm.ps1</CommandLine>
<Description>Move the CustomData file to the working directory</Description>
<Order>13</Order>
</SynchronousCommand>
</FirstLogonCommands>

View File

@@ -0,0 +1,16 @@
powershell.exe -c "Set-NetConnectionProfile -InterfaceAlias Ethernet -NetworkCategory Private"
Enable-PSRemoting -Force
winrm quickconfig -q
winrm quickconfig -transport:http
powershell.exe -c "winrm set winrm/config '@{MaxTimeoutms=\`"1800000\`"}'"
powershell.exe -c "winrm set winrm/config/winrs '@{MaxMemoryPerShellMB=\`"800\`"}'"
powershell.exe -c "winrm set winrm/config/service '@{AllowUnencrypted=\`"true\`"}'"
powershell.exe -c "winrm set winrm/config/service/auth '@{Basic=\`"true\`"}'"
powershell.exe -c "winrm set winrm/config/client/auth '@{Basic=\`"true\`"}'"
powershell.exe -c "winrm set winrm/config/listener?Address=*+Transport=HTTP '@{Port=\`"5985\`"}'"
netsh advfirewall firewall set rule group="Windows Remote Administration" new enable=yes
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=allow remoteip=any
reg add HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce /v StartWinRM /t REG_SZ /f /d "cmd.exe /c 'sc config winrm start= auto & sc start winrm'"
Restart-Service winrm
netsh advfirewall firewall add rule name="Port 5985" dir=in action=allow protocol=TCP localport=5985

View File

@@ -0,0 +1,6 @@
locals {
fleet_url = "https://${azurerm_public_ip.logger-publicip.ip_address}:8412"
splunk_url = "https://${azurerm_public_ip.logger-publicip.ip_address}:8000"
ata_url = "https://${azurerm_public_ip.wef-publicip.ip_address}"
guacamole_url = "https://${azurerm_public_ip.wef-publicip.ip_address}:8080/guacamole"
}

522
Azure/Terraform/main.tf Normal file
View File

@@ -0,0 +1,522 @@
# terraform init, plan, apply, destroy
# Note: does not support idempotence, don't execute twice with same scope.
# https://www.terraform.io/docs/providers/azurerm/index.html
# latest test: terraform 0.12.18
#
# FIXME!
# * apply: provisioning not working on Windows
# Error: Unsupported argument [...] An argument named "connection" is not expected here.
# apply => Error: timeout - last error: SSH authentication failed (root@:22): ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
# * apply: linux provisioning
# => works but script ends with error code for some reason (post bro install and splunk restart)
# Specify the provider and access details
provider "azurerm" {
version = "=2.12.0"
features {}
}
# https://github.com/terraform-providers/terraform-provider-azurerm/blob/1940d84dba45e41b2f1f868a22d7f7af1adea8a0/examples/virtual-machines/virtual_machine/vm-joined-to-active-directory/modules/active-directory/2-virtual-machine.tf
locals {
custom_data_content = file("${path.module}/files/winrm.ps1")
}
resource "azurerm_resource_group" "detectionlab" {
name = "DetectionLab-terraform"
location = "West US 2"
}
resource "azurerm_virtual_network" "detectionlab-network" {
name = "DetectionLab-vnet"
address_space = ["192.168.0.0/16"]
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
}
# Create a subnet to launch our instances into
resource "azurerm_subnet" "detectionlab-subnet" {
name = "DetectionLab-Subnet"
resource_group_name = azurerm_resource_group.detectionlab.name
virtual_network_name = azurerm_virtual_network.detectionlab-network.name
address_prefixes = ["192.168.38.0/24"]
}
resource "azurerm_network_security_group" "detectionlab-nsg" {
name = "DetectionLab-nsg"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
# SSH access
security_rule {
name = "SSH"
priority = 1001
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
# source_address_prefix = "*"
source_address_prefixes = var.ip_whitelist
destination_address_prefix = "*"
}
# Splunk access
security_rule {
name = "Splunk"
priority = 1002
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "8000"
source_address_prefixes = var.ip_whitelist
destination_address_prefix = "*"
}
# Fleet access
security_rule {
name = "Fleet"
priority = 1003
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "8412"
source_address_prefixes = var.ip_whitelist
destination_address_prefix = "*"
}
# RDP
security_rule {
name = "RDP"
priority = 1004
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "3389"
source_address_prefixes = var.ip_whitelist
destination_address_prefix = "*"
}
# WinRM
security_rule {
name = "WinRM"
priority = 1005
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "5985-5986"
source_address_prefixes = var.ip_whitelist
destination_address_prefix = "*"
}
# Windows ATA
security_rule {
name = "WindowsATA"
priority = 1006
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "443"
source_address_prefixes = var.ip_whitelist
destination_address_prefix = "*"
}
# Allow all traffic from the private subnet
security_rule {
name = "PrivateSubnet"
priority = 1007
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "*"
source_address_prefix = "192.168.38.0/24"
destination_address_prefix = "*"
}
# Guacamole access
security_rule {
name = "Guacamole"
priority = 1008
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "8080"
source_address_prefixes = var.ip_whitelist
destination_address_prefix = "*"
}
}
resource "azurerm_subnet_network_security_group_association" "detectionlab-nsga" {
subnet_id = azurerm_subnet.detectionlab-subnet.id
network_security_group_id = azurerm_network_security_group.detectionlab-nsg.id
}
resource "azurerm_public_ip" "logger-publicip" {
name = "logger-public-ip"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
allocation_method = "Static"
tags = {
role = "logger"
}
}
resource "azurerm_network_interface" "logger-nic" {
name = "logger-nic"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
ip_configuration {
name = "logger-NicConfiguration"
subnet_id = azurerm_subnet.detectionlab-subnet.id
private_ip_address_allocation = "Static"
private_ip_address = "192.168.38.105"
public_ip_address_id = azurerm_public_ip.logger-publicip.id
}
}
# Storage
resource "random_id" "randomId" {
keepers = {
# Generate a new ID only when a new resource group is defined
resource_group_name = azurerm_resource_group.detectionlab.name
}
byte_length = 8
}
resource "azurerm_storage_account" "detectionlab-storageaccount" {
name = "diag${random_id.randomId.hex}"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
account_replication_type = "LRS"
account_tier = "Standard"
}
# Linux VM
resource "azurerm_virtual_machine" "logger" {
name = "logger"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
network_interface_ids = [azurerm_network_interface.logger-nic.id]
vm_size = "Standard_D1_v2"
delete_os_disk_on_termination = true
storage_os_disk {
name = "OsDiskLogger"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
storage_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
os_profile {
computer_name = "logger"
admin_username = "vagrant"
admin_password = "vagrant"
}
os_profile_linux_config {
disable_password_authentication = true
ssh_keys {
path = "/home/vagrant/.ssh/authorized_keys"
key_data = file(var.public_key_path)
}
}
boot_diagnostics {
enabled = "true"
storage_uri = azurerm_storage_account.detectionlab-storageaccount.primary_blob_endpoint
}
# Provision
# https://github.com/terraform-providers/terraform-provider-azurerm/blob/master/examples/virtual-machines/provisioners/linux/main.tf
# https://www.terraform.io/docs/provisioners/connection.html
provisioner "remote-exec" {
connection {
host = azurerm_public_ip.logger-publicip.ip_address
user = "vagrant"
private_key = file(var.private_key_path)
}
inline = [
"sudo add-apt-repository universe && sudo apt-get -qq update && sudo apt-get -qq install -y git",
"echo 'logger' | sudo tee /etc/hostname && sudo hostnamectl set-hostname logger",
"sudo adduser --disabled-password --gecos \"\" vagrant && echo 'vagrant:vagrant' | sudo chpasswd",
"sudo mkdir /home/vagrant/.ssh && sudo cp /home/ubuntu/.ssh/authorized_keys /home/vagrant/.ssh/authorized_keys && sudo chown -R vagrant:vagrant /home/vagrant/.ssh",
"echo 'vagrant ALL=(ALL:ALL) NOPASSWD:ALL' | sudo tee -a /etc/sudoers",
"sudo git clone https://github.com/clong/DetectionLab.git /opt/DetectionLab",
"sudo sed -i 's/eth1/eth0/g' /opt/DetectionLab/Vagrant/bootstrap.sh",
"sudo sed -i 's/ETH1/ETH0/g' /opt/DetectionLab/Vagrant/bootstrap.sh",
"sudo sed -i 's#/usr/local/go/bin/go get -u#GOPATH=/root/go /usr/local/go/bin/go get -u#g' /opt/DetectionLab/Vagrant/bootstrap.sh",
"sudo sed -i 's#/vagrant/resources#/opt/DetectionLab/Vagrant/resources#g' /opt/DetectionLab/Vagrant/bootstrap.sh",
"sudo chmod +x /opt/DetectionLab/Vagrant/bootstrap.sh",
"sudo apt-get -qq update",
"sudo /opt/DetectionLab/Vagrant/bootstrap.sh 2>&1 |sudo tee /opt/DetectionLab/Vagrant/bootstrap.log",
]
}
tags = {
role = "logger"
}
}
# https://github.com/terraform-providers/terraform-provider-azurerm/tree/master/examples/virtual-machines/vm-joined-to-active-directory
# Windows VM
resource "azurerm_network_interface" "dc-nic" {
name = "dc-nic"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
ip_configuration {
name = "DC-NicConfiguration"
subnet_id = azurerm_subnet.detectionlab-subnet.id
private_ip_address_allocation = "Static"
private_ip_address = "192.168.38.102"
public_ip_address_id = azurerm_public_ip.dc-publicip.id
}
}
resource "azurerm_public_ip" "dc-publicip" {
name = "dc-public-ip"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
allocation_method = "Static"
tags = {
role = "dc"
}
}
resource "azurerm_network_interface" "wef-nic" {
name = "wef-nic"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
ip_configuration {
name = "WEF-NicConfiguration"
subnet_id = azurerm_subnet.detectionlab-subnet.id
private_ip_address_allocation = "Static"
private_ip_address = "192.168.38.103"
public_ip_address_id = azurerm_public_ip.wef-publicip.id
}
}
resource "azurerm_public_ip" "wef-publicip" {
name = "wef-public-ip"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
allocation_method = "Static"
tags = {
role = "wef"
}
}
resource "azurerm_network_interface" "win10-nic" {
name = "win10-nic"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
ip_configuration {
name = "myNicConfiguration"
subnet_id = azurerm_subnet.detectionlab-subnet.id
private_ip_address_allocation = "Static"
private_ip_address = "192.168.38.104"
public_ip_address_id = azurerm_public_ip.win10-publicip.id
}
}
resource "azurerm_public_ip" "win10-publicip" {
name = "win10-public-ip"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
allocation_method = "Static"
tags = {
role = "win10"
}
}
resource "azurerm_virtual_machine" "dc" {
name = "dc.windomain.local"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
network_interface_ids = [azurerm_network_interface.dc-nic.id]
vm_size = "Standard_D1_v2"
delete_os_disk_on_termination = true
storage_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2016-Datacenter"
version = "latest"
}
os_profile {
computer_name = "dc"
admin_username = "vagrant"
admin_password = "Vagrant123"
custom_data = local.custom_data_content
}
os_profile_windows_config {
provision_vm_agent = true
enable_automatic_upgrades = false
# Auto-Login's required to configure WinRM
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "AutoLogon"
content = "<AutoLogon><Password><Value>Vagrant123</Value></Password><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>vagrant</Username></AutoLogon>"
}
# Unattend config is to enable basic auth in WinRM, required for the provisioner stage.
# https://github.com/terraform-providers/terraform-provider-azurerm/blob/master/examples/virtual-machines/provisioners/windows/files/FirstLogonCommands.xml
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "FirstLogonCommands"
content = file("${path.module}/files/FirstLogonCommands.xml")
}
}
storage_os_disk {
name = "OsDiskDc"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
tags = {
role = "dc"
}
}
resource "azurerm_virtual_machine" "wef" {
name = "wef.windomain.local"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
network_interface_ids = [azurerm_network_interface.wef-nic.id]
vm_size = "Standard_D1_v2"
delete_os_disk_on_termination = true
storage_image_reference {
publisher = "MicrosoftWindowsServer"
offer = "WindowsServer"
sku = "2016-Datacenter"
version = "latest"
}
os_profile {
computer_name = "wef"
admin_username = "vagrant"
admin_password = "Vagrant123"
custom_data = local.custom_data_content
}
os_profile_windows_config {
provision_vm_agent = true
enable_automatic_upgrades = false
# Auto-Login's required to configure WinRM
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "AutoLogon"
content = "<AutoLogon><Password><Value>Vagrant123</Value></Password><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>vagrant</Username></AutoLogon>"
}
# Unattend config is to enable basic auth in WinRM, required for the provisioner stage.
# https://github.com/terraform-providers/terraform-provider-azurerm/blob/master/examples/virtual-machines/provisioners/windows/files/FirstLogonCommands.xml
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "FirstLogonCommands"
content = file("${path.module}/files/FirstLogonCommands.xml")
}
}
storage_os_disk {
name = "OsDiskWef"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
tags = {
role = "wef"
}
}
resource "azurerm_virtual_machine" "win10" {
name = "win10.windomain.local"
location = "West US 2"
resource_group_name = azurerm_resource_group.detectionlab.name
network_interface_ids = [azurerm_network_interface.win10-nic.id]
vm_size = "Standard_D1_v2"
delete_os_disk_on_termination = true
storage_image_reference {
publisher = "MicrosoftWindowsDesktop"
offer = "Windows-10"
sku = "19h1-pron"
version = "latest"
}
os_profile {
computer_name = "win10"
admin_username = "vagrant"
admin_password = "Vagrant123"
custom_data = local.custom_data_content
}
os_profile_windows_config {
provision_vm_agent = true
enable_automatic_upgrades = false
# Auto-Login's required to configure WinRM
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "AutoLogon"
content = "<AutoLogon><Password><Value>Vagrant123</Value></Password><Enabled>true</Enabled><LogonCount>1</LogonCount><Username>vagrant</Username></AutoLogon>"
}
# Unattend config is to enable basic auth in WinRM, required for the provisioner stage.
# https://github.com/terraform-providers/terraform-provider-azurerm/blob/master/examples/virtual-machines/provisioners/windows/files/FirstLogonCommands.xml
additional_unattend_config {
pass = "oobeSystem"
component = "Microsoft-Windows-Shell-Setup"
setting_name = "FirstLogonCommands"
content = file("${path.module}/files/FirstLogonCommands.xml")
}
}
storage_os_disk {
name = "OsDiskWin10"
caching = "ReadWrite"
create_option = "FromImage"
managed_disk_type = "Standard_LRS"
}
tags = {
role = "win10"
}
}

View File

@@ -0,0 +1,35 @@
output "region" {
value = var.region
}
output "logger_public_ip" {
value = azurerm_public_ip.logger-publicip.ip_address
}
output "dc_public_ip" {
value = azurerm_public_ip.dc-publicip.ip_address
}
output "wef_public_ip" {
value = azurerm_public_ip.wef-publicip.ip_address
}
output "win10_public_ip" {
value = azurerm_public_ip.win10-publicip.ip_address
}
output "ata_url" {
value = local.ata_url
}
output "fleet_url" {
value = local.fleet_url
}
output "splunk_url" {
value = local.splunk_url
}
output "guacamole_url" {
value = local.guacamole_url
}

View File

@@ -0,0 +1,10 @@
# https://azure.microsoft.com/en-us/global-infrastructure/locations/
region = "US WEST 2"
public_key_name = "id_logger" # This can be changed to whatever you want
# These values must point to a valid keypair.
# You'll log into the logger host via: ssh -i /home/user/.ssh/id_logger vagrant@<public_logger_ip>
public_key_path = "/home/user/.ssh/id_logger.pub"
private_key_path = "/home/user/.ssh/id_logger"
# Replace the IP address below with the IP address(es) you'll be using
# to connect to DetectionLab
ip_whitelist = ["1.2.3.4/32"]

View File

@@ -0,0 +1,42 @@
variable "region" {
default = "West US 2"
}
variable "profile" {
default = "terraform"
}
variable "availability_zone" {
description = "https://docs.microsoft.com/en-us/azure/availability-zones/az-overview"
default = ""
}
variable "public_key_name" {
description = "A name for SSH Keypair to use to auth to logger. Can be anything you specify."
default = "id_logger"
}
variable "public_key_path" {
description = "Path to the public key to be loaded into the logger authorized_keys file"
type = string
default = "/home/user/.ssh/id_logger.pub"
}
# Note: must use ssh key without passphrase. not supported by Terraform.
variable "private_key_path" {
description = "Path to the private key to use to authenticate to logger."
type = string
default = "/home/user/.ssh/id_logger"
}
variable "ip_whitelist" {
description = "A list of CIDRs that will be allowed to access the instances"
type = list(string)
default = [""]
}
variable "external_dns_servers" {
description = "Configure lab to allow external DNS resolution"
type = list(string)
default = ["8.8.8.8"]
}

View File

@@ -36,11 +36,11 @@ These commands can be run in parallel from three separate terminal sessions.
![Ansible](https://github.com/clong/DetectionLab/blob/master/img/esxi_console.png?raw=true)
4. **(5 Minutes)** Edit the variables in `ESXi/variables.tf` to match your local ESXi configuration or [create a terraform.tfvars file](https://www.terraform.io/docs/configuration/variables.html#variable-definitions-tfvars-files) (RECOMMENDED) to override them.
5. **(25 Minutes)** From `DetectionLab/ESXi`, run `terraform init && terraform apply`
6. Once Terraform has finished bringing the hosts online, change your directory to `DetectionLab/ESXi/ansible`
7. **(1 Minute)** Edit `DetectionLab/ESXi/ansible/inventory.yml` and replace the IP Addresses with the respective IP Addresses of your ESXi VMs. **These IP addresses much be reachable from your host machine!**
6. Once Terraform has finished bringing the hosts online, change your directory to `DetectionLab/ESXi/Ansible`
7. **(1 Minute)** Edit `DetectionLab/ESXi/Ansible/inventory.yml` and replace the IP Addresses with the respective IP Addresses of your ESXi VMs. **These IP addresses much be reachable from your host machine!**
8. **(3 Minute)s** Edit `DetectionLab/ESXi/resources/01-netcfg.yaml`. These are the IP addresses that will be applied to the logger network interfaces. These should be be able to be found in your ESXi console or from the Terraform outputs.
9. **(3 Minute)** Before running any Ansible playbooks, I highly recommend taking snapshots of all your VMs! If anything goes wrong with provisioning, you can simply restore the snapshot and easily debug the issue.
10. Change your directory to `DetectionLab/ESXi/ansible`
10. Change your directory to `DetectionLab/ESXi/Ansible`
11. **(30 Minutes)** Run `ansible-playbook -vvv detectionlab.yml`
12. If all goes well, you should see the following and your lab is complete!
![Ansible](https://github.com/clong/DetectionLab/blob/master/img/esxi_ansible.png?raw=true)

BIN
img/azure_ansible1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

BIN
img/azure_terraform1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB