sftpaudit

package
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// StatusPermissionDenied, politikanın reddettiği istek için.
	//
	// ⚠️ FAILURE (4) DEĞİL. İstemciler bu ikisine farklı tepki veriyor:
	// sftp/scp PERMISSION_DENIED'ı "bu yol sana kapalı" diye gösterip
	// devam ediyor, FAILURE ise çoğu istemcide "sunucu arızası" gibi
	// okunuyor. Reddin kullanıcıya DOĞRU görünmesi, reddin kendisi kadar
	// önemli: yanlış kod, yöneticiye açılmış bir arıza kaydı demek.
	StatusPermissionDenied uint32 = 3

	/*
	 * StatusOpUnsupported, TANIMADIĞIMIZ bir işlem için.
	 *
	 * ⚠️ REDDİN SEBEBİ DOĞRU SÖYLENMELİ. Tanımadığımız bir uzantıya
	 * "bu yola iznin yok" demek yalan: sorun yol değil, postern'in o fiili
	 * modellememesi. Taslak (draft-ietf-secsh-filexfer-02 §8) tanınmayan
	 * extended-request için bu kodu ŞART koşuyor.
	 *
	 * Pratik faydası da var: istemciler bu kodu görünce standart işlemlere
	 * geri düşüyor — yani postern'in DENETLEYEBİLDİĞİ fiillere. 3 dönseydik
	 * istemci yolu suçlar, geri düşmez ve kullanıcı neden çalışmadığını
	 * anlamazdı.
	 */
	StatusOpUnsupported uint32 = 8
)

Variables

This section is empty.

Functions

func StatusPacket added in v1.2.0

func StatusPacket(id uint32, code uint32, msg string) []byte

* StatusPacket, tek bir SSH_FXP_STATUS paketini tel biçiminde üretir. * * Biçim (v3): * * uint32 uzunluk (tip baytından itibaren, kendisi hariç) * byte 101 SSH_FXP_STATUS * uint32 istek kimliği * uint32 durum kodu * string mesaj UTF-8 * string dil etiketi * * ⚠️ İSTEK KİMLİĞİ REDDEDİLEN İSTEĞİNKİYLE AYNI OLMALI. İstemci * cevapları kimliğe göre eşliyor; başka bir kimlikle cevap vermek, o * isteği sonsuza kadar cevapsız bırakır — yani reddetmek yerine * istemciyi askıda bırakmış oluruz. * * ⚠️ DİL ETİKETİ BOŞ. RFC boş bırakmaya izin veriyor ve istemciler * yok sayıyor; uydurma bir etiket ("en") göndermek, mesajın gerçekten * o dilde olduğu iddiası olurdu.

Types

type Decider added in v1.2.0

type Decider func(Request) (allow bool, reason string)

* Decider, bir isteğe izin verilip verilmediğini söyler. * * ⚠️ BLOKLAMAMALI. Session'ın tek muteksi altında, veri yolunun üstünde * çalışıyor; burada geçen her milisaniye hedef→istemci akışını da * durduruyor (bkz. frame.go, decide). * * reason, hem istemciye giden STATUS mesajına hem de denetim satırına * giriyor: kullanıcı neden reddedildiğini görmeli, operatör de.

type Denial added in v1.2.0

type Denial struct {
	// Status, SFTP veri akışına yazılacak cevap paketi.
	Status []byte
	// Notice, stderr'e yazılacak insan satırı.
	Notice string
}

* Denial, reddedilen bir isteğe verilecek iki cevap. * * ⚠️ İKİSİ AYRI KANALDAN GİDİYOR ve ikisi de gerekli. Status, SFTP veri * akışında istemcinin PROTOKOL cevabı: onsuz istek kimliği açık kalır ve * istemci sonsuza kadar bekler. Notice ise İNSANA giden satır, stderr'den. * * ⚠️ NEDEN İNSAN SATIRI DA ŞART: OpenSSH'in sftp istemcisi STATUS'un * mesaj alanını hiç okumuyor (get_status yalnızca tip, kimlik ve kodu * ayrıştırıp tamponu bırakıyor) ve `get`/`ls` öncesi yaptığı stat * başarısız olunca "not found" yazıyor — durum kodundan bağımsız olarak. * Yani reddi UYGULUYORUZ, defterine YAZIYORUZ, ama kullanıcı * "engellendim" ile "dosya yok"u ayırt edemiyor. Ölçüldü.

type Event

