Skip to content

Commit 038df52

Browse files
authored
Merge pull request #19 from usnavy13/dev
Dev
2 parents 841b2cb + 8d6c5d8 commit 038df52

16 files changed

Lines changed: 967 additions & 82 deletions

.env.example

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ DOCKER_NETWORK_MODE=none
5858
DOCKER_READ_ONLY=true
5959

6060
# Resource Limits - Execution
61-
MAX_EXECUTION_TIME=30
61+
MAX_EXECUTION_TIME=120
6262
MAX_MEMORY_MB=512
6363
MAX_CPUS=1
6464
MAX_CPU_QUOTA=50000 #Deprecated
65-
MAX_PROCESSES=32
65+
MAX_PIDS=512
6666
MAX_OPEN_FILES=1024
6767

6868
# Resource Limits - Files
@@ -84,7 +84,7 @@ SESSION_ID_LENGTH=32
8484

8585
# MinIO Orphan Cleanup (optional)
8686
# Enable periodic pruning of MinIO objects older than TTL with missing Redis sessions
87-
ENABLE_ORPHAN_MINIO_CLEANUP=false
87+
ENABLE_ORPHAN_MINIO_CLEANUP=true
8888

8989
# Container Pool Configuration
9090
CONTAINER_POOL_ENABLED=true
@@ -174,4 +174,4 @@ HEALTH_CHECK_TIMEOUT=5
174174
# Development Configuration
175175
ENABLE_CORS=false
176176
# CORS_ORIGINS=http://localhost:3000,http://localhost:8080
177-
ENABLE_DOCS=true
177+
ENABLE_DOCS=false

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ coverage.xml
5858
.hypothesis/
5959
.pytest_cache/
6060
cover/
61-
61+
baseline_complexity.json
6262
# Translations
6363
*.mo
6464
*.pot

docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ services:
4040
- ${SSL_CERTS_PATH:-./ssl}:/app/ssl
4141
- ./dashboard:/app/dashboard
4242
- ./src:/app/src
43+
- ./docker:/app/docker:ro # Seccomp profile for container security
4344
depends_on:
4445
- redis
4546
- minio

