# AI एजेंट के लिए sudo access: हर elevation पर नियंत्रण

AI एजेंट को sudo देना सिर्फ इसलिए सही नहीं हो जाता कि वह पहले से रिमोट shell खोल सकता है। रिमोट execution इस सवाल का जवाब देता है, «क्या यह मशीन तक पहुंच सकता है?» Elevation दूसरे सवाल का जवाब देता है, «क्या इस अनुरोध से सुरक्षित स्थिति में बदलाव करने की अनुमति है?» इन दोनों फैसलों के लिए अलग सबूत, अलग नियंत्रण और अलग रिकॉर्ड चाहिए।

मैंने टीमों को इन्हें एक ही सुविधा में मिला देते देखा है: एजेंट SSH से जुड़ता है, `sudo` चलाता है और पीछे chat transcript छोड़ देता है। यह तब तक व्यवस्थित लगता है, जब तक कोई गलत कमांड गलत service को restart न कर दे, configuration फ़ाइल को बदल न दे या repository में छिपे निर्देश का पालन न कर बैठे। फिर कोई यह नहीं बता पाता कि authority किस process के पास थी, किसने मंजूरी दी या root access जरूरी क्यों लगा।

**AI एजेंट के लिए sudo access** का मतलब किसी बताए गए operation के लिए सीमित और किसी पहचान से जुड़ा अपवाद होना चाहिए। इसका मतलब यह कभी नहीं होना चाहिए कि एजेंट ने पूरे task के बाकी समय के लिए administrator की खुली शक्ति हासिल कर ली है।

## रिमोट shell और sudo अलग सवालों के जवाब देते हैं

SSH session यह साबित करता है कि किसी client ने remote account पर authentication किया है। इससे यह साबित नहीं होता कि उस session में टाइप या बनाई गई हर command को root authority मिलनी चाहिए। सामान्य account को discovery, build, test, log पढ़ने और deployment की तैयारी के लिए पर्याप्त उपयोगी रखें। सुरक्षित बदलावों के छोटे समूह को अलग elevation path के पीछे रखें।

यह फर्क अक्सर इसलिए मिट जाता है क्योंकि shell privilege escalation को syntax की छोटी-सी बात जैसा दिखाता है:

```sh
ssh deploy@api-02 'sudo systemctl restart payment-worker'
```

इस एक पंक्ति में कम से कम पांच फैसले छिपे हैं: इसे किस agent process ने जारी किया, किस host को लक्ष्य बनाया, service का नाम सही है या नहीं, restart क्यों चाहिए और क्या किसी व्यक्ति ने इसके परिणाम को स्वीकार किया। SSH connection को authenticate कर सकता है। `sudo` effective user बदल सकता है। लेकिन इनमें से कोई भी tool अपने आप कारण दर्ज नहीं करता और न ही autonomous work के लिए सही decision point देता है।

सामान्य remote commands को सामान्य ही रहने दें। एजेंट `systemctl status` चला सकता है, ऐसी service log देख सकता है जिसे पढ़ने की उसे पहले से अनुमति है, rendered configuration की तुलना कर सकता है या root मांगे बिना health check चला सकता है। इससे एक व्यावहारिक फायदा मिलता है: एजेंट किसी बदलाव की मंजूरी मांगने से पहले सबूत जुटा सकता है।

हर shell command के सामने human prompt लगाकर अति-सुधार न करें। इससे approval fatigue पैदा होती है और लोग उन cards पर क्लिक करने लगते हैं जिन्हें वे पढ़ते भी नहीं। रुकावट वहीं लगाएं जहां कार्रवाई सुरक्षित सीमा बदलती है: service management, system package changes, privileged file writes, account changes, network rules, secret material, boot settings और production data operations।

