GNS3 Üzerinde OpenLDAP, FreeRADIUS, TACACS+, Keycloak ve Elastic ile Merkezi AAA Lab Kurulumu
Bu doküman, GNS3 üzerinde Cisco switch'lerin merkezi kullanıcı veritabanı üzerinden kimlik doğrulaması yapmasını, grup bazlı yetki almasını, komut bazlı TACACS+ authorization uygulanmasını ve TACACS accounting loglarının Elastic/Kibana üzerinde görüntülenmesini özetler.
Kurulumda Active Directory yerine açık kaynak bir LDAP sunucusu olan OpenLDAP kullanıldı. Keycloak, OpenLDAP ile user federation yapacak şekilde eklendi. SW1 üzerinde RADIUS + OpenLDAP login doğrulandı. SW2 üzerinde final durumda TACACS+ + OpenLDAP kullanıldı; admin, operator ve readonly roller için komut bazlı yetkilendirme yapıldı.
Not: IP adresleri, parolalar ve bazı isimler dokümantasyon amacıyla maskelenmiştir. Kendi ortamınıza göre değiştirmeniz gerekir.
Terimler
Active Directory (AD): Microsoft'un merkezi kullanıcı ve grup dizinidir. Şirketteki kullanıcı hesapları, gruplar ve parolalar burada tutulur.
LDAP: AD veya OpenLDAP gibi dizin servisleriyle konuşmak için kullanılan protokol/dildir. Uygulamalar LDAP ile kullanıcı var mı, şifresi doğru mu, hangi grupta gibi sorular sorabilir.
OpenLDAP: LDAP protokolünü kullanan açık kaynak dizin servisidir. Bu labda AD yerine kullanıldı.
phpLDAPadmin: OpenLDAP yönetimi için web arayüzüdür. Kullanıcı, OU ve grup oluşturmak için kullanıldı.
Keycloak: Kimlik yönetimi ve SSO platformudur. Bu labda OpenLDAP ile user federation yapacak şekilde eklendi. Keycloak, LDAP kullanıcılarını/gruplarını görebilir ve writable modda LDAP'a kullanıcı/grup değişikliği yazabilir.
RADIUS: Ağ erişimi için yaygın kullanılan AAA protokolüdür. Wi-Fi, VPN, 802.1X ve temel cihaz login senaryolarında kullanılır.
TACACS+: Özellikle network cihazlarının yönetimi için kullanılan AAA protokolüdür. Switch/router/firewall CLI login, privilege seviyesi, komut bazlı authorization ve accounting için uygundur.
MAVIS: tac_plus-ng'nin LDAP gibi dış kaynaklarla konuşmak için kullandığı backend/adapter mekanizmasıdır. Bu labda tac_plus-ng, MAVIS LDAP script'i ile OpenLDAP'a bağlanır.
Logstash / Elasticsearch / Kibana: TACACS accounting loglarını parse etmek, indekslemek ve arayüzden filtrelemek için kullanıldı.
1. Amaç
Amaç, Cisco switch'lere lokal kullanıcılarla değil, merkezi dizindeki hesaplarla giriş yapılmasını sağlamaktır.
Hedef akış:
SW1 kullanıcı login -> FreeRADIUS -> OpenLDAP -> Grup kontrolü -> Yetki seviyesi
SW2 kullanıcı login -> TACACS+ -> OpenLDAP -> Grup kontrolü -> Yetki seviyesi + komut yetkilendirme + accounting log
Final yetkilendirme modeli:
network-admins -> admin profile -> privilege 15, tüm shell komutları izinli
network-operators -> operator profile -> privilege 15, sınırlı operasyon komutları izinli
network-readonly -> readonly profile -> privilege 1, sadece show/exit/logout izinli
Bu yapı sayesinde 81 il veya çok sayıda kullanıcı için TACACS config dosyasına tek tek kullanıcı yazılmaz. Kullanıcı hangi LDAP grubundaysa o profile uygulanır.
2. Kullanılan bileşenler
| Bileşen | Rolü |
|---|---|
| Ubuntu | Docker servislerinin ve local GNS3 server'ın çalıştığı makine |
| GNS3 | Ağ topolojisinin oluşturulduğu lab ortamı |
| OpenLDAP | Kullanıcı ve grup veritabanı |
| phpLDAPadmin | OpenLDAP için web yönetim arayüzü |
| Keycloak | OpenLDAP user federation ve IAM/SSO katmanı |
| FreeRADIUS | SW1 için RADIUS AAA sunucusu |
| tac_plus-ng | SW2 için TACACS+ sunucusu |
| rsyslog | Cisco switch syslog mesajlarını Ubuntu üzerinde UDP 514 ile alır ve dosyaya yazar |
| Logstash | TACACS accounting ve Cisco syslog loglarını parse eder |
| Elasticsearch | Parse edilen logları indeksler |
| Kibana | Logları arama, filtreleme ve dashboard için kullanılır |
| Cisco vIOS-L2 | Lab switch imajı |
| GNS3 Cloud + TAP | Switch'leri Ubuntu üzerindeki AAA servislerine bağlayan sanal ağ |
3. Lab IP planı
Gerçek IP adresleri dokümantasyon amacıyla maskelenmiştir.
Ubuntu AAA Server : <AAA_SERVER_IP>
GNS3 TAP Interface : <TAP_GATEWAY_IP>/24
SW1 Management IP : <SW1_MGMT_IP>/24
SW2 Management IP : <SW2_MGMT_IP>/24
OpenLDAP Docker IP : <LDAP_CONTAINER_IP>
FreeRADIUS Docker IP : <RADIUS_CONTAINER_IP>
TACACS+ Docker IP : <TACACS_CONTAINER_IP>
Keycloak Docker IP : <KEYCLOAK_CONTAINER_IP>
Elasticsearch Docker IP : <ELASTICSEARCH_CONTAINER_IP>
Kibana Docker IP : <KIBANA_CONTAINER_IP>
Logstash Docker IP : <LOGSTASH_CONTAINER_IP>
Lab içinde kullanılan örnek TAP ağı:
TAP Gateway : <TAP_GATEWAY_IP>
SW1 : <SW1_MGMT_IP>
SW2 : <SW2_MGMT_IP>
4. Klasör yapısı
Ana dizin:
/opt/aaa-stack
Klasör yapısı:
/opt/aaa-stack
├── docker-compose.yml
├── freeradius
│ ├── Dockerfile
│ ├── clients.conf
│ ├── mods-enabled
│ │ └── ldap
│ └── sites-enabled
│ └── default
├── tacacs
│ ├── tac_plus-ng.cfg
│ └── logs
│ └── accounting.log
├── logstash
│ ├── pipeline
│ │ ├── tacacs-accounting.conf
│ │ └── cisco-syslog.conf
│ └── data
├── elasticsearch
│ └── data
└── keycloak
└── data
5. Docker Compose servisleri
Aşağıdaki compose örneği labda kullanılan servislerin genel yapısını gösterir. Parolalar, IP adresleri ve subnet değerleri maskelenmiştir.
services:
openldap:
image: osixia/openldap:1.5.0
container_name: openldap
hostname: openldap
environment:
LDAP_ORGANISATION: "Lab"
LDAP_DOMAIN: "lab.local"
LDAP_ADMIN_PASSWORD: "<LDAP_ADMIN_PASSWORD>"
LDAP_CONFIG_PASSWORD: "<LDAP_CONFIG_PASSWORD>"
LDAP_TLS: "false"
volumes:
- openldap_data:/var/lib/ldap
- openldap_config:/etc/ldap/slapd.d
ports:
- "389:389"
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <LDAP_CONTAINER_IP>
phpldapadmin:
image: osixia/phpldapadmin:0.9.0
container_name: phpldapadmin
environment:
PHPLDAPADMIN_LDAP_HOSTS: openldap
PHPLDAPADMIN_HTTPS: "false"
ports:
- "8080:80"
depends_on:
- openldap
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <PHPLDAPADMIN_CONTAINER_IP>
freeradius:
build:
context: ./freeradius
container_name: freeradius
depends_on:
- openldap
ports:
- "1812:1812/udp"
- "1813:1813/udp"
volumes:
- ./freeradius/clients.conf:/etc/freeradius/3.0/clients.conf:ro
- ./freeradius/mods-enabled/ldap:/etc/freeradius/3.0/mods-enabled/ldap:ro
- ./freeradius/sites-enabled/default:/etc/freeradius/3.0/sites-enabled/default:ro
command: freeradius -X
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <RADIUS_CONTAINER_IP>
tacacs:
image: christianbecker/tac_plus-ng:latest
container_name: tacacs
depends_on:
- openldap
ports:
- "49:49/tcp"
volumes:
- ./tacacs/tac_plus-ng.cfg:/etc/tac_plus-ng.cfg:ro
- ./tacacs/logs:/var/log/tac_plus-ng
command: tac_plus-ng /etc/tac_plus-ng.cfg
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <TACACS_CONTAINER_IP>
keycloak:
image: quay.io/keycloak/keycloak:26.6.1
container_name: keycloak
command:
- start-dev
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: <KEYCLOAK_ADMIN_USER>
KC_BOOTSTRAP_ADMIN_PASSWORD: <KEYCLOAK_ADMIN_PASSWORD>
ports:
- "8081:8080"
volumes:
- ./keycloak/data:/opt/keycloak/data
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <KEYCLOAK_CONTAINER_IP>
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.15.3
container_name: elasticsearch
environment:
discovery.type: single-node
xpack.security.enabled: "false"
ES_JAVA_OPTS: "-Xms1g -Xmx1g"
ports:
- "9200:9200"
volumes:
- ./elasticsearch/data:/usr/share/elasticsearch/data
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <ELASTICSEARCH_CONTAINER_IP>
kibana:
image: docker.elastic.co/kibana/kibana:8.15.3
container_name: kibana
depends_on:
- elasticsearch
environment:
ELASTICSEARCH_HOSTS: "http://elasticsearch:9200"
ports:
- "5601:5601"
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <KIBANA_CONTAINER_IP>
logstash:
image: docker.elastic.co/logstash/logstash:8.15.3
container_name: logstash-tacacs
depends_on:
- elasticsearch
- tacacs
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline:ro
- ./logstash/data:/usr/share/logstash/data
- ./tacacs/logs:/var/log/tacacs:ro
- /var/log/cisco-gns3.log:/var/log/cisco/cisco-gns3.log:ro
restart: unless-stopped
networks:
aaa_net:
ipv4_address: <LOGSTASH_CONTAINER_IP>
networks:
aaa_net:
driver: bridge
ipam:
config:
- subnet: <DOCKER_AAA_SUBNET>
volumes:
openldap_data:
openldap_config:
Not: Keycloak bu labda
start-devile çalıştırıldı. Production ortamda dev mode yerine kalıcı veritabanı, TLS, reverse proxy/hostname ve HA planı yapılmalıdır.
6. OpenLDAP kullanıcı ve grup verisi
Bu labda ilk kullanıcı ve grup kayıtları phpLDAPadmin web arayüzü üzerinden oluşturuldu.
phpLDAPadmin arayüzüne tarayıcıdan erişildi:
http://<AAA_SERVER_IP>:8080
Login bilgileri:
Login DN : cn=admin,dc=lab,dc=local
Password : <LDAP_ADMIN_PASSWORD>
OpenLDAP içinde temel dizin yapısı:
dc=lab,dc=local
├── ou=people
└── ou=groups
Başlangıç kullanıcıları:
uid=netadmin,ou=people,dc=lab,dc=local
uid=readonly,ou=people,dc=lab,dc=local
Sonradan kullanıcılar eklendi:
uid=100001,ou=people,dc=lab,dc=local
uid=100002,ou=people,dc=lab,dc=local
Bu kullanıcı adları switch login username olarak kullanılır:
Username: 100001
Username: 100002
Gruplar:
cn=network-admins,ou=groups,dc=lab,dc=local
cn=network-operators,ou=groups,dc=lab,dc=local
cn=network-readonly,ou=groups,dc=lab,dc=local
Grup üyelikleri member attribute'u ile tanımlandı:
network-admins:
member: uid=netadmin,ou=people,dc=lab,dc=local
network-operators:
member: uid=100001,ou=people,dc=lab,dc=local
network-readonly:
member: uid=readonly,ou=people,dc=lab,dc=local
member: uid=100002,ou=people,dc=lab,dc=local
Özet yetkilendirme:
netadmin -> network-admins -> admin profile
100001 -> network-operators -> operator profile
readonly -> network-readonly -> readonly profile
100002 -> network-readonly -> readonly profile
Not:
groupOfNamesobjectClass'i en az birmemberattribute'u ister. Bu yüzden gruptaki son kullanıcı silinmeye çalışılırsa LDAP object class violation hatası alınabilir.
7. Keycloak ve OpenLDAP federation
Keycloak bu labda TACACS+ server'ın yerine geçmedi. Switch tarafında cihazlar TACACS+ konuştuğu için enforcement noktası tac_plus-ng olarak kaldı.
Keycloak'un bu labdaki rolü:
OpenLDAP kullanıcı ve gruplarını görmek
OpenLDAP ile user federation yapmak
Writable modda Keycloak UI üzerinden OpenLDAP'a kullanıcı/grup/membership yazabilmek
İleride SSO/OIDC/SAML/MFA gibi IAM ihtiyaçları için zemin sağlamak
Gerçek AAA flow şu şekildedir:
SW2 -> TACACS+ -> tac_plus-ng -> OpenLDAP
Keycloak doğrudan switch login isteğini karşılamaz. Cisco switch Keycloak token okumaz; switch TACACS+ konuşur.
7.1 Keycloak erişimi
Keycloak host üzerinde şu porttan yayınlandı:
http://<AAA_SERVER_IP>:8081
Admin login:
Username: <KEYCLOAK_ADMIN_USER>
Password: <KEYCLOAK_ADMIN_PASSWORD>
Realm:
network-aaa
7.2 LDAP User Federation ayarı
Keycloak içinde:
User federation -> Add provider -> ldap
Örnek ayarlar:
UI display name : openldap
Vendor : Other
Connection URL : ldap://openldap:389
Enable StartTLS : Off
Bind type : simple
Bind DN : cn=admin,dc=lab,dc=local
Bind credentials : <LDAP_ADMIN_PASSWORD>
Users DN : ou=people,dc=lab,dc=local
Username LDAP attr : uid
RDN LDAP attr : uid
UUID LDAP attr : entryUUID
Search scope : Subtree
Import users : On
Edit mode : WRITABLE
Sync registrations : On
Kullanıcı object class ayarı writable kullanım için sade tutuldu:
inetOrgPerson, organizationalPerson, person
Bu nedenle TACACS LDAP filter da inetOrgPerson ile uyumlu olacak şekilde güncellendi.
7.3 LDAP group mapper
Keycloak LDAP provider altında group mapper eklendi:
Mapper type : group-ldap-mapper
LDAP Groups DN : ou=groups,dc=lab,dc=local
Group Name LDAP Attribute : cn
Group Object Classes : groupOfNames
Membership LDAP Attribute : member
Membership Attribute Type : DN
Membership User LDAP Attribute: uid
User Groups Retrieve Strategy : LOAD_GROUPS_BY_MEMBER_ATTRIBUTE
Mode : LDAP_ONLY
Bu ayar ile Keycloak'ta kullanıcıyı bir gruba eklemek OpenLDAP'taki groupOfNames/member attribute'unu günceller.
7.4 Keycloak'tan oluşturulan kullanıcıların OpenLDAP'a yazılması
Keycloak üzerinden kullanıcı oluşturuldu:
Username: 100002
OpenLDAP üzerinde kontrol:
docker exec -it openldap ldapsearch \
-x \
-H ldap://localhost:389 \
-D "cn=admin,dc=lab,dc=local" \
-w '<LDAP_ADMIN_PASSWORD>' \
-b "ou=people,dc=lab,dc=local" \
"(uid=100002)"
Örnek beklenen çıktı:
dn: uid=100002,ou=people,dc=lab,dc=local
uid: 100002
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
cn: 100002
sn: 100002
userPassword: <PASSWORD_HASH_OR_VALUE>
Keycloak üzerinden 100002 kullanıcısı network-readonly grubuna eklendi. LDAP grup üyeliği şu komutla doğrulandı:
docker exec -it openldap ldapsearch \
-x \
-H ldap://localhost:389 \
-D "cn=admin,dc=lab,dc=local" \
-w '<LDAP_ADMIN_PASSWORD>' \
-b "ou=groups,dc=lab,dc=local" \
"(cn=network-readonly)" member
Beklenen:
member: uid=100002,ou=people,dc=lab,dc=local
Bu test ile şu doğrulandı:
Keycloak UI üzerinden kullanıcı oluşturuldu.
Kullanıcı OpenLDAP'a yazıldı.
Keycloak üzerinden grup üyeliği verildi.
Bu üyelik OpenLDAP groupOfNames/member attribute'una yazıldı.
TACACS+ aynı OpenLDAP grup üyeliğini okuyarak yetki verdi.
7.5 Keycloak token konusu
Keycloak üzerinden kullanıcı token'ı alınabilir ve token içinde roller görülebilir. Ancak Cisco IOS switch bu token'ı kullanmaz.
Önemli ayrım:
Keycloak token -> OIDC/SAML/web uygulamaları için anlamlıdır.
Cisco switch -> TACACS+ konuşur, OIDC token okumaz.
Bu nedenle bu labda Keycloak token'ı üzerinden doğrudan command authorization yapılmadı. Token/role bazlı doğrudan TACACS karar modeli istenirse ek bir MAVIS adapter, policy service veya bunu native destekleyen kurumsal AAA ürünü gerekir.
8. FreeRADIUS Dockerfile
Dosya:
/opt/aaa-stack/freeradius/Dockerfile
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y freeradius freeradius-ldap ldap-utils && \
rm -rf /var/lib/apt/lists/*
EXPOSE 1812/udp 1813/udp
CMD ["freeradius", "-X"]
9. FreeRADIUS client tanımları
Dosya:
/opt/aaa-stack/freeradius/clients.conf
client localhost {
ipaddr = 127.0.0.1
secret = <RADIUS_SHARED_SECRET>
require_message_authenticator = no
nas_type = other
}
client docker_bridge {
ipaddr = <DOCKER_AAA_SUBNET>
secret = <RADIUS_SHARED_SECRET>
require_message_authenticator = no
nas_type = other
}
client gns3_tap_lab {
ipaddr = <GNS3_TAP_SUBNET>
secret = <RADIUS_SHARED_SECRET>
require_message_authenticator = no
nas_type = cisco
}
10. FreeRADIUS LDAP modülü
Dosya:
/opt/aaa-stack/freeradius/mods-enabled/ldap
ldap {
server = "openldap"
port = 389
identity = "cn=admin,dc=lab,dc=local"
password = "<LDAP_ADMIN_PASSWORD>"
base_dn = "dc=lab,dc=local"
user {
base_dn = "ou=people,dc=lab,dc=local"
filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
}
group {
base_dn = "ou=groups,dc=lab,dc=local"
filter = "(objectClass=groupOfNames)"
membership_filter = "(&(objectClass=groupOfNames)(member=%{control:Ldap-UserDn}))"
name_attribute = cn
}
options {
chase_referrals = yes
rebind = yes
res_timeout = 10
srv_timelimit = 3
net_timeout = 1
idle = 60
probes = 3
interval = 3
}
tls {
start_tls = no
}
}
11. FreeRADIUS default site
Dosya:
/opt/aaa-stack/freeradius/sites-enabled/default
server default {
listen {
type = auth
ipaddr = *
port = 1812
}
listen {
type = acct
ipaddr = *
port = 1813
}
authorize {
ldap
if (ok) {
update control {
Auth-Type := LDAP
}
}
}
authenticate {
Auth-Type LDAP {
ldap
}
}
post-auth {
if (&LDAP-Group == "network-admins") {
update reply {
Service-Type := Administrative-User
Cisco-AVPair := "shell:priv-lvl=15"
Reply-Message := "LDAP RADIUS Admin Login OK"
}
}
elsif (&LDAP-Group == "network-readonly") {
update reply {
Service-Type := NAS-Prompt-User
Cisco-AVPair := "shell:priv-lvl=1"
Reply-Message := "LDAP RADIUS Readonly Login OK"
}
}
else {
reject
}
}
}
12. TACACS+ config
TACACS+ tarafında tac_plus-ng kullanıldı. Bu servis, MAVIS LDAP backend üzerinden OpenLDAP'a bağlanır.
Final model:
network-admins -> admin_profile
network-operators -> operator_profile
network-readonly -> readonly_profile
Dosya:
/opt/aaa-stack/tacacs/tac_plus-ng.cfg
id = spawnd {
listen { port = 49 }
spawn {
instances min = 1
instances max = 16
}
}
id = tac_plus-ng {
log acctlog {
destination = /var/log/tac_plus-ng/accounting.log
}
accounting log = acctlog
mavis module = external {
setenv LDAP_HOSTS = "ldap://openldap:389"
setenv LDAP_BASE = "ou=people,dc=lab,dc=local"
setenv LDAP_BASE_GROUP = "ou=groups,dc=lab,dc=local"
setenv LDAP_SCOPE = sub
setenv LDAP_SCOPE_GROUP = sub
setenv LDAP_FILTER = "(&(objectClass=inetOrgPerson)(uid=%s))"
setenv LDAP_FILTER_GROUP = "(&(objectClass=groupOfNames)(member=%s))"
setenv LDAP_USER = "cn=admin,dc=lab,dc=local"
setenv LDAP_PASSWD = "<LDAP_ADMIN_PASSWORD>"
exec = /usr/local/lib/mavis/mavis_tacplus-ng_ldap.pl
}
user backend = mavis
login backend = mavis
pap backend = mavis
device gns3_tap_switches {
address = <GNS3_TAP_SUBNET>
key = <TACACS_SHARED_SECRET>
}
profile admin_profile {
script {
if (service == shell) {
if (cmd == "") {
set priv-lvl = 15
permit
}
permit
}
}
}
profile operator_profile {
script {
if (service == shell) {
if (cmd == "") {
set priv-lvl = 15
permit
}
if (cmd =~ /^show(\s|$)/) {
permit
}
if (cmd =~ /^configure\s+terminal(\s|$)/) {
permit
}
if (cmd =~ /^interface\s+/) {
permit
}
if (cmd =~ /^ip\s+address\s+/) {
permit
}
if (cmd =~ /^no\s+shutdown(\s|$)/) {
permit
}
if (cmd =~ /^exit(\s|$)/) {
permit
}
if (cmd =~ /^end(\s|$)/) {
permit
}
if (cmd =~ /^logout(\s|$)/) {
permit
}
deny
}
}
}
profile readonly_profile {
script {
if (service == shell) {
if (cmd == "") {
set priv-lvl = 1
permit
}
if (cmd =~ /^show(\s|$)/) {
permit
}
if (cmd =~ /^exit(\s|$)/) {
permit
}
if (cmd =~ /^logout(\s|$)/) {
permit
}
deny
}
}
}
ruleset {
rule admin_rule {
enabled = yes
script {
if (memberof =~ /^cn=network-admins,ou=groups,dc=lab,dc=local$/) {
profile = admin_profile
permit
}
}
}
rule operator_rule {
enabled = yes
script {
if (memberof =~ /^cn=network-operators,ou=groups,dc=lab,dc=local$/) {
profile = operator_profile
permit
}
}
}
rule readonly_rule {
enabled = yes
script {
if (memberof =~ /^cn=network-readonly,ou=groups,dc=lab,dc=local$/) {
profile = readonly_profile
permit
}
}
}
rule deny_all {
enabled = yes
script {
deny
}
}
}
}
Syntax test:
docker run --rm -it \
--network aaa-stack_aaa_net \
-v /opt/aaa-stack/tacacs/tac_plus-ng.cfg:/etc/tac_plus-ng.cfg:ro \
-v /opt/aaa-stack/tacacs/logs:/var/log/tac_plus-ng \
christianbecker/tac_plus-ng:latest \
tac_plus-ng /etc/tac_plus-ng.cfg
Restart:
cd /opt/aaa-stack
docker compose restart tacacs
13. Servisleri başlatma
cd /opt/aaa-stack
docker compose up -d --build
Kontrol:
docker ps
Beklenen container'lar:
openldap
phpldapadmin
freeradius
tacacs
keycloak
elasticsearch
kibana
logstash-tacacs
Port kontrolü:
sudo ss -lntup | egrep ':389|:8080|:8081|:1812|:1813|:49|:9200|:5601'
Beklenen portlar:
389/tcp OpenLDAP
8080/tcp phpLDAPadmin
8081/tcp Keycloak
1812/udp RADIUS authentication
1813/udp RADIUS accounting
49/tcp TACACS+
9200/tcp Elasticsearch
5601/tcp Kibana
14. GNS3 tarafında yapılanlar
Local Ubuntu üzerindeki GNS3 server kullanıldı.
TAP interface oluşturuldu:
sudo ip tuntap add dev tap-gns3 mode tap user $USER
sudo ip addr add <TAP_GATEWAY_IP>/24 dev tap-gns3
sudo ip link set tap-gns3 up
GNS3 Cloud node içinde tap-gns3 seçildi.
Neden ESW1 kullanıldı?
Cloud üzerindeki TAP port tek bağlantı kabul ettiği için, birden fazla switch'i aynı TAP ağına bağlamak amacıyla araya GNS3 built-in Ethernet switch eklendi.
14.1 TAP interface'i kalıcı hale getirme
TAP interface reboot sonrasında kaybolmasın diye systemd service oluşturuldu.
Script:
sudo tee /usr/local/sbin/gns3-tap-up.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -e
TAP_NAME="tap-gns3"
TAP_USER="<LINUX_USER>"
TAP_IP="<TAP_GATEWAY_IP>/24"
if ! ip link show "$TAP_NAME" >/dev/null 2>&1; then
ip tuntap add dev "$TAP_NAME" mode tap user "$TAP_USER"
fi
ip addr flush dev "$TAP_NAME" || true
ip addr add "$TAP_IP" dev "$TAP_NAME"
ip link set "$TAP_NAME" up
EOF
sudo chmod +x /usr/local/sbin/gns3-tap-up.sh
Stop script:
sudo tee /usr/local/sbin/gns3-tap-down.sh > /dev/null <<'EOF'
#!/usr/bin/env bash
set -e
TAP_NAME="tap-gns3"
if ip link show "$TAP_NAME" >/dev/null 2>&1; then
ip link set "$TAP_NAME" down || true
ip tuntap del dev "$TAP_NAME" mode tap || true
fi
EOF
sudo chmod +x /usr/local/sbin/gns3-tap-down.sh
Systemd service:
sudo tee /etc/systemd/system/gns3-tap.service > /dev/null <<'EOF'
[Unit]
Description=Create persistent TAP interface for GNS3 lab
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/gns3-tap-up.sh
ExecStop=/usr/local/sbin/gns3-tap-down.sh
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable gns3-tap.service
sudo systemctl start gns3-tap.service
Kontrol:
systemctl status gns3-tap.service --no-pager
ip -br addr show tap-gns3
Beklenen:
tap-gns3 UP <TAP_GATEWAY_IP>/24
15. SW1 temel IP config
enable
conf t
hostname SW1
interface vlan 1
ip address <SW1_MGMT_IP> 255.255.255.0
no shutdown
exit
ip default-gateway <TAP_GATEWAY_IP>
end
write memory
Test:
show ip interface brief
ping <TAP_GATEWAY_IP>
16. SW2 temel IP config
enable
conf t
hostname SW2
interface vlan 1
ip address <SW2_MGMT_IP> 255.255.255.0
no shutdown
exit
ip default-gateway <TAP_GATEWAY_IP>
end
write memory
Test:
show ip interface brief
ping <TAP_GATEWAY_IP>
17. SW1 için Cisco RADIUS AAA config
SW1 final durumda RADIUS örneği olarak bırakıldı.
conf t
aaa new-model
radius server AAA-RADIUS
address ipv4 <TAP_GATEWAY_IP> auth-port 1812 acct-port 1813
key <RADIUS_SHARED_SECRET>
aaa group server radius RADIUS-GRP
server name AAA-RADIUS
aaa authentication login default group RADIUS-GRP local
aaa authorization exec default group RADIUS-GRP local
username localadmin privilege 15 secret <LOCAL_FALLBACK_PASSWORD>
line vty 0 15
login authentication default
authorization exec default
transport input telnet ssh
end
write memory
Kontrol:
show aaa servers
show running-config | section radius
show running-config | section aaa
SW1 login testi:
telnet <SW1_MGMT_IP>
Admin kullanıcı:
Username: netadmin
Password: <NETADMIN_PASSWORD>
Beklenen:
SW1#
Current privilege level is 15
Readonly kullanıcı:
Username: readonly
Password: <READONLY_PASSWORD>
Beklenen:
SW1>
Current privilege level is 1
18. SW2 için Cisco TACACS+ AAA config
SW2 final durumda TACACS+ kullanacak şekilde bırakıldı.
Lab imajı eski IOS syntax'ını kabul ettiği için TACACS server şu formatla tanımlandı:
conf t
aaa new-model
tacacs-server host <TAP_GATEWAY_IP> key <TACACS_SHARED_SECRET>
aaa group server tacacs+ TACACS-GRP
server <TAP_GATEWAY_IP>
username localadmin privilege 15 secret <LOCAL_FALLBACK_PASSWORD>
aaa authentication login default group TACACS-GRP local
aaa authorization exec default group TACACS-GRP local
aaa authorization commands 1 default group TACACS-GRP local
aaa authorization commands 15 default group TACACS-GRP local
aaa authorization config-commands
aaa accounting exec default start-stop group TACACS-GRP
aaa accounting commands 1 default start-stop group TACACS-GRP
aaa accounting commands 15 default start-stop group TACACS-GRP
line vty 0 15
login authentication default
authorization exec default
transport input ssh
end
write memory
Komutların anlamı:
aaa authentication login default group TACACS-GRP local
Login authentication için önce TACACS+, sonra local fallback.
aaa authorization exec default group TACACS-GRP local
Kullanıcı login sonrası shell/exec açabilir mi ve privilege seviyesi ne olacak?
aaa authorization commands 1/15 default group TACACS-GRP local
Privilege 1 ve 15 komutları için command authorization yap.
aaa authorization config-commands
Config mode içindeki komutları da authorization kapsamına al.
aaa accounting exec default start-stop group TACACS-GRP
Oturum başlangıç/bitiş accounting kaydı üret.
aaa accounting commands 1/15 default start-stop group TACACS-GRP
Privilege 1 ve 15 komutlarını accounting'e gönder.
Not:
localfallback labda kilitlenmemek için kullanıldı. Production ortamda fallback davranışı güvenlik politikasına göre ayrıca değerlendirilmelidir.
TACACS+ test komutları:
test aaa group TACACS-GRP netadmin <NETADMIN_PASSWORD> legacy
test aaa group TACACS-GRP netadmin <WRONG_PASSWORD> legacy
Beklenen:
Doğru şifre -> User was successfully authenticated
Yanlış şifre -> User authentication request was rejected by server
19. SW2 SSH config
Telnet yerine SSH kullanılması için SW2 üzerinde RSA key ve SSH v2 açıldı.
conf t
ip domain-name lab.local
crypto key generate rsa modulus 2048
ip ssh version 2
ip ssh time-out 60
ip ssh authentication-retries 3
line vty 0 15
login authentication default
authorization exec default
transport input ssh
end
write memory
Kontrol:
show ip ssh
show running-config | section line vty
Beklenen:
SSH Enabled - version 2.0
transport input ssh
Labdaki vIOS-L2 imajı eski SSH algoritmaları sunduğu için Ubuntu'dan bağlantıda legacy OpenSSH seçenekleri kullanıldı:
ssh \
-oKexAlgorithms=+diffie-hellman-group14-sha1 \
-oHostKeyAlgorithms=+ssh-rsa \
-oPubkeyAcceptedAlgorithms=+ssh-rsa \
<USERNAME>@<SW2_MGMT_IP>
Örnek:
ssh \
-oKexAlgorithms=+diffie-hellman-group14-sha1 \
-oHostKeyAlgorithms=+ssh-rsa \
-oPubkeyAcceptedAlgorithms=+ssh-rsa \
100001@<SW2_MGMT_IP>
Production ortamda güncel IOS-XE veya güncel SSH algoritmaları destekleyen cihazlarda normal SSH komutu yeterli olabilir.
20. SW2 TACACS+ komut bazlı yetkilendirme testleri
Admin test
Kullanıcı:
netadmin -> network-admins
Beklenen:
SW2#show privilege
Current privilege level is 15
SW2#write memory
# çalışır
Operator test
Kullanıcı:
100001 -> network-operators
Beklenen:
SW2#show privilege
Current privilege level is 15
SW2#conf t
SW2(config)#interface vlan 1
SW2(config-if)#no shutdown
# çalışır
SW2#write memory
Command authorization failed.
SW2#reload
Command authorization failed.
Operator kullanıcının privilege seviyesi 15 olsa bile tüm komutları çalıştıramaz. Çünkü command authorization açık olduğu için switch her yetkilendirilen komutta TACACS+ server'a sorar.
Readonly test
Kullanıcı:
100002 -> network-readonly
Beklenen:
SW2>show privilege
Current privilege level is 1
SW2>show ip interface brief
# çalışır
SW2>conf t
Command authorization failed.
21. TACACS accounting loglama
SW2 üzerinde accounting açıldığı için login/logout ve komut kayıtları TACACS+ server tarafında tutulur. Bu loglar switch diskine yazılmaz.
TACACS config içinde accounting log dosyası tanımı:
log acctlog {
destination = /var/log/tac_plus-ng/accounting.log
}
accounting log = acctlog
Docker volume sayesinde container içindeki log dosyası host üzerinde şu path'e yazılır:
/opt/aaa-stack/tacacs/logs/accounting.log
Canlı izleme:
tail -f /opt/aaa-stack/tacacs/logs/accounting.log
Örnek log satırları:
<TIMESTAMP> <SWITCH_IP> 100001 tty2 <SOURCE_IP> start shell
<TIMESTAMP> <SWITCH_IP> 100001 tty2 <SOURCE_IP> stop shell show privilege <cr>
<TIMESTAMP> <SWITCH_IP> 100001 tty2 <SOURCE_IP> stop shell write memory <cr>
Alanların anlamı:
timestamp -> olay zamanı
switch IP -> observer.ip, işlem yapılan switch
username -> user.name
tty -> terminal/session hattı
source IP -> switch'e bağlanan kaynak IP
start/stop -> accounting action
shell -> servis tipi
command -> çalıştırılan komut
source.ip labda genelde TAP gateway IP olarak görünür. Çünkü SSH bağlantıları Ubuntu host/TAP üzerinden yapılmaktadır. Gerçek ortamda bu alan admin bilgisayarı veya jump server IP'si olabilir.
22. Logstash pipeline
Logstash, TACACS accounting log dosyasını ve Cisco syslog dosyasını okuyup alanlara ayırır ve Elasticsearch'e gönderir. TACACS accounting ayrı indexe, Cisco syslog ayrı indexe yazılır.
Bu bölümde önce TACACS accounting pipeline gösterilir. Cisco syslog pipeline ise 24. bölümde anlatılmıştır.
Dosya:
/opt/aaa-stack/logstash/pipeline/tacacs-accounting.conf
input {
file {
path => "/var/log/tacacs/accounting.log"
start_position => "beginning"
sincedb_path => "/usr/share/logstash/data/tacacs-accounting.sincedb"
mode => "tail"
codec => "plain"
}
}
filter {
if [message] =~ /^\s*$/ {
drop { }
}
mutate {
replace => {
"[event][original]" => "%{message}"
}
add_field => {
"[event][dataset]" => "tacacs.accounting"
"[event][module]" => "tacacs"
}
}
grok {
match => {
"message" => "^%{YEAR:[tacacs][year]}-%{MONTHNUM:[tacacs][month]}-%{MONTHDAY:[tacacs][day]} %{TIME:[tacacs][time]} %{ISO8601_TIMEZONE:[tacacs][timezone]} %{IP:[observer][ip]}\t%{DATA:[user][name]}\t%{DATA:[tacacs][tty]}\t%{IP:[source][ip]}\t%{WORD:[event][action]}\t%{WORD:[service][name]}(?:\t%{GREEDYDATA:[tacacs][command]})?$"
}
tag_on_failure => [ "tacacs_parse_failed" ]
}
mutate {
add_field => {
"[tacacs][timestamp_raw]" => "%{[tacacs][year]}-%{[tacacs][month]}-%{[tacacs][day]} %{[tacacs][time]} %{[tacacs][timezone]}"
}
strip => [
"[tacacs][command]",
"[user][name]",
"[observer][ip]",
"[source][ip]",
"[event][action]",
"[service][name]"
]
}
date {
match => [ "[tacacs][timestamp_raw]", "yyyy-MM-dd HH:mm:ss Z" ]
target => "@timestamp"
tag_on_failure => [ "tacacs_dateparse_failed" ]
}
if ![tacacs][command] or [tacacs][command] == "" {
mutate {
add_field => {
"[tacacs][command_type]" => "session"
}
}
} else {
mutate {
add_field => {
"[tacacs][command_type]" => "command"
}
}
ruby {
code => '
cmd = event.get("[tacacs][command]")
if cmd && cmd.strip != ""
event.set("[tacacs][command_prefix]", cmd.strip.split(/\s+/)[0])
end
'
}
}
if [tacacs][command_prefix] == "no" {
mutate {
add_field => {
"[event][risk_score]" => "50"
"[tacacs][command_category]" => "negative_config"
}
}
} else if [tacacs][command_prefix] == "configure" {
mutate {
add_field => {
"[event][risk_score]" => "40"
"[tacacs][command_category]" => "configuration_mode"
}
}
} else if [tacacs][command_prefix] == "write" {
mutate {
add_field => {
"[event][risk_score]" => "30"
"[tacacs][command_category]" => "save_config"
}
}
} else if [tacacs][command_prefix] == "show" {
mutate {
add_field => {
"[event][risk_score]" => "10"
"[tacacs][command_category]" => "read_only"
}
}
}
mutate {
remove_field => [
"[tacacs][year]",
"[tacacs][month]",
"[tacacs][day]",
"[tacacs][time]",
"[tacacs][timezone]"
]
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "tacacs-accounting-%{+YYYY.MM.dd}"
}
stdout {
codec => rubydebug
}
}
Logstash restart:
cd /opt/aaa-stack
docker compose restart logstash
Index kontrolü:
curl "http://localhost:9200/_cat/indices/tacacs-accounting-*?v"
23. Kibana kullanımı
Kibana erişimi:
http://<AAA_SERVER_IP>:5601
Data view:
Name : tacacs-accounting
Index pattern : tacacs-accounting-*
Timestamp : @timestamp
Discover ekranında önerilen kolonlar:
@timestamp
observer.ip
source.ip
user.name
event.action
tacacs.command_type
tacacs.command_prefix
tacacs.command_category
tacacs.command
tacacs.tty
Örnek filtreler:
user.name : "100001"
observer.ip : "<SW2_MGMT_IP>"
tacacs.command_type : "command"
tacacs.command_prefix : "no"
tacacs.command : "write memory <cr>"
user.name : "100001" and tacacs.command_prefix : "configure"
user.name : "100002" and tacacs.command_type : "command"
Bu şekilde şu sorular yanıtlanabilir:
Kim switch'e login oldu?
Kim hangi switch üzerinde işlem yaptı?
Kim hangi komutu çalıştırdı?
Kim configure terminal yaptı?
Kim no ile başlayan komut çalıştırdı?
Kim write memory denedi?
Readonly kullanıcı hangi komutları çalıştırdı?
Operator kullanıcı hangi komutlarda deny aldı?
Not: TACACS accounting bazı IOS davranışlarında deny edilen her komutu loglamayabilir. Çalıştırılan/işlenen komutlar accounting tarafında daha net görünür. Deny eventlerinin ayrıca saklanması gerekiyorsa switch syslog veya TACACS authorization debug/log seçenekleri ayrıca değerlendirilmelidir.
24. Cisco syslog ve archive config logging
TACACS accounting'e ek olarak, SW2 üzerinde cihazın kendi syslog mesajları da toplandı. Bu kısım TACACS veya RADIUS üzerinden çalışmaz. Switch, syslog mesajlarını doğrudan Ubuntu üzerindeki rsyslog servisine UDP 514 ile gönderir.
Akış:
Cisco SW2
|
| UDP/514 syslog
v
Ubuntu rsyslog
|
| /var/log/cisco-gns3.log
v
Logstash
|
v
Elasticsearch -> Kibana
Bu nedenle bu bölümdeki log akışı TACACS+ authentication veya TACACS accounting'e bağlı değildir. TACACS accounting kullanıcı-komut audit kaynağı olarak kalır. Syslog ise cihaz eventleri, SSH olayları, interface/link durumları ve archive config logging ile üretilen config değişiklik mesajları için kullanılır.
24.1 SW2 üzerinde syslog ayarları
SW2 üzerinde syslog mesajlarının Ubuntu TAP gateway IP adresine gönderilmesi için aşağıdaki ayarlar yapıldı:
conf t
service timestamps log datetime msec localtime show-timezone
logging source-interface Vlan1
logging host <TAP_GATEWAY_IP>
logging trap informational
end
write memory
Komutların anlamı:
service timestamps log datetime msec localtime show-timezone
Syslog mesajlarına tarih/saat, milisaniye ve timezone bilgisini ekler.
logging source-interface Vlan1
Syslog paketlerinin kaynak interface'ini Vlan1 yapar.
Böylece syslog mesajları switch management IP'sinden gelmiş görünür.
logging host <TAP_GATEWAY_IP>
Syslog mesajlarının gönderileceği remote syslog sunucusudur.
Bu labda Ubuntu üzerindeki TAP IP adresidir.
logging trap informational
Informational ve daha kritik seviyedeki logların remote syslog'a gönderilmesini sağlar.
Kontrol:
show logging
show running-config | include logging|service timestamps
Beklenen örnek çıktı mantığı:
Syslog logging: enabled
Trap logging: level informational
Logging to <TAP_GATEWAY_IP> udp port 514
Logging Source-Interface: Vlan1
24.2 Archive config logging
Normal syslog ayarı yalnızca genel config eventini gösterir:
%SYS-5-CONFIG_I: Configured from console by netadmin on vty0 (<SOURCE_IP>)
Config mode içinde girilen komutların daha detaylı syslog'a düşmesi için archive config logging açıldı:
conf t
archive
log config
logging enable
notify syslog
hidekeys
end
write memory
Komutların anlamı:
archive log config
Cisco IOS üzerinde config değişikliklerinin loglanacağı bölümü açar.
logging enable
Config komutlarının loglanmasını aktif eder.
notify syslog
Loglanan config komutlarını syslog'a da gönderir.
hidekeys
Şifre, key ve secret gibi hassas değerlerin loglarda açık görünmesini engellemeye yardımcı olur.
Bu ayar sonrasında örnek bir test:
conf t
interface vlan 1
description SYSLOG_TEST_DESCRIPTION
no description
end
Ubuntu tarafında görülen örnek loglar:
%SYS-5-CONFIG_I: Configured from console by netadmin on vty0 (<SOURCE_IP>)
%PARSER-5-CFGLOG_LOGGEDCMD: User:netadmin logged command:interface Vlan1
%PARSER-5-CFGLOG_LOGGEDCMD: User:netadmin logged command:description SYSLOG_TEST_DESCRIPTION
%PARSER-5-CFGLOG_LOGGEDCMD: User:netadmin logged command:no description
Burada önemli nokta şudur:
Bu log akışında TACACS kullanılmaz.
Switch syslog mesajını doğrudan Ubuntu rsyslog'a gönderir.
Kalıcı merkezi saklama switch üzerinde değil Ubuntu/Elastic tarafında yapılır.
Archive config logging, config komutlarını syslog'a daha detaylı basmak için açılmıştır.
Switch üzerinde ayrıca persistent logging veya local disk dosyasına yazma yapılandırılmadı. Bu nedenle bu labda syslog saklama hedefi switch diski değil, Ubuntu ve Elasticsearch tarafıdır. Yine de archive config logging cihaz üzerinde ek log üretimi oluşturduğu için production ortamda log hacmi, hassas veri maskeleme ve retention politikası ayrıca değerlendirilmelidir.
24.3 Ubuntu rsyslog alıcı ayarı
Ubuntu üzerinde rsyslog UDP 514 dinleyecek ve Cisco loglarını ayrı bir dosyaya yazacak şekilde ayarlandı.
Dosya:
/etc/rsyslog.d/10-cisco-gns3.conf
İçerik:
module(load="imudp")
input(type="imudp" port="514")
if ($fromhost-ip == "<SW2_MGMT_IP>") then {
action(type="omfile" file="/var/log/cisco-gns3.log")
stop
}
Servis restart:
sudo systemctl restart rsyslog
UDP 514 dinleme kontrolü:
sudo ss -lunp | grep ':514'
Canlı log izleme:
sudo tail -f /var/log/cisco-gns3.log
Switch'ten paket gelip gelmediğini doğrulamak için:
sudo tcpdump -ni tap-gns3 udp port 514
Örnek beklenen trafik:
<SW2_MGMT_IP>.<random_port> > <TAP_GATEWAY_IP>.514: SYSLOG local7.notice
Logstash container'ın dosyayı okuyabilmesi için host üzerinde dosya izni düzenlendi:
sudo chmod 644 /var/log/cisco-gns3.log
24.4 Logstash'e Cisco syslog dosyasını mount etme
Logstash container'ın Ubuntu üzerindeki syslog dosyasını okuyabilmesi için docker-compose.yml içindeki logstash servisine aşağıdaki volume eklendi:
- /var/log/cisco-gns3.log:/var/log/cisco/cisco-gns3.log:ro
Mount değişikliği sonrası sadece restart yeterli değildir. Container yeniden oluşturuldu:
cd /opt/aaa-stack
docker compose up -d --force-recreate logstash
Container içinden dosya kontrolü:
docker exec -it logstash-tacacs ls -lah /var/log/cisco/
docker exec -it logstash-tacacs tail -n 5 /var/log/cisco/cisco-gns3.log
24.5 Cisco syslog Logstash pipeline
Cisco syslog logları için ayrı pipeline oluşturuldu.
Dosya:
/opt/aaa-stack/logstash/pipeline/cisco-syslog.conf
İçerik:
input {
file {
path => "/var/log/cisco/cisco-gns3.log"
start_position => "beginning"
sincedb_path => "/usr/share/logstash/data/cisco-syslog.sincedb"
mode => "tail"
codec => "plain"
}
}
filter {
if [message] =~ /^\s*$/ {
drop { }
}
mutate {
replace => { "[event][original]" => "%{message}" }
add_field => {
"[event][module]" => "cisco"
"[event][dataset]" => "cisco.syslog"
}
}
grok {
match => {
"message" => [
"^%{TIMESTAMP_ISO8601:[syslog][received_at]} %{IP:[observer][ip]} %{NUMBER:[cisco][sequence]}: \*%{SYSLOGTIMESTAMP:[cisco][device_timestamp]} %{WORD:[cisco][timezone]}: %%%{DATA:[cisco][facility]}-%{INT:[event][severity]}-%{DATA:[cisco][mnemonic]}: %{GREEDYDATA:[cisco][message]}$",
"^%{TIMESTAMP_ISO8601:[syslog][received_at]} %{IP:[observer][ip]} %{GREEDYDATA:[cisco][message]}$"
]
}
tag_on_failure => [ "cisco_syslog_parse_failed" ]
}
if [cisco][message] =~ /logged command:/ {
grok {
match => {
"[cisco][message]" => "^User:%{DATA:[user][name]}\s+logged command:%{GREEDYDATA:[cisco][command]}$"
}
tag_on_failure => [ "cisco_command_parse_failed" ]
}
mutate {
strip => [ "[user][name]", "[cisco][command]" ]
add_field => {
"[cisco][event_type]" => "config_command"
"[event][action]" => "config_command"
}
}
ruby {
code => '
cmd = event.get("[cisco][command]")
if cmd && cmd.strip != ""
event.set("[cisco][command_prefix]", cmd.strip.split(/\s+/)[0])
end
'
}
} else if [cisco][mnemonic] == "CONFIG_I" {
mutate {
add_field => {
"[cisco][event_type]" => "config_change"
"[event][action]" => "config_change"
}
}
} else {
mutate {
add_field => {
"[cisco][event_type]" => "system_event"
"[event][action]" => "system_event"
}
}
}
date {
match => [ "[syslog][received_at]", "ISO8601" ]
target => "@timestamp"
tag_on_failure => [ "cisco_syslog_dateparse_failed" ]
}
}
output {
elasticsearch {
hosts => ["http://elasticsearch:9200"]
index => "cisco-syslog-%{+YYYY.MM.dd}"
}
stdout {
codec => rubydebug
}
}
Logstash yeniden oluşturuldu:
cd /opt/aaa-stack
docker compose up -d --force-recreate logstash
Index kontrolü:
curl "http://localhost:9200/_cat/indices/cisco-syslog-*?v"
Beklenen:
cisco-syslog-YYYY.MM.DD
24.6 Kibana data view ve önerilen kolonlar
Kibana üzerinde ayrı bir data view oluşturuldu:
Name : cisco-syslog
Index pattern : cisco-syslog-*
Timestamp : @timestamp
Discover ekranında önerilen kolonlar:
@timestamp
observer.ip
user.name
event.action
cisco.event_type
cisco.facility
cisco.mnemonic
cisco.command
cisco.command_prefix
cisco.message
Örnek KQL filtreleri:
event.dataset : "cisco.syslog"
event.action : "config_command"
user.name : "netadmin"
cisco.command_prefix : "description"
observer.ip : "<SW2_MGMT_IP>"
24.7 TACACS accounting ve syslog farkı
Bu labda iki farklı log kaynağı birlikte kullanıldı:
TACACS accounting:
Kullanıcı login/logout ve CLI komut accounting kaynağıdır.
TACACS+ server üzerinden üretilir.
Kim hangi komutu çalıştırdı sorusu için ana kaynaktır.
Cisco syslog:
Cihaz eventleri ve archive config logging mesajları için kullanılır.
TACACS veya RADIUS üzerinden geçmez.
Switch doğrudan remote syslog server'a mesaj gönderir.
Özet:
TACACS accounting -> AAA komut audit için daha doğru kaynak.
Cisco syslog -> cihaz olayları, config değişiklik bildirimleri ve sistem eventleri için ek kaynak.
Syslog ile toplanabilecek örnek olaylar:
interface up/down
line protocol up/down
STP/VLAN eventleri
SSH login/logout veya login failure mesajları
reload/reboot mesajları
%SYS-5-CONFIG_I genel config eventleri
%PARSER-5-CFGLOG_LOGGEDCMD archive config komut logları
25. Log retention ve disk konusu
TACACS accounting logları switch diskine yazılmaz. Loglar AAA sunucusu tarafında tutulur:
/opt/aaa-stack/tacacs/logs/accounting.log
Bu yüzden switch diskini şişirmez. Disk büyümesi Ubuntu/Elastic tarafında olur.
Raw accounting log için logrotate örneği:
sudo tee /etc/logrotate.d/tacacs-accounting > /dev/null <<'EOF'
/opt/aaa-stack/tacacs/logs/accounting.log {
daily
rotate 30
compress
missingok
notifempty
copytruncate
}
EOF
Test:
sudo logrotate -d /etc/logrotate.d/tacacs-accounting
Elasticsearch tarafında production için ILM/retention policy planlanmalıdır.
26. Final Akış
Final durumda lab akışı şöyledir:
Keycloak UI
|
| LDAP federation / writable user & group management
v
OpenLDAP
|
| LDAP user + group membership
v
TACACS+ Server - tac_plus-ng
|
| permit / deny / priv-lvl
v
Cisco SW2
|\
| \ TACACS accounting
| \-> accounting.log -> Logstash -> Elasticsearch -> Kibana
|
| syslog UDP/514
v
Ubuntu rsyslog -> /var/log/cisco-gns3.log -> Logstash -> Elasticsearch -> Kibana
Switch login/authorization akışı:
1. Kullanıcı SSH ile SW2'ye bağlanır.
2. SW2, username/password bilgisini TACACS+ server'a gönderir.
3. tac_plus-ng, MAVIS LDAP backend ile OpenLDAP'a sorar.
4. OpenLDAP kullanıcıyı doğrular.
5. tac_plus-ng kullanıcının LDAP grup üyeliğini okur.
6. Grup üyeliğine göre admin/operator/readonly profile seçilir.
7. Kullanıcı komut çalıştırdığında SW2 yine TACACS+ server'a command authorization sorar.
8. tac_plus-ng ilgili profile göre permit/deny döner.
9. Accounting logları Logstash üzerinden Elasticsearch'e gider.
Keycloak'un rolü:
Keycloak doğrudan switch authentication server'ı değildir.
Switch Keycloak token okumaz.
Switch TACACS+ konuşur.
Keycloak bu labda OpenLDAP kullanıcı/grup yönetimi ve IAM/federation katmanıdır.
Bu nedenle doğru teknik anlatım:
Keycloak, OpenLDAP/AD ile entegre edildi. Kullanıcı ve grup yönetimi Keycloak üzerinden yapılabiliyor ve LDAP'a yazılıyor. TACACS+ ise switch CLI enforcement noktası olarak kalıyor. tac_plus-ng, LDAP grup üyeliklerine göre admin/operator/readonly profillerini uyguluyor. Tüm login ve komut kayıtları TACACS accounting ile Elastic/Kibana'ya aktarılıyor.
27. Sonuç
Bu lab ile Cisco switch login ve yönetim işlemleri merkezi hale getirildi.
Final mimari:
SW1 -> FreeRADIUS -> OpenLDAP -> LDAP Group -> Cisco privilege level
SW2 -> TACACS+ -> OpenLDAP -> LDAP Group -> Cisco privilege level + command authorization + accounting
Keycloak -> OpenLDAP federation/writable user-group management
TACACS accounting -> Logstash -> Elasticsearch -> Kibana
Cisco syslog/archive config logging -> rsyslog -> Logstash -> Elasticsearch -> Kibana
RADIUS tarafı merkezi login ve privilege atamasını göstermek için kullanıldı.
TACACS+ tarafı network cihaz yönetiminde daha ileri seviye kontrol sağlamak için kullanıldı:
Kullanıcı doğrulama
Exec authorization
Komut bazlı authorization
Komut accounting
Merkezi loglama
Keycloak bu yapıda kullanıcı/grup yönetimini ve IAM katmanını sağlar. Switch tarafındaki gerçek CLI enforcement ise TACACS+ ile yapılır.
Bu yapı gerçek ortamda Active Directory ile benzer mantıkta uygulanabilir:
Cisco Switch -> TACACS+/RADIUS -> AD/OpenLDAP -> Grup üyeliği -> Yetki profili
Top comments (0)