#!/usr/bin/env python3 """Delinea Secret Server PAM agent for privileged credential management.""" import json import sys import argparse from datetime import datetime try: import requests except ImportError: print("Install requests: pip install requests") sys.exit(1) class SecretServerClient: """Client for Delinea Secret Server REST API.""" def __init__(self, base_url, username, password, domain=None): self.base_url = base_url.rstrip("/") self.session = requests.Session() self.token = self._authenticate(username, password, domain) self.session.headers.update({"Authorization": f"Bearer {self.token}"}) def _authenticate(self, username, password, domain): """Authenticate and retrieve OAuth2 bearer token.""" url = f"{self.base_url}/oauth2/token" data = {"grant_type": "password", "username": username, "password": password} if domain: data["domain"] = domain resp = self.session.post(url, data=data, timeout=30) resp.raise_for_status() return resp.json()["access_token"] def search_secrets(self, search_text=None, folder_id=None, secret_template_id=None): """Search for secrets with optional filters.""" params = {} if search_text: params["filter.searchText"] = search_text if folder_id: params["filter.folderId"] = folder_id if secret_template_id: params["filter.secretTemplateId"] = secret_template_id resp = self.session.get(f"{self.base_url}/api/v1/secrets", params=params, timeout=30) resp.raise_for_status() return resp.json().get("records", []) def get_secret(self, secret_id): """Retrieve a secret by ID.""" resp = self.session.get(f"{self.base_url}/api/v1/secrets/{secret_id}", timeout=30) resp.raise_for_status() return resp.json() def create_secret(self, name, template_id, folder_id, fields): """Create a new secret in the vault.""" items = [{"fieldId": fid, "itemValue": val} for fid, val in fields.items()] data = {"name": name, "secretTemplateId": template_id, "folderId": folder_id, "items": items} resp = self.session.post(f"{self.base_url}/api/v1/secrets", json=data, timeout=30) resp.raise_for_status() return resp.json() def get_secret_templates(self): """List available secret templates.""" resp = self.session.get(f"{self.base_url}/api/v1/secret-templates", timeout=30) resp.raise_for_status() return resp.json().get("records", []) def get_folders(self, parent_id=None): """List folders, optionally under a parent.""" params = {} if parent_id: params["filter.parentFolderId"] = parent_id resp = self.session.get(f"{self.base_url}/api/v1/folders", params=params, timeout=30) resp.raise_for_status() return resp.json().get("records", []) def rotate_secret_password(self, secret_id): """Trigger password rotation for a secret (Remote Password Changing).""" resp = self.session.post( f"{self.base_url}/api/v1/secrets/{secret_id}/change-password", timeout=30) resp.raise_for_status() return resp.json() def get_secret_audit(self, secret_id): """Get audit trail for a specific secret.""" resp = self.session.get(f"{self.base_url}/api/v1/secrets/{secret_id}/audits", timeout=30) resp.raise_for_status() return resp.json().get("records", []) def checkout_secret(self, secret_id): """Check out a secret for exclusive access.""" resp = self.session.post( f"{self.base_url}/api/v1/secrets/{secret_id}/check-out", timeout=30) resp.raise_for_status() return resp.json() def checkin_secret(self, secret_id): """Check in a previously checked-out secret.""" resp = self.session.post( f"{self.base_url}/api/v1/secrets/{secret_id}/check-in", timeout=30) resp.raise_for_status() return resp.json() def get_users(self): """List all users.""" resp = self.session.get(f"{self.base_url}/api/v1/users", timeout=30) resp.raise_for_status() return resp.json().get("records", []) def get_roles(self): """List all roles.""" resp = self.session.get(f"{self.base_url}/api/v1/roles", timeout=30) resp.raise_for_status() return resp.json().get("records", []) def run_pam_audit(client): """Run a PAM security audit.""" print(f"\n{'='*60}") print(f" DELINEA SECRET SERVER PAM AUDIT") print(f" Generated: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC") print(f"{'='*60}\n") templates = client.get_secret_templates() print(f"--- SECRET TEMPLATES ({len(templates)}) ---") for t in templates[:10]: print(f" [{t['id']}] {t['name']}") folders = client.get_folders() print(f"\n--- FOLDERS ({len(folders)}) ---") for f in folders[:10]: print(f" [{f['id']}] {f['folderName']}") secrets = client.search_secrets() print(f"\n--- SECRETS ({len(secrets)}) ---") for s in secrets[:10]: print(f" [{s['id']}] {s['name']} (Template: {s.get('secretTemplateName', 'N/A')})") users = client.get_users() print(f"\n--- USERS ({len(users)}) ---") for u in users[:10]: print(f" [{u['id']}] {u.get('userName', 'N/A')} - Enabled: {u.get('isDisabled', True)}") roles = client.get_roles() print(f"\n--- ROLES ({len(roles)}) ---") for r in roles[:10]: print(f" [{r['id']}] {r['name']}") print(f"\n{'='*60}\n") return {"templates": len(templates), "folders": len(folders), "secrets": len(secrets), "users": len(users), "roles": len(roles)} def main(): parser = argparse.ArgumentParser(description="Delinea Secret Server PAM Agent") parser.add_argument("--url", required=True, help="Secret Server base URL") parser.add_argument("--username", required=True, help="Username") parser.add_argument("--password", required=True, help="Password") parser.add_argument("--domain", help="AD domain (optional)") parser.add_argument("--audit", action="store_true", help="Run PAM audit") parser.add_argument("--search", help="Search secrets by keyword") parser.add_argument("--rotate", type=int, help="Rotate password for secret ID") parser.add_argument("--output", help="Save report to JSON") args = parser.parse_args() client = SecretServerClient(args.url, args.username, args.password, args.domain) if args.audit: report = run_pam_audit(client) if args.output: with open(args.output, "w") as f: json.dump(report, f, indent=2) elif args.search: results = client.search_secrets(search_text=args.search) for s in results: print(f" [{s['id']}] {s['name']}") elif args.rotate: result = client.rotate_secret_password(args.rotate) print(json.dumps(result, indent=2)) else: parser.print_help() if __name__ == "__main__": main()