#!/usr/bin/env python3
"""Rsyslog Centralization Agent - Generates and deploys TLS-secured rsyslog configurations."""

import json
import logging
import argparse
import subprocess
from datetime import datetime

from jinja2 import Template

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)

SERVER_TEMPLATE = Template("""\
# Rsyslog Server Configuration - TLS Centralized Logging
# Generated by Syslog Centralization Agent

# Load modules
module(load="imuxsock")
module(load="imklog")
module(load="imtcp"
    StreamDriver.Name="gtls"
    StreamDriver.Mode="1"
    StreamDriver.Authmode="x509/name"
    PermittedPeer=["{{ permitted_peers | join('","') }}"]
)

# TLS Certificate Configuration
global(
    DefaultNetstreamDriver="gtls"
    DefaultNetstreamDriverCAFile="{{ ca_cert }}"
    DefaultNetstreamDriverCertFile="{{ server_cert }}"
    DefaultNetstreamDriverKeyFile="{{ server_key }}"
)

# TLS Listener
input(type="imtcp" port="{{ tls_port }}")

# Templates
template(name="PerHostDir" type="string"
    string="/var/log/remote/%HOSTNAME%/%PROGRAMNAME%.log")

template(name="JSONFormat" type="string"
    string='{"timestamp":"%TIMESTAMP:::date-rfc3339%","host":"%HOSTNAME%","facility":"%syslogfacility-text%","severity":"%syslogseverity-text%","program":"%PROGRAMNAME%","msg":"%msg:::json%"}\\n')

template(name="PerHostJSON" type="string"
    string="/var/log/remote/%HOSTNAME%/json/%PROGRAMNAME%.json")

# Rules - Store per-host with standard format
*.* ?PerHostDir

# Also store in JSON format for SIEM ingestion
*.* ?PerHostJSON;JSONFormat

# High-severity alerts to dedicated file
*.err /var/log/remote/errors.log
""")

CLIENT_TEMPLATE = Template("""\
# Rsyslog Client Configuration - TLS Forwarding
# Generated by Syslog Centralization Agent

# TLS Certificate Configuration
global(
    DefaultNetstreamDriver="gtls"
    DefaultNetstreamDriverCAFile="{{ ca_cert }}"
    DefaultNetstreamDriverCertFile="{{ client_cert }}"
    DefaultNetstreamDriverKeyFile="{{ client_key }}"
)

# Forward all logs to central server with TLS and reliable queue
action(
    type="omfwd"
    target="{{ server_ip }}"
    port="{{ tls_port }}"
    protocol="tcp"
    StreamDriver="gtls"
    StreamDriverMode="1"
    StreamDriverAuthMode="x509/name"
    StreamDriverPermittedPeers="{{ server_ip }}"
    queue.type="LinkedList"
    queue.filename="fwdRule1"
    queue.maxdiskspace="{{ queue_disk_space }}"
    queue.saveonshutdown="on"
    queue.size="{{ queue_size }}"
    action.resumeRetryCount="-1"
    action.resumeInterval="30"
)
""")


def generate_server_config(server_ip, clients, ca_cert, server_cert, server_key, tls_port=6514):
    """Generate rsyslog server configuration with TLS."""
    config = SERVER_TEMPLATE.render(
        permitted_peers=clients + [server_ip],
        ca_cert=ca_cert,
        server_cert=server_cert,
        server_key=server_key,
        tls_port=tls_port,
    )
    logger.info("Generated server config for %s with %d permitted peers", server_ip, len(clients))
    return config


def generate_client_config(server_ip, ca_cert, client_cert, client_key, tls_port=6514):
    """Generate rsyslog client configuration with TLS forwarding."""
    config = CLIENT_TEMPLATE.render(
        server_ip=server_ip,
        ca_cert=ca_cert,
        client_cert=client_cert,
        client_key=client_key,
        tls_port=tls_port,
        queue_disk_space="1g",
        queue_size="50000",
    )
    logger.info("Generated client config forwarding to %s:%d", server_ip, tls_port)
    return config


def generate_tls_certificates(output_dir, server_cn, client_cns):
    """Generate CA, server, and client TLS certificates using OpenSSL."""
    ca_key = f"{output_dir}/ca-key.pem"
    ca_cert = f"{output_dir}/ca.pem"
    subprocess.run([
        "openssl", "req", "-x509", "-newkey", "rsa:4096", "-keyout", ca_key,
        "-out", ca_cert, "-days", "3650", "-nodes",
        "-subj", f"/CN=Syslog CA/O=SOC/C=US",
    ], capture_output=True, check=True, timeout=120)
    logger.info("Generated CA certificate: %s", ca_cert)

    for cn in [server_cn] + client_cns:
        key_file = f"{output_dir}/{cn}-key.pem"
        cert_file = f"{output_dir}/{cn}-cert.pem"
        csr_file = f"{output_dir}/{cn}.csr"
        subprocess.run([
            "openssl", "req", "-newkey", "rsa:2048", "-keyout", key_file,
            "-out", csr_file, "-nodes", "-subj", f"/CN={cn}/O=SOC/C=US",
        ], capture_output=True, check=True, timeout=120)
        subprocess.run([
            "openssl", "x509", "-req", "-in", csr_file, "-CA", ca_cert,
            "-CAkey", ca_key, "-CAcreateserial", "-out", cert_file, "-days", "365",
        ], capture_output=True, check=True, timeout=120)
        logger.info("Generated certificate for %s", cn)
    return ca_cert