किसी command का जोखिम उसकी spelling से तय नहीं होता। `sudo cat /var/log/...` संवेदनशील सामग्री उजागर कर सकता है। `sudo systemctl restart ...` ग्राहक को दिखने वाली service को रोक सकता है। देखने में harmless `sudo install` किसी executable को बदल सकता है। असर, लक्ष्य और यह बात वर्गीकृत करें कि arguments मनमाने root behavior तक पहुंचने का रास्ता खोलते हैं या नहीं।

## Sudoers नियम allowlist हैं, सुरक्षा का तर्क नहीं

`sudoers` manual के अनुसार sudo यह तय करता है कि user command path और, जहां configured हो, command-line arguments के आधार पर command चला सकता है या नहीं। यह उपयोगी व्यवस्था है, लेकिन व्यापक allowlist को सुरक्षित delegation में नहीं बदलती।

ऐसा नियम जितना लोग समझते हैं, उससे कहीं अधिक अधिकार दे सकता है:

```sudoers
autobot ALL=(root) NOPASSWD: /usr/bin/systemctl *
```

यह account को स्थानीय `systemctl` द्वारा समर्थित किसी भी unit को start, stop, restart, enable, disable, mask और inspect करने देता है। अगर एजेंट किसी unit file, environment file या service executable को प्रभावित कर सकता है, तो वह अनुमति प्राप्त restart को root के रूप में code execution में बदल सकता है। इस नियम में change reason या expiry के लिए भी कोई जगह नहीं है।

Argument restrictions तभी मदद करते हैं जब अनुमति प्राप्त program का interface छोटा और स्थिर हो और वह attacker-controlled input को path, shell expression, plugin, editor, pager या configuration source की तरह interpret न कर सके। Administrators अक्सर यह बात इसलिए चूक जाते हैं क्योंकि command परिचित लगती है। Binary path absolute होने भर से command सीमित नहीं हो जाती।

तय operation और स्पष्ट validation वाले purpose-built wrapper को प्राथमिकता दें। उदाहरण के लिए, restart wrapper मनमाने `systemctl` arguments के बजाय hard-coded service name स्वीकार कर सकता है:

```sh
#!/bin/sh
set -eu

case "${1:-}" in
  payment-worker|report-worker) ;;
  *) echo "unsupported service" >&2; exit 64 ;;
esac

exec /usr/bin/systemctl restart "$1"
```

फिर sudo को उस wrapper और exact arguments तक सीमित करें, जहां platform यह सुविधा देता हो:

```sudoers
autobot ALL=(root) /usr/local/sbin/restart-approved-service payment-worker, \
                    /usr/local/sbin/restart-approved-service report-worker
```

इससे `systemctl` subcommand का अनियंत्रित फैलाव रुकता है। लेकिन इससे यह तय नहीं होता कि इस समय restart उचित है या नहीं। Wrapper को root ownership, non-writable parent directories और agent-controlled environment variables से सुरक्षा भी चाहिए। अगर एजेंट wrapper को बदल सकता है या उसके द्वारा चलाए जाने वाले किसी हिस्से को बदल सकता है, तो नियम विफल हो चुका है।

Automation के लिए «बस NOPASSWD इस्तेमाल करें» वाली लोकप्रिय सलाह से बचें। लोग इसे इसलिए पसंद करते हैं क्योंकि unattended jobs password prompt पर रुकना बंद कर देती हैं। Autonomous agent के लिए password prompt कभी सार्थक control नहीं था। उसे unrestricted elevation से बदलना केवल आखिरी दिखाई देने वाला विराम भी हटा देता है। उसकी जगह specific authorization और जवाबदेह record दें, खाली सुविधा नहीं।

## Elevation request में ऐसा कारण चाहिए जिसे reviewer परख सके

कारण authorization decision का हिस्सा है, बाद में ticket में चिपकाया जाने वाला सजावटी वाक्य नहीं। एजेंट को elevation से पहले request बनानी चाहिए और approval surface पर उस कारण के साथ proposed operation दिखना चाहिए।

