vdi-cloud/tools/provision-pve-access.py

63 lines
3.1 KiB
Python

"""Create scoped VDI API access through a known-host-verified SSH connection.
Requires paramiko. Password is prompted or read from VDI_PVE_SSH_PASSWORD.
The one-time API secret is written only to the requested private output file.
"""
import argparse
import getpass
import json
import os
import pathlib
import shlex
import paramiko
def main():
p = argparse.ArgumentParser()
p.add_argument('--host', required=True)
p.add_argument('--vmids', nargs='+', type=int, required=True)
p.add_argument('--output', required=True)
args = p.parse_args()
target = pathlib.Path(args.output)
if target.exists():
raise RuntimeError('Output already exists; reuse the existing token rather than replace it')
ssh = paramiko.SSHClient()
ssh.load_system_host_keys(str(pathlib.Path.home() / '.ssh/known_hosts'))
ssh.set_missing_host_key_policy(paramiko.RejectPolicy())
password = os.environ.get('VDI_PVE_SSH_PASSWORD') or getpass.getpass('PVE SSH password: ')
ssh.connect(args.host, username='root', password=password, look_for_keys=False, allow_agent=False, timeout=10)
def run(*parts):
_, out, err = ssh.exec_command(shlex.join(parts), timeout=30)
body = out.read().decode()
if out.channel.recv_exit_status() != 0:
raise RuntimeError(err.read().decode())
return body
try:
user, role, token = 'vdi-cloud@pve', 'VDICloudPower', 'console'
users = json.loads(run('pveum', 'user', 'list', '--output-format', 'json'))
if any(u['userid'] == user for u in users):
raise RuntimeError('VDI account already exists. Inspect it and recover the existing token first')
roles = json.loads(run('pveum', 'role', 'list', '--output-format', 'json'))
if not any(r['roleid'] == role for r in roles):
run('pveum', 'role', 'add', role, '--privs', 'VM.Audit VM.PowerMgmt')
elif set(next(r for r in roles if r['roleid'] == role)['privs'].split(',')) != {'VM.Audit','VM.PowerMgmt'}:
raise RuntimeError('Existing VDICloudPower role has unexpected privileges; inspect it first')
run('pveum', 'user', 'add', user, '--comment', 'VDI cloud API; scoped power management')
run('pveum', 'acl', 'modify', '/', '--users', user, '--roles', 'PVEAuditor')
for vmid in args.vmids:
run('pveum', 'acl', 'modify', f'/vms/{vmid}', '--users', user, '--roles', role)
result = json.loads(run('pveum', 'user', 'token', 'add', user, token, '--privsep', '0', '--output-format', 'json'))
target.parent.mkdir(parents=True, exist_ok=True)
with target.open('x', encoding='utf-8') as f:
json.dump({'PVE_URL': f'https://{args.host}:8006', 'PVE_TOKEN_ID': result['full-tokenid'], 'PVE_TOKEN_SECRET': result['value']}, f)
os.chmod(target, 0o600)
with ssh.open_sftp() as sftp:
sftp.get('/etc/pve/pve-root-ca.pem', str(target.parent / 'pve-ca.pem'))
print(f'Created dedicated account and VM power scopes {args.vmids}; credentials saved privately')
finally:
ssh.close()
if __name__ == '__main__':
main()