def deploy_config_ssh(host, config_content, remote_path, username="root", key_file=None):
    """Deploy rsyslog configuration to a remote host via SSH."""
    import paramiko
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    connect_kwargs = {"hostname": host, "username": username}
    if key_file:
        connect_kwargs["key_filename"] = key_file
    client.connect(**connect_kwargs)
    sftp = client.open_sftp()
    with sftp.file(remote_path, "w") as f:
        f.write(config_content)
    sftp.close()
    _, stdout, stderr = client.exec_command("systemctl restart rsyslog")
    exit_status = stdout.channel.recv_exit_status()
    client.close()
    logger.info("Deployed config to %s:%s (restart exit: %d)", host, remote_path, exit_status)
    return exit_status == 0


def validate_tls_connection(server_ip, tls_port=6514, ca_cert=None):
    """Validate TLS connectivity to the rsyslog server."""
    cmd = [
        "openssl", "s_client", "-connect", f"{server_ip}:{tls_port}",
        "-CAfile", ca_cert or "/etc/ssl/certs/ca-certificates.crt",
    ]
    try:
        result = subprocess.run(cmd, capture_output=True, text=True, timeout=10, input="")
        connected = "Verify return code: 0" in result.stdout
        logger.info("TLS validation to %s:%d: %s", server_ip, tls_port, "OK" if connected else "FAILED")
        return connected
    except subprocess.TimeoutExpired:
        return False


def generate_report(server_config, client_configs, deployments, tls_valid):
    """Generate syslog centralization deployment report."""
    report = {
        "timestamp": datetime.utcnow().isoformat(),
        "server_config_generated": bool(server_config),
        "client_configs_generated": len(client_configs),
        "deployments": deployments,
        "tls_validated": tls_valid,
    }
    print(f"SYSLOG REPORT: {len(client_configs)} client configs, TLS: {'OK' if tls_valid else 'PENDING'}")
    return report


def main():
    parser = argparse.ArgumentParser(description="Rsyslog Centralization Agent")
    parser.add_argument("--server-ip", required=True, help="Syslog server IP")
    parser.add_argument("--clients", required=True, help="Comma-separated client IPs")
    parser.add_argument("--ca-cert", default="/etc/rsyslog.d/ca.pem")
    parser.add_argument("--server-cert", default="/etc/rsyslog.d/server-cert.pem")
    parser.add_argument("--server-key", default="/etc/rsyslog.d/server-key.pem")
    parser.add_argument("--tls-port", type=int, default=6514)
    parser.add_argument("--deploy", action="store_true", help="Deploy configs via SSH")
    parser.add_argument("--config-dir", default="./rsyslog_configs")
    parser.add_argument("--output", default="syslog_report.json")
    args = parser.parse_args()

    clients = [c.strip() for c in args.clients.split(",")]
    import os
    os.makedirs(args.config_dir, exist_ok=True)

    server_config = generate_server_config(
        args.server_ip, clients, args.ca_cert, args.server_cert, args.server_key, args.tls_port
    )
    with open(f"{args.config_dir}/server.conf", "w") as f:
        f.write(server_config)

    client_configs = {}
    for client_ip in clients:
        config = generate_client_config(
            args.server_ip, args.ca_cert,
            f"/etc/rsyslog.d/{client_ip}-cert.pem",
            f"/etc/rsyslog.d/{client_ip}-key.pem",
            args.tls_port,
        )
        with open(f"{args.config_dir}/client-{client_ip}.conf", "w") as f:
            f.write(config)
        client_configs[client_ip] = config

    deployments = []
    if args.deploy:
        for client_ip, config in client_configs.items():
            ok = deploy_config_ssh(client_ip, config, "/etc/rsyslog.d/99-central.conf")
            deployments.append({"host": client_ip, "success": ok})

    tls_valid = validate_tls_connection(args.server_ip, args.tls_port, args.ca_cert)
    report = generate_report(server_config, client_configs, deployments, tls_valid)

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2)
    logger.info("Report saved to %s", args.output)


if __name__ == "__main__":
    main()