Request में इन fields को एक साथ बांधें:

- Exact target, जैसे `api-02` और `payment-worker`।
- मांगी गई privileged operation, fixed arguments सहित।
- कारण, जो incident, deployment, maintenance task या देखी गई स्थिति से जुड़ा हो।
- अपेक्षित असर और rollback action।
- इतनी छोटी expiry कि छोड़ी गई request स्थायी authority न बन सके।

कारण में ऐसा evidence होना चाहिए जिसे कोई इंसान परख सके। «Build ठीक करने के लिए sudo चाहिए» बताता है कि एजेंट ने अभी पर्याप्त diagnosis नहीं की है। «मौजूदा certificate renewal में parse error आने के बाद `/etc/acme/client.conf` को reviewed release configuration से बदलें; validation विफल हो तो पिछला version वापस लाएं» में file, स्थिति और recovery path तीनों स्पष्ट हैं।

Free-text explanation रखने पर भी request को structured रखें। Structured fields एजेंट के लिए planning और execution के बीच target को चुपचाप बदलना कठिन बनाते हैं। बाद में कई chat windows से decision दोबारा बनाने की जरूरत भी नहीं पड़ती।

Immutable request identifier इस्तेमाल करें। Approval को उसी identifier, बताए गए target और बताए गए operation को authorize करना चाहिए। «Outage संभालो» जैसे natural-language instruction को approve करके एजेंट को बाद में यह तय न करने दें कि कौन सी root commands उसके दायरे में आती हैं। इससे human approval असीमित blank check बन जाती है।

एक उपयोगी request record ऐसा दिख सकता है:

```json
{
  "request_id": "elev-7f4c2",
  "agent_session": "run-91b0",
  "host": "api-02",
  "operation": "/usr/local/sbin/restart-approved-service payment-worker",
  "reason": "Deployment 482 left payment-worker unhealthy; status shows repeated configuration parse errors.",
  "expected_effect": "Service restarts with the reviewed configuration.",
  "rollback": "Restore the previous configuration revision and restart the service.",
  "expires_at": "2025-03-08T14:25:00Z"
}
```

Execution record में `request_id` वापस शामिल होना चाहिए। इस binding के बिना approver ने एक operation स्वीकार किया हो सकता है, जबकि एजेंट ने दूसरा operation किया हो।

## Agent process की अपनी पहचान होनी चाहिए

साझा `deploy` account से नुकसान हो जाने के बाद हर actor एक जैसा दिखाई देता है। Agent run को ऐसी identity दें जिसमें यह दर्ज हो कि उसे किस executable ने शुरू किया, किस human या service ने शुरू किया, उसे कौन सा repository और task मिला और उसकी authority कब खत्म होती है।

Human identity और process identity अलग तथ्य हैं। कोई developer agent शुरू कर सकता है, लेकिन agent process घंटों बाद files, tool output और network responses पढ़ने के बाद command जारी करता है। Audit trail में दोनों तथ्य सुरक्षित रहने चाहिए। «Jamie ने run 91b0 शुरू किया» और «signed agent process 91b0 ने elevation मांगा» से investigator sponsorship और execution को अलग कर सकता है।

Code signing access मांगने वाले local executable की पहचान में मदद कर सकता है। इससे यह साबित नहीं होता कि model का instruction सुरक्षित था और न ही compromised repository भरोसेमंद बनती है। Process identity को यह सीमा मानें कि कौन request कर सकता है, न कि request समझदारी भरी है इसका प्रमाण।

Agent को reusable root password, ऐसा private SSH credential जो सीधे administrator account तक पहुंचता हो, या लंबे समय तक चलने वाला sudo timestamp न दें जिसे वह अनिश्चित समय तक refresh कर सके। इनमें से हर चीज सीमित request को portable capability में बदल देती है। Agent उस capability को workspace, log, build artifact या child process में copy कर सके, तो उसका फैलाव आपके नियंत्रण से बाहर हो जाता है।

