Iptables: mencocokkan lalu lintas keluar dengan conntrack dan pemilik. Bekerja dengan tetes yang aneh

11

Dalam skrip iptables saya, saya telah bereksperimen dengan menulis aturan sebanyak mungkin. Saya membatasi pengguna mana yang diizinkan untuk menggunakan layanan mana, sebagian untuk keamanan dan sebagian sebagai latihan pembelajaran.

Menggunakan iptables v1.4.16.2 pada Debian 6.0.6 yang menjalankan kernel 3.6.2.

Namun saya telah menemukan masalah yang belum saya mengerti ...

port keluar untuk semua pengguna

Ini berfungsi dengan baik. Saya tidak memiliki aturan pelacakan keadaan umum.

## Port keluar 81
$ IPTABLES-OUTPUT -p tcp --dport 81 -m conntrack --ctstate NEW, ESTABLISHED -j ACCEPT
$ IPTABLES -A INPUT -p tcp --sport 81 -s $ MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT

port keluar dengan pencocokan pengguna

## port keluar 80 untuk akun pengguna
$ IPTABLES - OUTPUT - pemilik pencocokan - pemilik cairan penggunaaccount -p tcp --port 80 -m conntrack --ctstate NEW, ESTABLISHED --sport 1024: 65535 -j ACCEPT
$ IPTABLES -A INPUT -p tcp --sport 80 --dport 1024: 65535 -d $ MYIP -m conntrack --ctstate ESTABLISHED -j ACCEPT

Ini memungkinkan port 80 hanya untuk akun "useraccount", tetapi aturan seperti ini untuk lalu lintas TCP memiliki masalah.

## Log keluar + aturan blok default
$ IPTABLES - OUTPUT -j LOG --log-awalan "BAD OUTGOING" --log-ip-options --log-tcp-options --log-uid
$ IPTABLES-OUTPUT -J DROP

Masalah

Di atas berfungsi, "useraccount" pengguna bisa mendapatkan file dengan sangat baik. Tidak ada pengguna lain di sistem yang dapat membuat koneksi keluar ke port 80.

useraccount @ host: $ wget http://cachefly.cachefly.net/10mb.test

Tetapi wget di atas membuat x7 menjatuhkan entri di syslog saya:

18 Okt 02:00:35 xxxx kernel: BAD OUTGOING IN = OUT = eth0 SRC = xx.xx.xx.xx DST = 205.234.175.175 LEN = 40 TOS = 0x00 PREC = 0x00 TTL = 64 ID = 12170 DF PROTO = TCP SPT = 37792 DPT = 80 SEQ = 164520678 ACK = 3997126942 WINDOW = 979 RES = 0x00 ACK URGP = 0  

Saya tidak mendapatkan tetes ini untuk aturan yang sama dengan lalu lintas UDP. Saya sudah memiliki aturan yang membatasi pengguna mana yang dapat membuat permintaan DNS.

Paket ACK keluar yang keluar sepertinya berasal dari akun root (URGP = 0) yang saya tidak mengerti. Bahkan ketika saya menukar akun pengguna dengan root.

Saya percaya bahwa paket ACK dikategorikan sebagai baru karena conntrack mulai melacak koneksi setelah langkah ke-3 dari jabat tangan 3 arah, tetapi mengapa dropnya?

Bisakah tetes ini diabaikan dengan aman?

Edit

Jadi saya sering melihat aturan seperti ini, yang berfungsi dengan baik untuk saya:

$ IPTABLES-OUTPUT -s $ MYIP -p tcp -m tcp --dport 80 -m state --state NEW, ESTABLISHED -j ACCEPT
$ IPTABLES -A INPUT -p tcp -m tcp --sport 80 -d $ MYIP -m state --state ESTABLISHED -j ACCEPT

Saya bertukar "-m state --state" untuk "-m conntrack --ctstate" karena pertandingan negara tampaknya sudah usang.

Apakah praktik terbaik untuk memiliki aturan pelacakan keadaan umum? Apakah aturan di atas dianggap tidak benar?

Untuk kontrol yang ketat atas koneksi pengguna keluar apakah ini akan lebih baik?

$ IPTABLES -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
$ IPTABLES-OUTPUT -m conntrack --ststate ESTABLISHED -j ACCEPT

$ IPTABLES - OUTPUT -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate NEW -m owner --uid-owner useraccount -j ACCEPT

$ IPTABLES - OUTPUT -p tcp --dport 80 -s $ SERVER_IP_TUNNEL -m conntrack --ctstate BARU -m pemilik - cairan-pemilik otheraccount -j MENERIMA
arcX
sumber
Bisakah Anda memposting semua aturan termasuk rantai FORWARD dan tabel nat jika host ini juga merutekan lalu lintas dari LAN.
Serge
Tuan rumah ini tidak melakukan perutean, lalu lintas berasal dari mesin dengan aturan ini. Saya hanya memposting aturan yang relevan untuk lalu lintas keluar tertentu.
arcX

Jawaban:

16

Singkatnya, ACK dikirim ketika soket itu bukan milik siapa pun. Alih-alih mengizinkan paket yang berkaitan dengan soket yang menjadi milik pengguna x, izinkan paket yang berkaitan dengan koneksi yang diprakarsai oleh soket dari pengguna x.

