diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08b5469 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.terraform* +terraform* \ No newline at end of file diff --git a/lw_aws_exploit.sh b/bash/lw_aws_exploit.sh similarity index 100% rename from lw_aws_exploit.sh rename to bash/lw_aws_exploit.sh diff --git a/lw_aws_inventory.sh b/bash/lw_aws_inventory.sh similarity index 100% rename from lw_aws_inventory.sh rename to bash/lw_aws_inventory.sh diff --git a/lw_aws_preflight.sh b/bash/lw_aws_preflight.sh similarity index 100% rename from lw_aws_preflight.sh rename to bash/lw_aws_preflight.sh diff --git a/lw_azure_inventory.sh b/bash/lw_azure_inventory.sh similarity index 100% rename from lw_azure_inventory.sh rename to bash/lw_azure_inventory.sh diff --git a/lw_gcp_inventory.sh b/bash/lw_gcp_inventory.sh similarity index 100% rename from lw_gcp_inventory.sh rename to bash/lw_gcp_inventory.sh diff --git a/pwsh/lw_aws_inventory.ps1 b/pwsh/lw_aws_inventory.ps1 new file mode 100644 index 0000000..f797017 --- /dev/null +++ b/pwsh/lw_aws_inventory.ps1 @@ -0,0 +1,187 @@ +# Script to fetch AWS inventory for Lacework sizing. +# Requirements: awscli + +# You can specify a profile with the `-p $PROFILE_NAME` flag, or get JSON output with the `-json $true` flag. +# Note: +# 1. You can specify multiple accounts by passing a comma seperated list, e.g. "default,qa,test", +# there are no spaces between accounts in the list +# 2. The script takes a while to run in large accounts with many resources, the final count is an aggregation of all resources found. + +param +( + [CmdletBinding()] + [bool] $json = $false, + + [CmdletBinding()] + [string] $p = "default", + + # enable verbose output + [CmdletBinding()] + [bool] $v = $false +) + +if (Get-Command "aws" -ErrorAction SilentlyContinue){ + $aws_installed = $true +}else{ + # setup aws-cli if not present? + throw "aws cli must be installed and configured prior to script execution!" +} + + +# Set the initial counts to zero. +$global:EC2_INSTANCES=0 +$global:RDS_INSTANCES=0 +$global:REDSHIFT_CLUSTERS=0 +$global:ELB_V1=0 +$global:ELB_V2=0 +$global:NAT_GATEWAYS=0 + +function getRegions { + param( + $profile + ) + + $(aws --profile $profile ec2 describe-regions --output json | ConvertFrom-Json).Regions.RegionName +} + +function getInstances { + param( + $profile, + $region + ) + + $(aws --profile $profile ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId]' --region $region --output json --no-paginate | ConvertFrom-Json).Count +} + +function getRDSInstances { + param( + $profile, + $region + ) + + $(aws --profile $profile rds describe-db-instances --region $region --output json --no-paginate | ConvertFrom-Json).DBInstances.Count +} + +function getRedshift { + param( + $profile, + $region + ) + + $(aws --profile $profile redshift describe-clusters --region $region --output json --no-paginate | ConvertFrom-Json).Clusters.Count +} + +function getElbv1 { + param( + $profile, + $region + ) + + $(aws --profile $profile elb describe-load-balancers --region $region --output json --no-paginate | ConvertFrom-Json).LoadBalancerDescriptions.Count +} + +function getElbv2 { + param( + $profile, + $region + ) + + $(aws --profile $profile elbv2 describe-load-balancers --region $region --output json --no-paginate | ConvertFrom-Json).LoadBalancers.Count +} + +function getNatGateways { + param( + $profile, + $region + ) + + $(aws --profile $profile ec2 describe-nat-gateways --region $region --output json --no-paginate | ConvertFrom-Json).NatGateways.Count +} + +function calculateInventory { + + param( + $profile + ) + + foreach ($r in $(getRegions -profile $profile)){ + if ($json -ne $true){ + Write-Host $r + } + + $instances=$(getInstances -region $r -profile $profile) + $global:EC2_INSTANCES=$(($global:EC2_INSTANCES + $instances)) + if ($v -eq $true){ + write-host "Region $r - EC2 instance count $instances" + } + + $rds=$(getRDSInstances -region $r -profile $profile) + $global:RDS_INSTANCES=$(($global:RDS_INSTANCES + $rds)) + if ($v -eq $true){ + write-host "Region $r - RDS instance count $rds" + } + + $redshift=$(getRedshift -region $r -profile $profile) + $global:REDSHIFT_CLUSTERS=$(($global:REDSHIFT_CLUSTERS + $redshift)) + if ($v -eq $true){ + write-host "Region $r - RedShift count $redshift" + } + + $elbv1=$(getElbv1 -region $r -profile $profile) + $global:ELB_V1=$(($global:ELB_V1 + $elbv1)) + if ($v -eq $true){ + write-host "Region $r - ELBv1 instance count $elbv1" + } + + $elbv2=$(getElbv2 -region $r -profile $profile) + $global:ELB_V2=$(($global:ELB_V2 + $elbv2)) + if ($v -eq $true){ + write-host "Region $r - ELBv2 instance count $elbv2" + } + + $natgw=$(getNatGateways -region $r -profile $profile) + $global:NAT_GATEWAYS=$(($global:NAT_GATEWAYS + $natgw)) + if ($v -eq $true){ + write-host "Region $r - NAT_GW instance count $natgw" + } + } + + #return $global:EC2_INSTANCES + $global:RDS_INSTANCES + $global:REDSHIFT_CLUSTERS + $global:ELB_V1 + $global:ELB_V2 + $global:NAT_GATEWAYS +} + +function textoutput { + write-output "######################################################################" + write-output "Lacework inventory collection complete." + write-output "" + write-output "EC2 Instances: $global:EC2_INSTANCES" + write-output "RDS Instances: $global:RDS_INSTANCES" + write-output "Redshift Clusters: $global:REDSHIFT_CLUSTERS" + write-output "v1 Load Balancers: $global:ELB_V1" + write-output "v2 Load Balancers: $global:ELB_V2" + write-output "NAT Gateways: $global:NAT_GATEWAYS" + write-output "====================" + write-output "Total Resources: $($global:EC2_INSTANCES + $global:RDS_INSTANCES + $global:REDSHIFT_CLUSTERS + $global:ELB_V1 + $global:ELB_V2 + $global:NAT_GATEWAYS)" +} + +function jsonoutput { + write-output "{" + write-output " `"ec2`": `"$global:EC2_INSTANCES`"," + write-output " `"rds`": `"$global:RDS_INSTANCES`"," + write-output " `"redshift`": `"$global:REDSHIFT_CLUSTERS`"," + write-output " `"v1_lb`": `"$global:ELB_V1`"," + write-output " `"v2_lb`": `"$global:ELB_V2`"," + write-output " `"nat_gw`": `"$global:NAT_GATEWAYS`"," + write-output " `"total`": `"$($global:EC2_INSTANCES + $global:RDS_INSTANCES + $global:REDSHIFT_CLUSTERS + $global:ELB_V1 + $global:ELB_V2 + $global:NAT_GATEWAYS)`"" + write-output "}" +} + +foreach ($awsProfile in $($p.Split(",").Trim())){ + calculateInventory -profile $awsProfile +} + +if ($json -eq $true){ + jsonoutput +}else{ + textoutput +} + diff --git a/pwsh/lw_azure_inventory.ps1 b/pwsh/lw_azure_inventory.ps1 new file mode 100644 index 0000000..4a87392 --- /dev/null +++ b/pwsh/lw_azure_inventory.ps1 @@ -0,0 +1,81 @@ +#!/bin/bash +# Script to fetch Azure inventory for Lacework sizing. +# Requirements: az cli + +# This script can be run from Azure Cloud Shell. +param +( + [CmdletBinding()] + [bool] $json = $false, + + # enable verbose output + [CmdletBinding()] + [bool] $v = $false +) + +# Set the initial counts to zero. +$AZURE_VMS=0 +$SQL_SERVERS=0 +$LOAD_BALANCERS=0 +$GATEWAYS=0 + +function getVMs { + $(az vm list -d --query "[?powerState=='VM running']" | ConvertFrom-Json).Count +} + +function getSQLServers { + $(az sql server list | ConvertFrom-Json).Count +} + +function getLoadBalancers { + $(az network lb list | ConvertFrom-Json).Count +} + +write-host "Starting inventory check." +write-host "Fetching VMs..." +$vms=$(getVMs) +$AZURE_VMS=$(($AZURE_VMS + $vms)) + +write-host "Fetching SQL Databases..." +$sql=$(getSQLServers) +$SQL_SERVERS=$(($SQL_SERVERS + $sql)) + +write-host "Fetching Load Balancers..." +$lbs=$(getLoadBalancers) +$LOAD_BALANCERS=$(($LOAD_BALANCERS + $lbs)) + +write-host "Fetching Gateways..." +#TODO -- replace this with a resource graph query... +# Microsoft.Network/virtualNetworkGateways +# need to run this to avoid an interactive prompt to use the resource graph extension +az config set extension.use_dynamic_install=yes_without_prompt +$GATEWAYS= $(az graph query -q "Resources | where type =~ 'Microsoft.Network/virtualNetworkGateways' | summarize count=count()" | ConvertFrom-Json).data.count + + +function textoutput { + write-output "######################################################################" + write-output "Lacework inventory collection complete." + write-output "" + write-output "Azure VMs: $AZURE_VMS" + write-output "SQL Servers: $SQL_SERVERS" + write-output "Load Balancers: $LOAD_BALANCERS" + write-output "Vnet Gateways: $GATEWAYS" + write-output "====================" + write-output "Total Resources: $(($AZURE_VMS + $SQL_SERVERS + $LOAD_BALANCERS + $GATEWAYS))" +} + +function jsonoutput { + write-output "{" + write-output " `"vms`": `"$AZURE_VMS`"," + write-output " `"sqlservers`": `"$SQL_SERVERS`"," + write-output " `"lb`": `"$LOAD_BALANCERS`"," + write-output " `"vnetgw`": `"$GATEWAYS`"," + Write-output " `"total`": `"$($AZURE_VMS + $SQL_SERVERS + $LOAD_BALANCERS + $GATEWAYS)`"" + write-output "}" +} + +if ($json -eq $true){ + jsonoutput +}else{ + textoutput +} \ No newline at end of file diff --git a/pwsh/lw_gcp_inventory.ps1 b/pwsh/lw_gcp_inventory.ps1 new file mode 100644 index 0000000..e321891 --- /dev/null +++ b/pwsh/lw_gcp_inventory.ps1 @@ -0,0 +1,115 @@ +#!/bin/bash +# Script to fetch GCP inventory for Lacework sizing. +# Requirements: gcloud, jq +param +( + [CmdletBinding()] + [bool] $json = $false, + + # Uncomment and replace with your own list of projects. Otherwise the script + # scans all the projects in your organization. You must use the Project ID. + #project_ids=@("stitch-dev-289221","stitch-vault","stitch-jenkins-288315","stitch-infra") + [CmdletBinding()] + [array] $project_ids = $null, + + # enable verbose output + [CmdletBinding()] + [bool] $v = $false +) + +# Set the initial counts to zero. +$global:GCE_INSTANCES=0 +$global:GKE_INSTANCES=0 +$global:SQL_INSTANCES=0 +$global:LOAD_BALANCERS=0 +$global:GATEWAYS=0 + +function getProjects { + $(gcloud projects list --format json | ConvertFrom-Json).projectId +} + +function isComputeEnabled { + #gcloud services list --format json | jq -r '.[] | .name' | grep -q "compute.googleapis.com" + $(gcloud services list --format json | ConvertFrom-Json).name -contains "compute.googleapis.com" +} + +# NOTE - it is technically possible to have a CloudSQL instance without the +# sqladmin API enabled; but you cannot check the instance programatically +# without the API enabled +function isCloudSQLEnabled { + $(gcloud services list --format json | ConvertFrom-Json).name -contains "sqladmin.googleapis.com" +} + +function getGKEInstances { + #gcloud compute instances list --format json | jq '[.[] | select(.name | contains("gke-"))] | length' + $((gcloud compute instances list --format json | ConvertFrom-Json).name | Where-Object {$_ -contains "gke-"}).Count +} + +function getGCEInstances { + #gcloud compute instances list --format json | jq '[.[] | select(.name | contains("gke-") | not)] | length' + $((gcloud compute instances list --format json | ConvertFrom-Json).name | Where-Object {$_ -notcontains "gke-"}).Count +} + +function getSQLInstances { + $(gcloud sql instances list --format json | ConvertFrom-Json).Count +} + +function getLoadBalancers { + $(gcloud compute forwarding-rules list --format json | ConvertFrom-Json).Count +} + +function getGateways { + #gcloud compute routers list --format json | jq '[.[] | .nats | length] | add' + $(gcloud compute routers list --format json | ConvertFrom-Json).nats.Count +} + +# Define PROJECT_IDS above to scan a subset of projects. Otherwise we scan +# all of the projects in the organization. +if ($project_ids -eq $null){ + $project_ids=$(getProjects) +} + +# Loop through all the projects and take inventory +foreach ($project in $project_ids){ + write-host "" + write-host "######################################################################" + write-host "Project: $project" + gcloud config set project $project + + if (isComputeEnabled) { + write-host "Checking for compute resources." + # Update the GCE instances + $gce_inst=$(getGCEInstances) + $global:GCE_INSTANCES=$(($global:GCE_INSTANCES + $gce_inst)) + + # Update the GKE instances + $gke_inst=$(getGKEInstances) + $global:GKE_INSTANCES=$(($global:GKE_INSTANCES + $gke_inst)) + + # Update the load balancers + $lbs=$(getLoadBalancers) + $global:LOAD_BALANCERS=$(($global:LOAD_BALANCERS + $lbs)) + + # Update the gateways + $gateways=$(getGateways) + $global:GATEWAYS=$(($global:GATEWAYS + $gateways)) + } + + # Check for SQL instances + if (isCloudSQLEnabled) { + write-host "Checking for Cloud SQL instances." + $sqls=$(getSQLInstances) + $global:SQL_INSTANCES=$(($global:SQL_INSTANCES + $sqls)) + } +} + +write-output "######################################################################" +write-output "Lacework inventory collection complete." +write-output "" +write-output "GCE Instances: $global:GCE_INSTANCES" +write-output "GKE Instances: $global:GKE_INSTANCES" +write-output "Load Balancers: $global:LOAD_BALANCERS" +write-output "Gateways: $global:GATEWAYS" +write-output "SQL Instances: $global:SQL_INSTANCES" +write-output "====================" +write-output "Total Resources: $(($global:GCE_INSTANCES + $global:GKE_INSTANCES + $global:LOAD_BALANCERS + $global:GATEWAYS + $global:SQL_INSTANCES))" diff --git a/test/aws/test.sh b/test/aws/test.sh new file mode 100755 index 0000000..ef97b77 --- /dev/null +++ b/test/aws/test.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +#setup RDS example +git clone https://github.com/terraform-aws-modules/terraform-aws-rds.git +cd ./terraform-aws-rds/examples/complete-mysql +terraform init +terraform apply --auto-approve +cd ../../.. + +# setup EC2 example +git clone https://github.com/terraform-aws-modules/terraform-aws-ec2-instance.git +cd ./terraform-aws-ec2-instance/examples/basic +terraform init +terraform apply --auto-approve +cd ../../.. + +#setup Redshift example +git clone https://github.com/terraform-aws-modules/terraform-aws-redshift.git +cd ./terraform-aws-redshift/examples/complete +terraform init +terraform apply --auto-approve +cd ../../.. + +#setup ELB example +git clone https://github.com/terraform-aws-modules/terraform-aws-elb.git +cd ./terraform-aws-elb/examples/complete +terraform init +terraform apply --auto-approve +cd ../../.. + +#setup NAT Gateway example -- TODO + +bash_results=$(sh ../../bash/lw_aws_inventory.sh -j) +echo $bash_results + +pwsh_results=$(pwsh -c "../../pwsh/lw_aws_inventory.ps1 -json 1") +echo $pwsh_results + +if [[ "$bash_results" == "$pwsh_results" ]]; then + echo "identical results between bash and pwsh!" + exit 0 +else + echo "results do not match!" + exit 1 +fi + +# cleanup +cd ./terraform-aws-rds/examples/complete-mysql +terraform destroy --auto-approve +cd ../../.. +cd ./terraform-aws-ec2-instance/examples/basic +terraform destroy --auto-approve +cd ../../.. +cd ./terraform-aws-redshift/examples/complete +terraform destroy --auto-approve +cd ../../.. +cd ./terraform-aws-elb/examples/complete +terraform destroy --auto-approve +cd ../../.. \ No newline at end of file diff --git a/test/azure/main.tf b/test/azure/main.tf new file mode 100644 index 0000000..042803b --- /dev/null +++ b/test/azure/main.tf @@ -0,0 +1,229 @@ + +provider "azurerm" { + features {} +} + +variable "prefix" { + default = "tfvmex" +} + +resource "azurerm_resource_group" "main" { + name = "${var.prefix}-resources" + location = "East US" +} + +resource "azurerm_virtual_network" "main" { + name = "${var.prefix}-network" + address_space = ["10.0.0.0/16"] + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name +} + +resource "azurerm_subnet" "internal" { + name = "internal" + resource_group_name = azurerm_resource_group.main.name + virtual_network_name = azurerm_virtual_network.main.name + address_prefixes = ["10.0.2.0/24"] +} + +resource "azurerm_network_interface" "main" { + name = "${var.prefix}-nic" + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + + ip_configuration { + name = "testconfiguration1" + subnet_id = azurerm_subnet.internal.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_virtual_machine" "main" { + name = "${var.prefix}-vm" + location = azurerm_resource_group.main.location + resource_group_name = azurerm_resource_group.main.name + network_interface_ids = [azurerm_network_interface.main.id] + vm_size = "Standard_DS1_v2" + + # Uncomment this line to delete the OS disk automatically when deleting the VM + delete_os_disk_on_termination = true + + # Uncomment this line to delete the data disks automatically when deleting the VM + delete_data_disks_on_termination = true + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + storage_os_disk { + name = "myosdisk1" + caching = "ReadWrite" + create_option = "FromImage" + managed_disk_type = "Standard_LRS" + } + os_profile { + computer_name = "hostname" + admin_username = "testadmin" + admin_password = "Password1234!" + } + os_profile_linux_config { + disable_password_authentication = false + } + tags = { + environment = "staging" + } +} + +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West US" +} + +resource "random_id" "storageaccount" { + byte_length = 8 +} + +resource "azurerm_storage_account" "example" { + name = "${lower(random_id.storageaccount.hex)}" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_mssql_server" "example" { + name = "example-sqlserver" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + version = "12.0" + administrator_login = "4dm1n157r470r" + administrator_login_password = "4-v3ry-53cr37-p455w0rd" +} + +resource "azurerm_mssql_database" "test" { + name = "acctest-db-d" + server_id = azurerm_mssql_server.example.id + collation = "SQL_Latin1_General_CP1_CI_AS" + license_type = "LicenseIncluded" + max_size_gb = 4 + read_scale = true + sku_name = "BC_Gen5_2" + zone_redundant = false + + extended_auditing_policy { + storage_endpoint = azurerm_storage_account.example.primary_blob_endpoint + storage_account_access_key = azurerm_storage_account.example.primary_access_key + storage_account_access_key_is_secondary = true + retention_in_days = 6 + } + + + tags = { + foo = "bar" + } + +} + +resource "azurerm_resource_group" "example2" { + name = "LoadBalancerRG" + location = "West US" +} + +resource "azurerm_public_ip" "example2" { + name = "PublicIPForLB" + location = "West US" + resource_group_name = azurerm_resource_group.example2.name + allocation_method = "Static" +} + +resource "azurerm_lb" "example2" { + name = "TestLoadBalancer" + location = "West US" + resource_group_name = azurerm_resource_group.example2.name + + frontend_ip_configuration { + name = "PublicIPAddress" + public_ip_address_id = azurerm_public_ip.example2.id + } +} + +resource "azurerm_virtual_network" "example2" { + name = "test" + location = azurerm_resource_group.example2.location + resource_group_name = azurerm_resource_group.example2.name + address_space = ["10.0.0.0/16"] +} + +resource "azurerm_subnet" "example2" { + name = "GatewaySubnet" + resource_group_name = azurerm_resource_group.example2.name + virtual_network_name = azurerm_virtual_network.example2.name + address_prefixes = ["10.0.1.0/24"] +} + +resource "azurerm_public_ip" "example3" { + name = "test" + location = azurerm_resource_group.example2.location + resource_group_name = azurerm_resource_group.example2.name + + allocation_method = "Dynamic" +} + +resource "azurerm_virtual_network_gateway" "example2" { + name = "test" + location = azurerm_resource_group.example2.location + resource_group_name = azurerm_resource_group.example2.name + + type = "Vpn" + vpn_type = "RouteBased" + + active_active = false + enable_bgp = false + sku = "Basic" + + ip_configuration { + name = "vnetGatewayConfig" + public_ip_address_id = azurerm_public_ip.example3.id + private_ip_address_allocation = "Dynamic" + subnet_id = azurerm_subnet.example2.id + } + + vpn_client_configuration { + address_space = ["10.2.0.0/24"] + + root_certificate { + name = "DigiCert-Federated-ID-Root-CA" + + public_cert_data = <