Pozor museum: Toto je archivní verze blogu. Pro vkládání komentářů přejděte na tento článek v nové verzi blogu.

AppArmor vs. iptables – blokování sítě

Člověk se pořád učí. Myslel jsem, že to jde jen ve firewallu v BSD systémech, ale on to umí i iptables, který tu samozřejmě mám a používám. O čem je řeč? O psaní firewallových pravidlech pro konkrétní uživatele, ne jen pro celý systém. Diskuse vznikla pod zprávičkou, kde Harvie psal, jak využít (zneužít) SSH server jako proxy, přestože si to jeho správce moc nepřeje (nastavil AllowTcpForwarding no). K tomu jsem poznamenal, že návod je sice fajn, ale bylo by taky dobré zmínit protiopatření – a navrhl jsem řešení pomocí AppArmoru.

Na to mi uživatel Lenin POWER! napsal, že na to jdu moc složitě a že stačí firewall. A měl pravdu. Skutečně stačí napsat:

iptables … --uid-owner …

a vybraného uživatele zablokujeme firewallem. Tak skvělý systém máme :-) GNU/Linux – ani nejsou potřeba žádné složité nadstavby.

Pokud tedy spravujete nějaký linuxový SSH server a nechcete, aby si z něj dělali uživatelé proxy, musíte použít firewall a ne jen AllowTcpForwarding no – tato volba má totiž smysl jen tehdy, když uživatelé nemůžou spouštět vlastní příkazy (nemají shell, ale jen třeba scp/sftp).

Pak je tu ještě filosofická otázka, jestli uživatele takhle omezovat nebo ne. Osobně jsem spíš proti – SSH přístup na své stroje dávám jen těm, kterým věřím, takže ti ať klidně přístup k síti mají. Někdy se ale SSH server používá jako brána do vnitřní sítě a pak se jemnější nastavení hodí – konkrétní uživatelé mají povolenou síť na konkrétní stroje, ale jiní můžou všude (např. kvůli aktualizacím).

Tak to bychom měli, iptables stačí. Přesto jsem ale řešení s AppArmorem tak úplně nevzdal a aspoň cvičně jsem si ho vyzkoušel.

Vsuvka o AppArmoru: AppArmor je linuxový bezpečnostní systém vydaný pod GNU GPL licencí, který umožňuje omezit práva programů pomocí tzv. profilů. Program běží pod nějakým uživatelem a na základě toho má nějaká práva (např. přístup k domovskému adresáři) a AppArmor mu tahle práva může ještě více omezit – např. tak, že program bude moci číst jen ~/dokumenty a zapisovat jen do ~/temp. K čemu je to dobré? Např. si s takovým profilem můžete spustit webový prohlížeč a i kdyby v něm bylo milion chyb a lezli jste na ty nejhorší stránky na Internetu, nic se vám nestane – i kdyby se stránce podařilo spustit nějaký vir (což je na Linuxu samo o sobě dost nepravděpodobné), ten vir by nemohl nic dělat – nedostal by se např. k vašim složkám ~/.ssh a ~/.gnupg a neukradl by vaše soukromé klíče, nebo by nemohl smazat vaše fotky a dokumenty – přestože vy jako uživatel na to práva máte. Pomocí AppArmoru můžete omezit např. ten webový prohlížeč nebo nějaký proprietární program, o kterém nemůžete vědět, co dělá (pokud už ho musíte spouštět – možná je lepší takové programy vůbec nepoužívat). Kromě toho, že AppArmorem jdou omezit práva programu k souborům, jde omezit i přístup k síti.

Zpět k SSH a našemu shellu bez sítě. Profily AppArmoru jsou vázané na cestu k programu. Odstřihnout takto od sítě /bin/bash není moc dobrý nápad, proto si vytvoříme jiný shell a ten omezíme pomocí profilu. Můžete buď jednoduše zkopírovat binárku BASHe nebo použít tenhle program – navíc se akorát vypíše, že uživatel nebude mít přístup na síť.

#include <stdlib.h>
#include <iostream>

int main() {
    using namespace std;
    cout << "\033[22;31m" << "Vítejte v offline shellu" << "\033[0m" << endl;
    system("/bin/bash");
    cout << "\033[22;31m" << "Končím offline shell" << "\033[0m" << endl;
}

