Skip to main content
Glama
muratti66

Ubuntu System Manager MCP Server

by muratti66
README.md
# Ubuntu System Manager MCP Server

[English](README_EN.md)

> **Resmi olmayan (unofficial) bir projedir.** Canonical/Ubuntu, Anthropic
> ya da Model Context Protocol projesiyle herhangi bir bağlantısı ya da
> onayı yoktur; bağımsız olarak geliştirilmiştir.

Ubuntu sunucusunun genel durumunu (sürüm, kaynak kullanımı, apt güncellemeleri,
servis sağlığı, güvenlik özeti, loglar, donanım) AI istemcilerine (Claude
Desktop, Claude Code, Open WebUI) MCP tool'ları olarak sunan **tamamen
salt-okunur** bir server. Sistemde hiçbir değişiklik yapmaz.

## Tool'lar

| Tool | Kapsam |
|---|---|
| `sistem_ozeti` | Ubuntu sürümü, kernel, uptime, CPU, fiziksel/sanal |
| `kaynak_kullanimi` | Load average, RAM/swap, disk/inode kullanımı |
| `wan_ip_getir` | Dış IP adresi |
| `guncelleme_durumu` | Yükseltilebilir paketler, güvenlik güncellemesi durumu, ESM, unattended-upgrades geçmişi |
| `servis_saglik` | Failed systemd servisleri + belirtilen servislerin durumu |
| `guvenlik_ozeti` | SSH başarısız giriş (24s), son 20 başarılı SSH girişi, aktif oturumlar |
| `log_ozeti` | journalctl ile kernel + syslog uyarı/hata (24s, maks. 100 satır) |

## Mimari: neden bu şekilde

Bu server host'un durumunu **değiştirmeden**, mümkün olduğunca **root
gerektirmeden** okumak üzerine kurulu. Container'a hiçbir yazma izni ya da
geniş capability verilmiyor; her veri kaynağı için host'tan sadece ilgili
dosya/dizin salt-okunur (`:ro`) mount ediliyor.

- **Disk/inode kullanımı** — host'un tüm kök dosya sistemini mount etmek
  yerine (geniş bir yüzey açardı), her izlenecek partition'da **boş bir
  "probe" dizini** oluşturulup sadece o boş dizin mount ediliyor.
  `statvfs()` bir dizinin içeriğine değil üzerinde bulunduğu dosya sistemine
  bakar, bu yüzden boş bir dizin bile partition'ın gerçek disk kullanımını
  verir — container host'un gerçek dosyalarını hiç görmez.
- **Apt güncelleme durumu** — container kendi `apt-get update`'ini
  **çalıştırmaz** (hem root/network gerektirir hem her çağrıda yavaştır).
  Bunun yerine host'taki `apt-daily.timer` (ya da bir cron) cache'i
  periyodik tazeler; container `/var/lib/apt/lists` ve
  `/var/lib/dpkg/status`'u salt-okunur mount edip `python-debian` ile
  doğrudan parse eder. Cache'in yaşı (`cache_son_guncelleme`) response'a
  eklenir ki host'taki timer bozulursa sessizce bayat veri dönmesin.
- **Servis sağlığı** — `systemctl --failed` / `is-active` gibi read-only
  sorgular host'un D-Bus system bus soketine (`/run/dbus/system_bus_socket`)
  bağlanarak çalışır. systemd'nin polkit politikasında read-only unit
  sorguları genelde authentication istemez (sadece start/stop/restart
  ister) — root gerekmemesi beklenir, ama bu davranış dağıtımdan dağıtıma
  farklılık gösterebileceği için hedef sistemde doğrulanmalı.
- **Güvenlik özeti** — `ufw`/`iptables` açık port özeti bilinçli olarak
  **kapsam dışı** bırakıldı: `ufw status` non-root kullanıcıda doğrudan
  reddediyor ve `--cap-add=NET_ADMIN` gibi geniş bir capability gerektiriyor,
  bu da "minimal yetki" hedefiyle çelişiyor. Son başarılı girişler için de
  `last`/wtmp yerine `auth.log`'daki `Accepted ...` satırları parse
  ediliyor — bazı dağıtımlarda (ör. Debian trixie) `last` komutu artık
  klasik `util-linux`'ta değil, ayrı bir pakette (`wtmpdb`) geliyor; bu hem
  ekstra bağımlılık hem de host'un klasik wtmp formatı kullandığı
  varsayımına dayanıyordu.
- **Loglar** — `dmesg` yerine **`journalctl -k`** kullanılıyor. Ham
  `dmesg`/`/dev/kmsg` erişimi `kernel.dmesg_restrict` sysctl'i yüzünden
  dosya izniyle değil `CAP_SYSLOG` capability'siyle (ya da root'la)
  açılıyor; `journalctl` aynı bilgiyi sadece `systemd-journal` grup
  üyeliğiyle sunuyor — ek capability gerekmiyor.
