mirror of
https://github.com/usmannasir/cyberpanel.git
synced 2025-11-13 08:46:09 +01:00
ssh logs
This commit is contained in:
@@ -566,58 +566,52 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- Fixed SSH Activity Modal -->
|
<!-- Fixed SSH Activity Modal -->
|
||||||
<div ng-if="showSSHActivityModal" class="modal-backdrop" style="
|
<div ng-if="showSSHActivityModal" class="modal-backdrop" style="position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.5); z-index: 10000; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box;" ng-click="closeModalOnBackdrop($event)">
|
||||||
position: fixed;
|
<div class="modal-content" style="max-width: 90vw; max-height: 90vh; width: 600px; background: #fff; border-radius: 16px; box-shadow: 0 8px 40px rgba(44,62,80,0.18); padding: 32px 28px 24px 28px; position: relative; overflow-y: auto;">
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
background: rgba(0,0,0,0.5);
|
|
||||||
z-index: 10000;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 20px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
" ng-click="closeModalOnBackdrop($event)">
|
|
||||||
<div class="modal-content" style="
|
|
||||||
max-width: 90vw;
|
|
||||||
max-height: 90vh;
|
|
||||||
width: 600px;
|
|
||||||
background: #fff;
|
|
||||||
border-radius: 16px;
|
|
||||||
box-shadow: 0 8px 40px rgba(44,62,80,0.18);
|
|
||||||
padding: 32px 28px 24px 28px;
|
|
||||||
position: relative;
|
|
||||||
overflow-y: auto;
|
|
||||||
">
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px;">
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px;">
|
||||||
<div style="font-size: 1.2rem; font-weight: 800; color: #4c5fad; letter-spacing: 0.5px;">
|
<div style="font-size: 1.2rem; font-weight: 800; color: #4c5fad; letter-spacing: 0.5px;">
|
||||||
User Activity: <span style="color: #222;">{$ sshActivityUser $}</span>
|
User Activity: <span style="color: #222;">{$ sshActivityUser $}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-outline-primary"
|
<button class="btn btn-sm btn-outline-primary" style="border-radius: 12px; font-size: 1.1rem; padding: 2px 12px; cursor: pointer;" ng-click="closeSSHActivityModal()">×</button>
|
||||||
style="border-radius: 12px; font-size: 1.1rem; padding: 2px 12px; cursor: pointer;"
|
|
||||||
ng-click="closeSSHActivityModal()">
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div ng-if="loadingSSHActivity" style="text-align: center; color: #888; padding: 20px 0;">Loading activity...</div>
|
||||||
<div ng-if="loadingSSHActivity" style="text-align: center; color: #888; padding: 20px 0;">
|
<div ng-if="errorSSHActivity" style="color: #e53935; padding: 10px 0;">{$ errorSSHActivity $}</div>
|
||||||
Loading activity...
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="errorSSHActivity" style="color: #e53935; padding: 10px 0;">
|
|
||||||
{$ errorSSHActivity $}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div ng-if="!loadingSSHActivity && !errorSSHActivity">
|
<div ng-if="!loadingSSHActivity && !errorSSHActivity">
|
||||||
<div ng-if="sshActivity.processes.length === 0 && sshActivity.w.length === 0"
|
<!-- GeoIP Details -->
|
||||||
style="text-align: center; color: #888; padding: 20px 0;">
|
<div ng-if="sshActivity.geoip && sshActivity.geoip.ip" style="background: #f8f9fa; border-radius: 10px; margin-bottom: 18px; padding: 14px 18px;">
|
||||||
No active processes found for this user.
|
<div style="font-weight: 700; color: #4c5fad; margin-bottom: 6px;">Login Location & ISP</div>
|
||||||
|
<div style="font-size: 1.01rem; color: #333;">
|
||||||
|
<span ng-if="sshActivity.geoip.city">{$ sshActivity.geoip.city $}, </span>
|
||||||
|
<span ng-if="sshActivity.geoip.region">{$ sshActivity.geoip.region $}, </span>
|
||||||
|
<span ng-if="sshActivity.geoip.country">{$ sshActivity.geoip.country $}</span><br/>
|
||||||
|
<span ng-if="sshActivity.geoip.isp">ISP: {$ sshActivity.geoip.isp $}</span>
|
||||||
|
<span ng-if="sshActivity.geoip.org">, Org: {$ sshActivity.geoip.org $}</span><br/>
|
||||||
|
<span ng-if="sshActivity.geoip.as">ASN: {$ sshActivity.geoip.as $}</span><br/>
|
||||||
|
<span style="color: #888; font-size: 0.97rem;">IP: {$ sshActivity.geoip.ip $}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Disk Usage -->
|
||||||
<div ng-if="sshActivity.processes.length > 0">
|
<div ng-if="sshActivity.disk_usage" style="background: #f8f9fa; border-radius: 10px; margin-bottom: 18px; padding: 14px 18px;">
|
||||||
<div style="font-weight: 700; color: #4c5fad; margin-bottom: 8px;">Processes:</div>
|
<div style="font-weight: 700; color: #4c5fad; margin-bottom: 6px;">Disk Usage</div>
|
||||||
|
<div style="font-size: 1.01rem; color: #333;">Home Directory: <b>{$ sshActivity.disk_usage $}</b></div>
|
||||||
|
</div>
|
||||||
|
<!-- Shell History -->
|
||||||
|
<div ng-if="sshActivity.shell_history && sshActivity.shell_history.length > 0" style="background: #f8f9fa; border-radius: 10px; margin-bottom: 18px; padding: 14px 18px;">
|
||||||
|
<div style="font-weight: 700; color: #4c5fad; margin-bottom: 6px;">Shell History (last 10 commands)</div>
|
||||||
|
<pre style="background: #fff; border-radius: 8px; padding: 10px 12px; color: #333; font-size: 0.97rem; white-space: pre-wrap; word-wrap: break-word; margin-bottom: 0;">{$ sshActivity.shell_history.join('\n') $}</pre>
|
||||||
|
</div>
|
||||||
|
<!-- Process Tree -->
|
||||||
|
<div ng-if="sshActivity.process_tree && sshActivity.process_tree.length > 0" style="background: #f8f9fa; border-radius: 10px; margin-bottom: 18px; padding: 14px 18px;">
|
||||||
|
<div style="font-weight: 700; color: #4c5fad; margin-bottom: 6px;">Process Tree</div>
|
||||||
|
<ul style="list-style: none; padding-left: 0;">
|
||||||
|
<li ng-repeat="proc in sshActivity.process_tree" style="font-family: 'Menlo', monospace; color: #333; padding-left: {$ proc.level * 18 $}px;">
|
||||||
|
<span style="color: #888;">[{$ proc.pid $}]</span> {$ proc.cmd $} <span ng-if="proc.cwd" style="color: #4c5fad; font-size: 0.95em;">(cwd: {$ proc.cwd $})</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<!-- Processes Table -->
|
||||||
|
<div ng-if="sshActivity.processes && sshActivity.processes.length > 0" style="background: #f8f9fa; border-radius: 10px; margin-bottom: 18px; padding: 14px 18px;">
|
||||||
|
<div style="font-weight: 700; color: #4c5fad; margin-bottom: 6px;">Processes</div>
|
||||||
<table class="table table-sm" style="font-size: 0.98rem;">
|
<table class="table table-sm" style="font-size: 0.98rem;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr style="background: #e3eafc;">
|
<tr style="background: #e3eafc;">
|
||||||
@@ -625,6 +619,7 @@
|
|||||||
<th>TTY</th>
|
<th>TTY</th>
|
||||||
<th>Time</th>
|
<th>Time</th>
|
||||||
<th>Command</th>
|
<th>Command</th>
|
||||||
|
<th>CWD</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -633,14 +628,15 @@
|
|||||||
<td style="font-family: 'Menlo', monospace;">{$ proc.tty $}</td>
|
<td style="font-family: 'Menlo', monospace;">{$ proc.tty $}</td>
|
||||||
<td style="font-family: 'Menlo', monospace;">{$ proc.time $}</td>
|
<td style="font-family: 'Menlo', monospace;">{$ proc.time $}</td>
|
||||||
<td style="font-family: 'Menlo', monospace;">{$ proc.cmd $}</td>
|
<td style="font-family: 'Menlo', monospace;">{$ proc.cmd $}</td>
|
||||||
|
<td style="font-family: 'Menlo', monospace; color: #4c5fad;">{$ proc.cwd $}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- w output -->
|
||||||
<div ng-if="sshActivity.w.length > 0">
|
<div ng-if="sshActivity.w.length > 0" style="background: #f8f9fa; border-radius: 10px; margin-bottom: 0; padding: 14px 18px;">
|
||||||
<div style="font-weight: 700; color: #4c5fad; margin: 12px 0 8px 0;">w output:</div>
|
<div style="font-weight: 700; color: #4c5fad; margin-bottom: 6px;">w output</div>
|
||||||
<pre style="background: #f8f9fa; border-radius: 8px; padding: 10px 12px; color: #333; font-size: 0.97rem; white-space: pre-wrap; word-wrap: break-word;">{$ sshActivity.w.join('\n') $}</pre>
|
<pre style="background: #fff; border-radius: 8px; padding: 10px 12px; color: #333; font-size: 0.97rem; white-space: pre-wrap; word-wrap: break-word; margin-bottom: 0;">{$ sshActivity.w.join('\n') $}</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ from websiteFunctions.models import Websites, WPSites
|
|||||||
from databases.models import Databases
|
from databases.models import Databases
|
||||||
from mailServer.models import EUsers
|
from mailServer.models import EUsers
|
||||||
from django.views.decorators.http import require_GET, require_POST
|
from django.views.decorators.http import require_GET, require_POST
|
||||||
|
import pwd
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
|
|
||||||
@@ -629,7 +630,7 @@ def getRecentSSHLogs(request):
|
|||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@require_POST
|
@require_POST
|
||||||
def getSSHUserActivity(request):
|
def getSSHUserActivity(request):
|
||||||
import json
|
import json, os
|
||||||
from plogical.processUtilities import ProcessUtilities
|
from plogical.processUtilities import ProcessUtilities
|
||||||
try:
|
try:
|
||||||
user_id = request.session.get('userID')
|
user_id = request.session.get('userID')
|
||||||
@@ -641,28 +642,95 @@ def getSSHUserActivity(request):
|
|||||||
data = json.loads(request.body.decode('utf-8'))
|
data = json.loads(request.body.decode('utf-8'))
|
||||||
user = data.get('user')
|
user = data.get('user')
|
||||||
tty = data.get('tty')
|
tty = data.get('tty')
|
||||||
|
login_ip = data.get('ip', '')
|
||||||
if not user:
|
if not user:
|
||||||
return HttpResponse(json.dumps({'error': 'Missing user'}), content_type='application/json', status=400)
|
return HttpResponse(json.dumps({'error': 'Missing user'}), content_type='application/json', status=400)
|
||||||
# Get processes for the user
|
# Get processes for the user
|
||||||
ps_cmd = f"ps -u {user} -o pid,tty,time,cmd --no-headers"
|
ps_cmd = f"ps -u {user} -o pid,ppid,tty,time,cmd --no-headers"
|
||||||
try:
|
try:
|
||||||
ps_output = ProcessUtilities.outputExecutioner(ps_cmd)
|
ps_output = ProcessUtilities.outputExecutioner(ps_cmd)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
ps_output = ''
|
ps_output = ''
|
||||||
processes = []
|
processes = []
|
||||||
|
pid_map = {}
|
||||||
if ps_output:
|
if ps_output:
|
||||||
for line in ps_output.strip().split('\n'):
|
for line in ps_output.strip().split('\n'):
|
||||||
parts = line.split(None, 3)
|
parts = line.split(None, 4)
|
||||||
if len(parts) == 4:
|
if len(parts) == 5:
|
||||||
pid, tty_val, time_val, cmd = parts
|
pid, ppid, tty_val, time_val, cmd = parts
|
||||||
if tty and tty not in tty_val:
|
if tty and tty not in tty_val:
|
||||||
continue
|
continue
|
||||||
processes.append({
|
# Try to get CWD
|
||||||
|
cwd = ''
|
||||||
|
try:
|
||||||
|
cwd_path = f"/proc/{pid}/cwd"
|
||||||
|
if os.path.islink(cwd_path):
|
||||||
|
cwd = os.readlink(cwd_path)
|
||||||
|
except Exception:
|
||||||
|
cwd = ''
|
||||||
|
proc = {
|
||||||
'pid': pid,
|
'pid': pid,
|
||||||
|
'ppid': ppid,
|
||||||
'tty': tty_val,
|
'tty': tty_val,
|
||||||
'time': time_val,
|
'time': time_val,
|
||||||
'cmd': cmd
|
'cmd': cmd,
|
||||||
})
|
'cwd': cwd
|
||||||
|
}
|
||||||
|
processes.append(proc)
|
||||||
|
pid_map[pid] = proc
|
||||||
|
# Build process tree
|
||||||
|
tree = []
|
||||||
|
def build_tree(parent_pid, level=0):
|
||||||
|
for proc in processes:
|
||||||
|
if proc['ppid'] == parent_pid:
|
||||||
|
proc_copy = proc.copy()
|
||||||
|
proc_copy['level'] = level
|
||||||
|
tree.append(proc_copy)
|
||||||
|
build_tree(proc['pid'], level+1)
|
||||||
|
build_tree('1', 0) # Start from init
|
||||||
|
# Find main shell process for history
|
||||||
|
shell_history = []
|
||||||
|
try:
|
||||||
|
shell_home = pwd.getpwnam(user).pw_dir
|
||||||
|
except Exception:
|
||||||
|
shell_home = f"/home/{user}"
|
||||||
|
history_file = ''
|
||||||
|
for shell in ['.bash_history', '.zsh_history']:
|
||||||
|
path = os.path.join(shell_home, shell)
|
||||||
|
if os.path.exists(path):
|
||||||
|
history_file = path
|
||||||
|
break
|
||||||
|
if history_file:
|
||||||
|
try:
|
||||||
|
with open(history_file, 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
shell_history = [l.strip() for l in lines[-10:]]
|
||||||
|
except Exception:
|
||||||
|
shell_history = []
|
||||||
|
# Disk usage
|
||||||
|
disk_usage = ''
|
||||||
|
try:
|
||||||
|
du_out = ProcessUtilities.outputExecutioner(f'du -sh {shell_home}')
|
||||||
|
disk_usage = du_out.strip().split('\t')[0] if du_out else ''
|
||||||
|
except Exception:
|
||||||
|
disk_usage = ''
|
||||||
|
# GeoIP details
|
||||||
|
geoip = {}
|
||||||
|
if login_ip and login_ip not in ['127.0.0.1', 'localhost']:
|
||||||
|
try:
|
||||||
|
geo = requests.get(f'http://ip-api.com/json/{login_ip}?fields=status,message,country,regionName,city,isp,org,as,query', timeout=2).json()
|
||||||
|
if geo.get('status') == 'success':
|
||||||
|
geoip = {
|
||||||
|
'country': geo.get('country'),
|
||||||
|
'region': geo.get('regionName'),
|
||||||
|
'city': geo.get('city'),
|
||||||
|
'isp': geo.get('isp'),
|
||||||
|
'org': geo.get('org'),
|
||||||
|
'as': geo.get('as'),
|
||||||
|
'ip': geo.get('query')
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
geoip = {}
|
||||||
# Optionally, get 'w' output for more info
|
# Optionally, get 'w' output for more info
|
||||||
w_cmd = f"w -h {user}"
|
w_cmd = f"w -h {user}"
|
||||||
try:
|
try:
|
||||||
@@ -673,6 +741,13 @@ def getSSHUserActivity(request):
|
|||||||
if w_output:
|
if w_output:
|
||||||
for line in w_output.strip().split('\n'):
|
for line in w_output.strip().split('\n'):
|
||||||
w_lines.append(line)
|
w_lines.append(line)
|
||||||
return HttpResponse(json.dumps({'processes': processes, 'w': w_lines}), content_type='application/json')
|
return HttpResponse(json.dumps({
|
||||||
|
'processes': processes,
|
||||||
|
'process_tree': tree,
|
||||||
|
'shell_history': shell_history,
|
||||||
|
'disk_usage': disk_usage,
|
||||||
|
'geoip': geoip,
|
||||||
|
'w': w_lines
|
||||||
|
}), content_type='application/json')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500)
|
return HttpResponse(json.dumps({'error': str(e)}), content_type='application/json', status=500)
|
||||||
|
|||||||
Reference in New Issue
Block a user