Cerita yang lebih panjang.

Untuk memahami masalah ini, ada baiknya Anda memahami cara wgetdan permintaan HTTP berfungsi secara umum.

Di

wget http://cachefly.cachefly.net/10mb.test

wgetmembuat koneksi TCP ke cachefly.cachefly.net, dan setelah didirikan mengirim permintaan dalam protokol HTTP yang mengatakan: "Tolong kirimkan saya isi /10mb.test( GET /10mb.test HTTP/1.1) dan omong-omong, bisakah Anda tidak menutup koneksi setelah Anda selesai ( Connection: Keep-alive). Alasannya hal itu terjadi karena seandainya server membalas dengan pengalihan untuk URL pada alamat IP yang sama, itu dapat menggunakan kembali koneksi.

Sekarang server dapat menjawab dengan baik, "ini dia data yang Anda minta, berhati-hatilah ukurannya 10MB ( Content-Length: 10485760), dan ya OK, saya akan membiarkan koneksi terbuka". Atau jika tidak mengetahui ukuran data, "Ini datanya, maaf saya tidak bisa membiarkan koneksi terbuka tetapi saya akan memberi tahu kapan Anda bisa berhenti mengunduh data dengan menutup ujung koneksi saya".

Dalam URL di atas, kami berada dalam kasus pertama.

Jadi, segera setelah wgetmendapatkan tajuk untuk respons, ia tahu tugasnya selesai setelah mengunduh 10MB data.

Pada dasarnya, apa yang wgetdilakukan adalah membaca data sampai 10MB telah diterima dan keluar. Tetapi pada saat itu, masih banyak yang harus dilakukan. Bagaimana dengan server? Sudah diperintahkan untuk membiarkan koneksi terbuka.

Sebelum keluar, wgettutup ( closepanggilan sistem) deskriptor file untuk soket. Setelah itu close, sistem selesai mengakui data yang dikirim oleh server dan mengirim FINuntuk mengatakan: "Saya tidak akan mengirim data lagi". Pada saat itu closekembali dan wgetkeluar. Tidak ada soket yang terkait dengan koneksi TCP lagi (setidaknya tidak ada yang dimiliki oleh pengguna mana pun). Namun itu belum selesai. Setelah menerima itu FIN, server HTTP melihat end-of-file saat membaca permintaan berikutnya dari klien. Dalam HTTP, itu berarti "tidak ada lagi permintaan, saya akan menutup akhir saya". Jadi ia mengirimkan FIN-nya juga, untuk mengatakan, "Saya juga tidak akan mengirim apa-apa, koneksi itu akan hilang".

Setelah menerima FIN itu, klien mengirim "ACK". Tetapi, pada saat itu, wgetsudah lama berlalu, sehingga ACK bukan dari pengguna mana pun. Karena itulah ia diblokir oleh firewall Anda. Karena server tidak menerima ACK, ia akan mengirim FIN berulang-ulang sampai menyerah dan Anda akan melihat lebih banyak ACK yang jatuh. Itu juga berarti bahwa dengan menjatuhkan ACK tersebut, Anda tidak perlu menggunakan sumber daya server (yang perlu mempertahankan soket dalam status LAST-ACK) untuk beberapa waktu.

Perilaku akan berbeda jika klien tidak meminta "Keep-live" atau server tidak menjawab dengan "Keep-Live".

Seperti yang telah disebutkan, jika Anda menggunakan pelacak koneksi, yang ingin Anda lakukan adalah membiarkan setiap paket dalam status ESTABLISHED dan TERKAIT melalui dan hanya khawatir tentang NEWpaket.

Jika Anda mengizinkan NEWpaket dari pengguna xtetapi bukan paket dari pengguna y, maka paket lain untuk koneksi yang dibuat oleh pengguna xakan melalui, dan karena tidak ada koneksi yang dibuat oleh pengguna y(karena kami memblokir NEWpaket yang akan membuat koneksi), tidak akan ada paket untuk ykoneksi pengguna yang melalui.

Stéphane Chazelas
sumber
3

Ini memungkinkan port 80 hanya untuk akun "useraccount"

- yah, setidaknya aturan yang Anda tunjukkan tidak menyiratkan ini, sebenarnya.

Ada juga ruang untuk saran - jangan lakukan pengguna memeriksa aliran ESTABLISHED, lakukan saja memeriksa BARU. Saya juga tidak melihat titik dalam memeriksa port sumber saat memeriksa Incoming ESTABLISHED, apa bedanya port mana, itu sudah dalam keadaan ESTABLISHED dari conntrack's PoV. Firewall harus sesederhana mungkin tetapi belum efisien, sehingga pendekatan pisau cukur Occam paling cocok.

poige
sumber
1
Terima kasih atas sarannya. Biasanya saya semua untuk kesederhanaan tetapi bukan itu inti dari latihan khusus ini.
arcX
1
@arcX, latihan ini bisa gagal karena terlalu kompleks dan tidak konsisten - TKI, perilaku yang Anda lihat mungkin bukan karena internalfilter netfilter (bug, quirks), tetapi karena cara Anda menggunakannya. Cobalah untuk menyederhanakan aturan terlebih dahulu ...
poige