- **Donanım (fiziksel/sanal)** — `systemd-detect-virt` container içinde
  çalıştırılırsa **host'u değil container'ın kendisini** algılar (muhtemelen
  "docker" döner). Bunun yerine `/sys/class/dmi/id/*` dosyaları doğrudan
  okunup bilinen VM imzalarıyla (KVM, VMware, VirtualBox, GCE, AWS...)
  karşılaştırılıyor.
- **Network** — local arayüz listesi/routing tablosu bilinçli olarak kapsam
  dışı bırakıldı (recon/bilgi ifşası riski, özellikle server WAN'a açıksa).

Sonuç: `docker run`'a hiçbir `--cap-add` gerekmiyor, container varsayılan
(dropped) capability setiyle çalışabiliyor; tek istisna, host D-Bus'ına ve
belirli log/config dosyalarına erişim için gereken salt-okunur mount'lar ve
grup üyelikleri.

## Kurulum

### 1. Host tarafı hazırlık

**Grup ID'lerini bul** (container'ın root olmadan log dosyalarını okuyabilmesi için):

```bash
getent group adm systemd-journal utmp
```

`.env.example`'ı `.env` olarak kopyala ve GID'leri güncelle:

```bash
cp .env.example .env
```

**Disk probe dizinleri oluştur** — izlemek istediğin her mount noktasında
boş bir dizin (yukarıdaki "Mimari" bölümüne bakın):

```bash
sudo mkdir -p /.mcp-disk-probe
# ek mount noktaları için, örn.:
# sudo mkdir -p /home/.mcp-disk-probe
# sudo mkdir -p /var/.mcp-disk-probe
```

Her ek probe dizini için `compose.yaml`'daki `volumes` listesine karşılık gelen
satırı ekle/aç.

**Apt cache'ini taze tut** — container kendi `apt-get update`'ini çalıştırmaz,
host'taki `apt-daily.timer`'ı saatlik çalışacak şekilde ayarla:

```bash
sudo systemctl edit apt-daily.timer
```

açılan dosyaya ekle:

```ini
[Timer]
OnCalendar=
OnCalendar=hourly
```

kaydet, sonra:

```bash
sudo systemctl restart apt-daily.timer
```

### 2. Build & çalıştır

```bash
docker compose build
docker compose up -d
```

`guncelleme_durumu` tool'unun cevabındaki `cache_son_guncelleme` alanı, apt
cache'inin ne kadar taze olduğunu gösterir — bu değer sürekli eskiyorsa
`apt-daily.timer`'ı kontrol et.

### 3. AI istemcisine bağla

**Claude Desktop / Claude Code (stdio):**

```json
{
  "mcpServers": {
    "ubuntu-system-summary": {
      "command": "docker",
      "args": [
        "compose", "-f", "/path/to/mcp-ubuntu-system-summary/compose.yaml",
        "run", "--rm", "-T", "ubuntu-system-summary"
      ]
    }
  }
}
```

**Open WebUI (streamable-http):** `.env`'de `TRANSPORT=streamable-http` yap,
`compose.yaml`'da `ports` bloğunu aç (tercihen sadece bir intranet/WireGuard
arayüzüne bind et), `docker compose up -d` sonrası `http://<host>:8000/mcp`
adresini Open WebUI'ye tanıt.

## Bilinen sınırlamalar / doğrulanması gerekenler

- **Ubuntu Pro/ESM durumu** (`guncelleme_durumu.pro_esm_durumu`) — `pro`
  komutunun root olmadan cache'lenmiş durumu mı döndüğü yoksa reddedip
  `kullanılamıyor` mu döneceği hedef sistemde test edilmemiştir.
- **Servis sağlığı** (`servis_saglik`) — read-only `systemctl` sorgularının
  container içinden host D-Bus'ına root gerektirmeden erişip erişemediği
  hedef sistemde test edilmemiştir; polkit politikası dağıtımdan dağıtıma
  farklılık gösterebilir.
- **`apt list --upgradable` karşılaştırması** apt'nin pinning/priority
  kurallarını uygulamaz — sadece görülen en yüksek versiyonu aday alır. Çok
  özel pinning kullanan sistemlerde apt'nin gerçek davranışından sapabilir.
- **Güvenlik güncelleme yaşı** (`cache_yasi_gun`), ilgili `-security`
  reposunun yerel cache'inin ne kadar eski olduğunu gösterir — belirli bir
  paketin ilk yayınlanma tarihi değildir.
- **22.04+ minimal kurulumlarda** rsyslog paketi olmayabilir ve
  `/var/log/auth.log` hiç oluşmayabilir — bu durumda SSH giriş verileri
  otomatik olarak `journalctl -t sshd` fallback'inden okunur.

## Güvenlik notları

- `auth.log`/`syslog` içeriği IP adresi, kullanıcı adı gibi hassas veri
  içerebilir. Bu server'ı WAN'a (ör. Open WebUI ile) açacaksan bu tool'ları
  kapsam dışı bırak ya da erişimi bir VPN/intranet katmanıyla sınırla.
- Container `read_only: true`, `cap_drop: [ALL]` ve `no-new-privileges` ile
  çalışır; tüm mount'lar `:ro`.

## Lisans

[MIT](LICENSE)