type Event struct {
	At   time.Time `json:"at"`
	Op   Op        `json:"op"`
	Path string    `json:"path"`
	// NewPath yalnızca rename ve symlink'te dolu.
	NewPath string `json:"new_path,omitempty"`
	// Flags yalnızca open/transfer'da dolu ("read", "write,creat,trunc").
	Flags string `json:"flags,omitempty"`

	// Read/Wrote, transfer olayında GERÇEKTEN taşınan bayt.
	Read  int64 `json:"read,omitempty"`
	Wrote int64 `json:"wrote,omitempty"`

	// OK, hedefin işlemi kabul edip etmediği.
	OK     bool   `json:"ok"`
	Status uint32 `json:"status,omitempty"`
	Detail string `json:"detail,omitempty"`

	/*
	 * Folded, bu satırın kaç AYRI reddi temsil ettiği — yalnızca
	 * katlanmış retlerin özet satırında dolu (flushDenyRunLocked).
	 *
	 * ⚠️ SAYIYI PROZDAN OKUMAK ZORUNDA KALMAMAK İÇİN VAR. Sayı zaten
	 * Detail'de bir cümlenin içinde duruyordu ("… (%d further identical
	 * refusals)") ve oradan okumanın tek yolu İngilizce ayrıştırmak.
	 * ÖLÇÜLDÜ: sayıyı olayları sayarak bulan bir tüketici, 32 KiB'lık
	 * parçalar hâlinde on bin kez reddedilen bir oturumu "2 ret" diye
	 * raporluyordu — yani en ısrarlı oturum en küçük sayıyı taşıyordu.
	 *
	 * ⚠️ ÖZET SATIRININ KENDİSİ BİR RET DEĞİL. Bir dizinin ilk reddi
	 * kendi satırını yazıyor; Folded yalnızca ONDAN SONRAKİLERİ sayıyor.
	 * İkisini toplayan tüketici doğru sayıyı buluyor (bkz. sftpJournal).
	 */
	Folded int `json:"folded,omitempty"`
}

Event, denetim kaydına düşen tek satır.

type Op

type Op string

Op, denetim olayının cinsi.

const (
	OpOpen     Op = "open"
	OpTransfer Op = "transfer"
	OpRemove   Op = "remove"
	OpRename   Op = "rename"
	OpMkdir    Op = "mkdir"
	OpRmdir    Op = "rmdir"
	OpSetstat  Op = "setstat"
	OpSymlink  Op = "symlink"
	OpOpendir  Op = "opendir"
	OpLink     Op = "hardlink"
	OpExtended Op = "extended"
	// OpUnknown, TANIMADIĞIMIZ bir istek türü.
	OpUnknown Op = "unknown"

	// Aşağıdakiler yalnızca REDDEDİLDİKLERİNDE satır üretiyor: izin verilen
	// üstveri istekleri sessiz kalıyor. Kararın verildiği tek yer
	// onRequest'in switch'i — orada listelenmeyen tip bekleyenler
	// tablosuna girmiyor, dolayısıyla satır da üretmiyor.
	OpStat     Op = "stat"
	OpReaddir  Op = "readdir"
	OpRealpath Op = "realpath"
	OpReadlink Op = "readlink"
)

type Request added in v1.2.0

type Request struct {
	// Op, isteğin denetimdeki adı.
	Op Op
	// Path, normalleştirilmiş yol.
	Path string
	// NewPath, ikinci yol (rename, symlink, link); yoksa boş.
	NewPath string
	// Write, isteğin ad uzayını ya da içeriği DEĞİŞTİRME amacı taşıdığı.
	Write bool
}

* Request, politikaya sunulan istek. * * ⚠️ YOLLAR NORMALLEŞTİRİLMİŞ GELİYOR. İstemci `/veri/../etc/shadow` * yazabiliyor ve hedef bunu `/etc/shadow` olarak çözüyor; politikaya ham * dizgiyi vermek, korumayı kâğıt üzerinde bırakırdı. * * ⚠️ NEYİ ÇÖZEMEDİĞİMİZ DE AÇIK OLMALI: sembolik bağlar. Dosya sistemine * erişimimiz yok, dolayısıyla izinli bir dizindeki `link -> /etc` üzerinden * geçen bir yol bize izinli görünür. Bunu kapatmanın yolu her istekte * hedefe fazladan gidiş-dönüş olurdu; agentless duruşu ve gecikme hedefi * buna izin vermiyor. Sınır belgede aynı sertlikte yazılı.

type Session

type Session struct {
	// contains filtered or unexported fields
}

* Session, tek bir SFTP kanalının iki yönünü izler. * * İki yön ayrı goroutine'lerden besleniyor (istemci→hedef ve hedef→ * istemci kopyaları paralel akıyor), bu yüzden durum kilit altında.

func NewSession

func NewSession(emit func(Event)) *Session

NewSession, izleyiciyi kurar. emit her olay için çağrılıyor.

func (*Session) Finish

func (s *Session) Finish()

* Finish, kanal kapanırken YARIM KALAN transferleri yazar. * * ⚠️ NEDEN ŞART: bağlantı transfer ortasında koparsa CLOSE hiç gelmez. * Bu olmadan, yarıda kesilen 1 GB'lık bir indirme denetim kaydında HİÇ * görünmezdi — yani veriyi çekip bağlantıyı koparmak, izi silmenin yolu * olurdu.

func (*Session) FromClient

func (s *Session) FromClient(p []byte) error

FromClient, istemci→hedef akışından gelen baytları verir.

func (*Session) FromClientTo added in v1.2.0