Short-lived credentials exposure घटाते हैं, लेकिन खराब command को अच्छा नहीं बनाते। Expiry को defined operation, bound process और reason के साथ इस्तेमाल करें। इनमें से कोई भी चीज न हो, तो आपके पास बेहतर नाम वाला access token ही है।

## Root shell एजेंट को instructions की गलत व्याख्या की बहुत गुंजाइश देता है

Agent session के लिए `sudo -i`, `sudo su`, `sudo sh` या unrestricted `sudo bash` को कभी approve न करें। Root shell बाद की हर command को authorize करता है, इनमें वे commands भी शामिल हैं जो ऐसे tool output से बनी हों जिसे पहली command approve करते समय किसी ने देखा तक न था।

यही चेतावनी उन command forms पर भी लागू होती है जो मनमाने interpreter को छिपाती हैं:

```sh
sudo python3 -c "$AGENT_TEXT"
sudo env CONFIG="$AGENT_TEXT" /usr/local/sbin/apply-config
sudo tee /etc/some-file.conf
```

हर उदाहरण तब तक सीमित लगता है, जब तक उसके input channel को न देखें। Python मनमाना code चलाता है। `env` program के व्यवहार को ऐसे बदल सकता है जिसकी caller ने कल्पना नहीं की थी। `tee` किसी मनमाने root-owned path पर लिखने की authority दे सकता है, अगर path सीमित न हो। Arguments और environment को न देखने वाली command policy केवल filename policy है।

बेहतर design diagnosis और execution को अलग करता है। Agent unprivileged facts देख सकता है, proposed change बना सकता है और उसे review के लिए भेज सकता है। फिर सीमित privileged helper को केवल approved inputs मिलें। Helper उन inputs को दोबारा validate करे, क्योंकि review-time validation और execution-time validation अलग failures से बचाते हैं।

मान लें deployment agent को service failure दिखती है। Broad sudo के तहत वह unit override बदल सकता है, manager reload कर सकता है और service restart कर सकता है। Repository-controlled configuration में मौजूद malicious string `ExecStart` line बन सकती है और restart उसे root authority के साथ चला सकता है। Constrained design में agent status पढ़कर reviewed configuration तैयार कर सकता है, लेकिन privileged deploy helper approved artifact location से केवल content digest स्वीकार करता है। वह unit files, arbitrary paths और unmanaged overrides को अस्वीकार करता है।

यह काम shell सौंप देने से कठिन है। लेकिन यही किसी ज्ञात operation और ऐसे interpreter के बीच का फर्क है जो approval के बाद नए operations खुद बना सकता है।

## Approval operation के जोखिम के साथ बदलनी चाहिए

Routine और सीमित remote actions के लिए एक session approval उचित हो सकती है। हर administrator capability के इस्तेमाल के लिए यह पर्याप्त नहीं है। Session authorization और per-call authorization को अलग controls मानें।

Session authorization का सवाल है: «क्या यह पहचाना गया agent process इस run की अवधि में सामान्य remote channels इस्तेमाल कर सकता है?» इससे कोई अज्ञात local process किसी ज्ञात agent का चुपचाप रूप नहीं धर सकता। Process खत्म होते ही यह authorization समाप्त होनी चाहिए और operator इसे तुरंत revoke कर सके।

Per-call authorization का सवाल है: «क्या यह specific action अभी चल सकती है?» इसे बड़े blast radius, दुर्लभ credentials, production impact या irreversible effects वाले operations पर लगाएं। Approval card में सबसे पहले process identity दिखे, फिर target, operation, reason, expiry और expected effect। Identity को लंबे prose के नीचे छिपाने से उद्देश्य ही खत्म हो जाता है।

