Kamis, 01 Mei 2025

SSH-Persetujuan

 Terima kasih, ini setup yang sangat bagus dan rapi. Untuk melengkapinya, kamu hanya perlu integrasikan script pam_exec di /opt/ssh_approval_web.py agar:

  1. Mengirim permintaan ke Redis dengan status "pending".

  2. Menunggu sampai status menjadi "approved" atau "denied".

  3. Jika "approved" → lanjut ke OTP.

  4. Jika "denied" atau timeout → tolak login.


✅ Contoh /opt/ssh_approval_web.py (final)

python
#!/usr/bin/env python3 import os import time import redis import sys username = os.environ.get("PAM_USER") key = f"ssh_approval:{username}" r = redis.Redis(host='localhost', port=6379, db=0) # Step 1: Tandai permintaan sebagai pending r.setex(key, 300, b"pending") # expired 5 menit print("Menunggu persetujuan atasan...") # Step 2: Tunggu approval for _ in range(60): # max 60 detik (1 menit) time.sleep(2) status = r.get(key) if status is None: print("Permintaan expired atau dibatalkan.") sys.exit(1) status = status.decode() if status == "approved": sys.exit(0) # lanjut ke OTP elif status == "denied": print("Permintaan ditolak.") sys.exit(1) # Timeout print("Permintaan tidak disetujui tepat waktu.") sys.exit(1)

πŸ” Pastikan File:

bash
chmod +x /opt/ssh_approval_web.py

πŸ›‚ PAM /etc/pam.d/sshd final:

pam
# 1. Verifikasi password user auth required pam_unix.so # 2. Tunggu approval atasan auth requisite pam_exec.so expose_authtok /opt/ssh_approval_web.py # 3. OTP dari PrivacyIDEA auth required pam_privacyidea.so url=http://privacy.yogya.com:5000 realm=realm-itba debug

Setelah ini, workflow login user menjadi:

Masukkan password → menunggu persetujuan atasan → jika disetujui, baru muncul prompt OTP.

Ingin saya bantu buat script testing cepat (simulasi login SSH dengan approval dan OTP)?

===============================================================

Berikut adalah tampilan web interface lengkap (frontend HTML + Flask backend) untuk portal persetujuan atasan. Portal ini terdiri dari:

  1. Halaman utama: daftar permintaan akses SSH yang tertunda

  2. Halaman detail: konfirmasi dan tombol approve/tolak


πŸ“ Struktur Direktori

swift
/opt/ssh-approval-web/ ├── app.py ├── templates/ ├── index.html └── approve.html ├── static/ └── style.css

1. πŸ”§ app.py (Flask backend)

python
from flask import Flask, render_template, request, redirect, url_for import redis app = Flask(__name__) r = redis.Redis(host='localhost', port=6379, db=0) @app.route('/') def index(): keys = [k.decode() for k in r.keys("ssh_approval:*") if b"pending" in r.get(k)] users = sorted({k.split(":")[1] for k in keys}) return render_template("index.html", users=users) @app.route('/approve/<username>', methods=['GET', 'POST']) def approve(username): key = f"ssh_approval:{username}" if request.method == "POST": action = request.form['action'] if action in ['approved', 'denied']: r.setex(key, 300, action.encode()) return redirect(url_for('index')) return render_template("approve.html", username=username) if __name__ == '__main__': app.run(host='0.0.0.0', port=8000)

2. πŸ–Ό templates/index.html (Halaman Utama)

html
<!DOCTYPE html> <html> <head> <title>Portal Persetujuan SSH</title> <link rel="stylesheet" href="/static/style.css"> </head> <body> <div class="container"> <h1>Permintaan SSH Aktif</h1> {% if users %} <ul class="list"> {% for user in users %} <li> <span>User <strong>{{ user }}</strong></span> <a class="button" href="/approve/{{ user }}">Tinjau</a> </li> {% endfor %} </ul> {% else %} <p>Tidak ada permintaan yang menunggu.</p> {% endif %} </div> </body> </html>

3. πŸ–Ό templates/approve.html (Halaman Approve)

html
<!DOCTYPE html> <html> <head> <title>Setujui Akses SSH</title> <link rel="stylesheet" href="/static/style.css"> </head> <body> <div class="container"> <h2>Persetujuan Akses SSH</h2> <p>User <strong>{{ username }}</strong> meminta akses SSH.</p> <form method="post"> <button class="button approve" name="action" value="approved">Setujui</button> <button class="button deny" name="action" value="denied">Tolak</button> </form> <a href="/" class="link">← Kembali</a> </div> </body> </html>

4. 🎨 static/style.css (Tampilan CSS)

css
body { font-family: Arial, sans-serif; background-color: #f5f7fa; color: #333; margin: 0; padding: 20px; } .container { max-width: 600px; margin: auto; background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 10px rgba(0,0,0,0.1); } h1, h2 { color: #2c3e50; } .list { list-style: none; padding: 0; } .list li { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #ddd; } .button { background-color: #3498db; color: white; border: none; padding: 8px 14px; text-decoration: none; border-radius: 6px; cursor: pointer; } .button.approve { background-color: #2ecc71; } .button.deny { background-color: #e74c3c; } .button:hover { opacity: 0.9; } .link { display: inline-block; margin-top: 15px; color: #2980b9; text-decoration: none; }

πŸ›  Jalankan Server Flask

bash
cd /opt/ssh-approval-web export FLASK_APP=app.py flask run --host=0.0.0.0 --port=8000

Akses portal di:
πŸ“Ž http://<ip-server>:8000



Tidak ada komentar:

Posting Komentar