Add package manager prototype
This commit is contained in:
parent
ab78d18491
commit
1c967a0431
@ -1,139 +1,14 @@
|
||||
{
|
||||
"apps": {
|
||||
"ckan": {
|
||||
"host": "ckan",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "CKAN",
|
||||
"visible": false
|
||||
},
|
||||
"crisiscleanup": {
|
||||
"host": "cc",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Crisis Cleanup",
|
||||
"visible": false
|
||||
},
|
||||
"cts": {
|
||||
"host": "cts",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "CTS",
|
||||
"visible": false
|
||||
},
|
||||
"frontlinesms": {
|
||||
"host": "sms",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Frontline SMS",
|
||||
"visible": false
|
||||
},
|
||||
"gnuhealth": {
|
||||
"host": "gh",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "GNU Health",
|
||||
"visible": false
|
||||
},
|
||||
"kanboard": {
|
||||
"host": "kb",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "KanBoard",
|
||||
"visible": false
|
||||
},
|
||||
"mifosx": {
|
||||
"host": "mifosx",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Mifos X",
|
||||
"visible": false
|
||||
},
|
||||
"motech": {
|
||||
"host": "motech",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Motech",
|
||||
"visible": false
|
||||
},
|
||||
"opendatakit": {
|
||||
"host": "odk",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "OpenDataKit Aggregate",
|
||||
"visible": false
|
||||
},
|
||||
"opendatakit-build": {
|
||||
"host": "odkbuild",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "OpenDataKit Build",
|
||||
"visible": false
|
||||
},
|
||||
"openmapkit": {
|
||||
"host": "omk",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "OpenMapKit",
|
||||
"visible": false
|
||||
},
|
||||
"pandora": {
|
||||
"host": "pandora",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Pan.do/ra",
|
||||
"visible": false
|
||||
},
|
||||
"sahana": {
|
||||
"host": "sahana",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Sahana EDEN",
|
||||
"visible": false
|
||||
},
|
||||
"sahana-demo": {
|
||||
"host": "sahana-demo",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Sahana EDEN Demo",
|
||||
"visible": false
|
||||
},
|
||||
"sambro": {
|
||||
"host": "sambro",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Sahana EDEN SAMBRO",
|
||||
"visible": false
|
||||
},
|
||||
"seeddms": {
|
||||
"host": "dms",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "SeedDMS",
|
||||
"visible": false
|
||||
},
|
||||
"sigmah": {
|
||||
"host": "sigmah",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Sigmah",
|
||||
"visible": false
|
||||
},
|
||||
"ushahidi": {
|
||||
"host": "ush",
|
||||
"login": "N/A",
|
||||
"password": "N/A",
|
||||
"title": "Ushahidi",
|
||||
"visible": false
|
||||
}
|
||||
},
|
||||
"apps": {},
|
||||
"common": {
|
||||
"email": "admin@example.com",
|
||||
"gmaps-api-key": ""
|
||||
},
|
||||
"host": {
|
||||
"repo": "https://dl.dasm.cz/spotter-repo",
|
||||
"adminpwd": "$2b$12$nLrIefUoWN.pK6j90gsfkO0/tg4EGXDmdjN8HOGB0U.9BcHTFxzWS",
|
||||
"domain": "spotter.vm",
|
||||
"firstrun": true,
|
||||
"port": "443"
|
||||
}
|
||||
}
|
||||
|
98
basic/srv/vm/mgr/pkgmgr.py
Normal file
98
basic/srv/vm/mgr/pkgmgr.py
Normal file
@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
import requests
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.primitives.serialization import load_pem_public_key
|
||||
|
||||
CONF_FILE = '/srv/vm/config.json'
|
||||
CERT_FILE = '/srv/vm/packages.pub'
|
||||
LXC_ROOT = '/var/lib/lxc'
|
||||
|
||||
class PackageManager:
|
||||
def __init__(self):
|
||||
# Load JSON configuration
|
||||
with open(CONF_FILE, 'r') as f:
|
||||
self.conf = json.load(f)
|
||||
self.online_packages = {}
|
||||
|
||||
def save_conf(self):
|
||||
# Save a sorted JSON configuration object with indentation
|
||||
with open(CONF_FILE, 'w') as f:
|
||||
json.dump(self.conf, f, sort_keys=True, indent=4)
|
||||
|
||||
def get_online_packages(self):
|
||||
# Fetches and verifies online packages. Can raise InvalidSignature
|
||||
repo_url = self.conf['host']['repo']
|
||||
packages = requests.get('{}/packages'.format(repo_url)).content
|
||||
packages_sig = requests.get('{}/packages.sig'.format(repo_url)).content
|
||||
with open(CERT_FILE, 'rb') as f:
|
||||
pub_key = load_pem_public_key(f.read(), default_backend())
|
||||
pub_key.verify(packages_sig, packages, ec.ECDSA(hashes.SHA512()))
|
||||
return json.loads(packages)
|
||||
|
||||
def install_package(self, name):
|
||||
self.online_packages = get_online_packages()
|
||||
for dep in self.get_deps(name):
|
||||
if dep not in self.conf['apps']:
|
||||
self.download_package(name)
|
||||
if 'host' in self.online_packages[name]:
|
||||
self.register_app(name, self.online_packages[name])
|
||||
self.setup_package()
|
||||
|
||||
def download_package(self, name):
|
||||
# Downloads, verifies, unpacks and sets up a package
|
||||
local_archive = tempfile.mkstemp('.tar.xz')
|
||||
r = requests.get('{}/{}.tar.xz'.format(self.repo_url, name), stream=True)
|
||||
with open(local_archive, 'wb') as f:
|
||||
for chunk in r.iter_content(chunk_size=65536):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
# Verify hash
|
||||
if self.online_packages[name]['sha512'] != hash_file(local_archive):
|
||||
raise InvalidSignature(name)
|
||||
# Unpack
|
||||
subprocess.run(['tar', 'xJf', local_archive], cwd=LXC_ROOT)
|
||||
os.unlink(local_archive)
|
||||
|
||||
def register_app(self, name, metadata):
|
||||
self.conf['apps'][name] = {
|
||||
'title': metadata['title'],
|
||||
'version': metadata['version'],
|
||||
'host': metadata['host'],
|
||||
'login': 'N/A',
|
||||
'password': 'N/A',
|
||||
'visible': False
|
||||
}
|
||||
self.save_conf()
|
||||
|
||||
def setup_package(self):
|
||||
setup_dir = os.path.join(LXC_ROOT, 'setup')
|
||||
setup_script = os.path.join(LXC_ROOT, 'setup.sh')
|
||||
if os.path.exists(setup_script):
|
||||
subprocess.run(setup_script)
|
||||
os.unlink(setup_script)
|
||||
if os.path.exists(setup_dir):
|
||||
shutil.rmtree(setup_dir)
|
||||
|
||||
def get_deps(self, name):
|
||||
deps = self.online_packages[name]['deps'] + [name]
|
||||
for dep in deps:
|
||||
deps[:0] = [d for d in self.get_deps(dep) if d not in deps]
|
||||
return deps
|
||||
|
||||
def hash_file(file_path):
|
||||
sha512 = hashlib.sha512()
|
||||
with open(file_path, 'rb') as f:
|
||||
while True:
|
||||
data = f.read(65536)
|
||||
if not data:
|
||||
break
|
||||
sha512.update(data)
|
||||
return sha512.hexdigest()
|
5
basic/srv/vm/packages.pub
Normal file
5
basic/srv/vm/packages.pub
Normal file
@ -0,0 +1,5 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEWJXH4Qm0kt2L86sntQH+C1zOJNQ0qMRt
|
||||
0vx4krTxRs9HQTQYAy//JC92ea2aKleA8OL0JF90b1NYXcQCWdAS+vE/ng9IEAii
|
||||
8C2+5nfuFeZ5YUjbQhfFblwHSM0c7hEG
|
||||
-----END PUBLIC KEY-----
|
@ -52,6 +52,7 @@ lxc-build ${SOURCE_DIR}/solr
|
||||
lxc-build ${SOURCE_DIR}/ushahidi
|
||||
|
||||
# Create packages
|
||||
lxc-pack ${SOURCE_DIR}/basic-runtimes/alpine.pkg
|
||||
lxc-pack ${SOURCE_DIR}/basic-runtimes/java.pkg
|
||||
lxc-pack ${SOURCE_DIR}/basic-runtimes/libxml.pkg
|
||||
lxc-pack ${SOURCE_DIR}/basic-runtimes/php.pkg
|
||||
|
Loading…
Reference in New Issue
Block a user