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

proxy

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: 20 Imported by: 0

Documentation

Overview

Package proxy wires an inbound (user) channel to an outbound (target) channel and shuttles data and requests between them.

Index

Constants

This section is empty.

Variables

View Source
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")

	// ErrUnavailable: hedefe ulaşılamadı, kayıt açılamadı, veritabanı
	// 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")
)
View Source
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ış.

View Source
var ErrShortPayload = errors.New("proxy: request payload too short")

ErrShortPayload, payload beklenen alanları taşımayacak kadar kısaysa döner.

View Source
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ı.

View Source
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

func TerminatedBy(cause error) (string, bool)

* 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) Run

func (b *Broker) Run(ctx context.Context) error

Run shuttles data and requests until the session ends, then returns.

func (*Broker) SFTPSeal added in v1.2.0

func (b *Broker) SFTPSeal() (events int64, digest string)

* 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

func (b *Broker) WithDenyLog(fn func(reqType, reason string)) *Broker

WithDenyLog, reddedilen istekleri deftere yazacak geri çağrıyı kurar.

func (*Broker) WithSFTP

func (b *Broker) WithSFTP(sink SFTPSink) *Broker

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

func (b *Broker) WithSFTPPolicy(d sftpaudit.Decider) *Broker

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

func (b *Broker) WithSFTPReadOnly(on bool) *Broker

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 NewLive

func NewLive() *Live

NewLive, boş bir defter kurar.

func (*Live) Running

func (l *Live) Running(id string) bool

Running, oturumun bu süreçte akıp akmadığını söyler.

func (*Live) RunningIDs

func (l *Live) RunningIDs() map[string]bool

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

func (l *Live) Terminate(id, by string) bool

* 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

func (l *Live) TerminateAll(cause error) int

* 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

type OwnWriter interface {
	WriteOwn(p []byte) (int, error)
	WriteOwnStderr(p []byte) (int, error)
}

* 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

type ProbePolicy struct {
	Enabled bool
	Refresh time.Duration
	Timeout time.Duration
}

* 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

type SFTPSink interface {
	Emit(sftpaudit.Event)
}

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

func Open(ctx context.Context, deps Deps, req Request) (*Session, error)

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

func (s *Session) Close(ctx context.Context)

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) Run

func (s *Session) Run(ctx context.Context, down ssh.Channel, downR <-chan *ssh.Request) error

func (*Session) SFTPPolicyActive added in v1.2.0

func (s *Session) SFTPPolicyActive() bool

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

func (s *Session) SetSFTPReadOnly(on bool)

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

type WindowChangeRequest struct {
	Columns, Rows uint32
	Width, Height uint32
}

func ParseWindowChange

func ParseWindowChange(payload []byte) (WindowChangeRequest, error)

ParseWindowChange parses a "window-change" payload.

Jump to

Keyboard shortcuts

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