Help us shape the future of Go! Take the 2026 Go Developer Survey

sftpaudit

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Sep 16, 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