func (s *Session) FromClientTo(p []byte, forward func([]byte) error) error

* FromClientTo, FromClient ile aynı işi yapıyor ve AYRICA iletimi * üstleniyor: hedefe gitmesi gereken aralıklar forward'a gidiyor, * reddedilen isteğin baytları hiç gitmiyor. * * ⚠️ ÇAĞIRAN DÖNÜŞTEN SONRA TakeDenials'I BOŞALTMALI. Reddedilen istek * hedefe gitmediği için hiçbir zaman cevaplanmayacak; istemciye postern'in * kendi cevabı gitmezse o istek kimliği sonsuza kadar açık kalır ve * istemci askıda bekler. * * ⚠️ DENETİMİN GÖRDÜĞÜ, HEDEFİN YAPABİLECEĞİNDEN ÖNCE. Bir paketin ilk * parçası hedefe gidebiliyor ama hedef paketi TAMAMLANMADAN işleyemiyor; * tamamlayan parça ise denetim olayı yazıldıktan sonra iletiliyor. Yani * "hedef bir isteği yaptıysa denetim onu görmüştür" değişmezi duruyor.

func (*Session) FromTarget

func (s *Session) FromTarget(p []byte) error

FromTarget, hedef→istemci akışından gelen baytları verir.

func (*Session) SetPolicy added in v1.2.0

func (s *Session) SetPolicy(d Decider)

SetPolicy, isteklere karar verecek geri çağrıyı kurar. nil ise yol kuralı uygulanmıyor (salt-okuma kısıtı ayrı, bkz. SetReadOnly).

func (*Session) SetReadOnly added in v1.2.0

func (s *Session) SetReadOnly(on bool)

* SetReadOnly, oturumu hedefte HİÇBİR ŞEYİ DEĞİŞTİREMEZ hâle getirir. * * ⚠️ YOL POLİTİKASINDAN AYRI BİR KISIT. Roldeki kurallar "nereye" * sorusunu cevaplıyor; bu, "ne yapabilir" sorusunu. Bir kanalın * salt-okunur olması kullanıcının yetkisiyle değil, o KANALIN ne için * açıldığıyla ilgili. * * ⚠️ SARMALAYICI OLARAK YAZILAMAZDI ve sebebi ölçülebilir: FXP_WRITE * politikaya HİÇ sorulmuyor — tanıtıcı üzerinden yazma, açılışta karara * bağlandığı için politikaya götürülmüyor. Decider'ı saran bir kısıt o * isteği hiç görmezdi. Yazma bayraklı OPEN reddedilirse yazma tanıtıcısı * zaten oluşmaz; ama buna güvenmek, kısıtı HEDEFİN kendi kontrolüne * bırakmak demek — bir bastion'ın tam olarak yapmaması gereken şey.

func (*Session) TakeDenials added in v1.2.0

func (s *Session) TakeDenials() []Denial

* TakeDenials, reddedilen isteklere üretilen STATUS paketlerini alır. * * ⚠️ ÇAĞIRAN BUNLARI OTURUM KİLİDİ DIŞINDA YAZMALI. Karar, veri yolunun * üstünde ve s.mu altında veriliyor; istemciye o kilidi tutarken yazmak, * okumayı durduran bir istemcide hedef→istemci yönünü de kilitlerdi.

func (*Session) TargetAtBoundary added in v1.2.0

func (s *Session) TargetAtBoundary() bool

* TargetAtBoundary, hedef→istemci akışının paket SINIRINDA olup olmadığı. * * ⚠️ NEDEN SORULUYOR: postern bazen istemciye KENDİ cevabını yazıyor * (reddedilen istekler). O baytları gerçek bir paketin ORTASINA sokmak * istemcinin çözümleyicisini bozar — paket yanlış yerden ayrışır ve * sonraki her bayt kayar. * * ⚠️ İKİ KOŞUL. lenHave == 0, çözümleyicinin paketler ARASINDA olduğunu * söylüyor: önek tamamlanmışsa (4) gövdenin ortasındayız, kısmen gelmişse * (1..3) önekin ortasındayız. got/need karşılaştırması bu bilgiyi tekrar * etmekten ibaret. * * ⚠️ AMA "HİÇ BAŞLAMADIK" SINIR DEĞİL, ve bu ayrım ölçülebilir bir arızayı * önlüyor: istemci INIT ile reddedilecek bir isteği boru hattıyla birlikte * gönderirse, hedef daha VERSION'ı yollamadan cevabımızı yazardık. * İstemcinin gördüğü ilk paket SSH_FXP_STATUS olurdu; OpenSSH'in sftp_init'i * yalnızca VERSION kabul ediyor ve fatal ile çıkıyor. * * ⚠️ ÇAĞIRAN, İSTEMCİYE YAZMAYI SERİ HÂLE GETİREN KİLİDİ TUTMALI. Aksi * hâlde cevap, yazılmış ama HENÜZ ÇÖZÜMLENMEMİŞ bir parçanın ardından * gelir ve bu fonksiyon bayat bir cevap verir.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL