-
Notifications
You must be signed in to change notification settings - Fork 163
Expand file tree
/
Copy pathsmtp-tunnel-adduser
More file actions
406 lines (335 loc) · 13.9 KB
/
Copy pathsmtp-tunnel-adduser
File metadata and controls
406 lines (335 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
#!/usr/bin/env python3
"""
SMTP Tunnel - Add User Script
Creates a new user and generates a client package (ZIP file).
Version: 1.3.0
"""
import argparse
import os
import sys
import secrets
import zipfile
import tempfile
import shutil
# Add current directory to path for imports
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from common import load_users, save_users, load_config, UserConfig
def generate_secret() -> str:
"""Generate a secure random secret."""
return secrets.token_urlsafe(32)
def create_client_config(server_host: str, server_port: int, username: str, secret: str) -> str:
"""Generate client config.yaml content."""
return f"""# SMTP Tunnel Client Configuration
# Generated for user: {username}
client:
# Server connection
server_host: "{server_host}"
server_port: {server_port}
# Authentication
username: "{username}"
secret: "{secret}"
# Local SOCKS5 proxy
socks_port: 1080
socks_host: "127.0.0.1"
# CA certificate for server verification
ca_cert: "ca.crt"
"""
def create_client_package(
username: str,
secret: str,
server_host: str,
server_port: int,
base_dir: str,
output_dir: str
) -> str:
"""
Create a ZIP package with everything the client needs.
Returns:
Path to the created ZIP file
"""
# Files to include from base directory
client_files = ['client.py', 'common.py', 'requirements.txt']
# Check for ca.crt
ca_cert_path = os.path.join(base_dir, 'ca.crt')
has_ca_cert = os.path.exists(ca_cert_path)
# Create temporary directory for package
with tempfile.TemporaryDirectory() as tmpdir:
pkg_dir = os.path.join(tmpdir, username)
os.makedirs(pkg_dir)
# Copy client files
for filename in client_files:
src = os.path.join(base_dir, filename)
if os.path.exists(src):
shutil.copy(src, pkg_dir)
else:
print(f"Warning: {filename} not found, skipping")
# Copy CA certificate if exists
if has_ca_cert:
shutil.copy(ca_cert_path, pkg_dir)
else:
print("Warning: ca.crt not found - client will not be able to verify server")
# Generate client config
config_content = create_client_config(server_host, server_port, username, secret)
config_path = os.path.join(pkg_dir, 'config.yaml')
with open(config_path, 'w') as f:
f.write(config_content)
# Create README for the user
readme_content = f"""# SMTP Tunnel Client - {username}
## Quick Start
1. Install dependencies:
pip install -r requirements.txt
2. Run the client:
python client.py
3. Configure your browser/apps to use SOCKS5 proxy:
Host: 127.0.0.1
Port: 1080
## Files
- start.bat - Windows launcher (double-click to run)
- start.sh - Linux/Mac launcher (run with ./start.sh)
- client.py - The tunnel client
- common.py - Shared library
- config.yaml - Your configuration (pre-configured)
- ca.crt - Server certificate for verification
- requirements.txt - Python dependencies
## Test Connection
curl -x socks5h://127.0.0.1:1080 https://ifconfig.me
## Easy Start
Windows: Double-click start.bat
Linux/Mac: Run ./start.sh
"""
readme_path = os.path.join(pkg_dir, 'README.txt')
with open(readme_path, 'w') as f:
f.write(readme_content)
# Create Windows launcher script (start.bat)
bat_content = f'''@echo off
chcp 65001 >nul 2>&1
title SMTP Tunnel - {username}
echo.
echo ╔═══════════════════════════════════════════════════════════╗
echo ║ ║
echo ║ ███████╗███╗ ███╗████████╗██████╗ ║
echo ║ ██╔════╝████╗ ████║╚══██╔══╝██╔══██╗ ║
echo ║ ███████╗██╔████╔██║ ██║ ██████╔╝ ║
echo ║ ╚════██║██║╚██╔╝██║ ██║ ██╔═══╝ ║
echo ║ ███████║██║ ╚═╝ ██║ ██║ ██║ ║
echo ║ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ║
echo ║ ║
echo ║ SMTP Tunnel Proxy Client ║
echo ║ User: {username:50}║
echo ║ ║
echo ╚═══════════════════════════════════════════════════════════╝
echo.
:: Check for Python
where python >nul 2>&1
if %errorlevel% neq 0 (
where python3 >nul 2>&1
if %errorlevel% neq 0 (
echo [ERROR] Python not found!
echo.
echo Please install Python 3.8+ from:
echo https://www.python.org/downloads/
echo.
echo Make sure to check "Add Python to PATH" during installation.
echo.
pause
exit /b 1
)
set PYTHON=python3
) else (
set PYTHON=python
)
echo [INFO] Found Python: %PYTHON%
:: Check/install requirements
echo [INFO] Checking dependencies...
%PYTHON% -c "import yaml" >nul 2>&1
if %errorlevel% neq 0 (
echo [INFO] Installing dependencies...
%PYTHON% -m pip install -r requirements.txt --quiet
if %errorlevel% neq 0 (
echo [ERROR] Failed to install dependencies
pause
exit /b 1
)
echo [INFO] Dependencies installed
)
echo.
echo [INFO] Starting SMTP Tunnel...
echo [INFO] SOCKS5 proxy will be available at 127.0.0.1:1080
echo.
echo Press Ctrl+C to stop
echo ─────────────────────────────────────────────────────────────
echo.
%PYTHON% client.py
echo.
echo Connection closed.
echo Press any key to exit...
pause >nul
'''
bat_path = os.path.join(pkg_dir, 'start.bat')
with open(bat_path, 'w', newline='\r\n') as f:
f.write(bat_content)
# Create Linux/Mac launcher script (start.sh)
sh_content = f'''#!/bin/bash
#
# SMTP Tunnel Client Launcher
# User: {username}
#
# Colors
RED='\\033[0;31m'
GREEN='\\033[0;32m'
YELLOW='\\033[1;33m'
BLUE='\\033[0;34m'
CYAN='\\033[0;36m'
NC='\\033[0m'
clear
echo ""
echo -e "${{CYAN}}"
echo " ╔═══════════════════════════════════════════════════════════╗"
echo " ║ ║"
echo " ║ ███████╗███╗ ███╗████████╗██████╗ ║"
echo " ║ ██╔════╝████╗ ████║╚══██╔══╝██╔══██╗ ║"
echo " ║ ███████╗██╔████╔██║ ██║ ██████╔╝ ║"
echo " ║ ╚════██║██║╚██╔╝██║ ██║ ██╔═══╝ ║"
echo " ║ ███████║██║ ╚═╝ ██║ ██║ ██║ ║"
echo " ║ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ║"
echo " ║ ║"
echo " ║ SMTP Tunnel Proxy Client ║"
echo " ║ User: {username:50}║"
echo " ║ ║"
echo " ╚═══════════════════════════════════════════════════════════╝"
echo -e "${{NC}}"
echo ""
# Find Python
if command -v python3 &> /dev/null; then
PYTHON=python3
elif command -v python &> /dev/null; then
PYTHON=python
else
echo -e "${{RED}}[ERROR]${{NC}} Python not found!"
echo ""
echo "Please install Python 3.8+:"
echo " Ubuntu/Debian: sudo apt install python3 python3-pip"
echo " macOS: brew install python3"
echo " Or download from: https://www.python.org/downloads/"
echo ""
exit 1
fi
echo -e "${{GREEN}}[INFO]${{NC}} Found Python: $PYTHON"
# Check/install requirements
echo -e "${{GREEN}}[INFO]${{NC}} Checking dependencies..."
if ! $PYTHON -c "import yaml" &> /dev/null; then
echo -e "${{YELLOW}}[INFO]${{NC}} Installing dependencies..."
$PYTHON -m pip install -r requirements.txt --quiet
if [ $? -ne 0 ]; then
echo -e "${{RED}}[ERROR]${{NC}} Failed to install dependencies"
echo "Try running: $PYTHON -m pip install -r requirements.txt"
exit 1
fi
echo -e "${{GREEN}}[INFO]${{NC}} Dependencies installed"
fi
echo ""
echo -e "${{GREEN}}[INFO]${{NC}} Starting SMTP Tunnel..."
echo -e "${{GREEN}}[INFO]${{NC}} SOCKS5 proxy will be available at 127.0.0.1:1080"
echo ""
echo -e "Press ${{YELLOW}}Ctrl+C${{NC}} to stop"
echo "─────────────────────────────────────────────────────────────"
echo ""
$PYTHON client.py
echo ""
echo -e "${{YELLOW}}Connection closed.${{NC}}"
'''
sh_path = os.path.join(pkg_dir, 'start.sh')
with open(sh_path, 'w') as f:
f.write(sh_content)
os.chmod(sh_path, 0o755)
# Create ZIP file
zip_filename = f"{username}.zip"
zip_path = os.path.abspath(os.path.join(output_dir, zip_filename))
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(pkg_dir):
for file in files:
file_path = os.path.join(root, file)
arcname = os.path.relpath(file_path, tmpdir)
zipf.write(file_path, arcname)
return zip_path
def main():
parser = argparse.ArgumentParser(
description='Add a new user to SMTP Tunnel',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s alice # Add user 'alice' with auto-generated secret
%(prog)s bob --secret mysecret # Add user 'bob' with specific secret
%(prog)s carol --whitelist 1.2.3.4 --whitelist 10.0.0.0/8
%(prog)s dave --no-logging # Add user without logging
"""
)
parser.add_argument('username', help='Username to add')
parser.add_argument('--secret', '-s', default=None, help='Secret (auto-generated if not provided)')
parser.add_argument('--whitelist', '-w', action='append', default=[], help='IP whitelist (can specify multiple)')
parser.add_argument('--no-logging', action='store_true', help='Disable logging for this user')
parser.add_argument('--users-file', '-u', default='/etc/smtp-tunnel/users.yaml', help='Users file (default: /etc/smtp-tunnel/users.yaml)')
parser.add_argument('--config', '-c', default='/etc/smtp-tunnel/config.yaml', help='Server config file (default: /etc/smtp-tunnel/config.yaml)')
parser.add_argument('--output-dir', '-o', default='.', help='Output directory for ZIP file (default: current)')
parser.add_argument('--no-package', action='store_true', help='Do not generate client ZIP package')
args = parser.parse_args()
# Get base directory (where this script is located)
base_dir = os.path.dirname(os.path.abspath(__file__))
# Load existing users
users_file = args.users_file
if not os.path.isabs(users_file):
users_file = os.path.join(base_dir, users_file)
users = load_users(users_file)
# Check if user already exists
if args.username in users:
print(f"Error: User '{args.username}' already exists")
return 1
# Generate secret if not provided
secret = args.secret or generate_secret()
# Create user config
user = UserConfig(
username=args.username,
secret=secret,
whitelist=args.whitelist if args.whitelist else [],
logging=not args.no_logging
)
# Add user
users[args.username] = user
# Save users file
save_users(users_file, users)
print(f"User '{args.username}' added to {users_file}")
# Generate client package
if not args.no_package:
# Load server config to get hostname and port
config_file = args.config
if not os.path.isabs(config_file):
config_file = os.path.join(base_dir, config_file)
try:
config_data = load_config(config_file)
server_conf = config_data.get('server', {})
server_host = server_conf.get('hostname', 'localhost')
server_port = server_conf.get('port', 587)
except FileNotFoundError:
print(f"Warning: Config file {config_file} not found, using defaults")
server_host = 'localhost'
server_port = 587
output_dir = args.output_dir
if not os.path.isabs(output_dir):
output_dir = os.path.join(os.getcwd(), output_dir)
zip_path = create_client_package(
username=args.username,
secret=secret,
server_host=server_host,
server_port=server_port,
base_dir=base_dir,
output_dir=output_dir
)
print(f"Client package created: {zip_path}")
print()
print("Send this ZIP file to the user. They just need to:")
print(" 1. Extract the ZIP")
print(" 2. pip install -r requirements.txt")
print(" 3. python client.py")
return 0
if __name__ == '__main__':
sys.exit(main())