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
45 changes: 41 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ https://github.com/m1ngsama/TNT/releases
```sh
tnt # default port 2222
tnt -p 3333 # custom port
tnt -d /var/lib/tnt
PORT=3333 tnt # via env var
```

### Connecting

```sh
ssh -p 2222 localhost
ssh -p 2222 chat.m1ng.space
```

**Anonymous access by default**: Users can connect with ANY username/password (or empty password). No SSH keys required. Perfect for public chat servers.
Expand Down Expand Up @@ -92,17 +93,26 @@ TNT_BIND_ADDR=127.0.0.1 tnt

# Bind to specific IP
TNT_BIND_ADDR=192.168.1.100 tnt

# Store host key and logs in an explicit state directory
TNT_STATE_DIR=/var/lib/tnt tnt

# Show the public SSH endpoint in startup logs
TNT_PUBLIC_HOST=chat.m1ng.space tnt
```

**Rate limiting:**
```sh
# Max total connections (default 64)
TNT_MAX_CONNECTIONS=100 tnt

# Max connections per IP (default 5)
# Max concurrent sessions per IP (default 5)
TNT_MAX_CONN_PER_IP=10 tnt

# Disable rate limiting (testing only)
# Max new connection attempts per IP in 60 seconds (default 10)
TNT_MAX_CONN_RATE_PER_IP=30 tnt

# Disable connection-rate and auth-failure blocking (testing only)
TNT_RATE_LIMIT=0 tnt
```

Expand All @@ -117,11 +127,24 @@ TNT_SSH_LOG_LEVEL=3 tnt
TNT_ACCESS_TOKEN="strong-password-123" \
TNT_BIND_ADDR=0.0.0.0 \
TNT_MAX_CONNECTIONS=200 \
TNT_MAX_CONN_PER_IP=3 \
TNT_MAX_CONN_PER_IP=30 \
TNT_MAX_CONN_RATE_PER_IP=60 \
TNT_SSH_LOG_LEVEL=1 \
tnt -p 2222
```

### SSH Exec Interface

TNT also exposes a small non-interactive SSH surface for scripts:

```sh
ssh -p 2222 chat.m1ng.space health
ssh -p 2222 chat.m1ng.space stats --json
ssh -p 2222 chat.m1ng.space users
ssh -p 2222 chat.m1ng.space "tail -n 20"
ssh -p 2222 operator@chat.m1ng.space post "service notice"
```

## Development

### Building
Expand All @@ -144,6 +167,7 @@ cd tests
./test_basic.sh # basic functionality
./test_security_features.sh # security features
./test_anonymous_access.sh # anonymous access
./test_connection_limits.sh # per-IP concurrency and rate limits
./test_stress.sh # stress test
```

Expand Down Expand Up @@ -202,6 +226,19 @@ sudo cp tnt.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable tnt
sudo systemctl start tnt

# Optional: override defaults without editing the unit
sudo tee /etc/default/tnt >/dev/null <<'EOF'
PORT=2222
TNT_BIND_ADDR=0.0.0.0
TNT_STATE_DIR=/var/lib/tnt
TNT_MAX_CONNECTIONS=200
TNT_MAX_CONN_PER_IP=30
TNT_MAX_CONN_RATE_PER_IP=60
TNT_RATE_LIMIT=1
TNT_SSH_LOG_LEVEL=0
TNT_PUBLIC_HOST=chat.m1ng.space
EOF
```

### Docker
Expand Down
9 changes: 6 additions & 3 deletions docs/ANONYMOUS_ACCESS_SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
**用户体验:**
```bash
# 用户连接(零配置)
ssh -p 2222 your.server.ip
ssh -p 2222 chat.m1ng.space
# 输入任意内容或直接按回车
# 开始聊天!
```
Expand Down Expand Up @@ -143,7 +143,7 @@ ssh -p 2222 your.server.ip
tnt

# 用户端(任何人)
ssh -p 2222 server.ip
ssh -p 2222 chat.m1ng.space
# 输入任何内容作为密码或直接回车
# 选择显示名称(可留空)
# 开始聊天!
Expand All @@ -164,9 +164,12 @@ TNT_ACCESS_TOKEN="secret" tnt
# 限制连接数
TNT_MAX_CONNECTIONS=100 tnt

