Interacting with Cyberwatch API using Ansible to manage agentless servers

Interacting with Cyberwatch API using Ansible to manage agentless servers

Introduction

I decided to start sharing some of my daily tasks in order to share knowldge and help others to gain time.

Luckely, I was requested today to add more than 160 servers to cyberwatch to be monitored, without using any kind of agents. And hopefully cyberwatch offer this kind of features (I used ssh in my case) but unfortunately library is not well documented.

You may asked me why I combined ansible with python,and not using only one of them? You might be right we can do that, but in my case, in addition to all ansible features, I chose to use it in order to benefits from dynamic inventories, to retrieve hosts from different kind of cloud providers. That I may talk about them in future articles.

In this article, we will present a script and an Ansible playbook that can be used to add, update, delete, and get agentless servers on Cyberwatch using ssh. Cyberwatch is a security and vulnerability management platform that helps organizations protect their IT infrastructure. It provides real-time visibility into the security posture of an organization's assets and helps identify and prioritize vulnerabilities.

One of the features of Cyberwatch is the ability to connect to remote servers using agentless connections. These connections allow Cyberwatch to scan for vulnerabilities and perform other security tasks on the remote servers without installing any agents. This can be especially useful in environments where installing agents is not possible or desirable.

The script provided in this article is a Python script that uses the Cyberwatch API to create, update, delete, and get agentless servers based on ssh. It takes various parameters as command-line arguments, including the action to perform (add, update, delete, or get), the address and port of the server, the login, the ssh key file, the node_id, and the server_groups. The script also reads the API key and secret key from environment variables.

The ansible playbook provided in this article uses the script to perform the desired action on a remote server. It first sets up a cyberwatch user on the remote server and copies the cyberwatch ssh public key to the authorized_keys file. It then sets various parameters, including the action to perform, the address and port of the server, the login, the ssh key file, the node_id, and the server_groups, as ansible facts. Finally, it runs the script on the localhost and prints the result.

Below, we will describe the script and the playbook in more detail and provide examples of how to use them.

Description of the script

The script is designed to interact with the Cyberwatch API in order to manage agentless servers. It does this by using the CBWApi object from the cbw_api_toolbox library, which allows the script to make various API calls.

The script begins by importing the necessary libraries and defining four functions: get_server, add_server, update_server, and delete_server. These functions are used to retrieve, create, update, and delete agentless servers in Cyberwatch, respectively by interacting with the Cyberwatch API.

Next, the script retrieves the necessary API credentials from environment variables: the base URL for the Cyberwatch API, the API key, and the secret key. These are used to create a CBWApi object, which is used to make API calls throughout the rest of the script.

The script then parses the command-line arguments passed to it. These arguments include the action to be performed (either add, update, delete, or get), the IP address or domain name of the target server, the port number to use, the login credentials, and the location of the ssh key file. If the action is add or update, the script also retrieves the node ID and server groups to be associated with the server.

Next, the script reads the contents of the ssh key file and stores it in a variable. It then creates the INFO dictionary, which includes all of the necessary parameters for the API call being made.

Based on the action passed as a command-line argument, the script calls one of the four functions defined at the beginning of the script. If the action is add, it calls add_server and passes the INFO dictionary as an argument. If the action is get, it calls get_server and passes the ID of the server to be retrieved as an argument. If the action is update, it calls update_server and passes both the ID of the server to be updated and the INFO dictionary as arguments. Finally, if the action is delete, it calls delete_server and passes the ID of the server to be deleted as an argument.

The script ends by handling any errors that may occur during the API call and printing the appropriate message.

Description of the playbook

The playbook starts by creating a user called cyberwatch on all the hosts in the inventory. This user is added to the sudo group and given a /bin/bash shell. This user will be used to access to servers by the cyberwatch main server and then to check vulnurabilities.

Next, the playbook allows the cyberwatch user to sudo without a password by adding a line to the /etc/sudoers file.

The playbook then creates a .ssh directory for the cyberwatch user and sets the appropriate permissions.

The public key for the cyberwatch user is then copied to the authorized_keys file in the .ssh directory. The permissions for the authorized_keys file are also set.

After setting up the cyberwatch user, the playbook sets some variables using the set_fact module. These variables include the action to be performed (add, update, delete, or get), the ID of the server (if applicable), the IP address or domain name, the port number, the login, the path to the ssh key file, the node ID, and the server groups.

Finally, the playbook runs the python script using the script module. The script is run on the local host using the delegate_to parameter, to ensure that the script will run on the controle node. The environment variables required by the script are also set using the environment parameter.

