[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M73FQ0T5RS352C2X52GPFZMT":3,"skill-same:01M73FQ0T5RS352C2X52GPFZMT":93},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":16,"sourceHtmlUrl":17,"categories":18,"keywords":19,"publishedAt":20,"linkOnly":21,"markdownContent":22,"markdownBody":23,"frontMatter":24,"refName":53,"commitSha":54,"skillDirectory":55,"forksCount":56,"repositoryHtmlUrl":57,"repositoryHomepageUrl":58,"ownerHtmlUrl":59,"ownerBlogUrl":60,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M73FQ0T5RS352C2X52GPFZMT","securing-remote-access-to-ot-environment",null,"This skill covers implementing secure remote access to OT\u002FICS environments for operators, engineers, and vendors while preventing unauthorized access that could compromise industrial operations. It addresses jump server architecture, multi-factor authentication, session recording, privileged access management, vendor remote access controls, and compliance with IEC 62443 and NERC CIP-005 remote access requirements.","Horonomos-laps\u002FYana-AI","Horonomos-laps","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F355299122?v=4","01M76B3QPXE88ET2SQPDPVATZT"," Horonomos-laps","01M72ZQRJPCYPSSWT4D00VJR2A",13,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fyanacuti1121\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fsecuring-remote-access-to-ot-environment\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fsecuring-remote-access-to-ot-environment\u002FSKILL.md",[],[],"2026-11-11 15:11:14",false,"---\nname: securing-remote-access-to-ot-environment\ndescription: 'This skill covers implementing secure remote access to OT\u002FICS environments\n  for operators, engineers, and vendors while preventing unauthorized access that\n  could compromise industrial operations. It addresses jump server architecture, multi-factor\n  authentication, session recording, privileged access management, vendor remote access\n  controls, and compliance with IEC 62443 and NERC CIP-005 remote access requirements.\n\n  '\ndomain: cybersecurity\nsubdomain: ot-ics-security\ntags:\n- ot-security\n- ics\n- scada\n- industrial-control\n- iec62443\n- remote-access\n- jump-server\n- mfa\nversion: 1.0.0\nauthor: mahipal\nlicense: Apache-2.0\nnist_csf:\n- PR.IR-01\n- DE.CM-01\n- ID.AM-05\n- GV.OC-02\nmitre_attack:\n- T1078\n- T1190\n- T1059\n- T0816\n- T0836\nsource: https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills\nsource_commit: 04450304b12645cb2b974ab96d28c0664758a88d\nnote: \"Vendored verbatim from an external Apache-2.0 security-skill library, pinned by commit. Exceeds the internal 300-line skill guideline (agent-code-constraints.md) -- kept as-is because this is vendored reference material (forensics\u002Fthreat-intel procedure), not Yana AI-authored content, and trimming would damage technical accuracy.\"\n---\n\n# Securing Remote Access to OT Environment\n\n## When to Use\n\n- When implementing or upgrading remote access architecture for OT environments\n- When onboarding vendors who require remote access to OT systems for support and maintenance\n- When implementing CIP-005-7 R2 requirements for remote access management including MFA\n- When replacing legacy direct VPN access to OT networks with a secure jump server architecture\n- When responding to an incident involving unauthorized remote access to industrial control systems\n\n**Do not use** for securing IT-only remote access without OT components, for configuring VPN for corporate workers (see general VPN guides), or for physical access control to OT facilities.\n\n## Prerequisites\n\n- DMZ infrastructure (Level 3.5) between corporate IT and OT networks\n- Jump server\u002Fbastion host platform (CyberArk, BeyondTrust, or hardened Windows\u002FLinux server)\n- Multi-factor authentication solution (Duo, RSA SecurID, YubiKey, smart cards)\n- Session recording capability for audit trail compliance\n- Firewall rules permitting remote access only through the DMZ intermediate system\n\n## Workflow\n\n### Step 1: Design Secure Remote Access Architecture\n\nImplement a defense-in-depth remote access architecture with an intermediate system in the DMZ that prevents any direct network connectivity between external users and OT systems.\n\n```yaml\n# OT Remote Access Architecture\n# Key principle: NO direct connection from external networks to OT systems\n\narchitecture:\n  external_access_point:\n    location: \"Internet-facing firewall\"\n    service: \"VPN gateway (IKEv2\u002FIPsec or SSL VPN)\"\n    authentication: \"Certificate + MFA (CIP-005-7 R2.4)\"\n    controls:\n      - \"Source IP allowlisting for vendor access\"\n      - \"Time-based access windows\"\n      - \"Bandwidth rate limiting\"\n\n  dmz_intermediate_system:\n    location: \"Level 3.5 DMZ\"\n    platform: \"CyberArk Privileged Access Security or hardened jump server\"\n    function: \"Session broker - terminates external connection, initiates new internal connection\"\n    controls:\n      - \"All sessions terminate at jump server (no pass-through)\"\n      - \"Session recording with keystroke logging\"\n      - \"Clipboard and file transfer restrictions\"\n      - \"Session timeout after 30 minutes inactivity\"\n      - \"Concurrent session limits per user\"\n\n  ot_internal_access:\n    location: \"Level 3 \u002F Level 2 OT network\"\n    method: \"Jump server initiates RDP\u002FSSH\u002FVNC to OT systems\"\n    controls:\n      - \"Firewall allows only jump server IP to reach OT\"\n      - \"Protocol restricted (RDP 3389, SSH 22, VNC 5900)\"\n      - \"Destination-specific per user role\"\n\n  vendor_access:\n    description: \"Third-party vendor remote access\"\n    additional_controls:\n      - \"Vendor account disabled by default; enabled on request\"\n      - \"Time-limited access windows (enable\u002Fdisable per session)\"\n      - \"OT operator must co-attend vendor sessions\"\n      - \"Real-time session monitoring by OT security\"\n      - \"No persistent credentials - one-time access tokens\"\n\n# Network flow:\n# User -> VPN -> Firewall -> DMZ Jump Server -> Internal FW -> OT System\n# (Two separate authenticated connections; no direct routing)\n```\n\n### Step 2: Configure Jump Server with Privileged Access Management\n\nDeploy and harden the jump server in the DMZ with session management, recording, and role-based access controls.\n\n```python\n#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"OT Remote Access Session Manager.\n\nManages authorized remote access sessions to OT environments\nincluding session creation, monitoring, recording, and termination.\nIntegrates with PAM solutions for credential vaulting.\n\"\"\"\n\nimport json\nimport hashlib\nimport sys\nfrom dataclasses import dataclass, field, asdict\nfrom datetime import datetime, timedelta\nfrom enum import Enum\n\n\nclass SessionState(str, Enum):\n    PENDING_APPROVAL = \"pending_approval\"\n    APPROVED = \"approved\"\n    ACTIVE = \"active\"\n    TERMINATED = \"terminated\"\n    EXPIRED = \"expired\"\n    DENIED = \"denied\"\n\n\nclass UserRole(str, Enum):\n    OT_OPERATOR = \"ot_operator\"\n    OT_ENGINEER = \"ot_engineer\"\n    VENDOR = \"vendor\"\n    SECURITY_ANALYST = \"security_analyst\"\n\n\n@dataclass\nclass RemoteAccessSession:\n    session_id: str\n    user_id: str\n    user_role: str\n    source_ip: str\n    target_system: str\n    target_ip: str\n    protocol: str\n    purpose: str\n    state: str = SessionState.PENDING_APPROVAL\n    mfa_verified: bool = False\n    approved_by: str = \"\"\n    created_at: str = \"\"\n    started_at: str = \"\"\n    ended_at: str = \"\"\n    max_duration_minutes: int = 120\n    recording_path: str = \"\"\n    actions_logged: list = field(default_factory=list)\n\n\nclass OTRemoteAccessManager:\n    \"\"\"Manages remote access sessions to OT environment.\"\"\"\n\n    def __init__(self):\n        self.sessions = {}\n        self.active_sessions = {}\n        self.access_policies = {}\n        self.audit_log = []\n\n    def define_access_policy(self, role, allowed_targets, protocols, max_duration):\n        \"\"\"Define access policy for a user role.\"\"\"\n        self.access_policies[role] = {\n            \"allowed_targets\": allowed_targets,\n            \"allowed_protocols\": protocols,\n            \"max_duration_minutes\": max_duration,\n            \"requires_co_attendance\": role == UserRole.VENDOR,\n            \"requires_approval\": role == UserRole.VENDOR,\n        }\n\n    def request_session(self, user_id, user_role, source_ip,\n                        target_system, target_ip, protocol, purpose):\n        \"\"\"Request a new remote access session.\"\"\"\n        # Generate unique session ID\n        session_id = hashlib.sha256(\n            f\"{user_id}{target_ip}{datetime.now().isoformat()}\".encode()\n        ).hexdigest()[:16]\n\n        # Check access policy\n        policy = self.access_policies.get(user_role)\n        if not policy:\n            return None, \"No access policy defined for role\"\n\n        if target_system not in policy[\"allowed_targets\"]:\n            self._audit(\"ACCESS_DENIED\", user_id, target_system,\n                       f\"Target not in allowed list for role {user_role}\")\n            return None, f\"Target {target_system} not authorized for role {user_role}\"\n\n        if protocol not in policy[\"allowed_protocols\"]:\n            self._audit(\"ACCESS_DENIED\", user_id, target_system,\n                       f\"Protocol {protocol} not allowed for role {user_role}\")\n            return None, f\"Protocol {protocol} not authorized\"\n\n        session = RemoteAccessSession(\n            session_id=session_id,\n            user_id=user_id,\n            user_role=user_role,\n            source_ip=source_ip,\n            target_system=target_system,\n            target_ip=target_ip,\n            protocol=protocol,\n            purpose=purpose,\n            max_duration_minutes=policy[\"max_duration_minutes\"],\n            created_at=datetime.now().isoformat(),\n        )\n\n        # Vendor sessions require approval\n        if policy.get(\"requires_approval\"):\n            session.state = SessionState.PENDING_APPROVAL\n        else:\n            session.state = SessionState.APPROVED\n\n        self.sessions[session_id] = session\n        self._audit(\"SESSION_REQUESTED\", user_id, target_system, purpose)\n\n        return session_id, \"Session created\"\n\n    def approve_session(self, session_id, approver_id):\n        \"\"\"Approve a pending vendor session.\"\"\"\n        session = self.sessions.get(session_id)\n        if not session:\n            return False, \"Session not found\"\n        if session.state != SessionState.PENDING_APPROVAL:\n            return False, \"Session not in pending approval state\"\n\n        session.state = SessionState.APPROVED\n        session.approved_by = approver_id\n        self._audit(\"SESSION_APPROVED\", approver_id, session.target_system,\n                    f\"Approved session {session_id} for {session.user_id}\")\n        return True, \"Session approved\"\n\n    def activate_session(self, session_id, mfa_token):\n        \"\"\"Activate an approved session after MFA verification.\"\"\"\n        session = self.sessions.get(session_id)\n        if not session or session.state != SessionState.APPROVED:\n            return False, \"Session not approved\"\n\n        # Verify MFA (simplified - real implementation calls MFA provider)\n        session.mfa_verified = True\n        session.state = SessionState.ACTIVE\n        session.started_at = datetime.now().isoformat()\n        session.recording_path = f\"\u002Frecordings\u002F{session_id}_{datetime.now().strftime('%Y%m%d')}.mp4\"\n\n        self.active_sessions[session_id] = session\n        self._audit(\"SESSION_ACTIVATED\", session.user_id, session.target_system,\n                    f\"MFA verified, recording to {session.recording_path}\")\n\n        return True, \"Session active\"\n\n    def terminate_session(self, session_id, reason=\"manual\"):\n        \"\"\"Terminate an active session.\"\"\"\n        session = self.active_sessions.pop(session_id, None)\n        if not session:\n            session = self.sessions.get(session_id)\n        if not session:\n            return False\n\n        session.state = SessionState.TERMINATED\n        session.ended_at = datetime.now().isoformat()\n        self._audit(\"SESSION_TERMINATED\", session.user_id, session.target_system, reason)\n        return True\n\n    def check_expired_sessions(self):\n        \"\"\"Terminate sessions that have exceeded their maximum duration.\"\"\"\n        now = datetime.now()\n        expired = []\n        for sid, session in list(self.active_sessions.items()):\n            started = datetime.fromisoformat(session.started_at)\n            if (now - started).total_seconds() > session.max_duration_minutes * 60:\n                self.terminate_session(sid, \"maximum_duration_exceeded\")\n                expired.append(sid)\n        return expired\n\n    def _audit(self, event_type, user_id, target, detail):\n        \"\"\"Write to audit log.\"\"\"\n        self.audit_log.append({\n            \"timestamp\": datetime.now().isoformat(),\n            \"event\": event_type,\n            \"user\": user_id,\n            \"target\": target,\n            \"detail\": detail,\n        })\n\n    def generate_report(self):\n        \"\"\"Generate remote access session report.\"\"\"\n        report = []\n        report.append(\"=\" * 70)\n        report.append(\"OT REMOTE ACCESS SESSION REPORT\")\n        report.append(f\"Date: {datetime.now().isoformat()}\")\n        report.append(\"=\" * 70)\n\n        # Session summary\n        total = len(self.sessions)\n        active = sum(1 for s in self.sessions.values() if s.state == SessionState.ACTIVE)\n        report.append(f\"\\nTotal Sessions: {total}\")\n        report.append(f\"Active Sessions: {active}\")\n\n        # Active sessions detail\n        if self.active_sessions:\n            report.append(\"\\nACTIVE SESSIONS:\")\n            for sid, s in self.active_sessions.items():\n                report.append(f\"  [{sid[:8]}] {s.user_id} ({s.user_role})\")\n                report.append(f\"    Target: {s.target_system} ({s.target_ip})\")\n                report.append(f\"    Started: {s.started_at}\")\n                report.append(f\"    MFA: {'Verified' if s.mfa_verified else 'NOT VERIFIED'}\")\n\n        return \"\\n\".join(report)\n\n\nif __name__ == \"__main__\":\n    manager = OTRemoteAccessManager()\n\n    # Define policies\n    manager.define_access_policy(\n        UserRole.OT_ENGINEER,\n        [\"HMI-01\", \"HMI-02\", \"EWS-01\", \"HISTORIAN-01\"],\n        [\"RDP\", \"SSH\"],\n        max_duration=240,\n    )\n    manager.define_access_policy(\n        UserRole.VENDOR,\n        [\"DCS-EWS-01\"],\n        [\"RDP\"],\n        max_duration=120,\n    )\n\n    # Request vendor session\n    sid, msg = manager.request_session(\n        \"vendor_honeywell_01\", UserRole.VENDOR,\n        \"203.0.113.50\", \"DCS-EWS-01\", \"10.30.1.20\",\n        \"RDP\", \"DCS firmware update per change request CR-2026-0045\")\n    print(f\"Session request: {msg} ({sid})\")\n\n    if sid:\n        manager.approve_session(sid, \"ot_manager_01\")\n        manager.activate_session(sid, \"123456\")\n        print(manager.generate_report())\n```\n\n## Key Concepts\n\n| Term | Definition |\n|------|------------|\n| Intermediate System | System in the DMZ that terminates external connections and brokers new connections to OT, preventing direct network access per CIP-005 |\n| Jump Server | Hardened bastion host in the DMZ used for remote access sessions to OT systems with session recording and access controls |\n| Session Recording | Capture of all remote access session activity (screen, keystrokes, commands) for security audit and incident investigation |\n| Privileged Access Management (PAM) | System for vaulting credentials, controlling access, and auditing privileged sessions to critical OT systems |\n| Co-Attendance | Requirement for an OT operator to monitor vendor remote access sessions in real time |\n| Time-Limited Access | Vendor accounts enabled only for specific maintenance windows and automatically disabled after the window closes |\n\n## Tools & Systems\n\n- **CyberArk Privileged Access Security**: Enterprise PAM with session management, credential vaulting, and recording for OT remote access\n- **BeyondTrust Privileged Remote Access**: Purpose-built remote access solution with session recording and granular access policies\n- **Claroty Secure Remote Access (SRA)**: OT-specific remote access solution with protocol-aware session controls\n- **Duo Security**: MFA provider supporting push notifications, hardware tokens, and biometrics for OT access verification\n\n## Output Format\n\n```\nOT Remote Access Security Report\n===================================\nActive Sessions: [N]\nPending Approval: [N]\nSessions Today: [N]\n\nMFA COMPLIANCE:\n  All sessions MFA verified: [Yes\u002FNo]\n  Sessions without MFA: [N]\n\nVENDOR ACCESS:\n  Active vendor sessions: [N]\n  Co-attended: [N]\n  Recorded: [N]\n```\n","# Securing Remote Access to OT Environment\n\n## When to Use\n\n- When implementing or upgrading remote access architecture for OT environments\n- When onboarding vendors who require remote access to OT systems for support and maintenance\n- When implementing CIP-005-7 R2 requirements for remote access management including MFA\n- When replacing legacy direct VPN access to OT networks with a secure jump server architecture\n- When responding to an incident involving unauthorized remote access to industrial control systems\n\n**Do not use** for securing IT-only remote access without OT components, for configuring VPN for corporate workers (see general VPN guides), or for physical access control to OT facilities.\n\n## Prerequisites\n\n- DMZ infrastructure (Level 3.5) between corporate IT and OT networks\n- Jump server\u002Fbastion host platform (CyberArk, BeyondTrust, or hardened Windows\u002FLinux server)\n- Multi-factor authentication solution (Duo, RSA SecurID, YubiKey, smart cards)\n- Session recording capability for audit trail compliance\n- Firewall rules permitting remote access only through the DMZ intermediate system\n\n## Workflow\n\n### Step 1: Design Secure Remote Access Architecture\n\nImplement a defense-in-depth remote access architecture with an intermediate system in the DMZ that prevents any direct network connectivity between external users and OT systems.\n\n```yaml\n# OT Remote Access Architecture\n# Key principle: NO direct connection from external networks to OT systems\n\narchitecture:\n  external_access_point:\n    location: \"Internet-facing firewall\"\n    service: \"VPN gateway (IKEv2\u002FIPsec or SSL VPN)\"\n    authentication: \"Certificate + MFA (CIP-005-7 R2.4)\"\n    controls:\n      - \"Source IP allowlisting for vendor access\"\n      - \"Time-based access windows\"\n      - \"Bandwidth rate limiting\"\n\n  dmz_intermediate_system:\n    location: \"Level 3.5 DMZ\"\n    platform: \"CyberArk Privileged Access Security or hardened jump server\"\n    function: \"Session broker - terminates external connection, initiates new internal connection\"\n    controls:\n      - \"All sessions terminate at jump server (no pass-through)\"\n      - \"Session recording with keystroke logging\"\n      - \"Clipboard and file transfer restrictions\"\n      - \"Session timeout after 30 minutes inactivity\"\n      - \"Concurrent session limits per user\"\n\n  ot_internal_access:\n    location: \"Level 3 \u002F Level 2 OT network\"\n    method: \"Jump server initiates RDP\u002FSSH\u002FVNC to OT systems\"\n    controls:\n      - \"Firewall allows only jump server IP to reach OT\"\n      - \"Protocol restricted (RDP 3389, SSH 22, VNC 5900)\"\n      - \"Destination-specific per user role\"\n\n  vendor_access:\n    description: \"Third-party vendor remote access\"\n    additional_controls:\n      - \"Vendor account disabled by default; enabled on request\"\n      - \"Time-limited access windows (enable\u002Fdisable per session)\"\n      - \"OT operator must co-attend vendor sessions\"\n      - \"Real-time session monitoring by OT security\"\n      - \"No persistent credentials - one-time access tokens\"\n\n# Network flow:\n# User -> VPN -> Firewall -> DMZ Jump Server -> Internal FW -> OT System\n# (Two separate authenticated connections; no direct routing)\n```\n\n### Step 2: Configure Jump Server with Privileged Access Management\n\nDeploy and harden the jump server in the DMZ with session management, recording, and role-based access controls.\n\n```python\n#!\u002Fusr\u002Fbin\u002Fenv python3\n\"\"\"OT Remote Access Session Manager.\n\nManages authorized remote access sessions to OT environments\nincluding session creation, monitoring, recording, and termination.\nIntegrates with PAM solutions for credential vaulting.\n\"\"\"\n\nimport json\nimport hashlib\nimport sys\nfrom dataclasses import dataclass, field, asdict\nfrom datetime import datetime, timedelta\nfrom enum import Enum\n\n\nclass SessionState(str, Enum):\n    PENDING_APPROVAL = \"pending_approval\"\n    APPROVED = \"approved\"\n    ACTIVE = \"active\"\n    TERMINATED = \"terminated\"\n    EXPIRED = \"expired\"\n    DENIED = \"denied\"\n\n\nclass UserRole(str, Enum):\n    OT_OPERATOR = \"ot_operator\"\n    OT_ENGINEER = \"ot_engineer\"\n    VENDOR = \"vendor\"\n    SECURITY_ANALYST = \"security_analyst\"\n\n\n@dataclass\nclass RemoteAccessSession:\n    session_id: str\n    user_id: str\n    user_role: str\n    source_ip: str\n    target_system: str\n    target_ip: str\n    protocol: str\n    purpose: str\n    state: str = SessionState.PENDING_APPROVAL\n    mfa_verified: bool = False\n    approved_by: str = \"\"\n    created_at: str = \"\"\n    started_at: str = \"\"\n    ended_at: str = \"\"\n    max_duration_minutes: int = 120\n    recording_path: str = \"\"\n    actions_logged: list = field(default_factory=list)\n\n\nclass OTRemoteAccessManager:\n    \"\"\"Manages remote access sessions to OT environment.\"\"\"\n\n    def __init__(self):\n        self.sessions = {}\n        self.active_sessions = {}\n        self.access_policies = {}\n        self.audit_log = []\n\n    def define_access_policy(self, role, allowed_targets, protocols, max_duration):\n        \"\"\"Define access policy for a user role.\"\"\"\n        self.access_policies[role] = {\n            \"allowed_targets\": allowed_targets,\n            \"allowed_protocols\": protocols,\n            \"max_duration_minutes\": max_duration,\n            \"requires_co_attendance\": role == UserRole.VENDOR,\n            \"requires_approval\": role == UserRole.VENDOR,\n        }\n\n    def request_session(self, user_id, user_role, source_ip,\n                        target_system, target_ip, protocol, purpose):\n        \"\"\"Request a new remote access session.\"\"\"\n        # Generate unique session ID\n        session_id = hashlib.sha256(\n            f\"{user_id}{target_ip}{datetime.now().isoformat()}\".encode()\n        ).hexdigest()[:16]\n\n        # Check access policy\n        policy = self.access_policies.get(user_role)\n        if not policy:\n            return None, \"No access policy defined for role\"\n\n        if target_system not in policy[\"allowed_targets\"]:\n            self._audit(\"ACCESS_DENIED\", user_id, target_system,\n                       f\"Target not in allowed list for role {user_role}\")\n            return None, f\"Target {target_system} not authorized for role {user_role}\"\n\n        if protocol not in policy[\"allowed_protocols\"]:\n            self._audit(\"ACCESS_DENIED\", user_id, target_system,\n                       f\"Protocol {protocol} not allowed for role {user_role}\")\n            return None, f\"Protocol {protocol} not authorized\"\n\n        session = RemoteAccessSession(\n            session_id=session_id,\n            user_id=user_id,\n            user_role=user_role,\n            source_ip=source_ip,\n            target_system=target_system,\n            target_ip=target_ip,\n            protocol=protocol,\n            purpose=purpose,\n            max_duration_minutes=policy[\"max_duration_minutes\"],\n            created_at=datetime.now().isoformat(),\n        )\n\n        # Vendor sessions require approval\n        if policy.get(\"requires_approval\"):\n            session.state = SessionState.PENDING_APPROVAL\n        else:\n            session.state = SessionState.APPROVED\n\n        self.sessions[session_id] = session\n        self._audit(\"SESSION_REQUESTED\", user_id, target_system, purpose)\n\n        return session_id, \"Session created\"\n\n    def approve_session(self, session_id, approver_id):\n        \"\"\"Approve a pending vendor session.\"\"\"\n        session = self.sessions.get(session_id)\n        if not session:\n            return False, \"Session not found\"\n        if session.state != SessionState.PENDING_APPROVAL:\n            return False, \"Session not in pending approval state\"\n\n        session.state = SessionState.APPROVED\n        session.approved_by = approver_id\n        self._audit(\"SESSION_APPROVED\", approver_id, session.target_system,\n                    f\"Approved session {session_id} for {session.user_id}\")\n        return True, \"Session approved\"\n\n    def activate_session(self, session_id, mfa_token):\n        \"\"\"Activate an approved session after MFA verification.\"\"\"\n        session = self.sessions.get(session_id)\n        if not session or session.state != SessionState.APPROVED:\n            return False, \"Session not approved\"\n\n        # Verify MFA (simplified - real implementation calls MFA provider)\n        session.mfa_verified = True\n        session.state = SessionState.ACTIVE\n        session.started_at = datetime.now().isoformat()\n        session.recording_path = f\"\u002Frecordings\u002F{session_id}_{datetime.now().strftime('%Y%m%d')}.mp4\"\n\n        self.active_sessions[session_id] = session\n        self._audit(\"SESSION_ACTIVATED\", session.user_id, session.target_system,\n                    f\"MFA verified, recording to {session.recording_path}\")\n\n        return True, \"Session active\"\n\n    def terminate_session(self, session_id, reason=\"manual\"):\n        \"\"\"Terminate an active session.\"\"\"\n        session = self.active_sessions.pop(session_id, None)\n        if not session:\n            session = self.sessions.get(session_id)\n        if not session:\n            return False\n\n        session.state = SessionState.TERMINATED\n        session.ended_at = datetime.now().isoformat()\n        self._audit(\"SESSION_TERMINATED\", session.user_id, session.target_system, reason)\n        return True\n\n    def check_expired_sessions(self):\n        \"\"\"Terminate sessions that have exceeded their maximum duration.\"\"\"\n        now = datetime.now()\n        expired = []\n        for sid, session in list(self.active_sessions.items()):\n            started = datetime.fromisoformat(session.started_at)\n            if (now - started).total_seconds() > session.max_duration_minutes * 60:\n                self.terminate_session(sid, \"maximum_duration_exceeded\")\n                expired.append(sid)\n        return expired\n\n    def _audit(self, event_type, user_id, target, detail):\n        \"\"\"Write to audit log.\"\"\"\n        self.audit_log.append({\n            \"timestamp\": datetime.now().isoformat(),\n            \"event\": event_type,\n            \"user\": user_id,\n            \"target\": target,\n            \"detail\": detail,\n        })\n\n    def generate_report(self):\n        \"\"\"Generate remote access session report.\"\"\"\n        report = []\n        report.append(\"=\" * 70)\n        report.append(\"OT REMOTE ACCESS SESSION REPORT\")\n        report.append(f\"Date: {datetime.now().isoformat()}\")\n        report.append(\"=\" * 70)\n\n        # Session summary\n        total = len(self.sessions)\n        active = sum(1 for s in self.sessions.values() if s.state == SessionState.ACTIVE)\n        report.append(f\"\\nTotal Sessions: {total}\")\n        report.append(f\"Active Sessions: {active}\")\n\n        # Active sessions detail\n        if self.active_sessions:\n            report.append(\"\\nACTIVE SESSIONS:\")\n            for sid, s in self.active_sessions.items():\n                report.append(f\"  [{sid[:8]}] {s.user_id} ({s.user_role})\")\n                report.append(f\"    Target: {s.target_system} ({s.target_ip})\")\n                report.append(f\"    Started: {s.started_at}\")\n                report.append(f\"    MFA: {'Verified' if s.mfa_verified else 'NOT VERIFIED'}\")\n\n        return \"\\n\".join(report)\n\n\nif __name__ == \"__main__\":\n    manager = OTRemoteAccessManager()\n\n    # Define policies\n    manager.define_access_policy(\n        UserRole.OT_ENGINEER,\n        [\"HMI-01\", \"HMI-02\", \"EWS-01\", \"HISTORIAN-01\"],\n        [\"RDP\", \"SSH\"],\n        max_duration=240,\n    )\n    manager.define_access_policy(\n        UserRole.VENDOR,\n        [\"DCS-EWS-01\"],\n        [\"RDP\"],\n        max_duration=120,\n    )\n\n    # Request vendor session\n    sid, msg = manager.request_session(\n        \"vendor_honeywell_01\", UserRole.VENDOR,\n        \"203.0.113.50\", \"DCS-EWS-01\", \"10.30.1.20\",\n        \"RDP\", \"DCS firmware update per change request CR-2026-0045\")\n    print(f\"Session request: {msg} ({sid})\")\n\n    if sid:\n        manager.approve_session(sid, \"ot_manager_01\")\n        manager.activate_session(sid, \"123456\")\n        print(manager.generate_report())\n```\n\n## Key Concepts\n\n| Term | Definition |\n|------|------------|\n| Intermediate System | System in the DMZ that terminates external connections and brokers new connections to OT, preventing direct network access per CIP-005 |\n| Jump Server | Hardened bastion host in the DMZ used for remote access sessions to OT systems with session recording and access controls |\n| Session Recording | Capture of all remote access session activity (screen, keystrokes, commands) for security audit and incident investigation |\n| Privileged Access Management (PAM) | System for vaulting credentials, controlling access, and auditing privileged sessions to critical OT systems |\n| Co-Attendance | Requirement for an OT operator to monitor vendor remote access sessions in real time |\n| Time-Limited Access | Vendor accounts enabled only for specific maintenance windows and automatically disabled after the window closes |\n\n## Tools & Systems\n\n- **CyberArk Privileged Access Security**: Enterprise PAM with session management, credential vaulting, and recording for OT remote access\n- **BeyondTrust Privileged Remote Access**: Purpose-built remote access solution with session recording and granular access policies\n- **Claroty Secure Remote Access (SRA)**: OT-specific remote access solution with protocol-aware session controls\n- **Duo Security**: MFA provider supporting push notifications, hardware tokens, and biometrics for OT access verification\n\n## Output Format\n\n```\nOT Remote Access Security Report\n===================================\nActive Sessions: [N]\nPending Approval: [N]\nSessions Today: [N]\n\nMFA COMPLIANCE:\n  All sessions MFA verified: [Yes\u002FNo]\n  Sessions without MFA: [N]\n\nVENDOR ACCESS:\n  Active vendor sessions: [N]\n  Co-attended: [N]\n  Recorded: [N]\n```\n",{"name":5,"note":25,"tags":26,"author":35,"domain":36,"source":37,"license":15,"version":38,"nist_csf":39,"subdomain":44,"description":45,"mitre_attack":46,"source_commit":52},"Vendored verbatim from an external Apache-2.0 security-skill library, pinned by commit. Exceeds the internal 300-line skill guideline (agent-code-constraints.md) -- kept as-is because this is vendored reference material (forensics\u002Fthreat-intel procedure), not Yana AI-authored content, and trimming would damage technical accuracy.",[27,28,29,30,31,32,33,34],"ot-security","ics","scada","industrial-control","iec62443","remote-access","jump-server","mfa","mahipal","cybersecurity","https:\u002F\u002Fgithub.com\u002Fmukul975\u002FAnthropic-Cybersecurity-Skills","1.0.0",[40,41,42,43],"PR.IR-01","DE.CM-01","ID.AM-05","GV.OC-02","ot-ics-security","This skill covers implementing secure remote access to OT\u002FICS environments for operators, engineers, and vendors while preventing unauthorized access that could compromise industrial operations. It addresses jump server architecture, multi-factor authentication, session recording, privileged access management, vendor remote access controls, and compliance with IEC 62443 and NERC CIP-005 remote access requirements.\n",[47,48,49,50,51],"T1078","T1190","T1059","T0816","T0836","04450304b12645cb2b974ab96d28c0664758a88d","main","481dcd7dd2e288b9d627a2bd017db70e64e6c6fa","core\u002Fskills\u002Fsecuring-remote-access-to-ot-environment",2,"https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI","https:\u002F\u002Fyana.vutam.link\u002F","https:\u002F\u002Fgithub.com\u002FHoronomos-laps","https:\u002F\u002Fyana.vutam.link","An open-source governed runtime & execution environment for multi-agent AI systems.","2026-11-12 23:12:27",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add Horonomos-laps\u002FYana-AI --skill securing-remote-access-to-ot-environment",{"tool":69,"command":70},"bunx","bunx skills add Horonomos-laps\u002FYana-AI --skill securing-remote-access-to-ot-environment",{"tool":72,"command":73},"pnpm","pnpm dlx skills add Horonomos-laps\u002FYana-AI --skill securing-remote-access-to-ot-environment","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fsecuring-remote-access-to-ot-environment\u002FSKILL.md",true,[77,81,87],{"path":78,"resourceType":78,"resourceTypeDesc":79,"sizeBytes":80,"sourceHtmlUrl":16},"LICENSE","许可证",11255,{"path":82,"resourceType":83,"resourceTypeDesc":84,"sizeBytes":85,"sourceHtmlUrl":86},"references\u002Fapi-reference.md","REFERENCE","参考文档",1737,"https:\u002F\u002Fgithub.com\u002Fyanacuti1121\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fsecuring-remote-access-to-ot-environment\u002Freferences\u002Fapi-reference.md",{"path":88,"resourceType":89,"resourceTypeDesc":90,"sizeBytes":91,"sourceHtmlUrl":92},"scripts\u002Fagent.py","SCRIPT","脚本",7218,"https:\u002F\u002Fgithub.com\u002Fyanacuti1121\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fsecuring-remote-access-to-ot-environment\u002Fscripts\u002Fagent.py",[94,102,109,117,125,133],{"skillNo":95,"name":96,"nameZh":6,"description":97,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":98,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":101,"publishedAt":20},"01M735GKET5YVYKAY62W2P5YXB","openai--sentry--sentry","Use when the user asks to inspect Sentry issues or events, summarize recent production errors, or pull basic Sentry health data via the Sentry API; perform read-only queries with the bundled script and require `SENTRY_AUTH_TOKEN`.","MIT","https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fopenai--sentry--sentry\u002FSKILL.md",[],[],{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":20},"01M73DCAP83T17HY7SMGK7MGFE","ito-basket-compare","Compare Itô prediction-market baskets against a user's knowledge base, portfolio notes, financial context, watchlist, or research thesis. Use for read-only basket comparison and gap analysis without investment advice or live trading.","https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fito-basket-compare\u002FSKILL.md",[],[],{"skillNo":110,"name":111,"nameZh":6,"description":112,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":113,"sourceHtmlUrl":114,"categories":115,"keywords":116,"publishedAt":20},"01M734CYEMZFDR3KH6SW0JP8TD","implementing-network-traffic-baselining","Build network traffic baselines from NetFlow\u002FIPFIX data using Python pandas for statistical analysis, z-score anomaly detection, and hourly\u002Fdaily traffic pattern profiling","https:\u002F\u002Fgithub.com\u002Fyanacuti1121\u002FYana-AI\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fimplementing-network-traffic-baselining\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fimplementing-network-traffic-baselining\u002FSKILL.md",[],[],{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":121,"sourceHtmlUrl":122,"categories":123,"keywords":124,"publishedAt":20},"01M73ASZ6NJRJM44Z2P84NGQ3Q","detecting-evasion-techniques-in-endpoint-logs","Detects defense evasion techniques used by adversaries in endpoint logs including log tampering, timestomping, process injection, and security tool disabling. Use when investigating suspicious endpoint behavior, building detection rules for evasion tactics, or conducting threat hunting for stealthy adversary activity. Activates for requests involving evasion detection, defense evasion analysis, log tampering detection, or MITRE ATT&CK TA0005.","https:\u002F\u002Fgithub.com\u002Fyanacuti1121\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fdetecting-evasion-techniques-in-endpoint-logs\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fdetecting-evasion-techniques-in-endpoint-logs\u002FSKILL.md",[],[],{"skillNo":126,"name":127,"nameZh":6,"description":128,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":129,"sourceHtmlUrl":130,"categories":131,"keywords":132,"publishedAt":20},"01M734AFNXD06Z78QRK0VKGT7Q","implementing-microsegmentation-with-guardicore","Implementing microsegmentation using Akamai Guardicore Segmentation to map application dependencies, create granular network policies, visualize east-west traffic flows, and enforce least-privilege communication between workloads across data centers and cloud.","https:\u002F\u002Fgithub.com\u002Fyanacuti1121\u002FYana-AI\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fimplementing-microsegmentation-with-guardicore\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fimplementing-microsegmentation-with-guardicore\u002FSKILL.md",[],[],{"skillNo":134,"name":135,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":136,"categories":137,"keywords":138,"publishedAt":20},"01M736TK27QMCXK0DVCH25SJN5","ramdisk-tmpfs-patterns","https:\u002F\u002Fgithub.com\u002FHoronomos-laps\u002FYana-AI\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Framdisk-tmpfs-patterns\u002FSKILL.md",[],[]]