storage

package
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: Sep 30, 2026 License: MIT Imports: 30 Imported by: 5

Documentation

Overview

storage defines a storage mechanism

Package storage - Redis-backed implementation of the Storage interface.

This file adds an additive, optional backend used when multiple interactsh server instances need to share state behind a load balancer. It does not replace or alter the default in-memory/LevelDB StorageDB; both backends coexist and the caller decides which one to construct.

storage implements a encrypted memory mechanism

Index

Constants

This section is empty.

Variables

View Source
var DefaultOptions = Options{
	MaxSize:               2500000,
	MaxSharedInteractions: defaultMaxSharedInteractions,
	EvictionStrategy:      EvictionStrategySliding,
}
View Source
var ErrCorrelationIdNotFound = errkit.New("could not get correlation-id from cache")
View Source
var ErrInvalidSecretKey = errkit.New("invalid secret key passed for correlation-id")

ErrInvalidSecretKey is returned when the secret key presented for a correlation-id does not match the one stored at registration. It is distinguishable from ErrCorrelationIdNotFound so callers can tell "wrong session" apart from "no such session".

Functions

func AESEncrypt added in v1.0.7

func AESEncrypt(key []byte, message []byte) (string, error)

AESEncrypt encrypts a message using AES and puts IV at the beginning of ciphertext.

func AppendMany added in v1.0.7

func AppendMany(sep string, slices ...[]byte) []byte

func ParseB64RSAPublicKeyFromPEM added in v1.0.7

func ParseB64RSAPublicKeyFromPEM(pubPEM string) (*rsa.PublicKey, error)

ParseB64RSAPublicKeyFromPEM parses a base64 encoded rsa pem to a public key structure

Types

type CacheMetrics added in v0.0.5

type CacheMetrics struct {
	HitCount         uint64        `json:"hit-count"`
	MissCount        uint64        `json:"miss-count"`
	LoadSuccessCount uint64        `json:"load-success-count"`
	LoadErrorCount   uint64        `json:"load-error-count"`
	TotalLoadTime    time.Duration `json:"total-load-time"`
	EvictionCount    uint64        `json:"eviction-count"`
}

type CorrelationData

type CorrelationData struct {
	sync.Mutex
	// data contains data for a correlation-id in AES encrypted json format.
	Data []string `json:"data"`
	// secretkey is a secret key for original user verification
	SecretKey string `json:"-"`
	// AESKey is the AES encryption key in encrypted format.
	AESKeyEncrypted string `json:"aes-key"`
	// decrypted AES key for signing
	AESKey      []byte               `json:"-"`
	ReadOffsets map[string]int       `json:"-"`
	LastSeen    map[string]time.Time `json:"-"`
	// Files is metadata for files uploaded against this correlation-id.
	// Guarded by the embedded Mutex. Not persisted: only interaction blobs
	// are written to disk, and uploads do not survive a restart.
	Files []UploadedFile `json:"-"`
}

CorrelationData is the data for a correlation-id.

type EvictionStrategy added in v1.3.0

type EvictionStrategy int
const (
	EvictionStrategySliding EvictionStrategy = iota // expire-after-access
	EvictionStrategyFixed                           // expire-after-write
)

type GetInteractionsFunc added in v1.0.7

type GetInteractionsFunc func() []string

type Options added in v1.0.7

type Options struct {
	DbPath                string
	EvictionTTL           time.Duration
	MaxSize               int
	MaxSharedInteractions int
	EvictionStrategy      EvictionStrategy
	// OnRemoval is called for each client session removed from cache
	// (deregistration, TTL expiry, size eviction, or cache close).
	OnRemoval func()
	// OnEviction is invoked when a correlation-id leaves the cache for any
	// reason: explicit removal, TTL expiry, capacity eviction or Close.
	// Unlike OnRemoval it fires for every entry, not just client sessions, and
	// receives the evicted data so callers can release resources keyed off it.
	// It runs on the cache's single event goroutine and must not block.
	OnEviction func(correlationID string, data *CorrelationData)
}

func (*Options) UseDisk added in v1.0.7

func (options *Options) UseDisk() bool

type RedisOptions added in v1.4.0

type RedisOptions struct {
	// URL is a redis connection string, e.g. redis://user:pass@host:6379/0
	URL string
	// KeyPrefix is prepended to every key. Defaults to "interactsh:".
	KeyPrefix string
	// TLS, when non-nil, enables TLS with the provided config.
	TLS *tls.Config
	// EvictionTTL controls how long correlation state survives without activity.
	// A value <= 0 disables TTL.
	EvictionTTL time.Duration
	// EvictionStrategy mirrors storage.Options.EvictionStrategy:
	//   - sliding: TTL is refreshed on access (default)
	//   - fixed:   TTL is set on write and not refreshed
	EvictionStrategy EvictionStrategy
	// MaxSharedInteractions caps the per-id buffer length when multiple
	// consumers share an id. <= 0 means unlimited.
	MaxSharedInteractions int
	// Client is an already-configured *redis.Client. If set, URL/TLS are ignored.
	// Useful for tests with miniredis.
	Client *redis.Client
}