The output of the script is registered as the result variable, which is then printed using the debug module.

In summary, the cyberwatch.yml playbook sets up the necessary prerequisites for running the cyberwatch.py script, sets the variables needed by the script, and then runs the script on the local host to add, update, delete, or get agentless servers from Cyberwatch based on ssh.

Usage of script

To use the script to add a server to Cyberwatch, need first if you like to create a virtual environment and install requirements (see source code section below). Then, you can run the following command:

python cyberwatch.py add ADDRESS PORT LOGIN KEY_FILE NODE_ID SERVER_GROUPS         

Where:

  • ADDRESS is the IP address or domain name of the targeted computer.
  • PORT is the port of the connection.
  • LOGIN is the login of the connection.
  • KEY_FILE is the path to the ssh key file used for the connection.
  • NODE_ID is the node ID to link the new agentless connection to.
  • SERVER_GROUPS is a comma-separated list of groups to be added to the computer (e.g. "groupA,groupB,groupC"). This argument is optional.

For example, to add a server with the IP address 192.168.1.10, port 22, login "cyberwatch", ssh key located at "/home/ansible/.ssh/cyberwatch", linked to node 1, and added to the "devops" group, you can run the following command:


python cyberwatch.py add 192.168.1.10 22 cyberwatch /home/ansible/.ssh/cyberwatch 1 devops        

Usage of playbook

Here is an example of how to use the playbook with an inventory file:

  1. Create an inventory file with the servers you want to add to Cyberwatch. Here is an example inventory file:


[servers] 
server1 ansible_host=1.1.1.1 ansible_user=cyberwatch 
server2 ansible_host=2.2.2.2 ansible_user=cyberwatch         

  1. Run the playbook using the inventory file:


ansible-playbook -i inventory.ini cyberwatch.yml         

This will add the servers server1 and server2 to Cyberwatch using the ssh credentials of the cyberwatch user.

Source Code

# Playbook (cyberwatch.yml)

---
- name: Create Cyberwatch user
? hosts: all
? become: true


? tasks:
? ? - name: Create the Cyberwatch user
? ? ? user:
? ? ? ? name: cyberwatch
? ? ? ? groups: sudo
? ? ? ? shell: /bin/bash


? ? - name: Allow Cyberwatch user to sudo without password
? ? ? lineinfile:
? ? ? ? path: /etc/sudoers
? ? ? ? regexp: "^%sudo"
? ? ? ? line: "%sudo ALL=(ALL) NOPASSWD: ALL"


? ? - name: Create .ssh directory for Cyberwatch user
? ? ? file:
? ? ? ? path: /home/cyberwatch/.ssh
? ? ? ? state: directory
? ? ? ? owner: cyberwatch
? ? ? ? group: cyberwatch
? ? ? ? mode: 0700


? ? - name: Copy Cyberwatch public key to authorized_keys
? ? ? copy:
? ? ? ? src: /home/ansible/.ssh/cyberwatch.pub
? ? ? ? dest: /home/cyberwatch/.ssh/authorized_keys
? ? ? ? owner: cyberwatch
? ? ? ? group: cyberwatch
? ? ? ? mode: 0600


? ? - name: Set permissions on authorized_keys
? ? ? file:
? ? ? ? path: /home/cyberwatch/.ssh/authorized_keys
? ? ? ? mode: 0600
? ? ? ? owner: cyberwatch
? ? ? ? group: cyberwatch


? ? - name: Set parameters
? ? ? tags:
? ? ? ? - "api"
? ? ? set_fact:
? ? ? ? action: "add" # "add" or "update" or "delete" or "get"
? ? ? ? id: "20"
? ? ? ? address: "{{ hostvars[inventory_hostname]['ansible_default_ipv4']['address'] }}"
? ? ? ? port: "22"
? ? ? ? login: "cyberwatch"
? ? ? ? key_file: /home/ansible/.ssh/cyberwatch
? ? ? ? node_id: "1"
? ? ? ? server_groups: "devops"


? ? - name: run python script
? ? ? tags:
? ? ? ? - "api"
? ? ? script: cyberwatch.py "{{ action }}" "{{ address }}" "{{ port }}" "{{ login }}" "{{ key_file }}" "{{ node_id }}" "{{ server_groups }}" "{{ id }}"
? ? ? delegate_to: localhost
? ? ? register: result
? ? ? environment:
? ? ? ? CBW_BASE_URL: ""
? ? ? ? CBW_API_KEY: ""
? ? ? ? CBW_SECRET_KEY: ""