Operator से generated shell की सौ पंक्तियां पढ़ने की उम्मीद न करें। Agent को named operations का vocabulary दें और उनके details render करें। «api-02 पर payment worker restart करें» review किया जा सकता है। Nested quoting वाला shell blob इंसान से ऐसी चीज पर rubber-stamp करवाता है जिसे वह समझ नहीं सकता।

Approval path में ऐसा refusal path भी होना चाहिए जो एजेंट को recover करने में मदद करे। स्पष्ट denial लौटाएं और, जहां उचित हो, «deployment change record जरूरी है» या «यह operation production पर उपलब्ध नहीं है» जैसा निर्देश दें। Agent को मामूली wording बदलकर बार-बार कोशिश करने न दें, जब तक थका हुआ व्यक्ति उसे स्वीकार न कर ले। Denial के बाद request बंद होनी चाहिए, जब तक कोई human materially अलग जानकारी के साथ नई request न बनाए।

Emergency में भी यही अनुशासन रखें। On-call engineer कम expiry और incident reference के साथ narrow request approve कर सकता है। जल्दी की जरूरत review को तेज कर सकती है, record मिटाने या interactive root shell देने को सही नहीं ठहराती।

## Audit records को request बनाने वाले agent से बचा रहना चाहिए

Terminal transcript debugging में मदद करता है, लेकिन वह audit record का पूरा भार नहीं उठा सकता। Agent उसे छोड़ सकता है, local files बदल सकता है या ऐसी path से commands चला सकता है जिसे transcript ने capture नहीं किया। Authorization और execution को ऐसे store में capture करें जिसे agent rewrite न कर सके।

Decision को action से अलग record करें। Approval record में यह दिखना चाहिए कि किसने और कब मंजूरी दी, process identity क्या थी, request की exact contents क्या थीं और expiry कब है। Execution record में दिखना चाहिए कि helper चला या नहीं, वह किस host तक पहुंचा, उसने क्या लौटाया, कब खत्म हुआ और कौन सा request identifier उसे authorize करता था।

संवेदनशील command output को व्यापक रूप से दिखने वाले journal में डालने के बजाय result digest या सीमित output summary रखें। जांच कर रहे व्यक्ति को इतना evidence चाहिए कि वह देख सके payment-worker restart हुआ और healthy हो गया। उसे copied database password की जरूरत नहीं है जो संयोग से stderr में आ गया हो।

Audit logs incident के बाद महत्वपूर्ण हो जाते हैं, इसलिए tamper evidence जरूरी है। Hash chain हर record को पिछले record से जोड़ती है। कोई entry बदली या हटाई जाए, तो verification continuity टूटना दिखा देती है। Verification को agent से स्वतंत्र रखें और संभव हो तो actions चलाने वाले credentials से भी स्वतंत्र रखें। ऐसा log जिसे verify करने के लिए compromised administrator credential चाहिए, सबसे जरूरी समय पर कम उपयोगी होता है।

उदाहरण के लिए, offline verifier को इस तरह का sequence दिखाना चाहिए:

```text
$ sp audit verify --file agent-audit.enc
verified records: 184
chain start: 8c1a...e72d
chain end: 4bf0...193a
status: valid
```

महत्वपूर्ण बात command का नाम नहीं है। महत्वपूर्ण यह है कि verifier agent से explanation मांगे बिना बदले हुए ciphertext record को पहचान सके। Machine के बाहर copies रखें, क्योंकि जिस machine पर agent काम करता है उसे नियंत्रित करने वाला attacker पूरा journal मिटा सकता है।

Sallyport credentials को agent से बाहर रखकर और write-blind, encrypted hash-chained audit log से session तथा individual-action journals दिखाकर इस separation को लागू करता है। यह model तब उपयोगी है जब agents को HTTP या SSH actions करने हों, लेकिन उनके पास उन्हें authorize करने वाले credentials कभी न हों।

## Privileged helpers के interfaces सीमित हों और hostile-input tests हों

