Zoho Vault Integration

Log Integration procedure:

Introduction

This document describes a Python script designed to fetch audit logs from Zoho Vault and send them to Logstash for further processing. Prerequisites

• Client credentials for Zoho Vault API access:

o Client ID

o Client Secret

o Refresh Token

• Zoho Vault API URL for audit logs

• Permissions:

o AuditLogRead.All

o DeviceManagementManagedDevices.Read.All

o DeviceManagementManagedDevices.PrivilgedOperations.All

Configuration

• Access Token Retrieval Script

This script retrieves a fresh access token using the refresh token and stores it in a configuration file. Replace placeholders with your credentials and desired file paths.

Code Snippet (Access Token Retrieval Script)

Python

import requests

import time

def get_new_access_token(refresh_token, client_id, client_secret, redirect_uri, grant_type='refresh_token'):

token_url = "https://accounts.zoho.com/oauth/v2/token"

payload = {

'refresh_token': refresh_token,

'client_id': client_id,

'client_secret': client_secret,

'redirect_uri': redirect_uri,

'grant_type': grant_type

}

headers = {

'Content-Type': 'application/x-www-form-urlencoded'

}

response = requests.post(token_url, data=payload, headers=headers)

if response.status_code == 200:

return response.json()

else:

response.raise_for_status()

Configuration (Replace placeholders with your details)

grant_type='refresh_token'

refresh_token = '1000.67b56571eee1b158deab9f23a41dc4ad.9a9456ecd77803400733212fe29379f8'

client_id = '1000.CL8HEMBZNW8LZAADBUNI4ZJBBLB8BA'

client_secret = 'f27949628fa35667f33233854de215bca7ca75be6d'

redirect_uri = 'http://www.zoho.com/'

access_token_file_path = '/opt/lc/conf/zoho/vault_access_token.conf' # Specify the path to store the access token

token_response = get_new_access_token(refresh_token, client_id, client_secret, redirect_uri) access_token = token_response['access_token']

with open(access_token_file_path, 'w') as file:

file.write(f'access_token={access_token}\n')

print('New access token stored in:', access_token_file_path)

print('Token response:', token_response)

Log Fetching and Processing Script

This script fetches logs from Zoho Vault in pages, extracts details, and sends them to Logstash. Update configurations for API URL, log file path, and Logstash details.

Code Snippet (Log Fetching and Processing Script)

Python

import requests

import os

import socket

import json

from datetime import datetime

def read_access_token(file_path):

try:

with open(file_path, 'r') as file:

for line in file:

if line.startswith('access_token='):

key, access_token = line.strip().split('=')

return access_token

except Exception as e:

print(f"An error occurred while reading the access token: {e}")

return None

def call_api_with_access_token(api_url, access_token,rows_per_page, page_number): headers = {

'Authorization': f'Bearer {access_token}'

}

params = {
    "rowsPerPage":rows_per_page,
    "page" : page_number
}

try:
    response = requests.get(api_url, headers=headers,params=params)
    response.raise_for_status()  # Raise an HTTPError for bad responses

    if response.status_code == 200:
        api_data = response.json()
        operation = api_data.get('operation', {})
        details = operation.get('Details', [])

        if details:
            data = details[0]
            start_count = details[1].get('start', 0)
            end_count = details[1].get('end', 0)
            totalrows_count = details[1].get('totalrows', 0)
            return start_count, end_count, totalrows_count,data
        else:
            return 0, 0, 0, {} # Handle case where data structure is unexpected

    else:
        print(f"Unexpected status code: {response.status_code}")
        print(response.text)  # Print response content for debugging
        return 0, 0, 0, {}

except requests.exceptions.HTTPError as err:
    print(f"HTTP error occurred: {err}")
    return 0, 0, 0, {}
except Exception as e:
    print(f"An error occurred: {e}")
    return 0, 0, 0, {}

def read_last_total_count(log_file_path):

try:

with open(log_file_path, 'r') as log_file:

lines = log_file.readlines()

for line in reversed(lines):

if line.startswith('next_start'):

total_count = int(line.split('=')[1].strip())

return total_count

return 0 # If no valid total count found, start from 0

except FileNotFoundError:

return 0 # Start from 0 if file does not exist

def store_total_count_to_file(log_file_path, totalrows_count):

updated_lines = []

count_updated = False

if os.path.exists(log_file_path):
    with open(log_file_path, 'r') as file:
        lines = file.readlines()
else:
    lines = []

for line in lines:
    if line.startswith("next_start"):
        count_updated = True
        updated_lines.append(f"next_start = {totalrows_count}\n")
    else:
        updated_lines.append(line)

if not count_updated:
    updated_lines.append(f"next_start = {totalrows_count}\n")

with open(log_file_path, 'w') as file:
    file.writelines(updated_lines)

def ensure_log_file(log_file_path):

if not os.path.exists(log_file_path):

with open(log_file_path, 'w') as file:

file.write(f"# Logcount for {datetime.now().strftime('%Y-%m-%d')}\n")

def send_to_logstash(logs):

status = -1

try:

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.connect((logstash_host, logstash_port))

for log in logs:

message = json.dumps(log)

sock.sendall(message.encode('utf-8'))

sock.sendall(b'\n')

status = 0

except Exception as e:

print(f"Failed to send logs to Logstash: {e}")

return status

Configuration

access_token_file_path = '/opt/lc/conf/zoho/vault_access_token.conf' # Path to the conf file where the access token is stored

api_url = 'https://vault.zoho.com/api/rest/json/v1/audit/logs' # Replace with the actual API URL you want to call

log_file_path = '/opt/apps/zoho-vault/zoho_audit_activity.conf' # Path to the single log file rows_per_page = 25

page = 1

Logstash configuration

logstash_host = 'localhost' # Change to your Logstash host if different

logstash_port = 12337 # Change to your Logstash port if different

try:

flag = True

while flag:

previous_totalrows_count = read_last_total_count(log_file_path)

if previous_totalrows_count == 0:

previous_totalrows_count = 1

start = previous_totalrows_count

access_token = read_access_token(access_token_file_path)

if access_token:

start_count, end_count, totalrows_count, details = call_api_with_access_token(api_url, access_token,rows_per_page,page)

start_count = start

end_count = end_count

totalrows_count = totalrows_count

page += 1

if totalrows_count == 0:

flag = False

else:

if totalrows_count >= start:

ensure_log_file(log_file_path)

next_start = end_count + 1

store_total_count_to_file(log_file_path, next_start)

send_log = send_to_logstash(details)

if send_log == -1:

print("unable to send logs to logstash")

flag = False

else: print('No new records added.')

flag = False

else:

print('Failed to retrieve the access token.')

print("Data retrieval and storage complete.")

except requests.exceptions.HTTPError as err:

print('HTTP error:', err)

except Exception as e:

print('An error occurred:', e)

Note: Copy the code from Meydan and SPC client.

3. Setup a Logstash pipeline to fetch the logs at input port 12337/tcp and follow further steps.

Last updated