Making the C version works...

parent 87e45f53
......@@ -3,6 +3,9 @@
# Parse command line arguments
BUILD_DEBIAN=false
BUILD_DEBIAN_ONLY=false
BUILD_SERVER_ONLY=false
BUILD_NO_SERVER=false
BUILD_WSSSHTOOLS_ONLY=false
while [[ $# -gt 0 ]]; do
case $1 in
--debian)
......@@ -14,9 +17,32 @@ while [[ $# -gt 0 ]]; do
BUILD_DEBIAN=true
shift
;;
--server-only)
BUILD_SERVER_ONLY=true
shift
;;
--no-server)
BUILD_NO_SERVER=true
shift
;;
--wssshtools-only)
BUILD_WSSSHTOOLS_ONLY=true
shift
;;
--help|-h)
echo "Usage: $0 [options]"
echo "Options:"
echo " --debian Build Debian package"
echo " --debian-only Build only Debian package (skip binaries)"
echo " --server-only Build only the server (wssshd)"
echo " --no-server Skip building the server (wssshd)"
echo " --wssshtools-only Build only the C tools (wssshtools)"
echo " --help, -h Show this help"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [--debian] [--debian-only]"
echo "Usage: $0 [--debian] [--debian-only] [--server-only] [--no-server] [--wssshtools-only]"
exit 1
;;
esac
......@@ -41,8 +67,8 @@ pip3 install pyinstaller
# Create dist directory if not exists
mkdir -p dist
# Skip Python/C building if --debian-only is specified
if [ "$BUILD_DEBIAN_ONLY" = false ]; then
# Skip Python/C building if --debian-only or --wssshtools-only is specified
if [ "$BUILD_DEBIAN_ONLY" = false ] && [ "$BUILD_WSSSHTOOLS_ONLY" = false ]; then
# Generate SSL certificates if they don't exist
if [ ! -f "cert.pem" ] || [ ! -f "key.pem" ]; then
echo "Generating SSL certificates..."
......@@ -76,17 +102,25 @@ if [ "$BUILD_DEBIAN_ONLY" = false ]; then
# Build Python binaries
echo "Building Python binaries..."
# Build wssshd (server) binary with certificates and web assets
pyinstaller --onefile --distpath dist --add-data "cert.pem:." --add-data "key.pem:." --add-data "templates:templates" --add-data "static:static" --runtime-tmpdir /tmp --clean wssshd.py
# Build wssshd (server) binary unless --no-server is specified
if [ "$BUILD_NO_SERVER" = false ]; then
pyinstaller --onefile --distpath dist --add-data "cert.pem:." --add-data "key.pem:." --add-data "templates:templates" --add-data "static:static" --runtime-tmpdir /tmp --clean wssshd.py
fi
# Build wssshc (client) binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wssshc.py
# Build client binaries
if [ "$BUILD_SERVER_ONLY" = false ]; then
# Build wssshc (client) binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wssshc.py
# Build wsssh binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsssh.py
# Build wsssh and wsscp binaries unless --no-server is specified
if [ "$BUILD_NO_SERVER" = false ]; then
# Build wsssh binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsssh.py
# Build wsscp binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsscp.py
# Build wsscp binary
pyinstaller --onefile --distpath dist --runtime-tmpdir /tmp --clean wsscp.py
fi
fi
# Build C version if wssshtools directory exists
if [ -d "wssshtools" ]; then
......@@ -94,7 +128,15 @@ if [ "$BUILD_DEBIAN_ONLY" = false ]; then
cd wssshtools
if [ -f "configure.sh" ]; then
./configure.sh
make
if [ "$BUILD_SERVER_ONLY" = true ]; then
# Build only wssshc (client) for server-only mode
make wssshc
elif [ "$BUILD_NO_SERVER" = true ]; then
# Build only wssshc for no-server mode
make wssshc
else
make
fi
cd ..
else
echo "Warning: configure.sh not found in wssshtools/"
......@@ -145,18 +187,38 @@ if [ "$BUILD_DEBIAN_ONLY" = true ]; then
echo "Package available in dist/ directory:"
echo "- dist/wsssh-tools*.deb (Debian package)"
fi
else
echo "Build complete. Binaries are in dist/ directory:"
echo "- dist/wssshd (server with web interface)"
echo "- dist/wssshc (client)"
echo "- dist/wsssh (SSH wrapper)"
echo "- dist/wsscp (SCP wrapper)"
elif [ "$BUILD_WSSSHTOOLS_ONLY" = true ]; then
echo "C tools build complete."
if [ -d "wssshtools" ] && [ -f "wssshtools/wssshc" ]; then
echo "C tools available in wssshtools/ directory:"
echo "- wssshtools/wssshc (C client)"
echo "- wssshtools/wsssh (C SSH wrapper)"
echo "- wssshtools/wsscp (C SCP wrapper)"
fi
else
echo "Build complete. Binaries are in dist/ directory:"
if [ "$BUILD_NO_SERVER" = false ]; then
echo "- dist/wssshd (server with web interface)"
fi
if [ "$BUILD_SERVER_ONLY" = false ]; then
echo "- dist/wssshc (client)"
if [ "$BUILD_NO_SERVER" = false ]; then
echo "- dist/wsssh (SSH wrapper)"
echo "- dist/wsscp (SCP wrapper)"
fi
fi
if [ -d "wssshtools" ] && [ -f "wssshtools/wssshc" ]; then
if [ "$BUILD_SERVER_ONLY" = true ] || [ "$BUILD_NO_SERVER" = true ]; then
echo "- wssshtools/wssshc (C client)"
else
echo "- wssshtools/wssshc (C client)"
echo "- wssshtools/wsssh (C SSH wrapper)"
echo "- wssshtools/wsscp (C SCP wrapper)"
fi
fi
if [ "$BUILD_DEBIAN" = true ] && ls dist/wsssh-tools*.deb >/dev/null 2>&1; then
echo "- dist/wsssh-tools*.deb (Debian package)"
......
logos/banner-800x200.png