docker/seccomp-sandbox.json

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
{
2+
"defaultAction": "SCMP_ACT_ALLOW",
3+
"defaultErrnoRet": 1,
4+
"architectures": [
5+
"SCMP_ARCH_X86_64",
6+
"SCMP_ARCH_X86",
7+
"SCMP_ARCH_X32",
8+
"SCMP_ARCH_AARCH64",
9+
"SCMP_ARCH_ARM"
10+
],
11+
"syscalls": [
12+
{
13+
"names": [
14+
"ptrace"
15+
],
16+
"action": "SCMP_ACT_ERRNO",
17+
"errnoRet": 1,
18+
"comment": "Block process tracing - caused container hang with PTRACE_TRACEME"
19+
},
20+
{
21+
"names": [
22+
"process_vm_readv",
23+
"process_vm_writev"
24+
],
25+
"action": "SCMP_ACT_ERRNO",
26+
"errnoRet": 1,
27+
"comment": "Block cross-process memory access"
28+
},
29+
{
30+
"names": [
31+
"personality"
32+
],
33+
"action": "SCMP_ACT_ERRNO",
34+
"errnoRet": 1,
35+
"comment": "Block ASLR disabling"
36+
},
37+
{
38+
"names": [
39+
"mount",
40+
"umount",
41+
"umount2"
42+
],
43+
"action": "SCMP_ACT_ERRNO",
44+
"errnoRet": 1,
45+
"comment": "Block filesystem manipulation (defense-in-depth, also blocked by CAP_SYS_ADMIN)"
46+
},
47+
{
48+
"names": [
49+
"pivot_root",
50+
"chroot"
51+
],
52+
"action": "SCMP_ACT_ERRNO",
53+
"errnoRet": 1,
54+
"comment": "Block container escape vectors (defense-in-depth, also blocked by CAP_SYS_CHROOT)"
55+
},
56+
{
57+
"names": [
58+
"reboot",
59+
"kexec_load",
60+
"kexec_file_load"
61+
],
62+
"action": "SCMP_ACT_ERRNO",
63+
"errnoRet": 1,
64+
"comment": "Block system disruption (defense-in-depth, also blocked by CAP_SYS_BOOT)"
65+
},
66+
{
67+
"names": [
68+
"init_module",
69+
"finit_module",
70+
"delete_module"
71+
],
72+
"action": "SCMP_ACT_ERRNO",
73+
"errnoRet": 1,
74+
"comment": "Block kernel module manipulation (defense-in-depth, also blocked by CAP_SYS_MODULE)"
75+
},
76+
{
77+
"names": [
78+
"acct"
79+
],
80+
"action": "SCMP_ACT_ERRNO",
81+
"errnoRet": 1,
82+
"comment": "Block process accounting manipulation (defense-in-depth, also blocked by CAP_SYS_PACCT)"
83+
},
84+
{
85+
"names": [
86+
"swapon",
87+
"swapoff"
88+
],
89+
"action": "SCMP_ACT_ERRNO",
90+
"errnoRet": 1,
91+
"comment": "Block swap manipulation (defense-in-depth, also blocked by CAP_SYS_ADMIN)"
92+
},
93+
{
94+
"names": [
95+
"sethostname",
96+
"setdomainname"
97+
],
98+
"action": "SCMP_ACT_ERRNO",
99+
"errnoRet": 1,
100+
"comment": "Block host identity changes (defense-in-depth, also blocked by CAP_SYS_ADMIN)"
101+
},
102+
{
103+
"names": [
104+
"clock_settime",
105+
"clock_adjtime",
106+
"settimeofday",
107+
"adjtimex"
108+
],
109+
"action": "SCMP_ACT_ERRNO",
110+
"errnoRet": 1,
111+
"comment": "Block time manipulation (defense-in-depth, also blocked by CAP_SYS_TIME)"
112+
},
113+
{
114+
"names": [
115+
"iopl",
116+
"ioperm"
117+
],
118+
"action": "SCMP_ACT_ERRNO",
119+
"errnoRet": 1,
120+
"comment": "Block direct I/O port access (defense-in-depth, also blocked by CAP_SYS_RAWIO)"
121+
},
122+
{
123+
"names": [
124+
"create_module",
125+
"get_kernel_syms",
126+
"query_module"
127+
],
128+
"action": "SCMP_ACT_ERRNO",
129+
"errnoRet": 1,
130+
"comment": "Block legacy kernel module syscalls"
131+
},
132+
{
133+
"names": [
134+
"unshare",
135+
"setns"
136+
],
137+
"action": "SCMP_ACT_ERRNO",
138+
"errnoRet": 1,
139+
"comment": "Block namespace manipulation"
140+
},
141+
{
142+
"names": [
143+
"userfaultfd"
144+
],
145+
"action": "SCMP_ACT_ERRNO",
146+
"errnoRet": 1,
147+
"comment": "Block userfaultfd - can be used in exploits"
148+
},
149+
{
150+
"names": [
151+
"bpf"
152+
],
153+
"action": "SCMP_ACT_ERRNO",
154+
"errnoRet": 1,
155+
"comment": "Block BPF - powerful and often exploited"
156+
},
157+
{
158+
"names": [
159+
"perf_event_open"
160+
],
161+
"action": "SCMP_ACT_ERRNO",
162+
"errnoRet": 1,
163+
"comment": "Block perf events - can leak kernel info"
164+
},
165+
{
166+
"names": [
167+
"add_key",
168+
"keyctl",
169+
"request_key"
170+
],
171+
"action": "SCMP_ACT_ERRNO",
172+
"errnoRet": 1,
173+
"comment": "Block kernel keyring manipulation"
174+
}
175+
]
176+
}

