Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,107 @@ Security features for the SSH server (`src/server/security/`):
- Thread-safe with fail-closed behavior on lock contention
- Configuration via `allowed_ips` and `blocked_ips` in server config

### File Transfer Filter Module

Policy-based filtering infrastructure for SFTP and SCP file transfer operations (`src/server/filter/`):

**Structure**:
- `mod.rs` - `TransferFilter` trait, `Operation` enum, `FilterResult` enum, `NoOpFilter`
- `policy.rs` - `FilterPolicy` engine, `FilterRule`, `Matcher` trait, `SharedFilterPolicy`
- `path.rs` - Path-based matchers: `PrefixMatcher`, `ExactMatcher`, `ComponentMatcher`, `ExtensionMatcher`
- `pattern.rs` - Pattern-based matchers: `GlobMatcher`, `RegexMatcher`, `CombinedMatcher`, `NotMatcher`

**Key Components**:

- **Operation**: Enum representing file operations
- `Upload`, `Download`, `Delete`, `Rename`
- `CreateDir`, `ListDir`, `Stat`, `SetStat`
- `Symlink`, `ReadLink`

- **FilterResult**: Actions to take on matched operations
- `Allow` - Permit the operation (default)
- `Deny` - Block the operation
- `Log` - Allow but log for auditing

- **TransferFilter Trait**: Interface for custom filter implementations
- `check(path, operation, user)` - Check single path operations
- `check_with_dest(src, dest, operation, user)` - Check two-path operations (rename, symlink)
- `is_enabled()` - Check if filtering is active

- **FilterPolicy**: First-match-wins rule evaluation engine
- Ordered rule evaluation
- Configurable default action
- Enable/disable filtering
- Create from YAML configuration via `from_config()`

- **FilterRule**: Combines matcher, action, and optional constraints
- Path pattern matcher
- Per-operation restrictions
- Per-user restrictions
- Named rules for debugging

**Built-in Matchers**:

| Matcher | Purpose | Example |
|---------|---------|---------|
| `GlobMatcher` | Wildcard patterns | `*.key`, `*.pem` |
| `RegexMatcher` | Full regex support | `(?i)\.exe$` |
| `PrefixMatcher` | Directory tree matching | `/etc/` |
| `ExactMatcher` | Specific file matching | `/etc/shadow` |
| `ComponentMatcher` | Path component matching | `.git`, `.ssh` |
| `ExtensionMatcher` | File extension matching | `exe`, `key` |
| `CombinedMatcher` | OR-combine matchers | Multiple patterns |
| `NotMatcher` | Invert matcher results | Exclude patterns |

**Security Features**:
- `normalize_path()` function for path traversal prevention
- ReDoS protection via regex size limits
- Case-insensitive extension matching

**Usage Example**:
```rust
use bssh::server::filter::{FilterPolicy, FilterResult, Operation};
use bssh::server::filter::pattern::GlobMatcher;
use bssh::server::filter::policy::FilterRule;
use std::path::Path;

// Create policy that blocks *.key files
let policy = FilterPolicy::new()
.with_default(FilterResult::Allow)
.add_rule(FilterRule::new(
Box::new(GlobMatcher::new("*.key").unwrap()),
FilterResult::Deny,
));

// Check if operation is allowed
let result = policy.check(
Path::new("/etc/secret.key"),
Operation::Download,
"alice"
);
assert_eq!(result, FilterResult::Deny);
```

**Configuration** (YAML):
```yaml
filter:
enabled: true
default_action: allow
rules:
- name: block-sensitive-keys
pattern: "*.{key,pem}"
action: deny
operations:
- download
- upload
- name: block-hidden-dirs
path_prefix: "/home"
pattern: ".*"
action: deny
users:
- guest
```

### Audit Logging Module

Comprehensive audit logging infrastructure for the SSH server (`src/server/audit/`):
Expand Down
30 changes: 29 additions & 1 deletion src/server/config/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ pub struct FilterConfig {
#[serde(default)]
pub enabled: bool,

/// Default action when no rules match.
///
/// Default: allow
#[serde(default)]
pub default_action: Option<FilterAction>,

/// Filter rules to apply.
///
/// Rules are evaluated in order. First matching rule determines action.
Expand All @@ -285,25 +291,47 @@ pub struct FilterConfig {
/// A single file transfer filter rule.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct FilterRule {
/// Rule name (for logging and debugging).
///
/// Example: "block-keys"
#[serde(default)]
pub name: Option<String>,

/// Glob pattern to match against file paths.
///
/// Example: "*.exe" matches all executable files
#[serde(default)]
pub pattern: Option<String>,

/// Path prefix to match.
///
/// Example: "/tmp/" matches all files in /tmp
#[serde(default)]
pub path_prefix: Option<String>,

/// Action to take when rule matches.
pub action: FilterAction,

/// Operations this rule applies to.
///
/// If not specified, the rule applies to all operations.
/// Valid values: upload, download, delete, rename, createdir, listdir
#[serde(default)]
pub operations: Option<Vec<String>>,

/// Users this rule applies to.
///
/// If not specified, the rule applies to all users.
#[serde(default)]
pub users: Option<Vec<String>>,
}

/// Action to take when a filter rule matches.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum FilterAction {
/// Allow the file transfer.
#[default]
Allow,

/// Deny the file transfer.
Expand Down
Loading
Loading