Skip to content

Commit

Permalink
Merge pull request #5216 from wazuh/enhancement/5198-dtt1-allocation-…
Browse files Browse the repository at this point in the history
…bug-instance-connection-error-to4495-dtt1-release

Fix allocation bug instance connection error
  • Loading branch information
teddytpc1 authored Apr 17, 2024
2 parents 6c8b29b + 7170ffc commit 0b7738c
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 30 deletions.
1 change: 1 addition & 0 deletions deployability/deps/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ pytest==7.4.4
paramiko==3.4.0
requests==2.31.0
chardet==5.2.0
pywinrm==0.3.0
82 changes: 80 additions & 2 deletions deployability/modules/allocation/allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# This program is a free software; you can redistribute it and/or modify it under the terms of GPLv2

import yaml
import paramiko, logging, time
import winrm

from pathlib import Path

Expand Down Expand Up @@ -58,8 +60,18 @@ def __create(cls, payload: models.CreationPayload):
instance.start()
logger.info(f"Instance {instance.identifier} started.")
# Generate the inventory and track files.
cls.__generate_inventory(instance, payload.inventory_output)
cls.__generate_track_file(instance, payload.provider, payload.track_output)
inventory = cls.__generate_inventory(instance, payload.inventory_output)
# Validate connection
check_connection = cls.__check_connection(inventory)
track_file = cls.__generate_track_file(instance, payload.provider, payload.track_output)
if check_connection is False:
if payload.rollback:
logger.warning(f"Rolling back instance creation.")
track_payload = {'track_output': track_file}
cls.__delete(track_payload)
logger.info(f"Instance {instance.identifier} deleted.")
else:
logger.warning(f'The VM will not be automatically removed. Please remove it executing Allocation module with --action delete.')

@classmethod
def __delete(cls, payload: models.InstancePayload) -> None:
Expand Down Expand Up @@ -138,6 +150,7 @@ def __generate_inventory(instance: Instance, inventory_path: Path) -> None:
with open(inventory_path, 'w') as f:
yaml.dump(inventory.model_dump(exclude_none=True), f)
logger.info(f"Inventory file generated at {inventory_path}")
return inventory