# 限制每IP连接数
# Limit concurrent sessions per IP
TNT_MAX_CONN_PER_IP=10 tnt

# Limit new connections per IP per 60 seconds
TNT_MAX_CONN_RATE_PER_IP=30 tnt

# 只允许本地访问
TNT_BIND_ADDR=127.0.0.1 tnt
```
Expand Down
19 changes: 19 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 2026-03-10 - SSH Runtime & Unix Interface Update

### Fixed
- moved SSH handshake/auth/channel setup out of the main accept loop
- replaced synchronous room-wide fan-out with room update sequencing and per-client refresh
- switched idle session handling to `ssh_channel_poll_timeout()` plus blocking reads so quiet sessions are not dropped incorrectly
- made `-d/--state-dir` create the runtime state directory automatically

### Added
- SSH exec commands: `help`, `health`, `users`, `stats --json`, `tail`, `post`
- PTY window-change handling for terminal resize
- `TNT_MAX_CONN_RATE_PER_IP` for per-IP connection-rate control
- `tests/test_exec_mode.sh`
- `tests/test_connection_limits.sh`

### Changed
- `TNT_MAX_CONN_PER_IP` now means concurrent sessions per IP
- stress tests now disable rate-based blocking so they exercise concurrency instead of self-throttling

## 2026-01-22 - Security Audit Fixes

Comprehensive security hardening addressing 23 identified vulnerabilities across 6 categories.
Expand Down
31 changes: 28 additions & 3 deletions docs/DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ sudo mv tnt-darwin-arm64 /usr/local/bin/tnt
1. Create user and directory:
```bash
sudo useradd -r -s /bin/false tnt
sudo mkdir -p /var/lib/tnt
sudo chown tnt:tnt /var/lib/tnt
```

2. Install service file:
Expand All @@ -45,7 +43,24 @@ sudo systemctl enable tnt
sudo systemctl start tnt
```

3. Check status:
3. Optional runtime overrides:
```bash
sudo tee /etc/default/tnt >/dev/null <<'EOF'
PORT=2222
TNT_BIND_ADDR=0.0.0.0
TNT_STATE_DIR=/var/lib/tnt
TNT_MAX_CONNECTIONS=200
TNT_MAX_CONN_PER_IP=30
TNT_MAX_CONN_RATE_PER_IP=60
TNT_RATE_LIMIT=1
TNT_SSH_LOG_LEVEL=0
TNT_PUBLIC_HOST=chat.m1ng.space
EOF

sudo systemctl restart tnt
```

4. Check status:
```bash
sudo systemctl status tnt
sudo journalctl -u tnt -f
Expand All @@ -64,6 +79,16 @@ Environment="PORT=3333"
sudo systemctl restart tnt
```

The service uses `StateDirectory=tnt`, so systemd creates `/var/lib/tnt` automatically.
Use `TNT_STATE_DIR` or `tnt -d DIR` when running outside systemd to avoid depending on the current working directory.

Recommended interpretation:

- `TNT_MAX_CONNECTIONS`: global connection ceiling
- `TNT_MAX_CONN_PER_IP`: concurrent sessions allowed from one IP
- `TNT_MAX_CONN_RATE_PER_IP`: new connection attempts allowed per IP per 60 seconds
- `TNT_RATE_LIMIT=0`: disables rate-based blocking and auth-failure IP blocking, but not the explicit capacity limits

## Firewall