? ? - name: Debug result
? ? ? tags:
? ? ? ? - api
? ? ? debug:
? ? ? ? var: result 
        

# Python script (cyberwatch.py)


import o
import sys
from cbw_api_toolbox.cbw_api import CBWApi


def get_server(id):
? ? server = cbw.remote_access(id)
? ? print(f"Server details: {server}")
? ? return server


def add_server(server):
? ? server = cbw.create_remote_access(server)
? ? print(f"Successfully added server with ID {server['id']}")


def update_server(id, server):
? ? server = cbw.update_remote_access(id, server)
? ? print(f"Successfully updated server with ID {id}")


def delete_server(id):
? ? server = cbw.delete_remote_access(id)
? ? print(f"Successfully deleted server with ID {id}")


# Get the base URL for the Cyberwatch API from the environment variable
BASE_URL = os.environ.get("CBW_BASE_URL")
if BASE_URL is None:
? ? print("Error: CBW_BASE_URL environment variable is not set.")
? ? sys.exit(1)


# Get the API key from the environment variable
API_KEY = os.environ.get("CBW_API_KEY")
if API_KEY is None:
? ? print("Error: CBW_API_KEY environment variable is not set.")
? ? sys.exit(1)


# Get the secret key from the environment variable
SECRET_KEY = os.environ.get("CBW_SECRET_KEY")
if SECRET_KEY is None:
? ? print("Error: CBW_SECRET_KEY environment variable is not set.")
? ? sys.exit(1)


# Create a CBWApi object with SSL verification disabled
cbw = CBWApi(api_url=BASE_URL, api_key=API_KEY, secret_key=SECRET_KEY, verify_ssl=False)


# Parse the command-line arguments
if len(sys.argv) < 6:
? ? print("Error: Required parameters are missing.")
? ? sys.exit(1)


action = sys.argv[1]
address = sys.argv[2]
port = sys.argv[3]
login = sys.argv[4]
key_file = sys.argv[5]
node_id = sys.argv[6]


# Open the ssh key file
with open(key_file, "r") as f:
? ? key = f.read()


# Set the node_id and server_groups variables to empty strings if there are not enough arguments
if len(sys.argv) < 8:
? ? server_groups = ""
else:
? ? server_groups = sys.argv[7]


# Create the INFO dictionary with the required parameters
INFO = {
? ? "type": "CbwRam::RemoteAccess::Ssh::WithKey",
? ? "address": address,
? ? "port": port,
? ? "login": login,
? ? "key": key,
? ? "node_id": node_id,
? ? "server_groups": server_groups,
}


if action == "add":
? ? add_server(INFO)
elif action == "get":
? ? if len(sys.argv) < 9:
? ? ? ? print("Error: Required ID parameter is missing for get action.")
? ? ? ? sys.exit(1)
? ? id = sys.argv[8]
? ? get_server(id)
elif action == "update":
? ? if len(sys.argv) < 9:
? ? ? ? print("Error: Required ID parameter is missing for update action.")
? ? ? ? sys.exit(1)
? ? id = sys.argv[8]
? ? update_server(id, INFO)
elif action == "delete":
? ? if len(sys.argv) < 9:
? ? ? ? print("Error: Required ID parameter is missing for delete action.")
? ? ? ? sys.exit(1)
? ? id = sys.argv[8]
? ? delete_server(id)
else:
? ? print("Error: Invalid action. Must be 'add', 'update', or 'delete'.")
? ? sys.exit(1)
        

# requirement.txt


ansible==6.7.
ansible-core==2.13.7
cbw-api-toolbox==2.3.1
certifi==2022.12.7
cffi==1.15.1
chardet==5.1.0
charset-normalizer==2.0.12
cryptography==36.0.0
et-xmlfile==1.1.0
idna==3.4
Jinja2==3.1.2
lxml==4.9.2
MarkupSafe==2.1.1
openpyxl==3.0.10
packaging==22.0
pycparser==2.21
pyOpenSSL==22.0.0
python-dateutil==2.8.2
pyvmomi==7.0.3
PyYAML==6.0
requests==2.28.1
resolvelib==0.8.1
six==1.16.0
toml==0.10.2
urllib3==1.26.13
vSphere-Automation-SDK==1.80.0
xlrd==2.0.1
XlsxWriter==3.0.30        



Author

要查看或添加评论,请登录

Anas DADI ??的更多文章

  • Big Data Technologies Resume

    Big Data Technologies Resume

    As a Big Data and Cloud Computing engineering student in ENSET Mohammedia, I attended some remarkable Big data courses…

    6 条评论