gNSI Acctz — حسابرسی (Accounting) صفحه مدیریت در Cisco IOS XR
مشکل
Certz احراز هویت کلاینتها را از طریق TLS انجام میدهد. Authz تعیین میکند که کاربر مجاز به فراخوانی کدام سرویسهای gRPC است. Pathz کنترل میکند که کاربر به کدام مسیرهای gNMI دسترسی دارد. اما پس از تمام این مراحل، واقعاً چه اتفاقی افتاده است؟ حسابرسی (Accounting) سنتی AAA در IOS XR، نشستهای CLI و NETCONF را پوشش میدهد، اما فعالیتهای مبتنی بر gRPC (شامل gNMI، gNOI، gRIBI و خود gNSI) فاقد یک مسیر حسابرسی (Audit Trail) استاندارد هستند. بدون وجود سوابق حسابرسی ساختاریافته برای صفحه مدیریت (Management Plane) در gRPC، تیمهای انطباق (Compliance) راهی برای پاسخ به این پرسش ندارند که «چه کسی، چه کاری را، در چه زمانی و از کجا انجام داده است؟». پیش از این، تنها گزینه موجود استفاده از syslog بود.
Acctz چیست؟
gNSI Acctz این شکاف را پر میکند. این قابلیتسوابق حسابداری (Accounting Records) ساختاریافته رااز دستگاه به یکجمعآوریکننده (Collector)از طریق gRPC ارسال میکند. این سوابق تمامی مواردی را که با صفحه مدیریت (Management Plane) در تعامل هستند، پوشش میدهند:
- رابط خط فرمان (CLI)وپوسته (Shell)دستورات (بسط یافته به فرم کامل —
sh ver detتبدیل میشود بهshow version detail) - نشستهای (Sessions) NETCONFRESTCONF
- فعالیت رابط کاربری وب (WebUI)وخدمات (Services) gRPC — شامل gNMI, gNOI, gNSI, gRIBI, P4RTورود (Login)
- خروج (Logout)رویدادها (Events)
- ورودوخروجرویدادها
هر رکورد شامل موارد زیر است:متادیتای نشست (Session metadata)(5-tuple، شناسه کانال، TTY)،جزئیات احراز هویت (Authentication details)(نوع، وضعیت، علت شکست)، ونتیجه مجوزدهی (Authorization outcome)(مجاز/غیرمجاز (PERMIT/DENY) به همراه جزئیات خطمشی). برای رکوردهای gRPC، نام RPC (مانند/gnmi.gNMI/Set, /gnsi.certz.v1.Certz/Rotate) و یک اسنپشات اختیاری از محتوا (Payload) گنجانده شده است. محتواهای حساس (کلیدهای خصوصی که از طریق Certz / Credentialz) چرخش داده شدهاند، ممکن است سانسور (Redacted) شوند.
این دستگاه دارای یکبافر چرخشی (Circular Buffer)از سوابق اخیر است. یک جمعآوریکننده (Collector) متدRecordSubscribeرا فراخوانی میکند، یک برچسب زمانی (Timestamp) ارسال مینماید و دستگاه تمام سوابق را از آن نقطه به بعد استریم میکند. یک برچسب زمانیِ0به معنای «ارسال تمام تاریخچه بافر شده» است. این استریم تا زمانی که نشست (Session) gNSI پایان یابد یا خطایی رخ دهد، به طور نامحدود ادامه مییابد.
در چارچوب AAA، بخش Acctz همانAccountingاست — یک مسیر حسابرسی (Audit trail) ساختاریافته برای هر عملیاتی که با صفحه مدیریت (Management plane) در ارتباط است، صرفنظر از اینکه آن عملیات مجاز بوده یا رد شده باشد.
پشتیبانی از IOS XR
| نسخه (Release) | تغییرات |
|---|---|
| 24.3.1 | معرفی قابلیت ثبت وقایع (Logging) در gNSI Acctz — دوطرفه (bidirectional)Acctz.RecordSubscribeرکوردهای دستور و سرویس gRPC.grpc aaa accounting queue-sizeرابط خط فرمان (CLI). |
| 24.4.1 | AcctzStream(استریمینگ سمت سرور) جایگزین حالت دوطرفه (bidirectional) منسوخ شده استAcctz. grpc aaa accounting history-memoryرابط خط فرمان (CLI). آمار به تفکیک جمعآوریکننده (Per-Collector) درshow gnsi acctz statistics. |
پلتفرمها: خانوادههای Cisco 8000، NCS 5500 و ASR 9000. شماره قطعه (SKU) دقیق و PI/SMU را در یادداشتهای انتشار (Release Notes) تأیید کنید.
اعتبارسنجی شده در:این مقاله به صورت کامل بر روی Cisco NCS-540 با سیستمعامل IOS XR اجرا شده است.25.3.1در مواردی که رفتار دستگاه با پروتکل/مشخصات فنی (proto/spec) یا متن اصلی xrdocs تفاوت داشته باشد، این تفاوت در ادامه به صورت درونخطی (inline) ذکر شده است.
دو سرویس gRPC
این acctz.proto دو سرویس را تعریف میکند:
| سرویس (Service) | نوع جریان (Stream type) | وضعیت (Status) |
|---|---|---|
Acctz |
جریان دوطرفه (Bidirectional stream) (اشتراک رکورد (RecordSubscribe)) |
منسوخ شده (Deprecated) (IOS XR 24.3.1) |
جریان Acctz (AcctzStream) |
جریان کلاینت به سرور (Client → server stream) (اشتراک رکورد (RecordSubscribe)) |
ترجیحی (Preferred) (IOS XR 24.4.1+) |
AcctzStreamیک سرویس استریمینگ یکطرفه از سمت سرور است — کلکتور (Collector) یکRecordRequestارسال میکند و سرور پیامهایRecordResponseرا به صورت استریم بازمیگرداند. سرویس منسوخشدهAcctzاز یک استریم دوطرفه استفاده میکرد. پیادهسازیها ممکن است یکی از این دو را ارائه دهند؛ همیشه باReflection.
نحوه عملکرد استریمینگ (Streaming)
- A جمعآوریکننده (Collector)فراخوانیها (calls)RecordSubscribe.
- RecordRequestحامل یکمهر زمانی (timestamp)(نانوثانیه از مبدأ زمان).
0به معنای «ارسال تمام تاریخچه بافر شده» است. زمان فعلی به معنای «فقط رکوردهای جدید» است. - سرور جریان میدهدRecordResponseپیامها به محض ایجاد رکوردهای جدید.
- این جریان تا زمانی که نشست (session) gNSI پایان یابد، یک TCP keepalive منقضی شود، یا سرور یک جمعآوریکننده (Collector) متوقفشده را که بافر خروجی آن برای مدت طولانی پر مانده است قطع کند، به طور نامحدود ادامه مییابد.
+-----------------+
| Remote Collector|
+-----------------+
^
| gNSI Acctz stream
|
+----------------------------------+
| IOS XR Router |
| CLI NETCONF gNMI gNOI gRIBI |
| +------+-------+------+-----+--+
| Acctz Service |
+----------------------------------+
تاریخچه و بافر چرخشی (Circular Buffer)
این دستگاه یک بافر چرخشی (Circular Buffer) را حفظ میکند تا در صورت قطعیهای کوتاه در Collector، تمامی دادهها از دست نروند. رفتار کلیدی:
- عمق تاریخچه (History depth)از طریق دستورات زیر قابل پیکربندی است:
grpc aaa accounting queue-size(پیشفرض: 40 رکورد، محدوده: 1 تا 512) وgrpc aaa accounting history-memory(پیشفرض: 40 مگابایت، محدوده: 1 تا 400 مگابایت). - هنگامی که صف پر است و هیچ جمعکنندهای (Collector) متصل نیست، قدیمیترین رکوردها بازنویسی میشوند.
- علاوه بر رکوردهای صف که برای استریمینگ ذخیره شدهاند، تا512 رکورد اضافیدر حین پردازش نگهداری میشوند.
- بدترین حالت مصرف حافظه EMSd:(N+1) × history-memoryکه در آن N تعداد جمعکنندههای (Collector) متصل است.
- If
history_istruncatedدر یکRecordResponseظاهر میشود، جمعکننده (Collector) درخواستی برای یک برچسب زمانی (Timestamp) قدیمیتر از بافر ارسال کرده است — برخی رکوردها از دست رفتهاند.
در نسخه 25.3.1:
RecordResponse.history_istruncatedistrueدر اولین رکورد ازهربازپخش (Replay)، حتی در یک بافر تازه باtimestamp = "1970-01-01T00:00:00Z. این پرچم (Flag) را در این بیلد (Build) صرفاً جهت اطلاع در نظر بگیرید و با دستورTotal History Truncationinshow gnsi acctz statisticsبرای تشخیص پر شدن واقعی بافر (Buffer wrap) بررسی مجدد کنید.
ساختار RecordResponse
هرRecordResponseشامل چهار دسته اطلاعات است:
| دستهبندی | فیلدهای کلیدی | هدف |
|---|---|---|
| نشست (Session) | آدرس محلی (local_address), آدرس راه دور (remote_address), پورت راه دور (remote_port), شناسه کانال (channel_id), ترمینال (tty), وضعیت (status) | چه کسی و از کجا متصل شده است |
| کاربر / احراز هویت (Authn) | هویت (identity), نقش (role), نوع احراز هویت (authn.type), وضعیت احراز هویت (authn.status) | چه کسی و چگونه احراز هویت شده است |
| محتوای سرویس (Service payload) | سرویس دستورات (cmd_service)(CLI/NETCONF/etc.) یاسرویس gRPC (grpc_service)(gNMI/gNOI/gNSI/etc.) | چه چیزی اجرا شده است |
| مجوزدهی (Authorization) | وضعیت مجوزدهی (authz.status)(مجاز/غیرمجاز – PERMIT/DENY),جزئیات مجوزدهی (authz.detail) | آیا اجازه دسترسی داده شده است |
درخت کامل فیلد پروتو (proto) در پیوست در انتهای این مقاله قرار دارد.
رکوردهای Command در مقابل gRPC
| نوع رکورد | آنچه ثبت میکند | فیلدهای کلیدی |
|---|---|---|
| سرویس دستور (CommandService) | CLI, Shell, NETCONF, RESTCONF, WebUI | دستور (cmd) + آرگومانهای دستور (cmd_args)(بسط یافته از مخفف),مجوز (authz)نتیجه (outcome) |
| سرویس جیآرپیسی (GrpcService) | gNMI, gNOI, gNSI, gRIBI, P4RT | نام فراخوانی (rpc_name)(HTTP/2مسیر (path):هدر (header)، اختیاریمحتوا (payload), مجوز (authz)نتیجه (outcome) |
توجه:رکوردهای GRIBI میتوانند بسیار پرحجم باشند. در توضیحات پروتکل (proto comments) توصیه شده است که ثبت وقایع (logging) مربوط به GRIBI در خطمشیهای احراز هویت (Authorization Policy) بهصورت پیشفرض غیرفعال باشد.
مرجع رابط خط فرمان (CLI) برای IOS XR
| دستور (Command) | عملکرد | پیشفرض (Default) | محدوده (Range) | نسخه (Release) |
|---|---|---|---|---|
aaa accounting commands default start-stop group tacacs+ |
فعالسازی حسابداری AAA (مورد نیاز برای Acctz) | — | — | — |
grpc aaa accounting queue-size |
تعداد رکوردهایی که بافر چرخشی (Circular Buffer) نگه میدارد | 40 | 1–512 | 24.3.1 |
grpc aaa accounting history-memory |
حداکثر حافظه برای رکوردهای تاریخچه کش شده | 40 MB | 1–400 MB | 24.4.1 |
| دستور نمایش (Show command) | آنچه نمایش میدهد |
|---|---|
show gnsi state |
وضعیت زیرسیستم gNSI (شامل Acctz Thread و وضعیت RDSFS) |
show gnsi acctz statistics |
بافر تاریخچه (History Buffer)، شمارندههای صف (ارسالشده/حذفشده/کوتاهشده)، کالکتورهای متصل، و رکوردها/حذفها به تفکیک هر کالکتور |
show aaa accounting statistics |
شمارندههای رویداد حسابداری (Accounting) در سطح AAA |
show tech-support gnsi |
بسته کامل عیبیابی (Diagnostic) برای gNSI |
کار عملی: جمعآوری حسابداری (Acctz) سرتاسری (End-to-End)
پیشنیازها
- روتر IOS XR با قابلیت gRPC فعال (نسخه 24.3.1 یا بالاتر برای Acctz؛ نسخه 24.4.1 یا بالاتر برای AcctzStream)
- grpcurl نصب شده
- پروتکلهای OpenConfig gNSI کلون شده:
git clone https://github.com/openconfig/gnsi.git - حسابداری AAA (AAA Accounting) روی روتر پیکربندی شده است (پیکربندی زیر را مشاهده کنید)
این راهنما تمامی مراحل را پوشش میدهد: پیکربندی روتر (Router)، شناسایی RPCها، شروع جریان جمعآوریکننده (Collector stream)، فعالسازی فعالیتهای صفحه مدیریت (Management Plane)، بررسی رکوردها و تأیید نهایی با استفاده از دستورات IOS XR CLI.
محیط (ایستگاه کاری)
export GNSI_IMPORT="${GNSI_IMPORT:-$PWD}"
export XR="${XR:-198.51.100.1:57400}"
export XR_USER="${XR_USER:-cisco}"
export XR_PASS="${XR_PASS:-}"
GRPC_MD=(-H "username:${XR_USER}" -H "password:${XR_PASS}")
PROTO_ACCTZ="acctz/acctz.proto"
فقط برای آزمایشگاه:تمام مثالهای زیر از
-plaintext(بدون TLS) استفاده میکنند. در محیط عملیاتی، جایگزین کنید-plaintextبا-cacert,-certو-keyفلگها. مشاهده کنید مقاله Certz برای راهاندازی TLS.
پیکربندی روتر (IOS XR CLI)
قابلیت حسابرسی AAA (AAA Accounting) را فعال کنید (اگر قبلاً فعال نشده است) و بافر Acctz را تنظیم (Tune) کنید:
configure
aaa accounting commands default start-stop group tacacs+
aaa accounting exec default start-stop group tacacs+
grpc aaa accounting queue-size 100
grpc aaa accounting history-memory 80
commit
end
تأیید فعال بودن gNSI:
show gnsi state
لطفاً متن ورودی خود را برای ترجمه ارسال کنید تا طبق قوانین و دستورالعملهای تعیینشده، ترجمه آن را انجام دهم.
----------------GNSI state--------------
Global:
Main Thread cerrno: Success
Acctz Thread cerrno: Success
State: Active
RDSFS State: Active
فهرست سرویسها (Reflection)
بررسی کنید که دستگاه کدامیک از سرویسهای حسابداری (Acctz Services) را صادر (Export) میکند:
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "$PROTO_ACCTZ" \
"${GRPC_MD[@]}" \
"${XR}" \
list gnsi.acctz.v1.Acctz
gnsi.acctz.v1.Acctz.RecordSubscribe
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "$PROTO_ACCTZ" \
"${GRPC_MD[@]}" \
"${XR}" \
list gnsi.acctz.v1.AcctzStream
gnsi.acctz.v1.AcctzStream.RecordSubscribe
هر دو سرویس ممکن است در Reflection ظاهر شوند، اما در ایمیجهای فعلی تنهاAcctzStreamپیادهسازی شده است. سرویس منسوخ شدهAcctz(دو طرفه) در هنگام فراخوانی مقدارUnimplementedرا بازمیگرداند. همیشه ازAcctzStream.
توصیف RPC (فراخوانی رویه از راه دور)
متد RecordSubscribe را بررسی کنید:
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "$PROTO_ACCTZ" \
"${GRPC_MD[@]}" \
"${XR}" \
describe gnsi.acctz.v1.AcctzStream.RecordSubscribe
پاسخ روتر (Router response)
gnsi.acctz.v1.AcctzStream.RecordSubscribe is a method:
rpc RecordSubscribe (.gnsi.acctz.v1.RecordRequest) returns ( stream.gnsi.acctz.v1.RecordResponse);
چاپ یک قالب برای پیام درخواست:
grpcurl -import-path "$GNSI_IMPORT" \
-proto "$PROTO_ACCTZ" \
-msg-template describe gnsi.acctz.v1.RecordRequest
پاسخ روتر (Router response)
Message template:
{
"timestamp": "1970-01-01T00:00:00Z"
}
فیلدمهر زمانی (timestamp)از فرمت RFC 3339 استفاده میکند.1970-01-01T00:00:00Z(مبدأ زمان) به معنای “ارسال تمام تاریخچه ذخیره شده (buffered history)” است.
grpcurlنکته:این فیلد یکgoogle.protobuf.Timestampاست، بنابراین{"timestamp":"0"}با خطای تجزیه (Parse Error) در سمت کلاینت مواجه میشود. از{"timestamp":"1970-01-01T00:00:00Z"}یا صرفاً{}— هر دو کل بافر را بازپخش (Replay) میکنند.
اشتراک (Subscribe): بازپخش تمامی رکوردهای بافر شده (Buffered Records)
مهر زمانی (Timestamp)1970-01-01T00:00:00Zبازپخش تمام موارد موجود در بافر چرخشی (Circular Buffer):
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "$PROTO_ACCTZ" \
"${GRPC_MD[@]}" \
-d '{"timestamp": "1970-01-01T00:00:00Z"}' \
"${XR}" \
gnsi.acctz.v1.AcctzStream.RecordSubscribe
این دستور تمام تاریخچه بافر شده را استریم کرده و سپس با رکوردهای جدید به صورت بلادرنگ (Real-time) ادامه میدهد. کلیدهایCtrl+Cرا برای توقف فشار دهید. اولین رکورد ممکن است شاملhistoryIstruncated": trueباشد — به این معنی که بافر چرخشی (Circular Buffer) پر شده و رکوردهای قدیمیتر از دست رفتهاند:
{
"sessionInfo": {
"localAddress": "172.20.163.10",
"localPort": 57344,
"remoteAddress": "10.189.10.189",
"remotePort": 58091,
"ipProto": 6,
"channelId": "0",
"status": "SESSION_STATUS_OPERATION",
"user": {
"identity": "cisco"
}
},
"timestamp": "2026-04-16T05:51:01.332142269Z",
"historyIstruncated": true,
"grpcService": {
"serviceType": "GRPC_SERVICE_TYPE_GNSI",
"rpcName": "/gnsi.credentialz.v1.Credentialz/GetPublicKeys",
"authz": {
"status": "AUTHZ_STATUS_PERMIT"
},
"protoVal": {
"@type": "type.googleapis.com/google.protobuf.BytesValue",
"value": null
}
}
}
اشتراک: فقط جریان (Stream) رکوردهای اخیر
برای استریم کردن رکوردها از یک ساعت گذشته (با فرمت ISO 8601/RFC 3339):
# macOS:
SINCE=$(date -u -v-1H +"%Y-%m-%dT%H:%M:%SZ")
# Linux:
# SINCE=$(date -u -d "1 hour ago" +"%Y-%m-%dT%H:%M:%SZ")
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "$PROTO_ACCTZ" \
"${GRPC_MD[@]}" \
-d "{\"timestamp\": \"${SINCE}\"}" \
"${XR}" \
gnsi.acctz.v1.AcctzStream.RecordSubscribe
فقط برای استریمجدیدثبت رکوردها از این پس (پرش از تاریخچه):
NOW=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "$PROTO_ACCTZ" \
"${GRPC_MD[@]}" \
-d "{\"timestamp\": \"${NOW}\"}" \
"${XR}" \
gnsi.acctz.v1.AcctzStream.RecordSubscribe
فعالسازی فعالیت و مشاهده ورود رکوردها
در حالی که جریان (Stream) در یک ترمینال در حال اجرا است، ترمینال دوم را باز کرده و مقداری فعالیت gRPC را فعال کنید:
# Credentialz CanGenerateKey
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "credentialz/credentialz.proto" \
"${GRPC_MD[@]}" \
-d '{"keyParams": "KEY_GEN_SSH_KEY_TYPE_EDDSA_ED25519"}' \
"${XR}" \
gnsi.credentialz.v1.Credentialz.CanGenerateKey
# Certz GetProfileList
grpcurl -plaintext \
-import-path "$GNSI_IMPORT" \
-proto "certz/certz.proto" \
"${GRPC_MD[@]}" \
-d '{}' \
"${XR}" \
gnsi.certz.v1.Certz.GetProfileList
به ترمینال جریان Acctz بازگردید. پیامهای جدید RecordResponse ظاهر میشوند — یکی برای هر فراخوانی gRPC RPC که اجرا شده است.
در نسخه 25.3.1:هر فراخوانی (RPC) یکتای gRPC (
gNMI/Get,gNMI/Set,Pathz/Get,Credentialz/CanGenerateKey,Credentialz/GetPublicKeys) دو رکورد با فاصله زمانی حدود 80 میکروثانیه (µs) صادر میکند که دارای مقادیر یکسان درgrpcService.rpcNameauthzو,protoValهستند. ظرفیت جمعآوریکننده (Collector) و هشدارهای مربوط به هر RPC را بر اساس ضریب 2 برابر برای فراخوانیهای یکتا (Unary calls) برنامهریزی کنید.protoValظرفیت جمعآوریکننده (Collector) و هشدارهای هر RPC را بر اساس 2 برابر ضریب انتشار (Fan-out) برای فراخوانیهای یکتایی (Unary calls) برنامهریزی کنید.
خواندن یک RecordResponse
در اینجا یک رکورد سرویس gRPC واقعی که از یک روتر Cisco 8000 گرفته شده است، آورده شده است. این رکورد زمانی تولید شد کهCredentialz.CanGenerateKeyفراخوانی شد:
{
"sessionInfo": {
"localAddress": "172.20.163.10",
"localPort": 57344,
"remoteAddress": "10.189.10.189",
"remotePort": 58913,
"ipProto": 6,
"channelId": "0",
"status": "SESSION_STATUS_OPERATION",
"user": {
"identity": "cisco",
"role": "root-lr, netadmin, cisco-support"
}
},
"timestamp": "2026-04-16T06:01:47.028597996Z",
"grpcService": {
"serviceType": "GRPC_SERVICE_TYPE_GNSI",
"rpcName": "/gnsi.credentialz.v1.Credentialz/CanGenerateKey",
"authz": {
"status": "AUTHZ_STATUS_PERMIT"
},
"protoVal": {
"@type": "type.googleapis.com/google.protobuf.BytesValue",
"value": "CAQ="
}
}
}
و یک رکورد برایCertz.GetProfileList— به محموله (payload) دقت کنیدnull(بدنه پاسخ خالی است، کوتاه نشده است):
{
"sessionInfo": {
"localAddress": "172.20.163.10",
"localPort": 57344,
"remoteAddress": "10.189.10.189",
"remotePort": 58914,
"ipProto": 6,
"channelId": "0",
"status": "SESSION_STATUS_OPERATION",
"user": {
"identity": "cisco"
}
},
"timestamp": "2026-04-16T06:01:47.400770922Z",
"grpcService": {
"serviceType": "GRPC_SERVICE_TYPE_GNSI",
"rpcName": "/gnsi.certz.v1.Certz/GetProfileList",
"authz": {
"status": "AUTHZ_STATUS_PERMIT"
},
"protoVal": {
"@type": "type.googleapis.com/google.protobuf.BytesValue",
"value": null
}
}
}
یک رکورد برای یک RPC جریانی (Streaming RPC) مانندRotateHostParameters— توجهpayloadIstruncated: true(محتوای جریانی بزرگ به طور کامل ثبت نشده است):
{
"sessionInfo": {
"localAddress": "172.20.163.10",
"localPort": 57344,
"remoteAddress": "10.189.10.189",
"remotePort": 58076,
"ipProto": 6,
"channelId": "0",
"status": "SESSION_STATUS_OPERATION",
"user": {
"identity": "cisco"
}
},
"timestamp": "2026-04-16T05:50:39.054469860Z",
"grpcService": {
"serviceType": "GRPC_SERVICE_TYPE_GNSI",
"rpcName": "/gnsi.credentialz.v1.Credentialz/RotateHostParameters",
"payloadIstruncated": true,
"authz": {
"status": "AUTHZ_STATUS_PERMIT"
}
}
}
و خود جریان Acctz یک رکورد (Record) خود-ارجاع ایجاد میکند:
{
"sessionInfo": {
"localAddress": "172.20.163.10",
"localPort": 57344,
"remoteAddress": "10.189.10.189",
"remotePort": 58880,
"ipProto": 6,
"channelId": "0",
"status": "SESSION_STATUS_OPERATION",
"user": {
"identity": "cisco",
"role": "root-lr, netadmin, cisco-support"
}
},
"timestamp": "2026-04-16T06:01:22.999917154Z",
"grpcService": {
"serviceType": "GRPC_SERVICE_TYPE_GNSI",
"rpcName": "/gnsi.acctz.v1.AcctzStream/RecordSubscribe",
"payloadIstruncated": true,
"authz": {
"status": "AUTHZ_STATUS_PERMIT"
}
}
}
فیلدهای کلیدی برای بررسی:
| فیلد | توضیحات |
|---|---|
sessionInfo.user.identity |
چه کسی— نام کاربری یا SPIFFE ID |
sessionInfo.user.role |
گروههای کاربری — root-lr, netadmin, cisco-supportو غیره (ممکن است در برخی رکوردها ظاهر شود) |
sessionInfo.remoteAddress |
از کجا— آدرس IP کلاینت |
sessionInfo.status |
نوع نشست (Session) — SESSION_STATUS_OPERATIONبرای فراخوانیهای RPC,SESSION_STATUS_LOGIN/LOGOUTبرای رویدادهای نشست |
grpcService.rpcName |
چه چیزی— مسیر کامل gRPC (برای مثال:/gnsi.credentialz.v1.Credentialz/CanGenerateKey) |
grpcService.serviceType |
کدام خانواده gRPC — GRPC_SERVICE_TYPE_GNSI, GRPC_SERVICE_TYPE_GNMI, و غیره |
grpcService.authz.status |
مجاز یا غیرمجاز — AUTHZ_STATUS_PERMIT or AUTHZ_STATUS_DENY |
grpcService.payloadIstruncated |
آیا محتوا (Payload) برای گنجاندن کامل، بیش از حد بزرگ بوده است |
grpcService.protoVal |
نمونه محتوا (protobuf کدگذاری شده با base64) — برای RPCهای تکی موجود است، برای RPCهای جریانیِ کوتاه شده، موجود نیست |
cmdService.cmd + cmdArgs |
دستور CLI— بسط یافته از اختصارات (برای رکوردهای CommandService) |
timestamp |
چه زمانی— استاندارد RFC 3339 با دقت نانوثانیه |
historyIstruncated |
آیا رکوردها قبل از این نقطه از دست رفتهاند (پر شدن بافر) |
در نسخه 25.3.1:
نقش کاربر در sessionInfo.user.roleناپایدار است — رکوردهای متوالی برای یک کاربر مشابه در یک کانال یکسان گاهی شاملrole": "root-lr, cisco-supportمیشوند و گاهی کلیدroleرا کاملاً حذف میکنند. قوانین هشدار (Alerting rules) را بر اساسهویت (identity)مرتبط کنید، نه بر اساسroleدر این نسخه (build).
در نسخه 25.3.1:احراز هویت ناموفق gRPC به اشتباه برچسبگذاری شده است. یک فراخوانی
grpcurlبا رمز عبور اشتباه، یک رکوردSESSION_STATUS_LOGINبا برچسبCMD_SERVICE_TYPE_CLI(با وجود اینکه هیچ CLI لمس نشده است) به همراه یک رکوردSESSION_STATUS_OPERATION / AUTHZ_STATUS_DENYرویServerReflectionInfoصادر میکند. رکوردهای سمت LOGIN همچنین دارایlocalAddress: "None/remoteAddress: "Noneهستند، بنابراین خطلولههای انطباق (compliance pipelines) که بر اساس 5-tuple عمل میکنند، نمیتوانند این ورود ناموفق را به منبع خاصی نسبت دهند.
مربوط به امنیت در نسخه 25.3.1:هنگامی که یک
gNMI/Setتوسط Pathz رد میشود، رکورد Acctz همچنان شامل محتوای کامل base64-encoded از چیزی است که فراخواننده سعی در تنظیم آن داشته است. یکprotoValرد شده از نوعCredentialz/RotateAccountCredentials.passwordorیاCertz/RotateprotoValبنابراین باعث نشت اطلاعات محرمانه به لاگ حسابرسی (Audit Log) میشود. این مورد را
تأیید در IOS XR
پس از اجرای جریان (Stream)، اطمینان حاصل کنید که رکوردها (Records) تولید و تحویل داده شدهاند:
show gnsi acctz statistics
خروجی نمونه (کوتاه شده):
Acctz History Buffer:
Total Records: 80029
Total History Truncation: 0
Grpc Service Records:
GNMI: 80002
GNOI: 0
GNSI: 4
GRIBI: 0
P4RT: 0
Cmd Service Records:
Shell: 0
Cli: 23
Netconf: 0
History snapshot:
Max Memory Size: 80 MB
Memory Used: 0 MB
Max Number Of Records: 100
Records Used: 30
gRPC Accounting Queue:
Grpc services:
GNMI: 80002 sent, 0 dropped, 0 truncated
GNOI: 0 sent, 0 dropped, 0 truncated
GNSI: 4 sent, 0 dropped, 4 truncated
...
Cmd Accounting Queue:
Cmd services:
Shell: 0 sent, 0 dropped, 0 truncated
Cli: 23 sent, 0 dropped, 0 truncated
Netconf: 0 sent, 0 dropped, 0 truncated
...
Client Stats:
Number Of Clients: 1
History Truncation Events: 0
Record Requests: 4
Record Responses: 80029
Collectors:
Collector Statistics:
IP: 192.168.122.1
Port: 25906
Total: Records: 80029, Drops: 0
Grpc Service Records:
gNMI: Records: 80002, Drops: 0
gNSI: Records: 4, Drops: 0
Cmd Service Records:
CLI: Records: 23, Drops: 0
موارد قابل بررسی:
| بخش | موارد قابل بررسی |
|---|---|
| بافر تاریخچه (History Buffer) | مجموع رکوردهاباید غیر صفر باشد.مجموع حذفهای تاریخچه (Total History Truncation)باید0— غیر صفر بودن به معنای از دست رفتن رکوردها است. |
| شمارندههای صف (Queue Counters) | حذف شده (dropped)وکوتاه شده (truncated)شمارندهها به ازای هر سرویس. مقادیر غیر صفر در بخش حذفها (drops) به معنای سرریز شدن صف است. |
| آمار کلاینت (Client Stats) | تعداد کلاینتها ≥ 1. پاسخهای رکورد (Record Responses)باید با آنچه جمعآوریکننده (Collector) دریافت کرده است، مطابقت داشته باشد. |
| آمار جمعآوریکننده (Collector Statistics) | رکوردها و حذفها به ازای هر جمعآوریکننده (Per-Collector) — تأییدکننده تحویل دادهها به هر جمعآوریکننده متصل. |
همچنین حسابداری (Accounting) در سطح AAA را تأیید کنید:
show aaa accounting statistics
شکافهای حسابرسی (Auditing) رابط خط فرمان (CLI) در نسخه 25.3.1.دو مشاهده مرتبط که اپراتورها باید برای آن برنامهریزی کنند:
show aaa accounting statisticsاز gNSI Acctz جدا شده است — نمای AAA در وضعیتGNMI/GNSI/GNOI/GRIBI/P4RT: 0باقی میماند، صرفنظر از اینکه AcctzStream چند رکورد ارسال میکند. تنها نمای معتبر از رکوردهای استریم شده،show gnsi acctz statisticsبهعلاوه خودِ استریم است.- SSH
showدستوراتتولیدنمیکنندCommandServiceرکوردها (فقط یک موردSESSION_STATUS_LOGINدر هر نشست). ثبت وقایع (Logging) دستور به دستور Acctz برای CLI مبتنی بر SSH، یک شکاف در این نسخه است؛ حسابرسی ساختاریافته CLI همچنان به TACACS+ AAA متکی است.
پاکسازی (Cleanup): توقف جمعآوریکننده (Collector)
برای متوقف کردن جریان (Stream)، کلیدهای Ctrl+C را در ترمینال grpcurl فشار دهید. سرور به طور خودکار نشست (Session) جمعآوریکننده (Collector) را پاکسازی میکند. با دستور زیر تأیید کنید:Ctrl+Cدر ترمینال grpcurl برای متوقف کردن جریان (Stream). سرور به طور خودکار نشست (Session) Collector را پاکسازی میکند. با دستور زیر تأیید کنید:
show gnsi acctz statistics
تعداد کلاینتها (Clients)باید یک واحد کاهش یابد.
ابزارهای عیبیابی (Debugging) با grpcurl
# Describe the RPC and its streaming semantics
grpcurl -import-path "$GNSI_IMPORT" -proto "$PROTO_ACCTZ" \
describe gnsi.acctz.v1.AcctzStream.RecordSubscribe
# Show the RecordRequest message template
grpcurl -import-path "$GNSI_IMPORT" -proto "$PROTO_ACCTZ" \
-msg-template describe gnsi.acctz.v1.RecordRequest
# Show the RecordResponse message structure
grpcurl -import-path "$GNSI_IMPORT" -proto "$PROTO_ACCTZ" \
-msg-template describe gnsi.acctz.v1.RecordResponse
پایش (Monitoring) با استفاده از YANG Telemetry
مدل YANG از نوع OpenConfigopenconfig-gnsi-acctzشمارندهها (Counters) را هم برای سمت جمعآوریکننده (Collector) و هم برای سمت تولید رکورد (Record) در دسترس قرار میدهد.
در نسخه 25.3.1:دستگاه تبلیغ میکند
openconfig-gnsi-acctz, 0.2.0(نه نسخه بالادستی0.4.0)، و هر مسیر عملیاتی تحت/system/aaa/acctzو/system/aaa/accountingبازمیگرداندNotFound. استفاده کنید ازshow gnsi acctz statisticsتا زمانی که سمت gNMI پیادهسازی شود. تنها سیگنال برنامهنویسی موجود در حال حاضر، ردیف شمارنده به ازای هر RPC برای/gnsi.acctz.v1.AcctzStream/RecordSubscribeتحتopenconfig-gnsi-authz:authz-policy-counters.
شمارندههای کلاینت (Client Counters)(پایه:/system/grpc-servers/grpc-server[name=):
| مسیر YANG (نسبت به پایه) | توضیح عملکرد |
|---|---|
.../record-requests |
مجموع پیامهای RecordRequest دریافت شده از جمعکنندهها (Collectors) |
.../record-responses |
مجموع پیامهای RecordResponse ارسال شده به جمعکنندهها (Collectors) |
.../history-istruncated |
تعداد دفعاتی که یک درخواست با برچسب زمانی (Timestamp) قدیمیتر از بافر مواجه شده است — رکوردها از دست رفتهاند |
شمارندههای رکورد منبع(پایه:/system/aaa/accounting/acctz/source-records/source-record[service=):
[سرویس] |
[نوع] |
آنچه شمارش میشود |
|---|---|---|
CMD_SERVICE |
CLI |
ثبت دستورات CLI |
CMD_SERVICE |
SHELL |
ثبتهای Shell |
CMD_SERVICE |
NETCONF |
ثبتهای NETCONF |
CMD_SERVICE |
RESTCONF |
ثبتهای RESTCONF |
CMD_SERVICE |
WEBUI |
ثبتهای WebUI |
GRPC_SERVICE |
GNMI |
ثبتهای gNMI |
GRPC_SERVICE |
GNOI |
ثبتهای gNOI |
GRPC_SERVICE |
GNSI |
ثبتهای gNSI |
GRPC_SERVICE |
GRIBI |
ثبتهای gRIBI |
GRPC_SERVICE |
P4RT |
ثبتهای P4RT |
SESSION_SERVICE |
LOGIN |
ثبت رویداد ورود (Login) |
SESSION_SERVICE |
LOGOUT |
ثبت رویداد خروج (Logout) |
هر ترکیب یک.../recordsرا نمایش میدهد. مسیر کامل را با جایگذاری[service]و[type]در مسیر پایه بالا بسازید.
اشتراک درhistory-istruncatedبرای تشخیص زمان سرریز شدن بافر چرخشی (Circular Buffer)؛ این بدان معناست که رکوردها پیش از آنکه توسط جمعآوریکننده (Collector) دریافت شوند، از دست میروند. اگر این شمارنده (Counter) همچنان در حال افزایش است، مقدارgrpc aaa accounting queue-size / grpc aaa accounting history-memoryرا افزایش دهید یا ظرفیت جمعآوریکننده (Collector) را ارتقا دهید.
شمارندههای رکورد منبع (Source Record Counters) که بر اساس نوع سرویس تفکیک شدهاند، تصویری لحظهای از فعالترین رابطهای صفحه مدیریت (Management Plane) ارائه میدهند که برای برنامهریزی ظرفیت (Capacity Planning) و نظارت امنیتی (Security Monitoring) بسیار مفید است.
بررسی سریع تلهمتری (Telemetry) با استفاده از gnmic
تأیید شمارندههای Acctz با استفاده از یک Get در gNMI (gnmic):
gnmic -a "${XR}" --insecure -u "${XR_USER}" -p "${XR_PASS}" \
get --path "/system/grpc-servers/grpc-server/acctz/state/counters/record-responses" \
--path "/system/grpc-servers/grpc-server/acctz/state/counters/history-istruncated"
نکاتی که باید به آنها توجه کرد
- سرریز بافر (Buffer overflow):بافر چرخشی (Circular Buffer) محدود است. زمانی کهشمارنده حذف تاریخچه کلی (Total History Truncation Counter) دردستور show gnsi acctz statisticsغیر صفر باشد، یعنی جمعآوریکننده (Collector) عقب مانده است. مقدارqueue-sizeوhistory-memoryرا افزایش دهید، یا پهنای باند (Bandwidth) جمعآوریکننده را اضافه کنید.(در نسخه 25.3.1 NCS-540،history_istruncatedدر پاسخ، سیگنال قابل اعتمادی نیست — به بخش تاریخچه و بافر چرخشی مراجعه کنید.)
- محرمانگی لاگ حسابرسی (Audit-log secrecy):
رد شده (denied)gNMI Setرکوردها شامل کل محموله (Payload) پیشنهادی به صورت base64 protobuf درprotoValهستند. مسیر حسابرسی (Audit pipe) را به صورت سرتاسری رمزنگاری کنید و فرض کنید هرممکن است حاوی اسراری باشد که تماسگیرنده قبل از مسدود شدن سعی در نوشتن آنها داشته است.هستند. مسیر حسابرسی (Audit pipe) را به صورت سرتاسری رمزنگاری کنید و فرض کنید - هردو رکورد برای هر RPC یکطرفه (25.3.1): هر gRPC RPC یکطرفه، دو رکورد Acctz تقریباً یکسان تولید میکند. ظرفیت جمعآوریکننده و هشدارهای هر RPC را بر اساس ضریب 2 برابر برای RPCهای یکطرفه برنامهریزی کنید (RPCهای جریانی همچنان یک رکورد واحد باpayloadIstruncated: trueتولید میکنند).
- اندازه رکورد: محمولههای بزرگ gRPC (مانند gNMI Set با تنظیمات حجیم یا ورودیهای gRIBI AFT) رکوردهای بزرگی تولید میکنند. فضای ذخیرهسازی جمعآوریکننده را بر این اساس برنامهریزی کنید. لاگگیری gRIBI بسیار پرحجم است — توضیحات proto توصیه میکنند که آن را به صورت پیشفرض در سیاستهای احراز هویت (Authorization Policy) خاموش نگه دارید.
- فیلدهای حذف (Truncation fields):هر دو فیلد cmd_istruncatedhistory-memory و payload_istruncated نشان میدهند که سرویس مبدأ، میانافزار (Middleware) یا گیرنده به محدودیت اجرایی رسیده است. اتوماسیون باید این پرچمها را بررسی کرده و یک هشدار ثبت کند.
- جمعآوریکنندههای متوقفشده (Stuck Collectors): اگر بافر خروجی سرور به سمت یک جمعآوریکننده برای مدت طولانی پر بماند، سرور آن جمعآوریکننده را قطع میکند (مشابه رفتار BGP send-hold-timer). رکوردهای جدید تا زمانی که جمعآوریکننده دوباره متصل شده و شروع به مصرف مجدد کند، حذف میشوند.
- بسط دستور (Command expansion): برای رکوردهای CLI و Shell، دستورات مخفف به شکل کامل خود بسط داده میشوند (
sh ver det | gr foo→show version detail | grep foo). آرگومانهای شناختهشده در واژهنامه بسط مییابند؛ توکنهای ناشناخته به همان شکل باقی میمانند - امنیت: جمعآوریکنندگان باید مانند هر ترافیک سطح مدیریت، احراز هویت و از نظر شبکه ایزوله شوند. دادههای حساس (کلیدهای خصوصی که از طریق Certz / Credentialz جابجا میشوند) در رکورد حذف میشوند.
- کانالهای موازی: syslog و TACACS+ میتوانند همچنان به عنوان کانالهای حسابرسی (Audit) موازی باقی بمانند. Acctz جایگزین آنها نمیشود، بلکه در کنار آنها، قابلیت حسابداری (Accounting) ساختاریافته و مبتنی بر gRPC را اضافه میکند.
- AcctzStream در برابر Acctz: پیادهسازی عملیاتی، AcctzStream (پخش جریانی از سمت سرور) است. Acctz (که از نوع دوطرفه بوده و منسوخ شده است) ممکن است در Reflection دیده شود، اما در صورت فراخوانی، خطای Unimplemented (پیادهسازینشده) را برمیگرداند. همیشه از AcctzStream.RecordSubscribe استفاده کنید.
نتیجهگیری
Acctz شکاف دیداری بین حسابداری (Accounting) سنتی AAA و عملیاتهای مبتنی بر gRPC را پر میکند. هر دستور CLI، ویرایش NETCONF و gRPC RPC یک رکورد ساختاریافته شامل جزئیات نشست (Session)، احراز هویت (Authentication) و مجوزدهی (Authorization) دریافت میکند که به صورت بلادرنگ برای جمعآوریکنندههای (Collectors) راه دور قابل استریم است. Certz و Credentialz که مدیریت احراز هویت (Authentication) را بر عهده دارند،Authz و و Pathz که مدیریت مجوزدهی (Authorization) را انجام میدهند، به همراه Acctz که حسابداری (Accounting) را تکمیل میکند، هر سه رکن مدل AAA را در IOS XR پوشش میدهند.
- Acctzجریانها (streams)ارسال رکوردهای حسابداری (Accounting Records) ساختاریافته به جمعآوریکنندهها (Collectors) — CLI، Shell، NETCONF، RESTCONF، WebUI، gNMI، gNOI، gNSI، gRIBI، P4RT، ورود/خروج.
- مهر زمانی (Timestamp)
0بازپخش کل بافر چرخشی (Circular Buffer). زمان فعلی به معنای «فقط رکوردهای جدید» است. - پاسخ رکورد (RecordResponse)حامل متادیتای نشست (Session)، جزئیات احراز هویت (Authentication)، نتیجه مجوزدهی (Authorization) و محمولههای هر سرویس (با حذف موارد حساس).
- هر رکورد شامل موارد زیر استجزئیات مجوزدهی (AuthzDetail)(مجاز/غیرمجاز) — خط لولههای انطباق (Compliance pipelines) میتوانند تصمیمات مجوزدهی را در کنار خود فعالیت ردیابی کنند.
- پیکربندی ظرفیت بافر با
grpc aaa accounting queue-size(پیشفرض 40، حداکثر 512) وgrpc aaa accounting history-memory(پیشفرض 40 مگابایت، حداکثر 400 مگابایت). - ترجیح دهید ازAcctzStream(نسخه 24.4.1 به بعد) به جای روش دوطرفه منسوخ شده استفاده کنید؛ تأیید روی دستگاه با
Acctzبازتاب (Reflection)تلهمتری YANG از طریق. - openconfig-gnsi-acctz
مصرف جمعآوریکننده، سرریز بافر و تولید رکورد به ازای هر سرویس را ردیابی میکند.ردیابی مصرف جمعکننده (Collector)، سرریز بافر (Buffer Overflow) و تولید رکورد به ازای هر سرویس (Per-Service Record).
گام بعدی:هر پنج سرویس gNSI شامل احراز هویت (Authentication) (Certz, Credentialz)، تعیین سطح دسترسی (Authorization) (Authz, Pathz) و حسابرسی (Accounting) (Acctz) اکنون تحت پوشش قرار گرفتهاند. برای مشاهده تصویر کامل به مرور کلی سری بازگردید.
مراجع
- XRdocs — AcctZ
- OpenConfig —
acctz.proto,acctz/README.md - OpenConfig —
openconfig-gnsi-acctzمدل YANG نسخه 0.4.0 - Cisco IOS XR — راهنمای پیکربندی برنامهپذیری (Programmability): برنامهها و پیکربندی gRPC (بخشهای gNSI Acctz Logging + AcctzStream)
- Cisco IOS XR — مرجع دستورات برنامهپذیری (Programmability): دستورات gRPC (
grpc aaa accounting history-memory,grpc aaa accounting queue-size,show gnsi acctz statistics) - سری — نمای کلی gNSI در IOS XR
پیوست: درخت فیلد RecordResponse
ساختار کامل پروتکل برای مرجع. بخش ساختار RecordResponse در بالا، چهار دستهبندی کلیدی را برجسته میکند.
RecordSubscribe(RecordRequest) → stream RecordResponse
│
├── RecordRequest
│ └── timestamp: google.protobuf.Timestamp
│ (0 = "send all available Records")
│
└── stream RecordResponse
├── session_info (SessionInfo)
│ ├── local_address / local_port
│ ├── remote_address / remote_port
│ ├── ip_proto (6=TCP, 17=UDP)
│ ├── channel_id
│ ├── tty (console0, vty0, pty0)
│ ├── status (LOGIN, LOGOUT, ONCE, ENABLE, IDLE, OPERATION)
│ ├── user (UserDetail)
│ │ ├── identity (username, SPIFFE ID)
│ │ ├── role (privilege level, user group)
│ │ └── ssh_principal
│ └── authn (AuthnDetail)
│ ├── type (PASSWORD, SSHKEY, SSHCERT, TLSCERT, PAP, CHAP)
│ ├── status (SUCCESS, FAIL, ERROR)
│ └── cause
├── timestamp
├── history_istruncated: bool
├── service_request (oneof)
│ ├── cmd_service (CommandService)
│ │ ├── service_type (SHELL, CLI, WEBUI, RESTCONF, NETCONF)
│ │ ├── cmd, cmd_istruncated
│ │ ├── cmd_args[], cmd_args_istruncated
│ │ └── authz (AuthzDetail: PERMIT/DENY + detail)
│ └── grpc_service (GrpcService)
│ ├── service_type (GNMI, GNOI, GNSI, GRIBI, P4RT)
│ ├── rpc_name ("/gnmi.gNMI/Set", etc.)
│ ├── payload (proto_val or string_val)
│ ├── payload_istruncated
│ └── authz (AuthzDetail: PERMIT/DENY + detail)
├── component_name
└── task_ids[]