Helper केवल बीस lines का हो, फिर भी security-sensitive code है। उसे ऐसे test करें जैसे हर argument, environment variable, working directory और referenced file attacker से आया हो, क्योंकि AI agent को बिना नुकसान की मंशा के भी hostile material पास करने के लिए उकसाया जा सकता है।

Operation inventory से शुरू करें। Agent को legitimately चाहिए हर privileged effect लिखें, जैसे किसी खास service को restart करना या signed release artifact install करना। अगर किसी effect को «arbitrary command चलाएं» कहे बिना समझाया नहीं जा सकता, तो operation अभी design नहीं हुआ है।

हर helper को sudoers में डालने से पहले इन सवालों के जवाब दें:

1. Caller कौन से exact inputs दे सकता है और helper हर input को कैसे validate करता है?
2. कौन से filesystem paths, executables, configuration files और environment variables उसके व्यवहार को प्रभावित करते हैं?
3. क्या कोई accepted input shell, interpreter, pager, editor, plugin loader या network fetch चला सकता है?
4. क्या helper कार्रवाई से पहले ownership, permissions और content identity verify करता है?
5. Validation विफल होने और execution सफल होने, दोनों स्थितियों में वह कौन सा record बनाता है?

केवल happy path नहीं, negative tests भी चलाएं। `../` path traversal, shell metacharacters, खाली service name, बहुत बड़ा input, अनपेक्षित Unicode और ऐसा valid name दें जो unsafe state की ओर जाता हो। Validation और use के बीच referenced file बदलने की कोशिश करें। देखें कि writable log directory, temporary directory या parent directory agent को root output redirect करने देती है या नहीं।

Updates के बाद dependencies भी जांचें। किसी command के एक version के खिलाफ सुरक्षित helper नए version में configuration path स्वीकार करने या extension load करने वाला option आने पर असुरक्षित हो सकता है। Narrow interfaces maintenance burden घटाते हैं, खत्म नहीं करते।

## Elevation को controlled exception की तरह लागू करें

सबसे खतरनाक ambient privilege forms हटाने से शुरुआत करें: shared administrator accounts, unrestricted `NOPASSWD`, agent configuration में reusable root secrets और root shells। यह बदलाव करने से पहले आपको पूरा automation रोकने की जरूरत नहीं है। पहले read-only diagnosis बचाए रखें, फिर एक-एक protected operation को explicit helper और approval path में ले जाएं।

ऐसा operation चुनें जो process को परखने के लिए पर्याप्त बार होता हो, लेकिन जिसका rollback सीमित हो, जैसे reviewed deployment के बाद named worker restart करना। Agent से host, reason, effect, rollback और expiry मांगें। Approver को vague requests reject करने दें। यह friction agent workflow को सिखाती है कि power मांगने से पहले कौन सा evidence जुटाना है।

Denied requests की समीक्षा successful requests जितनी गंभीरता से करें। Arbitrary file writes की requests का समूह दिखा सकता है कि deployment interface में जरूरी operation नहीं है। यह भी दिखा सकता है कि agent boundary को पार करने की कोशिश करता रहता है। दोनों अलग समस्याएं हैं और audit trail से आप उन्हें अलग पहचान सकते हैं।

फिर revocation का अभ्यास करें। Agent के काम करते समय session बंद करें, expiry के बाद पहले से जारी request को deny करें और verify करें कि copied request identifier दूसरी action authorize नहीं कर सकता। Teams approval screens को test करके अक्सर इस हिस्से को छोड़ देती हैं। जब run के बीच agent अलग तरह से व्यवहार करने लगे, तब revocation ही वह control है जिसकी जरूरत पड़ती है।

परिपक्वता को इस बात से न मापें कि agent root के रूप में कितनी commands चला सकता है। इसे इस बात से मापें कि agent या उसके inputs के गलत होने पर भी हर elevation सीमित, किसी पहचान से जुड़ी, समय-बद्ध, review योग्य और recoverable रहती है या नहीं।