```bash
Expand Down
11 changes: 7 additions & 4 deletions docs/EASY_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ tnt # 监听 2222 端口
用户只需要一个SSH客户端即可,无需任何配置:

```bash
ssh -p 2222 your.server.ip
ssh -p 2222 chat.m1ng.space
```

**重要提示**:
Expand Down Expand Up @@ -125,7 +125,7 @@ That's it! Your server is now running.
Users only need an SSH client, no configuration required:

```bash
ssh -p 2222 your.server.ip
ssh -p 2222 chat.m1ng.space
```

**Important**:
Expand Down Expand Up @@ -181,9 +181,12 @@ PORT=3333 tnt
# Limit max connections
TNT_MAX_CONNECTIONS=100 tnt

# Limit connections per IP
# Limit concurrent sessions per IP
TNT_MAX_CONN_PER_IP=10 tnt

# Limit new connections per IP per 60 seconds
TNT_MAX_CONN_RATE_PER_IP=30 tnt

# Bind to localhost only
TNT_BIND_ADDR=127.0.0.1 tnt

Expand Down Expand Up @@ -213,7 +216,7 @@ TNT_ACCESS_TOKEN="your_secret_password" tnt
tnt

# 用户连接(从任何机器)
ssh -p 2222 chat.example.com
ssh -p 2222 chat.m1ng.space
# 输入任意密码或直接回车
# 输入显示名称或留空
# 开始聊天!
Expand Down
9 changes: 5 additions & 4 deletions docs/IMPLEMENTATION_SUMMARY.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@ Branch 4: fix/resource-management (Medium Priority)
Branch 5: fix/auth-protection (Critical Priority)
--------------------------------------------------
✅ Add optional access token (TNT_ACCESS_TOKEN)
✅ IP-based rate limiting (10 conn/IP/60s)
✅ IP-based connection-rate limiting (10 new conn/IP/60s)
✅ Auth failure tracking (5 failures → 5 min block)
✅ Connection counting (total and per-IP)
✅ Configurable limits (TNT_MAX_CONNECTIONS, TNT_MAX_CONN_PER_IP)
✅ Connection counting (total, per-IP active sessions, per-IP recent attempts)
✅ Configurable limits (TNT_MAX_CONNECTIONS, TNT_MAX_CONN_PER_IP, TNT_MAX_CONN_RATE_PER_IP)
✅ Rate limit toggle (TNT_RATE_LIMIT)

Branch 6: fix/concurrency-safety (High Priority)
Expand All @@ -84,7 +84,8 @@ TNT_BIND_ADDR - Configurable bind address (default: 0.0.0.0)
TNT_SSH_LOG_LEVEL - SSH logging verbosity 0-4 (default: 1)
TNT_RATE_LIMIT - Enable/disable rate limiting (default: 1)
TNT_MAX_CONNECTIONS - Global connection limit (default: 64)
TNT_MAX_CONN_PER_IP - Per-IP connection limit (default: 5)
TNT_MAX_CONN_PER_IP - Concurrent sessions allowed per IP (default: 5)
TNT_MAX_CONN_RATE_PER_IP - New connections allowed per IP per 60s (default: 10)

Security Enhancements:
---------------------
Expand Down
12 changes: 10 additions & 2 deletions docs/SECURITY_QUICKREF.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ Connect: `sshpass -p "YourSecretPassword" ssh -p 2222 localhost`
| `TNT_SSH_LOG_LEVEL` | `1` | SSH logging (0-4) | `TNT_SSH_LOG_LEVEL=3` |
| `TNT_RATE_LIMIT` | `1` | Rate limiting on/off | `TNT_RATE_LIMIT=0` |
| `TNT_MAX_CONNECTIONS` | `64` | Total connection limit | `TNT_MAX_CONNECTIONS=100` |
| `TNT_MAX_CONN_PER_IP` | `5` | Per-IP limit | `TNT_MAX_CONN_PER_IP=3` |
| `TNT_MAX_CONN_PER_IP` | `5` | Concurrent sessions per IP | `TNT_MAX_CONN_PER_IP=3` |
| `TNT_MAX_CONN_RATE_PER_IP` | `10` | New connections per IP per 60s | `TNT_MAX_CONN_RATE_PER_IP=20` |

---

Expand Down Expand Up @@ -75,7 +76,8 @@ TNT_MAX_CONN_PER_IP=2 \
## Rate Limiting

### Defaults
- **Connection Rate:** 10 connections per IP per 60 seconds
- **Concurrent Sessions:** 5 per IP
- **Connection Rate:** 10 new connections per IP per 60 seconds
- **Auth Failures:** 5 failures → 5 minute IP block
- **Window:** 60 second rolling window

Expand Down Expand Up @@ -110,6 +112,12 @@ TNT_MAX_CONN_PER_IP=3 ./tnt
```
Each IP can have max 3 concurrent connections.