52.7 KB | W: | H:

logos/banner-800x200.png

52.7 KB | W: | H:

logos/banner-800x200.png
logos/banner-800x200.png
logos/banner-800x200.png
logos/banner-800x200.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-128.png

23.2 KB | W: | H:

logos/icon-128.png

23.2 KB | W: | H:

logos/icon-128.png
logos/icon-128.png
logos/icon-128.png
logos/icon-128.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-16.png

2.34 KB | W: | H:

logos/icon-16.png

2.34 KB | W: | H:

logos/icon-16.png
logos/icon-16.png
logos/icon-16.png
logos/icon-16.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-256.png

84.6 KB | W: | H:

logos/icon-256.png

84.6 KB | W: | H:

logos/icon-256.png
logos/icon-256.png
logos/icon-256.png
logos/icon-256.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-32.png

3.24 KB | W: | H:

logos/icon-32.png

3.24 KB | W: | H:

logos/icon-32.png
logos/icon-32.png
logos/icon-32.png
logos/icon-32.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-48.png

4.91 KB | W: | H:

logos/icon-48.png

4.91 KB | W: | H:

logos/icon-48.png
logos/icon-48.png
logos/icon-48.png
logos/icon-48.png
  • 2-up
  • Swipe
  • Onion skin
logos/icon-64.png

7.23 KB | W: | H:

logos/icon-64.png

7.23 KB | W: | H:

logos/icon-64.png
logos/icon-64.png
logos/icon-64.png
logos/icon-64.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-128.png

23.2 KB | W: | H:

logos/logo-128.png

23.2 KB | W: | H:

logos/logo-128.png
logos/logo-128.png
logos/logo-128.png
logos/logo-128.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-256.png

84.6 KB | W: | H:

logos/logo-256.png

84.6 KB | W: | H:

logos/logo-256.png
logos/logo-256.png
logos/logo-256.png
logos/logo-256.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-512.png

307 KB | W: | H:

logos/logo-512.png

307 KB | W: | H:

logos/logo-512.png
logos/logo-512.png
logos/logo-512.png
logos/logo-512.png
  • 2-up
  • Swipe
  • Onion skin
logos/logo-64.png

7.23 KB | W: | H:

logos/logo-64.png

7.23 KB | W: | H:

