Files
nscertkeycreate/.github/copilot-instructions.md
deamonkai fc94008530 initial
2026-01-23 12:11:21 -06:00

9.6 KiB
Raw Blame History

Copilot / Agent Instructions

Purpose: Help an AI coding agent become productive quickly in this small repo.

  • Project type: Single-file Python CLI tool (nscertkeycreate.py). It interacts with a Citrix NetScaler (Console) Nitro API and optionally executes ADC CLI on managed devices.

  • Big picture: There are two main flows:

    • Console-only CSR: NitroClient.create_csr_console_side posts to ns_ssl_csr and saves CSR locally.
    • Deliver-to-device: NitroClient.execute_on_device tries Console-side command execution (preferred: config_command / config_job) and falls back to ns_command. Device-side commands are generated by adc_cli_commands_for_key_and_csr and adc_cli_command_to_cat_csr.
  • Key integration points:

    • macOS Keychain via security CLI — helpers: keychain_get, keychain_set, keychain_delete.
    • HTTP: uses urllib.request with Basic auth; base URLs built from --console into /nitro/v1/config and /nitro/v2/config.
    • ADC command execution: multiple payload shapes are tried (see payloads_config_command and payloads_ns_command). To support a new Console schema, add payload shapes here.
    • Task polling: try_poll_task_logs performs best-effort polling of task_log and task_command_log.
  • Error & observability patterns:

    • Failures raise NitroError containing the raw payload returned by the server; callers typically json.dumps that payload and print it.
    • When adding parsing for device command output, prefer dumping the raw JSON first (the code writes *.device_cat_csr.response.json) and then implement a deterministic extractor.
  • Developer workflows & quick commands:

    • Run interactively (examples live in the CLI epilog):

      • Console-only CSR:

        ./nscertkeycreate.py --console https://CONSOLE --user nsroot --name app1

      • Deliver to Primary device and fetch CSR back:

        ./nscertkeycreate.py --console https://CONSOLE --user nsroot --name app1 --deliver-to-device --download-csr-from-device

    • On macOS, inspect keychain entries with the security tool; the script stores entries under service names like netscaler-console:<console> and netscaler-keypass:<console>.

  • Where to make common changes:

    • Add/adjust Nitro payload shapes: edit payloads_config_command / payloads_ns_command inside NitroClient.execute_on_device.
    • Change how CSR text is extracted from Console responses: modify extract_csr_text.
    • Adapt device-response parsing (stdout extraction) after execute_on_device: check where *.device_cat_csr.response.json is written and add a deterministic parser.
  • Automation notes:

    • The CLI is interactive by default (prompts for password, passphrase, subject fields). For automation, pre-seed macOS Keychain entries so the script wont prompt, and pass flags like --outdir and --timeout on the command line.
  • Files to inspect for context:


Concrete payload examples

  • Console CSR POST (used by create_csr_console_side):
{
  "ns_ssl_csr": {
    "file_name": "app1.csr",
    "commonname": "app.example.com",
    "organizationname": "Example Ltd",
    "countryname": "US",
    "statename": "California",
    "passphrase": "<key-pass>",
    "keyalgorithm": "RSA",
    "keystrength": "4096"
  }
}
  • config_command payload shapes tried in execute_on_device:
{"config_command": {"device_id": "<id>", "command": "<newline-separated commands>"}}
{"config_command": {"device_id": "<id>", "commands": ["cmd1","cmd2"]}}
{"config_command": {"instance_id": "<id>", "command": "..."}}
  • ns_command fallback shapes:
{"ns_command": {"device_id": "<id>", "commands": ["cmd1"]}}
{"ns_command": {"id": "<id>", "command": "cmd1\ncmd2"}}

When Console responses fail, the script aggregates attempts into NitroError.payload. Use that dumped JSON to see which schema matched and where stdout may live.

Targeted code references

Use these links when you need to change payload shapes, CSR extraction, or device-command parsing.


Sample Console responses