RedisOptions configures the Redis-backed Storage.

All eviction-related fields mirror the semantics of the local Options struct; they are duplicated here intentionally so that adding the Redis backend does not touch the existing Options/StorageDB code paths.

type Storage

type Storage interface {
	GetCacheMetrics() (*CacheMetrics, error)
	SetIDPublicKey(correlationID, secretKey, publicKey string) error
	SetID(ID string) error
	AddInteraction(correlationID string, data []byte) error
	AddInteractionWithId(id string, data []byte) error
	GetInteractions(correlationID, secret string) ([]string, string, error)
	GetInteractionsWithId(id string) ([]string, error)
	GetInteractionsWithIdForConsumer(id, consumerID string) ([]string, error)
	RemoveConsumer(id, consumerID string) error
	RemoveID(correlationID, secret string) error
	GetCacheItem(token string) (*CorrelationData, error)
	Close() error
}

type StorageDB added in v1.0.7

type StorageDB struct {
	Options *Options
	// contains filtered or unexported fields
}

Storage is an storage for interactsh interaction data as well as correlation-id -> rsa-public-key data.

func New

func New(options *Options) (*StorageDB, error)

New creates a new storage instance for interactsh data.

func (*StorageDB) AddInteraction added in v1.0.7

func (s *StorageDB) AddInteraction(correlationID string, data []byte) error

AddInteraction adds an interaction data to the correlation ID after encrypting it with Public Key for the provided correlation ID.

func (*StorageDB) AddInteractionWithId added in v1.0.7

func (s *StorageDB) AddInteractionWithId(id string, data []byte) error

AddInteractionWithId adds an interaction data to the id bucket

func (*StorageDB) Close added in v1.0.7

func (s *StorageDB) Close() error

func (*StorageDB) GetCacheItem added in v1.0.7

func (s *StorageDB) GetCacheItem(token string) (*CorrelationData, error)

GetCacheItem returns an item as is

func (*StorageDB) GetCacheMetrics added in v1.0.7

func (s *StorageDB) GetCacheMetrics() (*CacheMetrics, error)

func (*StorageDB) GetInteractions added in v1.0.7

func (s *StorageDB) GetInteractions(correlationID, secret string) ([]string, string, error)

GetInteractions returns the interactions for a correlationID and removes it from the storage. It also returns AES Encrypted Key for the IDs.

func (*StorageDB) GetInteractionsWithId added in v1.0.7

func (s *StorageDB) GetInteractionsWithId(id string) ([]string, error)

GetInteractions returns the interactions for a id and empty the cache

func (*StorageDB) GetInteractionsWithIdForConsumer added in v1.3.1

func (s *StorageDB) GetInteractionsWithIdForConsumer(id, consumerID string) ([]string, error)

GetInteractionsWithIdForConsumer returns unseen interactions for a consumer using per-consumer read offsets.

func (*StorageDB) ListUploads added in v1.4.0

func (s *StorageDB) ListUploads(correlationID string) ([]UploadedFile, bool)

ListUploads returns the upload metadata for a correlation-id. No secret is required: this backs the file-serving path, where possession of the correlation-id is the only credential.

func (*StorageDB) RemoveConsumer added in v1.3.1

func (s *StorageDB) RemoveConsumer(id, consumerID string) error

RemoveConsumer removes a consumer's read offset and compacts consumed data.

func (*StorageDB) RemoveID added in v1.0.7

func (s *StorageDB) RemoveID(correlationID, secret string) error

RemoveID removes data for a correlation ID and data related to it.

func (*StorageDB) SetID added in v1.0.7

func (s *StorageDB) SetID(ID string) error

func (*StorageDB) SetIDPublicKey added in v1.0.7

func (s *StorageDB) SetIDPublicKey(correlationID, secretKey, publicKey string) error

SetIDPublicKey sets the correlation ID and publicKey into the cache for further operations.

func (*StorageDB) UpdateUploads added in v1.4.0

func (s *StorageDB) UpdateUploads(correlationID, secret string, fn func([]UploadedFile) ([]UploadedFile, error)) error

UpdateUploads verifies the secret key for a correlation-id and then runs fn while holding that correlation-id's lock, replacing the upload metadata with whatever fn returns. Callers perform their disk writes inside fn so that the quota check and the commit are atomic with respect to concurrent uploads against the same session.

type StorageRedis added in v1.4.0

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

StorageRedis implements Storage backed by a Redis server (or compatible).

Key layout (all keys are placed in the same hash slot via the {id} hash tag so the implementation is safe under Redis Cluster as well):

<prefix>meta:{<id>}        HASH   secret, aes_key, aes_key_enc
<prefix>data:{<id>}        LIST   ordered AES-encrypted interaction strings
<prefix>consumers:{<id>}   SET    consumer ids currently subscribed
<prefix>off:{<id>}:<cid>   STRING per-consumer read offset (integer)
<prefix>seen:{<id>}:<cid>  STRING per-consumer last-seen unix nano

