This commit is contained in:
Will Nilges 2022-07-10 18:06:03 -04:00
parent 1134939d3c
commit 98d1408693
4 changed files with 54 additions and 13 deletions

View file

@ -17,6 +17,13 @@ It is available to house members at [proxstar.csh.rit.edu](https://proxstar.csh.
1. [Fork](https://help.github.com/en/articles/fork-a-repo) this repository 1. [Fork](https://help.github.com/en/articles/fork-a-repo) this repository
- Optionally create a new [git branch](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell) if your change is more than a small tweak (`git checkout -b BRANCH-NAME-HERE`) - Optionally create a new [git branch](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell) if your change is more than a small tweak (`git checkout -b BRANCH-NAME-HERE`)
2. Create a Virtualenv to do your work in.
```
mkdir venv
python3.8 -m venv venv
source venv/bin/activate
```
3. Make your changes locally, commit, and push to your fork 3. Make your changes locally, commit, and push to your fork
- If you want to test locally, you should copy `config.py` to `config_local.py`, and talk to an RTP about filling in secrets. - If you want to test locally, you should copy `config.py` to `config_local.py`, and talk to an RTP about filling in secrets.
- Lint and format your local changes with `pylint proxstar` and `black proxstar` - Lint and format your local changes with `pylint proxstar` and `black proxstar`

View file

@ -13,7 +13,17 @@ from redis import Redis
from rq_scheduler import Scheduler from rq_scheduler import Scheduler
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from flask import Flask, render_template, request, redirect, session, abort, url_for, jsonify, Response from flask import (
Flask,
render_template,
request,
redirect,
session,
abort,
url_for,
jsonify,
Response,
)
import sentry_sdk import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.flask import FlaskIntegration
from sentry_sdk.integrations.rq import RqIntegration from sentry_sdk.integrations.rq import RqIntegration
@ -135,6 +145,7 @@ if 'cleanup_vnc' not in scheduler:
interval=3600, interval=3600,
) )
def add_rq_dashboard_auth(blueprint): def add_rq_dashboard_auth(blueprint):
@blueprint.before_request @blueprint.before_request
@auth.oidc_auth @auth.oidc_auth
@ -159,6 +170,7 @@ def forbidden(e):
user = User(session['userinfo']['preferred_username']) user = User(session['userinfo']['preferred_username'])
return render_template('403.html', user=user, e=e), 403 return render_template('403.html', user=user, e=e), 403
@app.route('/') @app.route('/')
@app.route('/user/<string:user_view>') @app.route('/user/<string:user_view>')
@auth.oidc_auth @auth.oidc_auth
@ -287,15 +299,23 @@ def vm_console(vmid):
if user.rtp or int(vmid) in user.allowed_vms: if user.rtp or int(vmid) in user.allowed_vms:
# import pdb; pdb.set_trace() # import pdb; pdb.set_trace()
vm = VM(vmid) vm = VM(vmid)
vnc_ticket, vnc_port = open_vnc_session(vmid, vm.node, app.config['PROXMOX_USER'], app.config['PROXMOX_PASS']) vnc_ticket, vnc_port = open_vnc_session(
vmid, vm.node, app.config['PROXMOX_USER'], app.config['PROXMOX_PASS']
)
node = f'{vm.node}.csh.rit.edu' node = f'{vm.node}.csh.rit.edu'
token = add_vnc_target(node, vnc_port) token = add_vnc_target(node, vnc_port)
# return {'host' : node, 'port' : vnc_port, 'token' : token, 'password' : vnc_ticket}, 200 # return {'host' : node, 'port' : vnc_port, 'token' : token, 'password' : vnc_ticket}, 200
return {'host' : app.config['VNC_HOST'], 'port' : 8081, 'token' : token, 'password' : vnc_ticket}, 200 return {
'host': app.config['VNC_HOST'],
'port': 8081,
'token': token,
'password': vnc_ticket,
}, 200
else: else:
return '', 403 return '', 403
@app.route('/vm/<string:vmid>/cpu/<int:cores>', methods=['POST']) @app.route('/vm/<string:vmid>/cpu/<int:cores>', methods=['POST'])
@auth.oidc_auth @auth.oidc_auth
def vm_cpu(vmid, cores): def vm_cpu(vmid, cores):

