Documentation
¶
Overview ¶
Package proxy wires an inbound (user) channel to an outbound (target) channel and shuttles data and requests between them.
Index ¶
- Variables
- func TerminatedBy(cause error) (string, bool)
- type Broker
- func (b *Broker) Run(ctx context.Context) error
- func (b *Broker) SFTPSeal() (events int64, digest string)
- func (b *Broker) WithDenyLog(fn func(reqType, reason string)) *Broker
- func (b *Broker) WithSFTP(sink SFTPSink) *Broker
- func (b *Broker) WithSFTPPolicy(d sftpaudit.Decider) *Broker
- func (b *Broker) WithSFTPReadOnly(on bool) *Broker
- type Deps
- type ExecRequest
- type Live
- type OwnWriter
- type ProbePolicy
- type PtyRequest
- type Request
- type RequestPolicy
- type SFTPSink
- type Session
- type SubsystemRequest
- type WindowChangeRequest
Constants ¶
This section is empty.
Variables ¶
var ( // ErrAccessDenied: kimlik ya da yetki reddi — bir arıza DEĞİL, // güvenlik olayı. Çağıran bunu istemciye "access denied" olarak // yansıtır (SSH reject sebebi / HTTP 403). ErrAccessDenied = errors.New("proxy: access denied") // ErrIdleTimeout / ErrMaxLifetime: oturumu KAPATAN sınırlar. // Hata olarak çağırana dönmezler — context.Cause ile okunup denetim // kaydına yazılırlar (bkz. idle.go). ErrIdleTimeout = errors.New("proxy: session idle timeout") ErrMaxLifetime = errors.New("proxy: session max lifetime") // ErrRecordingFailed: kayıt yazımı oturum ortasında bozuldu. // Oturum KAPATILIR — kayıtsız oturum yok. ErrRecordingFailed = errors.New("proxy: recording failed") // erişilemiyor. Kullanıcının suçu değil; birinin müdahale etmesi // gerekir. Çağıran "connection failed" / HTTP 503 der. ErrUnavailable = errors.New("proxy: session unavailable") )
var ErrDirectoryRefused = errors.New("proxy: the directory no longer vouches for this user")
* ErrDirectoryRefused, kimlik kaynağının bu kullanıcı için oturumu * reddettiği: dizinde YOK ya da hesap KAPATILMIŞ. * * Arızadan ayrı bir hata olması şart. "Cevap veremedim" ile "bu kişi * artık burada değil" aynı yola düşerse, bir dizin kesintisi ya herkesi * dışarıda bırakır ya da kapatılmış hesapları içeri alır — hangi tarafa * eğdiğimize bakmaksızın yanlış.
var ErrShortPayload = errors.New("proxy: request payload too short")
ErrShortPayload, payload beklenen alanları taşımayacak kadar kısaysa döner.
var ErrShuttingDown = errors.New("proxy: the bastion is shutting down")
* ErrShuttingDown: bastion kapanıyor ve bekleme süresi doldu. * * ⚠️ ErrTerminated'DAN AYRI TUTULUYOR. İkisi de oturumu kapatıyor ama * denetim kaydına ve kullanıcıya bambaşka şeyler söylüyorlar: biri "bir * yönetici seni kesti", öbürü "makine yeniden başlıyor". Aynı sebebi * kullansaydık, bir dağıtım gecesi denetim defteri kimsenin yapmadığı * yüzlerce kesme ile dolardı.
var ErrTerminated = errors.New("proxy: session terminated by an administrator")
ErrTerminated: oturumu bir yönetici kesti. bound()'un ürettiği ErrIdleTimeout / ErrMaxLifetime ile aynı raftadır: Run bunu context.Cause'dan okuyup denetim satırına "closed_by" olarak yazıyor.
Functions ¶
func TerminatedBy ¶
* TerminatedBy, kesme sebebinden KİMİN kestiğini çıkarır. * * ⚠️ errors.As, metin bölme DEĞİL. Aktör tipli hatanın alanında * duruyor; sebebi Error() metninden geri kazımak, içinde ": " geçen bir * adı sessizce kırpardı.
Types ¶
type Broker ¶
type Broker struct {
// contains filtered or unexported fields
}
Broker, bir kullanıcı kanalı (down) ile bir hedef kanalı (up) arasında veriyi ve request'leri iki yönde taşır.
func New ¶
func New(down ssh.Channel, downR <-chan *ssh.Request, up ssh.Channel, upR <-chan *ssh.Request, rec *record.Writer, recordInput bool, policy RequestPolicy, logger *slog.Logger) *Broker
New wires an inbound channel to an outbound one.
rec nil geçilebilir (kayıt kapalı). recordInput yalnızca config açıkça istediğinde true olmalı.
func (*Broker) SFTPSeal ¶ added in v1.2.0
* SFTPSeal, kaydın mühür satırındaki iki sayı: kaç olay ve özetleri. * * ⚠️ RUN DÖNDÜKTEN SONRA OKUNMALI: mühür satırı Run'ın içinde yazılıyor * (sealSFTPCast) ve daha önce okunan değer, o oturumun son olaylarını * saymaz. * * ⚠️ MÜHÜRÜN TÜKETİCİSİ VARDI AMA PROGRAMATİK DEĞİLDİ: satır yalnızca * .cast dosyasının içinde duruyordu, yani ona bakmanın tek yolu kaydı * açıp gözle okumaktı. Defterin eksik ya da DEĞİŞTİRİLMİŞ olup * olmadığını söyleyen kontrol (verify.JournalOf) bu iki sayıyı istiyor; * oturumun satırına yazılmaları buradan başlıyor. * * ⚠️ OLAY YOKKEN ÖZET BOŞ DÖNÜYOR, SIFIRLARIN ONALTILIĞI DEĞİL. Kayda * mühür satırı da yazılmıyor (yukarısı); veritabanına bir özet yazmak, * dosyada karşılığı olmayan bir değeri "kayıt böyle diyor" diye * saklamak olurdu.
func (*Broker) WithDenyLog ¶ added in v1.2.0
WithDenyLog, reddedilen istekleri deftere yazacak geri çağrıyı kurar.
func (*Broker) WithSFTP ¶
WithSFTP, SFTP denetim hedefini bağlar.
Ayrı bir kurucu yerine ayarlayıcı olması bilinçli: SFTP kapalıyken (varsayılan) çağıranların hiçbiri değişmiyor.
func (*Broker) WithSFTPPolicy ¶ added in v1.2.0
WithSFTPPolicy, SFTP isteklerine karar verecek politikayı kurar.
⚠️ WithSFTP'DEN ÖNCE YA DA SONRA ÇAĞRILABİLİR; politika oturum KURULURKEN okunuyor (beginSFTP), kanal açılırken değil.
func (*Broker) WithSFTPReadOnly ¶ added in v1.2.0
WithSFTPReadOnly, kanalı salt-okunur yapar.
⚠️ SUNUCU TARAFINDA, İSTEMCİDE DEĞİL. "Salt-okunur" panelin JavaScript'inde yaşasaydı, çalınmış bir oturum FXP_WRITE'ı elle yazar ve kısıt hiç var olmamış olurdu.
type Deps ¶
type Deps struct {
Store *store.Store
Records *record.Store
/*
* RecordMinFree, altına inildiğinde YENİ oturumun reddedildiği boş
* alan. 0 kapalı. Gerekçesi Open'daki kullanım yerinde.
*/
RecordMinFree uint64
Authority *ca.CA
Logger *slog.Logger
RecordInput bool
// Requests, oturum kanalı request'lerinin süzgeci (requests.go).
// Sıfır değeri kullanılabilir: env whitelist'i varsayılana düşer,
// tip listeleri zaten sabit.
Requests RequestPolicy
// IdleTimeout, iki yönde de bayt akmayan oturumun kapatılma süresi.
// 0 = kapalı (bkz. config.SessionConfig.IdleTimeout).
IdleTimeout time.Duration
// MaxLifetime, oturumun mutlak ömrü. 0 = kapalı.
MaxLifetime time.Duration
// Probe, hedefte KOMUT ÇALIŞTIRARAK tanıma. Sıfır değeri KAPALI —
// yani bu oturum yolu hedefte kullanıcının oturumu dışında bir şey
// çalıştırmaz. (Panelin yönetim bağlantısı ayrı bir yol ve ayrı bir
// ayar: config.ManageConfig.)
Probe ProbePolicy
/*
* FreshenRoles, oturum AÇILIRKEN kullanıcının rollerini aktif kimlik
* kaynağından tazeler. nil ise tazeleme yapılmaz.
*
* ⚠️ NEDEN BURADA: iki kapı (SSH kanalı ve web terminali) tek
* fonksiyondan geçiyor, yani kural ikisine birden uygulanıyor —
* "iki kapı, tek gerçek". Kimlik doğrulama geri çağrısında değil,
* çünkü orası el sıkışmanın içi: bir handshake'te birden çok kez
* çağrılıyor ve orada ağ isteği yapmak, kimliği doğrulanmamış bir
* bağlantıyı dizine karşı bir yükseltici hâline getirirdi.
*
* ErrDirectoryRefused dönerse oturum REDDEDİLİR; başka bir hata
* yalnızca loglanır ve saklanan rollerle devam edilir (dizin arızası
* yetki yokluğu değildir).
*/
FreshenRoles func(ctx context.Context, username string) error
// Events, canlı izleme akışı. nil ise olay yayınlanmaz.
//
// ⚠️ Publish BLOKLAMAZ (bkz. events.Bus): burası bir oturumun açılış
// ve kapanış yolu ve izleyen bir panel, izlediği oturumu
// bekletemez.
Events events.Publisher
// Live, akan oturumların defteri: yöneticinin kesme düğmesi buradan
// çalışıyor. nil olabilir — o zaman kayıt tutulmuyor ve hiçbir
// oturum kesilemiyor (Live'ın bütün metotları nil-güvenli).
Live *Live
}
Deps, oturum açmak için gereken altyapı. Hem sshd.Server hem httpapi.Server bunu kurar.
type ExecRequest ¶
type ExecRequest struct {
Command string
}
ExecRequest, "exec" payload'ı (RFC 4254 §6.5).
func ParseExec ¶
func ParseExec(payload []byte) (ExecRequest, error)
ParseExec parses an "exec" payload.
type Live ¶
type Live struct {
// contains filtered or unexported fields
}
Live, akan oturumların id -> iptal eşlemesi.
func (*Live) RunningIDs ¶
RunningIDs, bu süreçte akan oturumların kimlikleri.
Panel, veritabanındaki "açık" satırları bununla süzüyor: açık ama akmayan satır, çökmeden kalmış bir izdir ve "çalışıyor" diye gösterilmemeli.
func (*Live) Terminate ¶
* Terminate, oturumu keser ve KESTİĞİNİ bildirir. * * Dönen false "böyle akan bir oturum yok" demek — çağıran bunu * "kesildi" diye çevirmemeli. Bu ayrım, bu dosyanın var olma sebebi. * * ⚠️ SEBEP KİMİ İÇERİYOR. context.Cause zincirinden okunan metin * doğrudan denetim satırına ve kullanıcıya giden cümleye dönüşüyor; * "kesildi" ile "yönetici admin kesti" arasındaki fark, olay sonrası * kaydı okuyan için işin tamamı. errors.Is(ErrTerminated) yine * çalışıyor, çünkü sarmalıyoruz.
func (*Live) TerminateAll ¶
* TerminateAll, akan bütün oturumları verilen sebeple keser ve kaç * tanesini kestiğini döner. * * ⚠️ KAPANIŞIN SON ADIMI. Bekleme süresi dolduğunda çağrılıyor: * oturumları öylece bırakıp süreçten çıkmak, kaydı yarım ve arşiv * kuyruğuna hiç girmemiş hâlde bırakmak demekti. Buradan geçen her * oturum normal kapanış yolunu yürüyor — kullanıcı sebebini görüyor, * kayıt kapanıyor, Close arşivi kuyruğa yazıyor. * * ⚠️ KOPYA ÜZERİNDE GEZİLİYOR. cancel çağrıları Run'ın kendi * goroutine'lerini uyandırıyor ve onlar remove'a giriyor; defterin * kilidini tutarken iptal etmek kilitlenme demekti (Terminate'teki * notun aynısı).
type OwnWriter ¶ added in v1.2.0
* OwnWriter, postern'in KENDİ ürettiği baytları hedefinkilerden ayırt * edilebilir biçimde taşıyabilen istemci ucu. * * ⚠️ NEDEN VAR: postern reddettiği isteğe kendi SFTP cevabını yazıyor ve * kullanıcıya kendi gerekçesini stderr'den söylüyor — ama hedefin cevabı * da, stderr'i de AYNI iki akıştan geçiyor. İstemci ikisini yalnızca * metne bakarak ayırmaya çalışırsa, ayrımı hedef yazıyor demektir: * "postern: " diye başlayan bir hedef mesajı, bastion'ın cümlesi diye * okunur. Köken, yazan yerin SEÇTİĞİ bir şey olmalı. * * ⚠️ İSTEĞE BAĞLI, ZORUNLU DEĞİL. Gerçek bir ssh.Channel bunu * karşılayamaz: SSH'ta CHANNEL_DATA ve EXTENDED_DATA dışında akış yok ve * uydurmak protokolü bozardı. O uçta düz Write'a düşüyoruz — yani bir * `sftp` istemcisi ayrımı GÖREMİYOR ve bu sınır burada yazılı. Panelin * kendi taşıması var (httpapi/wschannel.go, akış etiketi).
type ProbePolicy ¶
* ProbePolicy, tanımanın açık olup olmadığı ve sınırları. * * Sıfır değeri KAPALI ve bu kasıtlı: yapıyı sıfır değeriyle kuran her * çağıran (testler dahil) hedefe dokunmayan davranışı alır.
type PtyRequest ¶
type PtyRequest struct {
Term string
Columns, Rows uint32
Width, Height uint32 // piksel cinsinden; genelde 0
Modes string // opaque terminal modes — parse etme, olduğu gibi ilet (Ek C.4)
}
PtyRequest, "pty-req" payload'ı (RFC 4254 §6.2).
func ParsePty ¶
func ParsePty(payload []byte) (PtyRequest, error)
ParsePty parses a "pty-req" payload.
type Request ¶
type Request struct {
// Username, DOĞRULANMIŞ postern kullanıcı adıdır — istemcinin iddia
// ettiği değil. SSH tarafında Permissions'tan, web tarafında oturum
// kaydından gelir; ikisi de kimliği kendi kapısında doğrulamış olur.
Username string
/*
* AccountID, bağlantının bağlı olduğu users.id.
*
* ⚠️ AD DEĞİL KİMLİK, VE BOŞ GEÇİLEMİYOR. Ad yeniden
* kullanılabiliyor (purge onu serbest bırakıyor), kimlik kalıcı.
* Bunu kapıda çözüp buraya taşımak, kanal açılırken "bu bağlantı
* HÂLÂ o kişiye mi ait" sorusunun sorulabilmesinin tek yolu.
*/
AccountID string
TargetName string
SrcIP string
}
Request, açılacak oturumun kim/nereye bilgisi.
type RequestPolicy ¶
type RequestPolicy struct {
// AcceptEnv, geçmesine izin verilen ortam değişkeni adları.
// Sondaki * joker: "LC_*" tüm LC_ ile başlayanları kapsar.
//
// nil = varsayılan (defaultAcceptEnv). BOŞ DİLİM = hiçbiri; ikisi
// farklı şeyler ve YAML bu ikisini ayırt edebiliyor.
AcceptEnv []string
/*
* AllowSFTP, `subsystem sftp` kanalını açar.
*
* ⚠️ VARSAYILAN KAPALI ve öyle kalmalı. Açık geldiğinde, yükseltme
* yapan bir operatör hiçbir şey yapmadan yeni bir veri çıkış yolu
* kazanmış olurdu — üstelik farkında olmadan.
*
* Açıkken bile SFTP yalnızca dosya olayları denetlenerek geçiyor
* (sftp.go). Denetim kurulamıyorsa kanal AÇILMIYOR: bu bayrağın
* anlamı "sftp serbest" değil, "denetimli sftp serbest".
*/
AllowSFTP bool
}
RequestPolicy, oturum kanalı request'lerinin geçiş kuralları.
type SFTPSink ¶
SFTPSink, üretilen dosya olaylarının gideceği yer.
Emit veri yolundan çağrılıyor: yavaş olmamalı. Kalıcılaştırma (veritabanına yazım) çağıranın işi.
type Session ¶
type Session struct {
// ID, denetim satırının ve .cast dosyasının ortak anahtarı.
ID string
// OSUser, policy'nin verdiği karar — çağıran log'a yazsın diye açık.
OSUser string
// Log, oturumun alanları bağlanmış logger'ı (user, target, session_id,
// record_path). Çağıran kendi olaylarını bununla yazsın ki satırlar
// aynı oturumda birleşsin.
Log *slog.Logger
// contains filtered or unexported fields
}
Session, açılmış ama henüz sürülmemiş bir oturum: hedefe bağlanılmış, kayıt dosyası açılmış, denetim satırı yazılmıştır. Run ile sürülür, Close ile kapatılır.
func Open ¶
Open, yetkiyi denetler ve oturumu hedefe kadar kurar.
Hata döndüğünde HİÇBİR ŞEY açık kalmaz: kısmi kurulum kendi içinde temizlenir ve dönen Session nil'dir — çağıranın Close çağırmasına gerek yoktur.
func (*Session) Close ¶
Close, oturumu kapatır: kayıt dosyası, denetim satırı, hedef bağlantısı. İkinci çağrı no-op — çağıran defer'la koyabilsin diye.
func (*Session) SFTPPolicyActive ¶ added in v1.2.0
Run, istemci kanalını hedefe bağlar ve oturum bitene kadar sürer.
down/downR istemci tarafı: SSH'ta kabul edilmiş ssh.Channel, web'de WebSocket'i ssh.Channel gibi giydiren adaptör. Broker ikisini ayırt etmez — arayüz sözleşmesinin bütün faydası bu.
- SFTPPolicyActive, bu oturumda bir yol politikasının kurulu olup
- olmadığını söyler. *
- ⚠️ ÇAĞIRAN BUNA GÖRE KAPALI TARAFA DÜŞEBİLİR. Kuralsız rol kısıtsız
- (bkz. policy.SFTPDecider) ve taze bir kurulumda hiçbir rolün kuralı
- yok. "Politika riski sınırlar" gerekçesine dayanan bir yüzey — panelin
- dosya tarayıcısı gibi — tam da o kurulumlarda hiçbir şeye dayanmıyor
- demektir; açılmaması gereken yer orası.
func (*Session) SetSFTPReadOnly ¶ added in v1.2.0
SetSFTPReadOnly, oturumun SFTP kanalını salt-okunur yapar. Run'dan ÖNCE çağrılmalı: kısıt kanal kurulurken okunuyor.
type SubsystemRequest ¶ added in v1.2.0
type SubsystemRequest struct {
Name string
}
WindowChangeRequest, "window-change" payload'ı (RFC 4254 §6.7).
- SubsystemRequest, "subsystem" isteğinin gövdesi (RFC 4254 §6.5). *
- Tek alan: alt sistemin adı. Broker yalnızca "sftp" adını geçiriyor ve
- gerekçesi requests.go'da yazılı — adını çözemediğimiz bir alt sistemi
- "denetleniyor" diye geçirmek olurdu.
type WindowChangeRequest ¶
func ParseWindowChange ¶
func ParseWindowChange(payload []byte) (WindowChangeRequest, error)
ParseWindowChange parses a "window-change" payload.