Below are common response shapes you may encounter. When in doubt, dump the full JSON (the script writes *.device_cat_csr.response.json) and adapt an extractor that searches string fields for the PEM block.

  • Successful ns_ssl_csr response that returns CSR text (how extract_csr_text expects it):
{
  "ns_ssl_csr": [
    {
      "file_name": "app1.csr",
      "csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIIC...\n-----END CERTIFICATE REQUEST-----",
      "errorcode": 0
    }
  ],
  "errorcode": 0
}
  • Typical config_command / config_job success payloads (IDs/tokens are returned under several keys):
{ "config_command": { "id": "cc-1001", "message": "Accepted", "errorcode": 0 } }
{ "config_command": { "job_id": "job-42", "message": "Queued", "errorcode": 0 } }
{ "task_id": "task-900", "message": "Submitted", "errorcode": 0 }
  • ns_command responses sometimes return per-command stdout/rows. Look for stdout, response, or nested arrays:
{
  "ns_command": {
    "id": "nc-2001",
    "commands": [
      {
        "command": "shell cat /nsconfig/ssl/app1.csr",
        "stdout": "-----BEGIN CERTIFICATE REQUEST-----\nMIIC...\n-----END CERTIFICATE REQUEST-----"
      }
    ],
    "errorcode": 0
  }
}
  • task_log / task_command_log entries (polled by try_poll_task_logs) may contain tokens referencing the originating ID:
{
  "task_log": [
    { "id": "t-1", "message": "Executed config_command id=cc-1001", "errorcode": 0 }
  ],
  "errorcode": 0
}

Parsing guidance:

  • To wire automatic CSR extraction from device responses, first save the raw JSON (the script already does this), then implement a deterministic extractor that:
    • searches for PEM blocks (regex for -----BEGIN CERTIFICATE REQUEST-----...-----END CERTIFICATE REQUEST-----) across any string fields, and
    • falls back to looking at ns_command -> commands[*] -> stdout or response fields.
  • When NitroError.payload contains attempts, inspect each attempt's url and payload to determine which schema succeeded or returned useful stdout.

Real-world Console response examples

The snippets below are realistic, slightly-expanded examples you may see when interacting with NetScaler Consoles. Use them to test extractors and to map where stdout or CSR text appears.

  • Example A — ns_ssl_csr returns CSR in csr field (successful Console-side CSR creation):
{
  "ns_ssl_csr": [
    {
      "file_name": "app1.csr",
      "csr": "-----BEGIN CERTIFICATE REQUEST-----\nMIICWjCCA...\n-----END CERTIFICATE REQUEST-----",
      "errorcode": 0
    }
  ],
  "errorcode": 0
}
  • Example B — config_command accepted and queued (returns id/job_id/task_id in different builds):
{
  "config_command": {
    "id": "cc-1001",
    "message": "Accepted",
    "errorcode": 0
  }
}

{
  "config_command": {
    "job_id": "job-42",
    "message": "Queued",
    "errorcode": 0
  }
}

{
  "task_id": "task-900",
  "message": "Submitted",
  "errorcode": 0
}
  • Example C — ns_command with per-command stdout (device-side shell cat returns CSR text here):
{
  "ns_command": {
    "id": "nc-2001",
    "commands": [
      {
        "command": "shell cat /nsconfig/ssl/app1.csr",
        "stdout": "-----BEGIN CERTIFICATE REQUEST-----\nMIICWjCCA...\n-----END CERTIFICATE REQUEST-----",
        "response": null
      }
    ],
    "errorcode": 0
  }
}
  • Example D — ns_command returns nested arrays or rows (some Console builds):
{
  "ns_command": {
    "id": "nc-2002",
    "commands": [
      {
        "command": "shell cat /nsconfig/ssl/app1.csr",
        "rows": [
          "-----BEGIN CERTIFICATE REQUEST-----",
          "MIICWjCCA...",
          "-----END CERTIFICATE REQUEST-----"
        ]
      }
    ],
    "errorcode": 0
  }
}
  • Example E — task_log entry referencing a config_command/job (polled by try_poll_task_logs):
{
  "task_log": [
    {
      "id": "t-1",
      "message": "Executed config_command id=cc-1001 on device id=dev-5",
      "errorcode": 0
    }
  ],
  "errorcode": 0
}

Testing tips:

  • Save any device response JSON produced by the script (e.g., out/app1.device_cat_csr.response.json) and run a quick search for -----BEGIN CERTIFICATE REQUEST----- to locate the PEM.
  • A robust extractor should:
    • recursively walk the JSON and scan every string value for a PEM regex, returning the first match; and
    • if no PEM is found, inspect ns_command -> commands[*] for stdout, response, rows, etc.

Small regex to find PEM blocks (Python):

import re
PEM_RE = re.compile(r"-----BEGIN CERTIFICATE REQUEST-----(?:.|\n)+?-----END CERTIFICATE REQUEST-----")

If you want, I can implement a small extract_pem_from_json(resp: dict) -> Optional[str] helper in nscertkeycreate.py and wire it to save the CSR automatically when --download-csr-from-device is used. Paste one or two real Console JSON dumps if you want the extractor tailored to your Console's exact schema.

If anything here is unclear or you want more examples (e.g., sample payload shapes from a real Console), tell me which area to expand and Ill update this file.