View file

@ -281,10 +281,19 @@ class VM:
path = f'/etc/pve/local/qemu-server/{self.id}.conf' path = f'/etc/pve/local/qemu-server/{self.id}.conf'
with paramiko.SSHClient() as ssh: with paramiko.SSHClient() as ssh:
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(self.node, port=22, username=ssh_user, key_filename='proxmox_ssh_key', passphrase=ssh_pass) ssh.connect(
ssh.exec_command(f"if grep -- '{config}' {path}; then echo identical config found; else sed -i /dev/null '/-vnc/d' {path}") #YOLO self.node,
ssh.exec_command(f"if grep -- '-vnc' {path}; then echo found config; else echo {config} >> {path}; fi") port=22,
username=ssh_user,
key_filename='proxmox_ssh_key',
passphrase=ssh_pass,
)
ssh.exec_command(
f"if grep -- '{config}' {path}; then echo identical config found; else sed -i /dev/null '/-vnc/d' {path}"
) # YOLO
ssh.exec_command(
f"if grep -- '-vnc' {path}; then echo found config; else echo {config} >> {path}; fi"
)
@retry(wait=wait_fixed(2), stop=stop_after_attempt(5)) @retry(wait=wait_fixed(2), stop=stop_after_attempt(5))
def eject_iso(self): def eject_iso(self):

View file

@ -37,6 +37,7 @@ def get_vnc_targets():
target_file.close() target_file.close()
return targets return targets
def add_vnc_target(node, port): def add_vnc_target(node, port):
# TODO (willnilges): This doesn't throw an error if the target file is wrong. # TODO (willnilges): This doesn't throw an error if the target file is wrong.
# TODO (willnilges): This will duplicate targets # TODO (willnilges): This will duplicate targets
@ -52,6 +53,7 @@ def add_vnc_target(node, port):
target_file.close() target_file.close()
return token return token
def delete_vnc_target(node, port): def delete_vnc_target(node, port):
targets = get_vnc_targets() targets = get_vnc_targets()
target = next((target for target in targets if target['host'] == f'{node}:{port}'), None) target = next((target for target in targets if target['host'] == f'{node}:{port}'), None)
@ -62,6 +64,7 @@ def delete_vnc_target(node, port):
target_file.write(f"{target['token']}: {target['host']}\n") target_file.write(f"{target['token']}: {target['host']}\n")
target_file.close() target_file.close()
def open_vnc_session(vmid, node, proxmox_user, proxmox_pass): def open_vnc_session(vmid, node, proxmox_user, proxmox_pass):
"""Pings the Proxmox API to request a VNC Proxy connection. Authenticates """Pings the Proxmox API to request a VNC Proxy connection. Authenticates
against the API using a Uname/Pass, gets a few tokens back, then uses those against the API using a Uname/Pass, gets a few tokens back, then uses those
@ -91,11 +94,12 @@ def open_vnc_session(vmid, node, proxmox_user, proxmox_pass):
timeout=5, timeout=5,
params=proxy_params, params=proxy_params,
headers={"CSRFPreventionToken": csrf_prevention_token}, headers={"CSRFPreventionToken": csrf_prevention_token},
cookies={"PVEAuthCookie": ticket} cookies={"PVEAuthCookie": ticket},
).json()["data"] ).json()["data"]
return urllib.parse.quote_plus(vncproxy_response_data['ticket']), vncproxy_response_data['port'] return urllib.parse.quote_plus(vncproxy_response_data['ticket']), vncproxy_response_data['port']
def start_ssh_tunnel(node, port): def start_ssh_tunnel(node, port):
"""Forwards a port on a node """Forwards a port on a node
to the proxstar container to the proxstar container
@ -113,6 +117,7 @@ def start_ssh_tunnel(node, port):
server.start() server.start()
return server return server
def stop_ssh_tunnel(vmid, ssh_tunnels): def stop_ssh_tunnel(vmid, ssh_tunnels):
# Tear down the SSH tunnel and VNC target entry for a given VM # Tear down the SSH tunnel and VNC target entry for a given VM
port = 5900 + int(vmid) port = 5900 + int(vmid)