func NewRedis added in v1.4.0

func NewRedis(options *RedisOptions) (*StorageRedis, error)

NewRedis builds a Redis-backed Storage. The caller is responsible for keeping the underlying Redis server reachable for the lifetime of the returned instance.

func (*StorageRedis) AddInteraction added in v1.4.0

func (s *StorageRedis) AddInteraction(correlationID string, data []byte) error

AddInteraction encrypts and appends an interaction for the given correlation id.

func (*StorageRedis) AddInteractionWithId added in v1.4.0

func (s *StorageRedis) AddInteractionWithId(id string, data []byte) error

AddInteractionWithId mirrors AddInteraction; the distinction in the original StorageDB is purely semantic.

func (*StorageRedis) Close added in v1.4.0

func (s *StorageRedis) Close() error

Close releases the redis client. Existing data in Redis is preserved so that the next instance can resume; this matches the multi-instance use case the Redis backend was introduced for.

func (*StorageRedis) GetCacheItem added in v1.4.0

func (s *StorageRedis) GetCacheItem(token string) (*CorrelationData, error)

GetCacheItem returns the CorrelationData associated with the token. Only the fields stored in the meta hash are populated; per-consumer offsets live in separate keys.

func (*StorageRedis) GetCacheMetrics added in v1.4.0

func (s *StorageRedis) GetCacheMetrics() (*CacheMetrics, error)

func (*StorageRedis) GetInteractions added in v1.4.0

func (s *StorageRedis) GetInteractions(correlationID, secret string) ([]string, string, error)

GetInteractions returns all buffered interactions for the correlation id and atomically clears the buffer (preserving the legacy single-consumer semantics of GetInteractions).

func (*StorageRedis) GetInteractionsWithId added in v1.4.0

func (s *StorageRedis) GetInteractionsWithId(id string) ([]string, error)

GetInteractionsWithId returns and drains the buffered interactions for an id.

func (*StorageRedis) GetInteractionsWithIdForConsumer added in v1.4.0

func (s *StorageRedis) GetInteractionsWithIdForConsumer(id, consumerID string) ([]string, error)

GetInteractionsWithIdForConsumer returns unseen interactions for the given consumer, advancing its offset and evicting stale consumers atomically via a Lua script.

func (*StorageRedis) RemoveConsumer added in v1.4.0

func (s *StorageRedis) RemoveConsumer(id, consumerID string) error

RemoveConsumer drops the consumer's offset/last-seen tracking and compacts the underlying data list when possible.

func (*StorageRedis) RemoveID added in v1.4.0

func (s *StorageRedis) RemoveID(correlationID, secret string) error

RemoveID drops every key associated with the correlation id after secret verification.

func (*StorageRedis) SetID added in v1.4.0

func (s *StorageRedis) SetID(id string) error

SetID registers a correlation id without an associated public key. Used for the wildcard / auth-token path.

func (*StorageRedis) SetIDPublicKey added in v1.4.0

func (s *StorageRedis) SetIDPublicKey(correlationID, secretKey, publicKey string) error

SetIDPublicKey registers a correlation id along with its RSA public key. Any pre-existing keyspace for the id is cleared first to mirror the StorageDB behaviour after cache eviction + session restore.

type UploadStorage added in v1.4.0

type UploadStorage interface {
	// UpdateUploads verifies the secret key for a correlation-id and then runs
	// fn under that correlation-id's lock, replacing the upload metadata with
	// whatever fn returns. Callers perform their disk writes inside fn so the
	// quota check and the commit are atomic against concurrent uploads for the
	// same session. fn is invoked at most once.
	UpdateUploads(correlationID, secret string, fn func([]UploadedFile) ([]UploadedFile, error)) error
	// ListUploads returns the upload metadata for a correlation-id, and whether
	// the correlation-id is known at all.
	ListUploads(correlationID string) ([]UploadedFile, bool)
}

UploadStorage is the optional capability of tracking per-session uploaded file metadata, implemented by StorageDB.

It is deliberately kept out of Storage. The uploaded bytes live on the local filesystem of a single server instance and the capacity quota is an in-process counter, so the feature is only coherent for instance-local backends. A shared backend such as Redis would let one instance advertise files whose bytes only exist on another instance's disk, so it does not implement this and file hosting is refused when it is selected.

type UploadedFile added in v1.4.0

type UploadedFile struct {
	Name      string    `json:"name"`
	Size      int64     `json:"size"`
	SHA256    string    `json:"sha256"`
	Timestamp time.Time `json:"timestamp"`
}

UploadedFile is metadata for a file uploaded by the owner of a correlation-id. The bytes themselves live on disk, managed by the server's upload store; this record exists so that cache eviction and deregistration can drive file cleanup.

Jump to

Keyboard shortcuts

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