@staticmethod
def __generate_track_file(instance: Instance, provider_name: str, track_path: Path) -> None:
Expand Down Expand Up @@ -172,3 +185,68 @@ def __generate_track_file(instance: Instance, provider_name: str, track_path: P
if Path(str(instance.path) + "/ppc-key").exists():
Path(str(instance.path) + "/ppc-key").unlink()
logger.info(f"Track file generated at {track_path}")
return track_path

@staticmethod
def __check_connection(inventory: models.InventoryOutput, attempts=30, sleep=30) -> None:
"""
Checks if the ssh connection is successful.
Args:
inventory (InventoryOutput): The inventory object.
attempts (int): The number of attempts to try the connection.
sleep (int): The time to wait between attempts.
"""

for attempt in range(1, attempts + 1):
if inventory.ansible_connection == 'winrm':
if inventory.ansible_port == 5986:
protocol = 'https'
else:
protocol = 'http'
endpoint_url = f'{protocol}://{inventory.ansible_host}:{inventory.ansible_port}'
try:
session = winrm.Session(endpoint_url, auth=(inventory.ansible_user, inventory.ansible_password),transport='ntlm', server_cert_validation='ignore')
cmd = session.run_cmd('ipconfig')
if cmd.status_code == 0:
logger.info("WinRM connection successful.")
return True
else:
logger.error(f'WinRM connection failed. Check the credentials in the inventory file.')
return False
except Exception as e:
logger.warning(f'Error on attempt {attempt} of {attempts}: {e}')
time.sleep(sleep)
else:
ssh = paramiko.SSHClient()
paramiko.util.get_logger("paramiko").setLevel(logging.WARNING)
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
if inventory.ansible_ssh_private_key_file:
ssh_parameters = {
'hostname': inventory.ansible_host,
'port': inventory.ansible_port,
'username': inventory.ansible_user,
'key_filename': inventory.ansible_ssh_private_key_file
}
else:
ssh_parameters = {
'hostname': inventory.ansible_host,
'port': inventory.ansible_port,
'username': inventory.ansible_user,
'password': inventory.ansible_password
}
try:
ssh.connect(**ssh_parameters)
logger.info("SSH connection successful.")
ssh.close()
return True
except paramiko.AuthenticationException:
logger.error(f'Authentication error. Check the credentials in the inventory file.')
return False
except Exception as e:
logger.warning(f'Error on attempt {attempt} of {attempts}: {e}')
time.sleep(sleep)

logger.error(f'Connection attempts failed after {attempts} tries. Connection timeout.')
return False
55 changes: 47 additions & 8 deletions deployability/modules/allocation/aws/helpers/windowsUserData.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,59 @@

<powershell>
try {
Write-Output "Executing winrm quickconfig"
if (-not ([Net.ServicePointManager]::SecurityProtocol -band [Net.SecurityProtocolType]::Tls12)) {
Write-Output "Enabling TLS 1.2"
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
}
# Check if WinRM HTTPS listener is configured
$httpsListener = Get-Item -Path WSMan:\LocalHost\Listener\* | Where-Object { $_.Keys -contains 'Transport' -and $_.Transport -eq 'HTTPS' }

if ($httpsListener) {
# Remove existing HTTPS listener
Write-Output "Removing existing HTTPS listener."
Remove-Item -Path WSMan:\LocalHost\Listener\$($httpsListener.Name) -Force
}
# Create a self-signed certificate
$cert = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName $env:COMPUTERNAME

# Enable PSRemoting and set up HTTPS listener
Enable-PSRemoting -SkipNetworkProfileCheck -Force
New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $cert.Thumbprint -Force

# Add firewall rule for WinRM HTTPS
New-NetFirewallRule -DisplayName "Windows Remote Management (HTTPS-In)" -Name "Windows Remote Management (HTTPS-In)" -Profile Any -LocalPort 5986 -Protocol TCP
$url = "https://raw.githubusercontent.com/ansible/ansible/6e325d9e4dbdc020eb520a81148866d988a5dbc5/examples/scripts/ConfigureRemotingForAnsible.ps1"
$file = "$env:temp\ConfigureRemotingForAnsible.ps1"
(New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)
powershell.exe -ExecutionPolicy ByPass -File $file
Write-Output "WinRM enabled on HTTPS."
} catch {
$_.Exception.Message
"Error enabling WinRM on HTTPS."
Write-Output "Error enabling WinRM on HTTPS."
}
# Check if wazuh-user user exists
if (-not (Get-LocalUser -Name "wazuh-user" -ErrorAction SilentlyContinue)) {
# Create wazuh-user user
Write-Output "Creating wazuh-user user"
$password = ConvertTo-SecureString "ChangeMe" -AsPlainText -Force
New-LocalUser "wazuh-user" -Password $password -FullName "wazuh-user" -Description "wazuh-user user for remote desktop"

Write-Output "Adding wazuh-user user to RDP group."
# Add wazuh-user to Remote Desktop Users group
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "wazuh-user"

Write-Output "Adding wazuh-user user to Administrators group."
# Add wazuh-user to wazuh-users group
Add-LocalGroupMember -Group "Administrators" -Member "wazuh-user"
} else {
Write-Output "wazuh-user user already exists."
# Set the password for the wazuh-user account
$admin = [ADSI]"WinNT://./wazuh-user, user"
$password = "ChangeMe"
$admin.SetPassword($password)
$admin.SetInfo()
Write-Output "wazuh-user password changed successfully."
}
New-LocalUser "Administrator" -Password ChangeMe -FullName "Administrator" -Description "Administrator user for remote desktop"
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "Administrator"
# Set Administrator user to administrator group
Add-LocalGroupMember -Group "Administrators" -Member "Administrator"
# Set the password for the Administrator account
$admin = [ADSI]"WinNT://./Administrator, user"
$admin.SetPassword("ChangeMe")
$admin.SetInfo()
</powershell>
2 changes: 1 addition & 1 deletion deployability/modules/allocation/aws/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def ssh_connection_info(self) -> ConnectionInfo:
if self.platform == 'windows':
return ConnectionInfo(hostname=self._instance.public_dns_name,
user=self._user,
port=3389,
port=5986,
password=str(self.credentials.name))
else:
return ConnectionInfo(hostname=self._instance.public_dns_name,
Expand Down
16 changes: 9 additions & 7 deletions deployability/modules/allocation/aws/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,11 @@ def _create_instance(cls, base_dir: Path, params: CreationPayload, config: AWSCo
instance_dir = Path(base_dir, instance_id)
logger.debug(f"Renaming temp {temp_dir} directory to {instance_dir}")
os.rename(temp_dir, instance_dir)
if not ssh_key:
credentials.key_path = (instance_dir / credentials.name)
else:
credentials.key_path = (os.path.splitext(ssh_key)[0])
if platform != "windows":
if not ssh_key:
credentials.key_path = (instance_dir / credentials.name)
else:
credentials.key_path = (os.path.splitext(ssh_key)[0])

instance_params = {}
instance_params['instance_dir'] = instance_dir
Expand Down Expand Up @@ -159,16 +160,17 @@ def _destroy_instance(cls, destroy_parameters: InstancePayload) -> None:
destroy_parameters (InstancePayload): The parameters for destroying the instance.
"""
credentials = AWSCredentials()
key_id = os.path.basename(destroy_parameters.key_path)
credentials.load(key_id)
if destroy_parameters.platform != 'windows':
key_id = os.path.basename(destroy_parameters.key_path)
credentials.load(key_id)
instance_params = {}
instance_params['instance_dir'] = destroy_parameters.instance_dir
instance_params['identifier'] = destroy_parameters.identifier
instance_params['platform'] = destroy_parameters.platform
instance_params['host_identifier'] = destroy_parameters.host_identifier

instance = AWSInstance(InstancePayload(**instance_params), credentials)
if os.path.dirname(destroy_parameters.key_path) == str(destroy_parameters.instance_dir):
if os.path.dirname(destroy_parameters.key_path) == str(destroy_parameters.instance_dir) and destroy_parameters.platform != 'windows':
logger.debug(f"Deleting credentials: {instance.credentials.name}")
instance.credentials.delete()
instance.delete()
Expand Down
1 change: 1 addition & 0 deletions deployability/modules/allocation/generic/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class InputPayload(BaseModel):
label_team: str | None = None
label_termination_date: str | None = None
instance_name: str | None = None
rollback: bool

class CreationPayload(InputPayload):
provider: str
Expand Down
1 change: 1 addition & 0 deletions deployability/modules/allocation/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def parse_arguments():
parser.add_argument("--label-team", required=False, default=None)
parser.add_argument("--label-termination-date", required=False, default=None)
parser.add_argument("--instance-name", required=False, default=None)
parser.add_argument("--rollback", choices=['True', 'False'], required=False, default=True)
return parser.parse_args()


Expand Down
18 changes: 7 additions & 11 deletions deployability/modules/allocation/static/specs/os.yml
Original file line number Diff line number Diff line change
Expand Up @@ -367,24 +367,20 @@ aws:
windows-desktop-10-amd64:
ami: ami-0a747df120215911a
zone: us-east-1
user: Administrator
windows-desktop-11-amd64:
ami: ami-09d8cef159442d5b0
zone: us-east-1
user: Jenkins
user: wazuh-user
windows-server-2012r2-amd64:
ami: ami-09a3ef2bc0c6a252f
ami: ami-05710c71113d5a40e
zone: us-east-1
user: Administrator
user: wazuh-user
windows-server-2016-amd64:
ami: ami-04d7825822fe66af3
ami: ami-0f279f6c0764bef8f
zone: us-east-1
user: Administrator
user: wazuh-user
windows-server-2019-amd64:
ami: ami-06cc514f1012a7431
zone: us-east-1
user: Administrator
user: wazuh-user
windows-server-2022-amd64:
ami: ami-0f9c44e98edf38a2b
zone: us-east-1
user: Administrator
user: wazuh-user
2 changes: 1 addition & 1 deletion deployability/modules/allocation/vagrant/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ def ssh_connection_info(self) -> ConnectionInfo:
if match and key == 'hostname':
ip = match.group(1).strip()
ssh_config['hostname'] = ip
ssh_config['port'] = 3389
ssh_config['port'] = 5985
ssh_config['user'] = 'vagrant'
ssh_config['password'] = 'vagrant'
else:
Expand Down

0 comments on commit 0b7738c

Please sign in to comment.