### Per-IP Rate Limit
```bash
TNT_MAX_CONN_RATE_PER_IP=20 ./tnt
```
Each IP can open at most 20 new connections per 60 seconds before being temporarily blocked.

### Combined Example
```bash
TNT_MAX_CONNECTIONS=100 TNT_MAX_CONN_PER_IP=10 ./tnt
Expand Down
13 changes: 7 additions & 6 deletions docs/TEST_RESULTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
| **Crypto** | RSA Key Size | 4096-bit (upgraded from 2048) | ✅ |
| **Crypto** | Key Permissions | Atomic generation with 0600 perms | ✅ |
| **Auth** | Access Token | Optional password protection | ✅ |
| **Auth** | Rate Limiting | IP-based connection throttling | ✅ |
| **Auth** | Connection Limits | Global and per-IP limits | ✅ |
| **Auth** | Rate Limiting | Per-IP connection-rate throttling | ✅ |
| **Auth** | Connection Limits | Global and per-IP concurrent session limits | ✅ |
| **Input** | Username Validation | Shell metacharacter rejection | ✅ |
| **Input** | Log Sanitization | Pipe/newline replacement | ✅ |
| **Input** | UTF-8 Validation | Overlong encoding prevention | ✅ |
Expand Down Expand Up @@ -114,9 +114,10 @@ TNT_BIND_ADDR=127.0.0.1 ./tnt

### Strict Limits
```bash
TNT_MAX_CONNECTIONS=10 TNT_MAX_CONN_PER_IP=2 ./tnt
TNT_MAX_CONNECTIONS=10 TNT_MAX_CONN_PER_IP=2 TNT_MAX_CONN_RATE_PER_IP=10 ./tnt
# Max 10 total connections
# Max 2 connections per IP address
# Max 2 concurrent sessions per IP address
# Max 10 new connections per IP per 60 seconds
```

### Disabled Rate Limiting (Testing)
Expand Down Expand Up @@ -155,7 +156,7 @@ gcc -fsanitize=thread -g -O1 -c src/chat_room.c

## Known Limitations

1. **Interactive Only:** Server requires PTY sessions (no command execution via SSH)
1. **Exec Surface Is Minimal:** The SSH exec interface is intentionally small and currently focused on operational commands
2. **libssh Deprecations:** Uses deprecated PTY width/height functions (4 warnings)
3. **UTF-8 Unit Test:** Skipped in automated tests (requires manual compilation)

Expand All @@ -165,7 +166,7 @@ gcc -fsanitize=thread -g -O1 -c src/chat_room.c

✅ **All 23 security vulnerabilities fixed and verified**

✅ **100% test pass rate** (10/10 tests)
✅ **100% security-suite pass rate** (12/12 tests)

✅ **Backward compatible** - server remains open by default

Expand Down
4 changes: 4 additions & 0 deletions include/chat_room.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ typedef struct {
int client_capacity;
message_t *messages;
int message_count;
uint64_t update_seq;
} chat_room_t;

/* Global chat room instance */
Expand Down Expand Up @@ -47,4 +48,7 @@ int room_get_message_count(chat_room_t *room);
/* Get online client count */
int room_get_client_count(chat_room_t *room);

/* Get room update sequence */
uint64_t room_get_update_seq(chat_room_t *room);

#endif /* CHAT_ROOM_H */
8 changes: 8 additions & 0 deletions include/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <time.h>
#include <limits.h>
#include <pthread.h>

/* Project Metadata */
Expand All @@ -17,9 +18,11 @@
#define MAX_MESSAGES 100
#define MAX_USERNAME_LEN 64
#define MAX_MESSAGE_LEN 1024
#define MAX_EXEC_COMMAND_LEN 1024
#define MAX_CLIENTS 64
#define LOG_FILE "messages.log"
#define HOST_KEY_FILE "host_key"
#define TNT_DEFAULT_STATE_DIR "."

/* ANSI color codes */
#define ANSI_RESET "\033[0m"
Expand All @@ -43,4 +46,9 @@ typedef enum {
LANG_ZH
} help_lang_t;

/* Runtime helpers */
const char* tnt_state_dir(void);
int tnt_ensure_state_dir(void);
int tnt_state_path(char *buffer, size_t buf_size, const char *filename);

#endif /* COMMON_H */
Loading