Přeložíte pomocí g++ program.cpp -o bash-offline a výsledek nakopírujete, kam je potřeba. A tenhle shell nastavíte uživateli jako jeho přihlašovací (v souboru /etc/passwd). Teď vytvoříme AppArmor profil pro program bash-offline. Profily se nacházejí v /etc/apparmor.d/. Ten náš bude velmi jednoduchý:

/usr/local/bin/bash-offline {
    /** rixwmkl,
}

Přístup k souborům neomezujeme (stačí standardní unixová práva) a síť je zakázaná, protože jsme ji nepovolili. Jednoduché, ne? :-) Postupně můžeme přidat další omezení… Řešení to není hotové ani dokonalé, spíš je to takový prvotní nástřel, jak by to mohlo vypadat, a vůbec upozornění na AppArmor. Náměty a připomínky vítám.

Na závěr ještě ukázka:

$ bash-offline 
Vítejte v offline shellu
$ ping localhost
socket: Permission denied
$ exit
Končím offline shell
$ ping localhost
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.078 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.069 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.074 ms
^C
--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
rtt min/avg/max/mdev = 0.069/0.073/0.078/0.010 ms
$

Zdrojáky najdete i v mercurialu mezi ostatními příklady a programy.

P.S. aspoň k něčemu jsou ty internetové diskuse dobré :-)

Průměr: 4.7 (3 hlasů)

zaklínadlo rixwmklšumpoplex

Možná by stálo za to vysvětlit tu kouzelnou formuli
/** rixwmkl,
počítám, že /** je nějaká maska a každě další písmenko asi něco znamená... šlo by to rozvést?

kouzlo AppArmor

jo, to jsem mohl :-)

/** je cesta, root a všechno ostatní. Dají se používat i různé proměnné jako @{HOME}/ – tzn. adresář uživatele, který spouští daný program.

rw jsou celkem jasné (čtení, zápis)

m je mapování do paměti, k zamykání souborů a l vytváření pevných odkazů.

ix je právo spouštět s tím, že se dědí omezení daná AppArmorem (což se hodí třeba když prohlížeč spouští nějaký plugin, tak se pravidla vztahují i na něj, i když je to jiný program, než ke kterému se vztahuje ten náš profil).

a co chsh?

Ahoj, a nemůže si zlý hoch změnit shell pomocí příkazu chsh? Možná by pak pomohlo místo kopie Bashe udělat kopii RestrictedBashe (rbash), který má v sobě nějakou ochranu proti změně shellu, viz man chsh.

chsh

To je pravda – pomocí chsh jde změnit – pak záleží na nastavení daného systému, jestli je změna shellu povolená a jaké jsou dostupné (/etc/shells).

Nefunguje to :-(

Hmm,
tak sem to dneska testoval (chvili mi trvalo, nez sem udelal balicek s AppArmorem pro ArchLinux :-) a asi si zapomnel na jednu zasadni vec...

funguje to jenom na sitovy operace, ktery vyzadujou net_raw capability (coz stejne v normalnich pripadech umi jenom root a suid binarky jako ping).

problem s obchazenim ssh forwardingu je tedy stejne nutno resit pomoci iptables.

takze priste misto ping localhost zkus radeji curl localhost.

Funguje

Zvláštní, že ti to nejde – u mě to funguje:

$ ./bash-offline 
Vítejte v offline shellu
$ ping localhost
socket: Permission denied
$ curl http://seznam.cz
curl: (6) Couldn't resolve host 'seznam.cz'
$ curl http://77.75.72.3
curl: (7) couldn't connect to host
$ curl http://localhost
curl: (7) couldn't connect to host
$ strace curl http://localhost
…
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = -1 EACCES (Permission denied)
…
$ wget http://77.75.72.3
--2012-01-20 16:07:59--  http://77.75.72.3/
Navazuje se spojení s 77.75.72.3:80… nezdařilo se: Operace zamítnuta.
Zkusí se to znovu.

Testováno na Kubuntu 11.10 s Apparmorem 2.7.0~beta1+bzr1774-1ubuntu2 (normální distribuční balíček).