logos/logo-64.png
logos/logo-64.png
logos/logo-64.png
logos/logo-64.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -100,8 +100,11 @@
<div class="col-md-4 text-center mb-3">
<h6><i class="fab fa-bitcoin text-warning"></i> Bitcoin</h6>
<div class="input-group">
<input type="text" class="form-control form-control-sm" value="bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx" readonly>
<div class="mb-2">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=bitcoin:bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx" alt="BTC QR Code" class="img-fluid rounded">
</div>
<div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm font-monospace" value="bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx" readonly style="font-size: 0.75rem;">
<button class="btn btn-outline-secondary btn-sm" type="button" onclick="copyToClipboard('bc1q3zlkpu95amtcltsk85y0eacyzzk29v68tgc5hx')">
<i class="fas fa-copy"></i>
</button>
......@@ -110,8 +113,11 @@
<div class="col-md-4 text-center mb-3">
<h6><i class="fab fa-ethereum text-secondary"></i> Ethereum</h6>
<div class="input-group">
<input type="text" class="form-control form-control-sm" value="0xdA6dAb526515b5cb556d20269207D43fcc760E51" readonly>
<div class="mb-2">
<img src="https://api.qrserver.com/v1/create-qr-code/?size=120x120&data=ethereum:0xdA6dAb526515b5cb556d20269207D43fcc760E51" alt="ETH QR Code" class="img-fluid rounded">
</div>
<div class="input-group input-group-sm">
<input type="text" class="form-control form-control-sm font-monospace" value="0xdA6dAb526515b5cb556d20269207D43fcc760E51" readonly style="font-size: 0.75rem;">
<button class="btn btn-outline-secondary btn-sm" type="button" onclick="copyToClipboard('0xdA6dAb526515b5cb556d20269207D43fcc760E51')">
<i class="fas fa-copy"></i>
</button>
......
......@@ -66,11 +66,41 @@
</h3>
</div>
<div class="card-body">
<p class="mb-1"><strong>WebSocket Port:</strong> {{ config.WEBSOCKET_PORT or 'N/A' }}</p>
<p class="mb-1"><strong>Domain:</strong> {{ config.DOMAIN or 'N/A' }}</p>
<p class="mb-0"><strong>Connected Clients:</strong> {{ clients|length }}</p>
<p class="mb-1"><strong>WebSocket Port:</strong> <span id="websocket-port">{{ websocket_port or 'N/A' }}</span></p>
<p class="mb-1"><strong>Domain:</strong> <span id="domain">{{ domain or 'N/A' }}</span></p>
<p class="mb-0"><strong>Connected Clients:</strong> <span id="client-count">{{ clients|length }}</span></p>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
let currentClients = {{ clients|tojson }};
function updateClients() {
fetch('/api/clients')
.then(response => response.json())
.then(data => {
// Update client count
document.getElementById('client-count').textContent = data.count;
// Check if client list changed
if (JSON.stringify(data.clients.sort()) !== JSON.stringify(currentClients.sort())) {
// Reload the page to show updated client list
location.reload();
}
})
.catch(error => {
console.log('Error fetching client data:', error);
});
}
// Update every 5 seconds
setInterval(updateClients, 5000);
// Initial update after 1 second
setTimeout(updateClients, 1000);
</script>
{% endblock %}
\ No newline at end of file
......@@ -10,9 +10,14 @@
<h3 class="card-title mb-0">
<i class="fas fa-users"></i> User Management
</h3>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addUserModal">
<i class="fas fa-plus"></i> Add User
</button>
<div>
<a href="{{ url_for('index') }}" class="btn btn-outline-secondary btn-sm me-2">
<i class="fas fa-home"></i> Back to Home
</a>
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addUserModal">
<i class="fas fa-plus"></i> Add User
</button>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
......
......@@ -31,7 +31,7 @@ import uuid
debug = False
async def handle_tunnel(ws, local_port, request_id):
async def handle_tunnel(ws, local_port, request_id, ready_event=None):
"""Handle tunnel data forwarding"""
try:
# Open local TCP listener
......@@ -42,6 +42,10 @@ async def handle_tunnel(ws, local_port, request_id):
if debug: print(f"[DEBUG] Listening on localhost:{local_port}")
# Signal that server is ready
if ready_event:
ready_event.set()
async with server:
await server.serve_forever()
except Exception as e:
......@@ -110,11 +114,15 @@ async def run_scp(server_ip, server_port, client_id, local_port, scp_args):
if data.get('type') == 'tunnel_ack':
if debug: print(f"[DEBUG] Tunnel request acknowledged: {request_id}")
# Start tunnel handler
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id))
# Start tunnel handler (opens listening port)
ready_event = asyncio.Event()
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id, ready_event))
# Wait for tunnel to be ready (listening port opened)
await ready_event.wait()
# Launch SCP with modified arguments
scp_cmd = ['scp'] + scp_args + ['-P', str(local_port)]
scp_cmd = ['scp'] + scp_args
if debug: print(f"[DEBUG] Launching: {' '.join(scp_cmd)}")
# Run SCP process
......@@ -171,8 +179,8 @@ def main():
global debug
debug = args.debug
# Automatically prepend 'scp' to the remaining arguments
modified_args = ['scp'] + remaining
# Don't prepend 'scp' - we'll handle it in execvp
modified_args = remaining
# Find host arguments and -P port option in modified args (SCP can have multiple host:paths)
hosts = []
......@@ -224,7 +232,13 @@ def main():
# Modify modified_args to use localhost:local_port
final_args = []
for arg in modified_args:
i = 0
while i < len(modified_args):
arg = modified_args[i]
if arg == '-P' and i + 1 < len(modified_args):
# Skip -P and the port value as it's for wssshd, not SCP
i += 2
continue
if ':' in arg:
host_part, path_part = arg.split(':', 1)
if '@' in host_part:
......@@ -240,9 +254,10 @@ def main():
final_args.append(arg)
else:
final_args.append(arg)
i += 1
# Add port argument for local tunnel
final_args.extend(['-P', str(local_port)])
# Add port argument for local tunnel at the beginning
final_args = ['-P', str(local_port)] + final_args
if debug: print(f"[DEBUG] Final SCP args: {final_args}")
......
......@@ -31,7 +31,7 @@ import uuid
debug = False
async def handle_tunnel(ws, local_port, request_id):
async def handle_tunnel(ws, local_port, request_id, ready_event=None):
"""Handle tunnel data forwarding"""
try:
# Open local TCP listener
......@@ -42,6 +42,10 @@ async def handle_tunnel(ws, local_port, request_id):
if debug: print(f"[DEBUG] Listening on localhost:{local_port}")
# Signal that server is ready
if ready_event:
ready_event.set()
async with server:
await server.serve_forever()
except Exception as e:
......@@ -110,11 +114,15 @@ async def run_ssh(server_ip, server_port, client_id, local_port, ssh_args):
if data.get('type') == 'tunnel_ack':
if debug: print(f"[DEBUG] Tunnel request acknowledged: {request_id}")
# Start tunnel handler
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id))
# Start tunnel handler (opens listening port)
ready_event = asyncio.Event()
tunnel_task = asyncio.create_task(handle_tunnel(websocket, local_port, request_id, ready_event))
# Wait for tunnel to be ready (listening port opened)
await ready_event.wait()
# Launch SSH with modified arguments
ssh_cmd = ['ssh'] + ssh_args + ['-p', str(local_port), 'localhost']
ssh_cmd = ['ssh'] + ssh_args
if debug: print(f"[DEBUG] Launching: {' '.join(ssh_cmd)}")
# Run SSH process
......@@ -171,8 +179,8 @@ def main():
global debug
debug = args.debug
# Automatically prepend 'ssh' to the remaining arguments
modified_args = ['ssh'] + remaining
# Don't prepend 'ssh' - we'll handle it in execvp
modified_args = remaining
# Find the host argument and -p port option in modified args
host = None
......@@ -190,8 +198,8 @@ def main():
# Handle user@host format
host = arg.split('@', 1)[1]
break
elif not arg.startswith('-') and i == 1: # Skip 'ssh' at index 0
# First non-option argument after 'ssh' might be host
elif not arg.startswith('-') and i == 0:
# First non-option argument might be host
host = arg
break
......
......@@ -24,9 +24,33 @@ import asyncio
import ssl
import websockets
import json
import socket
debug = False
# Active tunnels: request_id -> {'reader': tcp_reader, 'writer': tcp_writer}
active_tunnels = {}
async def forward_tcp_to_ws(tcp_reader, websocket, request_id):
try:
while True:
data = await tcp_reader.read(1024)
if not data:
break
if debug: print(f"[DEBUG] Sending tunnel_response, data len: {len(data)}, data: {data[:50].decode('utf-8', errors='replace') if data else 'empty'}, hex: {data.hex()[:100]}...")
await websocket.send(json.dumps({
"type": "tunnel_response",
"request_id": request_id,
"data": data.hex()
}))
except Exception as e:
if debug: print(f"[DEBUG] TCP to WS error: {e}")
finally:
# Cleanup tunnel
if request_id in active_tunnels:
del active_tunnels[request_id]
if debug: print(f"[DEBUG] Tunnel {request_id} closed")
async def connect_to_server(server_ip, port, client_id, password, interval):
uri = f"wss://{server_ip}:{port}"
ssl_context = ssl.create_default_context()
......@@ -53,14 +77,35 @@ async def connect_to_server(server_ip, port, client_id, password, interval):
if debug: print(f"[DEBUG] WebSocket message: {message[:100]}...")
data = json.loads(message)
if data.get('type') == 'tunnel_request':
print(f"Tunnel request received: {data['request_id']}")
# Client would handle tunnel setup here
request_id = data['request_id']
if debug: print(f"[DEBUG] Tunnel request received: {request_id}")
try:
# Connect to local SSH server
tcp_reader, tcp_writer = await asyncio.open_connection('localhost', 22)
active_tunnels[request_id] = {'reader': tcp_reader, 'writer': tcp_writer}
# Start forwarding from TCP to WS
asyncio.create_task(forward_tcp_to_ws(tcp_reader, websocket, request_id))
if debug: print(f"[DEBUG] Connected to local SSH server for tunnel {request_id}")
except Exception as e:
if debug: print(f"[DEBUG] Tunnel setup error: {e}")
elif data.get('type') == 'tunnel_data':
print(f"Tunnel data received: {len(data.get('data', ''))} bytes")
# Client would forward data here
request_id = data['request_id']
if request_id in active_tunnels:
# Forward data to local SSH
tunnel_data = bytes.fromhex(data['data'])
if debug: print(f"[DEBUG] Received tunnel_data, hex len: {len(data['data'])}, hex: {data['data'][:100]}..., decoded: {tunnel_data[:50].decode('utf-8', errors='replace') if tunnel_data else 'empty'}")
active_tunnels[request_id]['writer'].write(tunnel_data)
await active_tunnels[request_id]['writer'].drain()
elif data.get('type') == 'tunnel_close':
print(f"Tunnel closed: {data['request_id']}")
# Client would close tunnel here
request_id = data['request_id']
if request_id in active_tunnels:
# Close tunnel
active_tunnels[request_id]['writer'].close()
await active_tunnels[request_id]['writer'].wait_closed()
del active_tunnels[request_id]
if debug: print(f"[DEBUG] Tunnel {request_id} closed")
except Exception as e:
print(f"Connection failed: {e}, retrying in {interval} seconds")
await asyncio.sleep(interval)
......
......@@ -34,6 +34,8 @@ from werkzeug.security import generate_password_hash, check_password_hash
# Client registry: id -> websocket
clients = {}
# Active tunnels: request_id -> {'client_ws': ws, 'wsssh_ws': ws, 'client_id': id}
active_tunnels = {}
debug = False
server_password = None
......@@ -72,7 +74,10 @@ with app.app_context():
@app.route('/')
@login_required
def index():
return render_template('index.html', clients=list(clients.keys()))
return render_template('index.html',
clients=list(clients.keys()),
websocket_port=args.port,
domain=args.domain)
@app.route('/login', methods=['GET', 'POST'])
def login():
......@@ -155,6 +160,14 @@ def terminal(client_id):
return redirect(url_for('index'))
return render_template('terminal.html', client_id=client_id)
@app.route('/api/clients')
@login_required
def get_clients():
return jsonify({
'clients': list(clients.keys()),
'count': len(clients)
})
async def handle_websocket(websocket, path=None):
try:
async for message in websocket:
......@@ -172,46 +185,80 @@ async def handle_websocket(websocket, path=None):
await websocket.send(json.dumps({"type": "registration_error", "error": "Invalid password"}))
elif data.get('type') == 'tunnel_request':
client_id = data['client_id']
request_id = data['request_id']
if client_id in clients:
# Store tunnel mapping
active_tunnels[request_id] = {
'client_ws': clients[client_id],
'wsssh_ws': websocket,
'client_id': client_id
}
# Forward tunnel request to client
await clients[client_id].send(json.dumps({
"type": "tunnel_request",
"request_id": data['request_id']
"request_id": request_id
}))
await websocket.send(json.dumps({
"type": "tunnel_ack",
"request_id": data['request_id']
"request_id": request_id
}))
else:
await websocket.send(json.dumps({
"type": "tunnel_error",
"request_id": data['request_id'],
"request_id": request_id,
"error": "Client not registered"
}))
elif data.get('type') == 'tunnel_data':
# Forward tunnel data to appropriate client
client_id = data.get('client_id')
if client_id and client_id in clients:
await clients[client_id].send(json.dumps({
# Forward tunnel data using active tunnel mapping
request_id = data['request_id']
if request_id in active_tunnels:
tunnel = active_tunnels[request_id]
# Forward to client
await tunnel['client_ws'].send(json.dumps({
"type": "tunnel_data",
"request_id": request_id,
"data": data['data']
}))
elif data.get('type') == 'tunnel_response':
# Forward tunnel response from client to wsssh
request_id = data['request_id']
if request_id in active_tunnels:
tunnel = active_tunnels[request_id]
await tunnel['wsssh_ws'].send(json.dumps({
"type": "tunnel_data",
"request_id": data['request_id'],
"request_id": request_id,
"data": data['data']
}))
elif data.get('type') == 'tunnel_close':
client_id = data.get('client_id')
if client_id and client_id in clients:
await clients[client_id].send(json.dumps({
request_id = data['request_id']
if request_id in active_tunnels:
tunnel = active_tunnels[request_id]
# Forward close to client
await tunnel['client_ws'].send(json.dumps({
"type": "tunnel_close",
"request_id": data['request_id']
"request_id": request_id
}))
# Clean up tunnel
del active_tunnels[request_id]
except websockets.exceptions.ConnectionClosed:
# Remove from registry
# Remove from registry and clean up tunnels
disconnected_client = None
for cid, ws in clients.items():
if ws == websocket:
disconnected_client = cid
del clients[cid]
print(f"Client {cid} disconnected")
break
# Clean up active tunnels for this client
if disconnected_client:
tunnels_to_remove = []
for request_id, tunnel in active_tunnels.items():
if tunnel['client_id'] == disconnected_client:
tunnels_to_remove.append(request_id)
for request_id in tunnels_to_remove:
del active_tunnels[request_id]
async def main():
parser = argparse.ArgumentParser(description='WebSocket SSH Daemon (wssshd)')
parser.add_argument('--host', required=True, help='WebSocket server host')
......@@ -220,6 +267,7 @@ async def main():
parser.add_argument('--password', required=True, help='Registration password')
parser.add_argument('--web-host', help='Web interface host (optional)')
parser.add_argument('--web-port', type=int, help='Web interface port (optional)')
parser.add_argument('--web-https', action='store_true', help='Enable HTTPS for web interface')
parser.add_argument('--debug', action='store_true', help='Enable debug output')
args = parser.parse_args()
......@@ -249,12 +297,29 @@ async def main():
# Start web interface if specified
if args.web_host and args.web_port:
# Handle HTTPS setup
ssl_context = None
protocol = "http"
if args.web_https:
protocol = "https"
# Use config directory for web certificates
web_cert_path = os.path.join(config_dir, 'web-cert.pem')
web_key_path = os.path.join(config_dir, 'web-key.pem')
# Generate self-signed certificate if it doesn't exist
if not os.path.exists(web_cert_path) or not os.path.exists(web_key_path):
print("Generating self-signed certificate for web interface...")
os.system(f'openssl req -x509 -newkey rsa:4096 -keyout {web_key_path} -out {web_cert_path} -days 365 -nodes -subj "/C=US/ST=State/L=City/O=Organization/CN=localhost"')
ssl_context = (web_cert_path, web_key_path)
def run_flask():
app.run(host=args.web_host, port=args.web_port, debug=False, use_reloader=False)
app.run(host=args.web_host, port=args.web_port, debug=debug, use_reloader=False, ssl_context=ssl_context)
flask_thread = threading.Thread(target=run_flask, daemon=True)
flask_thread.start()
print(f"Web interface available at http://{args.web_host}:{args.web_port}")
print(f"Web interface available at {protocol}://{args.web_host}:{args.web_port}")
await ws_server.wait_closed()
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment