mirror of
https://github.com/Security-Onion-Solutions/securityonion.git
synced 2025-12-06 17:22:49 +01:00
hypervisor highstate after image creation, not when key accepted
This commit is contained in:
@@ -1,3 +1,14 @@
|
|||||||
|
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
|
||||||
|
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
|
||||||
|
# https://securityonion.net/license; you may not use this file except in compliance with the
|
||||||
|
# Elastic License 2.0.
|
||||||
|
#
|
||||||
|
# Note: Per the Elastic License 2.0, the second limitation states:
|
||||||
|
#
|
||||||
|
# "You may not move, change, disable, or circumvent the license key functionality
|
||||||
|
# in the software, and you may not remove or obscure any functionality in the
|
||||||
|
# software that is protected by the license key."
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This runner performs the initial setup required for hypervisor hosts in the environment.
|
This runner performs the initial setup required for hypervisor hosts in the environment.
|
||||||
It handles downloading the Oracle Linux KVM image, setting up SSH keys for secure
|
It handles downloading the Oracle Linux KVM image, setting up SSH keys for secure
|
||||||
@@ -37,10 +48,12 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import pwd
|
import pwd
|
||||||
import requests
|
import requests
|
||||||
|
import salt.client
|
||||||
import salt.utils.files
|
import salt.utils.files
|
||||||
import socket
|
import socket
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import yaml
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||||
|
|
||||||
@@ -55,6 +68,40 @@ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(messag
|
|||||||
stream_handler.setFormatter(formatter)
|
stream_handler.setFormatter(formatter)
|
||||||
log.addHandler(stream_handler)
|
log.addHandler(stream_handler)
|
||||||
|
|
||||||
|
def _check_license():
|
||||||
|
"""Check if the license file exists and contains required values."""
|
||||||
|
license_path = '/opt/so/saltstack/local/pillar/soc/license.sls'
|
||||||
|
|
||||||
|
if not os.path.exists(license_path):
|
||||||
|
log.error("LICENSE: License file not found at %s", license_path)
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
with salt.utils.files.fopen(license_path, 'r') as f:
|
||||||
|
license_data = yaml.safe_load(f)
|
||||||
|
|
||||||
|
if not license_data:
|
||||||
|
log.error("LICENSE: Empty or invalid license file")
|
||||||
|
return False
|
||||||
|
|
||||||
|
license_id = license_data.get('license_id')
|
||||||
|
features = license_data.get('features', [])
|
||||||
|
|
||||||
|
if not license_id:
|
||||||
|
log.error("LICENSE: No license_id found in license file")
|
||||||
|
return False
|
||||||
|
|
||||||
|
if 'hvn' not in features:
|
||||||
|
log.error("LICENSE: 'hvn' feature not found in license")
|
||||||
|
return False
|
||||||
|
|
||||||
|
log.info("LICENSE: License validation successful")
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
log.error("LICENSE: Error reading license file: %s", str(e))
|
||||||
|
return False
|
||||||
|
|
||||||
def _check_file_exists(path):
|
def _check_file_exists(path):
|
||||||
"""Check if a file exists and create its directory if needed."""
|
"""Check if a file exists and create its directory if needed."""
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
@@ -254,7 +301,7 @@ def _check_vm_exists(vm_name: str) -> bool:
|
|||||||
log.info("MAIN: VM %s already exists", vm_name)
|
log.info("MAIN: VM %s already exists", vm_name)
|
||||||
return exists
|
return exists
|
||||||
|
|
||||||
def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G'):
|
def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G', minion_id: str = None):
|
||||||
"""
|
"""
|
||||||
Main entry point to set up the hypervisor environment.
|
Main entry point to set up the hypervisor environment.
|
||||||
This includes downloading the base image, generating SSH keys for remote access,
|
This includes downloading the base image, generating SSH keys for remote access,
|
||||||
@@ -269,6 +316,14 @@ def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G'):
|
|||||||
Returns:
|
Returns:
|
||||||
dict: Dictionary containing setup status and VM creation results
|
dict: Dictionary containing setup status and VM creation results
|
||||||
"""
|
"""
|
||||||
|
# Check license before proceeding
|
||||||
|
if not _check_license():
|
||||||
|
return {
|
||||||
|
'success': False,
|
||||||
|
'error': 'Invalid license or missing hvn feature',
|
||||||
|
'vm_result': None
|
||||||
|
}
|
||||||
|
|
||||||
log.info("MAIN: Starting setup_environment in setup_hypervisor runner")
|
log.info("MAIN: Starting setup_environment in setup_hypervisor runner")
|
||||||
|
|
||||||
# Check if environment is already set up
|
# Check if environment is already set up
|
||||||
@@ -332,6 +387,21 @@ def setup_environment(vm_name: str = 'sool9', disk_size: str = '220G'):
|
|||||||
success = vm_result.get('success', False)
|
success = vm_result.get('success', False)
|
||||||
log.info("MAIN: Setup environment completed with status: %s", "SUCCESS" if success else "FAILED")
|
log.info("MAIN: Setup environment completed with status: %s", "SUCCESS" if success else "FAILED")
|
||||||
|
|
||||||
|
# If setup was successful and we have a minion_id, run highstate
|
||||||
|
if success and minion_id:
|
||||||
|
log.info("MAIN: Running highstate on hypervisor %s", minion_id)
|
||||||
|
try:
|
||||||
|
# Initialize the LocalClient
|
||||||
|
local = salt.client.LocalClient()
|
||||||
|
# Run highstate on the hypervisor
|
||||||
|
highstate_result = local.cmd(minion_id, 'state.highstate', [], timeout=1800)
|
||||||
|
if highstate_result and minion_id in highstate_result:
|
||||||
|
log.info("MAIN: Highstate completed on %s", minion_id)
|
||||||
|
else:
|
||||||
|
log.error("MAIN: Highstate failed or timed out on %s", minion_id)
|
||||||
|
except Exception as e:
|
||||||
|
log.error("MAIN: Error running highstate on %s: %s", minion_id, str(e))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'success': success,
|
'success': success,
|
||||||
'error': vm_result.get('error') if not success else None,
|
'error': vm_result.get('error') if not success else None,
|
||||||
@@ -349,6 +419,13 @@ def create_vm(vm_name: str, disk_size: str = '220G'):
|
|||||||
Returns:
|
Returns:
|
||||||
dict: Dictionary containing success status and commands to run on hypervisor
|
dict: Dictionary containing success status and commands to run on hypervisor
|
||||||
"""
|
"""
|
||||||
|
# Check license before proceeding
|
||||||
|
if not _check_license():
|
||||||
|
return {
|
||||||
|
'success': False,
|
||||||
|
'error': 'Invalid license or missing hvn feature',
|
||||||
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Input validation
|
# Input validation
|
||||||
if not isinstance(vm_name, str) or not vm_name:
|
if not isinstance(vm_name, str) or not vm_name:
|
||||||
@@ -566,6 +643,11 @@ def regenerate_ssh_keys():
|
|||||||
Returns:
|
Returns:
|
||||||
bool: True if successful, False on error
|
bool: True if successful, False on error
|
||||||
"""
|
"""
|
||||||
|
# Check license before proceeding
|
||||||
|
if not _check_license():
|
||||||
|
log.error("MAIN: Invalid license or missing hvn feature")
|
||||||
|
return False
|
||||||
|
|
||||||
log.info("MAIN: Starting SSH key regeneration")
|
log.info("MAIN: Starting SSH key regeneration")
|
||||||
try:
|
try:
|
||||||
# Verify current state
|
# Verify current state
|
||||||
|
|||||||
@@ -623,6 +623,12 @@ function updateMineAndApplyStates() {
|
|||||||
#checkMine "network.ip_addrs"
|
#checkMine "network.ip_addrs"
|
||||||
# calls so-common and set_minionid sets MINIONID to local minion id
|
# calls so-common and set_minionid sets MINIONID to local minion id
|
||||||
set_minionid
|
set_minionid
|
||||||
|
|
||||||
|
# We don't want a hypervisor node to highstate until the image is downloaded and built. This will be triggered from the setup_hypervisor runner
|
||||||
|
if [[ "$NODETYPE" == "HYPERVISOR" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
# if this is a searchnode or heavynode, start downloading logstash and elasticsearch containers while the manager prepares for the new node
|
# if this is a searchnode or heavynode, start downloading logstash and elasticsearch containers while the manager prepares for the new node
|
||||||
if [[ "$NODETYPE" == "SEARCHNODE" || "$NODETYPE" == "HEAVYNODE" ]]; then
|
if [[ "$NODETYPE" == "SEARCHNODE" || "$NODETYPE" == "HEAVYNODE" ]]; then
|
||||||
salt-run state.orch orch.container_download pillar="{'setup': {'newnode': $MINION_ID }}" > /dev/null 2>&1 &
|
salt-run state.orch orch.container_download pillar="{'setup': {'newnode': $MINION_ID }}" > /dev/null 2>&1 &
|
||||||
@@ -661,7 +667,7 @@ case "$OPERATION" in
|
|||||||
updateMineAndApplyStates
|
updateMineAndApplyStates
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"addVirt")
|
"addVM")
|
||||||
setupMinionFiles
|
setupMinionFiles
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
{% if data['act'] == 'accept' and data['id'].endswith(('_hypervisor', '_managerhyper')) and data['result'] == True %}
|
{% if data['act'] == 'accept' and data['id'].endswith(('_hypervisor', '_managerhyper')) and data['result'] == True %}
|
||||||
check_and_trigger:
|
check_and_trigger:
|
||||||
runner.setup_hypervisor.setup_environment: []
|
runner.setup_hypervisor.setup_environment:
|
||||||
|
- kwargs:
|
||||||
|
minion_id: {{ data['id'] }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def run():
|
|||||||
with open("/opt/so/saltstack/local/pillar/hypervisor/" + hv_name + "/" + minionid + ".sls", 'w') as f:
|
with open("/opt/so/saltstack/local/pillar/hypervisor/" + hv_name + "/" + minionid + ".sls", 'w') as f:
|
||||||
yaml.dump(vm_out_data, f, default_flow_style=False)
|
yaml.dump(vm_out_data, f, default_flow_style=False)
|
||||||
|
|
||||||
rc = call("NODETYPE=" + DATA['NODETYPE'] + " /usr/sbin/so-minion -o=addVirt -m=" + minionid + " -n=" + DATA['MNIC'] + " -i=" + DATA['MAINIP'] + " -a=" + DATA['INTERFACE'] + " -c=" + str(DATA['CPU']) + " -d='" + DATA['NODE_DESCRIPTION'] + "'" + " -e=" + DATA['ES_HEAP_SIZE'] + " -l=" + DATA['LS_HEAP_SIZE'], shell=True)
|
rc = call("NODETYPE=" + DATA['NODETYPE'] + " /usr/sbin/so-minion -o=addVM -m=" + minionid + " -n=" + DATA['MNIC'] + " -i=" + DATA['MAINIP'] + " -a=" + DATA['INTERFACE'] + " -c=" + str(DATA['CPU']) + " -d='" + DATA['NODE_DESCRIPTION'] + "'" + " -e=" + DATA['ES_HEAP_SIZE'] + " -l=" + DATA['LS_HEAP_SIZE'], shell=True)
|
||||||
|
|
||||||
logging.error('sominion_setup reactor: rc: %s' % rc)
|
logging.error('sominion_setup reactor: rc: %s' % rc)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user