docker/security-profiles.json

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,8 @@
1010
"tmpfs": {
1111
"/tmp": "rw,noexec,nosuid,size=100m"
1212
},
13+
"pidsLimit": 512,
1314
"ulimits": [
14-
{
15-
"name": "nproc",
16-
"soft": 32,
17-
"hard": 32
18-
},
1915
{
2016
"name": "nofile",
2117
"soft": 1024,
@@ -34,20 +30,16 @@
3430
"restrictedProfile": {
3531
"name": "code-interpreter-restricted",
3632
"description": "Highly restricted security profile for untrusted code",
37-
"securityOpt": ["no-new-privileges:true", "seccomp:unconfined"],
33+
"securityOpt": ["no-new-privileges:true"],
3834
"capDrop": ["ALL"],
3935
"capAdd": [],
4036
"readOnly": true,
4137
"networkMode": "none",
4238
"tmpfs": {
4339
"/tmp": "rw,noexec,nosuid,size=50m"
4440
},
41+
"pidsLimit": 256,
4542
"ulimits": [
46-
{
47-
"name": "nproc",
48-
"soft": 16,
49-
"hard": 16
50-
},
5143
{
5244
"name": "nofile",
5345
"soft": 512,
@@ -67,36 +59,15 @@
6759
"languageProfiles": {
6860
"python": {
6961
"inherits": "defaultProfile",
70-
"description": "Security profile optimized for Python execution",
71-
"ulimits": [
72-
{
73-
"name": "nproc",
74-
"soft": 64,
75-
"hard": 64
76-
}
77-
]
62+
"description": "Security profile optimized for Python execution"
7863
},
7964
"nodejs": {
8065
"inherits": "defaultProfile",
81-
"description": "Security profile optimized for Node.js execution",
82-
"ulimits": [
83-
{
84-
"name": "nproc",
85-
"soft": 48,
86-
"hard": 48
87-
}
88-
]
66+
"description": "Security profile optimized for Node.js execution"
8967
},
9068
"java": {
9169
"inherits": "defaultProfile",
92-
"description": "Security profile optimized for Java execution",
93-
"ulimits": [
94-
{
95-
"name": "nproc",
96-
"soft": 128,
97-
"hard": 128
98-
}
99-
]
70+
"description": "Security profile optimized for Java execution"
10071
},
10172
"go": {
10273
"inherits": "defaultProfile",

docs/CONFIGURATION.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,13 @@ Docker is used for secure code execution in containers.
184184

185185
#### Execution Limits
186186

187-
| Variable | Default | Description |
188-
| -------------------- | ------- | ------------------------------------- |
189-
| `MAX_EXECUTION_TIME` | `30` | Maximum code execution time (seconds) |
190-
| `MAX_MEMORY_MB` | `512` | Maximum memory per execution (MB) |
191-
| `MAX_CPU_QUOTA` | `50000` | CPU quota (100000 = 1 CPU) |
192-
| `MAX_PROCESSES` | `32` | Maximum processes per container |
193-
| `MAX_OPEN_FILES` | `1024` | Maximum open files per container |
187+
| Variable | Default | Description |
188+
| -------------------- | ------- | ---------------------------------------------------------------- |
189+
| `MAX_EXECUTION_TIME` | `30` | Maximum code execution time (seconds) |
190+
| `MAX_MEMORY_MB` | `512` | Maximum memory per execution (MB) |
191+
| `MAX_CPU_QUOTA` | `50000` | CPU quota (100000 = 1 CPU) |
192+
| `MAX_PIDS` | `512` | Per-container process limit (cgroup pids_limit, prevents fork bombs) |
193+
| `MAX_OPEN_FILES` | `1024` | Maximum open files per container |
194194

195195
#### File Limits
196196

src/api/files.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# Local application imports
1616
from ..config import settings
1717
from ..dependencies import FileServiceDep
18+
from ..services.execution.output import OutputProcessor
1819
from ..utils.id_generator import generate_session_id
1920

2021

@@ -119,13 +120,13 @@ async def upload_file(
119120
content_type=file.content_type,
120121
)
121122

122-
# Get file info for complete details
123-
file_info = await file_service.get_file_info(session_id, file_id)
123+
# Sanitize filename to match what will be used in container
124+
sanitized_name = OutputProcessor.sanitize_filename(file.filename)
124125

125126
uploaded_files.append(
126127
{
127128
"id": file_id,
128-
"name": file.filename,
129+
"name": sanitized_name,
129130
"session_id": session_id,
130131
"content": None, # LibreChat doesn't return content in upload response
131132
"size": len(content),
@@ -207,10 +208,12 @@ async def list_files(
207208
# Return simple file information
208209
simple_files = []
209210
for file_info in files:
211+
# Return sanitized filename to match container
212+
sanitized_name = OutputProcessor.sanitize_filename(file_info.filename)
210213
simple_files.append(
211214
{
212215
"id": file_info.file_id,
213-
"name": file_info.filename,
216+
"name": sanitized_name,
214217
"path": file_info.path,
215218
}
216219
)
@@ -219,9 +222,11 @@ async def list_files(
219222
# Return full file details - LibreChat format
220223
detailed_files = []
221224
for file_info in files:
225+
# Return sanitized filename to match container
226+
sanitized_name = OutputProcessor.sanitize_filename(file_info.filename)
222227
detailed_files.append(
223228
{
224-
"name": file_info.filename,
229+
"name": sanitized_name,
225230
"id": file_info.file_id,
226231
"session_id": session_id,
227232
"content": None, # Not returned in list

0 commit comments

Comments
 (0)