PHP rewrite, first release

This commit is contained in:
Paolo Asperti 2016-07-18 14:41:10 +02:00
parent 9653ef9014
commit cc8a26960a
30 changed files with 976 additions and 336 deletions

97
da_integrare.spec Normal file
View File

@ -0,0 +1,97 @@
Summary: apache/mod_php stack for Doraemon
Name: doraemon
Version: 1.3.0.1
Release: 1%{?dist}
License: GPL
URL: %{url_prefix}/%{name}
BuildArch: noarch
Source0: %{name}-%{version}.tar.gz
BuildRequires: nethserver-devtools
BuildRequires: systemd
Requires(post): systemd
Requires(preun): systemd
Requires(postun): systemd
Requires: httpd, php, sudo, php-xml, php-mcrypt, nethserver-httpd-admin
Requires: nethserver-base, nethserver-php
Requires: nethserver-lang-en
%description
Runs an Apache instance on port 3000 that serves
the doraemon web application
%prep
%setup
# Nethgui:
cd %{_builddir}/nethgui-%{nethgui_commit}
%build
perl createlinks
mkdir -p root/%{_nseventsdir}/%{name}-update
%install
(cd root ; find . -depth -print | cpio -dump %{buildroot})
rm -f %{name}-%{version}-%{release}-filelist
%{genfilelist} %{buildroot} \
> %{name}-%{version}-%{release}-filelist
mkdir -p %{buildroot}/%{_localstatedir}/log/doraemon
mkdir -p %{buildroot}/%{_localstatedir}/cache/doraemon
# Copy the server-manager dir
mkdir -p %{buildroot}%{_nsuidir}
cp -av nethserver-manager %{buildroot}%{_nsuidir}/nethserver-manager
%files -f %{name}-%{version}-%{release}-filelist
%defattr(-,root,root)
%doc %{extradocs}
%{_nsuidir}/nethserver-manager
%dir %{_nseventsdir}/%{name}-update
%dir %{_sysconfdir}/httpd/doraemon.d
%attr(0750,srvmgr,srvmgr) %dir %{_localstatedir}/cache/doraemon
%attr(0644,root,root) %ghost %{_sysconfdir}/init/doraemon.conf
%attr(0644,root,root) %ghost %{_sysconfdir}/httpd/doraemon/httpd.conf
%attr(0700,root,root) %dir %{_localstatedir}/log/doraemon
%attr(0644,root,root) %config %ghost %{_localstatedir}/log/doraemon/access_log
%attr(0644,root,root) %config %ghost %{_localstatedir}/log/doraemon/error_log
%config(noreplace) /etc/sysconfig/doraemon
%post
%systemd_post doraemon.service
%preun
%systemd_preun doraemon.service
%postun
%systemd_postun
%changelog
* Tue Oct 27 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.2.1-2.ns6
- Packing corrections to rpm to fix upgrading issue
* Tue Oct 27 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.2.1-1.ns6
- Packing new minor correction on how packages are extracted from db
* Mon Sep 11 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.2.0-1.ns6
- Packing new version with ansible dynamic inventory support, epoptes server role support.
* Mon Sep 07 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.1.1-1.ns6
- Packing new version with correct roles.
* Sun Sep 06 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.1.0-1.ns6
- Packing new version with new functionalities.
* Wed Jul 15 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.0.0-2.ns6
- Added logrotate file.
* Mon Jul 13 2015 Emiliano Vavassori <syntaxerrormmm-AT-gmail.com> - 1.0.0-1.ns6
- Unique binary for both ansiblehelper and mac2hostname.

View File

@ -1 +0,0 @@
private

View File

@ -1,16 +0,0 @@
[Daemon]
Database = /var/lib/doraemon/doraemon.db
LogFile = /var/log/doraemon.log
PIDFile = /var/run/doraemon.pid
BindAddress = 0.0.0.0
Port = 3000
[NameSettings]
Base = lab
Role = client
Digits = 2
[Files]
Domain = /etc/domain.yml
MgmtKey = /home/amgmt/.ssh/id_rsa.pub
VaultPassFile = /home/amgmt/.ansible/vault.txt

View File

@ -1,6 +0,0 @@
/var/log/doraemon.log {
daily
rotate 15
compress
missingok
}

View File

@ -1,234 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Helps a client to join domain and maintain itself."""
__author__ = 'BgLUG'
__email__ = 'info@bglug.it'
from contextlib import contextmanager, closing
from subprocess import Popen, PIPE
from sqlite3 import connect
from bottle import Bottle, request
from json import dumps
from ConfigParser import ConfigParser
from Crypto.Hash import MD5
from Crypto.Cipher import AES
import base64
import json
import sys
import re
import os
class MyApp:
def __init__(self, configfile = '/etc/mac2hostname.ini'):
if not os.path.isfile(configfile):
print "Cannot find main file, %s. Exiting." % configfile
sys.exit(1)
# Loads configurations
config = ConfigParser()
config.read(configfile)
# Populates private properties with configuration
self.__dbfile = config.get('Daemon', 'Database') if config.has_option('Daemon', 'Database') else '/var/lib/doraemon/doraemon.db'
self.__logdir = config.get('Daemon', 'LogFile') if config.has_option('Daemon', 'LogFile') else '/var/log/doraemon.log'
self.__pidfile = config.get('Daemon', 'PIDFile') if config.has_option('Daemon', 'PIDFile') else '/var/run/doraemon.pid'
self.__bindaddress = config.get('Daemon', 'BindAddress') if config.has_option('Daemon', 'BindAddress') else '127.0.0.1'
self.__port = config.getint('Daemon', 'Port') if config.has_option('Daemon',
'Port') else 8080
self.__defaultbase = config.get('NameSettings', 'Base') if config.has_option('NameSettings', 'Base') else 'client'
self.__defaultrole = config.get('NameSettings', 'Role') if config.has_option('NameSettings', 'Role') else 'client'
self.__namedigits = config.get('NameSettings', 'Digits') if config.has_option('NameSettings', 'Digits') else '2'
self.__domainfile = config.get('Files', 'Domain') if config.has_option('Files', 'Domain') else None
self.__mgmtfile = config.get('Files', 'MgmtKey') if config.has_option('Files', 'MgmtKey') else None
self.__vaultpassfile = config.get('Files', 'VaultPassFile') if config.has_option('Files', 'VaultPassFile') else None
self.__app = Bottle()
# Applies routes
self.__route()
# Assures table creation
self.__init_tables()
@contextmanager
def __getcursor(self):
with connect(self.__dbfile) as connection:
with closing(connection.cursor()) as cursor:
yield cursor
# Private methods
def __route(self):
self.__app.get('/mac2hostname', callback=self.mac2hostname)
self.__app.get('/whatsmyhostname', callback=self.whatsmyhostname)
self.__app.get('/hosts', callback=self.hosts)
self.__app.get('/domain', callback=self.domain)
self.__app.get('/mgmtkey', callback=self.mgmtkey)
self.__app.get('/vaultpass', callback=self.vaultpass)
self.__app.get('/ansible_list', callback=self.ansible_list)
self.__app.get('/ansible_host', callback=self.ansible_host)
self.__app.get('/epoptes-srv', callback=self.epoptes_srv)
def __init_tables(self):
with self.__getcursor() as cursor:
cursor.execute('CREATE TABLE IF NOT EXISTS client (id INT PRIMARY KEY,'
'hostname TEXT NOT NULL UNIQUE, mac TEXT UNIQUE, role TEXT)')
cursor.execute('CREATE INDEX IF NOT EXISTS idxmac ON client(mac)')
def __getmac(self, ip):
Popen(['ping', '-c1', '-t2', ip], stdout=PIPE).communicate()
arp = Popen(['arp', '-n', ip], stdout=PIPE).communicate()[0]
return re.search(r'(([\da-fA-F]{1,2}\:){5}[\da-fA-F]{1,2})', arp).group(1).lower()
def __normalizemac(self, mac):
return ':'.join(x.zfill(2) for x in mac.split('_')).lower()
def __gethostname(self, mac, base = None, role = None):
if base == None:
base = self.__defaultbase
if role == None:
role = self.__defaultrole
with self.__getcursor() as cursor:
(newid,) = cursor.execute('SELECT COALESCE(MAX(id) + 1, 1) FROM client').fetchone()
# Constucts the hostname format
formatstring = '%s-%0' + self.__namedigits + 'd'
data = (newid, formatstring % (base, newid), mac, role)
# Since MAC is Unique, this fails with the same MAC address
cursor.execute('INSERT OR IGNORE INTO client VALUES (?, ?, ?, ?)', data)
(hostname,) = cursor.execute('SELECT hostname FROM client WHERE mac = "%s"' % mac).fetchone()
return hostname
# This should be used only after registration of the client
# (whatsmyhostname)
def __getrole(self):
macaddress = self.__normalizemac(self.__getmac(request['REMOTE_ADDR']))
hostname = self.__gethostname(macaddress)
with self.__getcursor() as cursor:
(role,) = cursor.execute("SELECT role FROM client WHERE hostname = '%s' AND mac = '%s'" % (hostname, macaddress)).fetchone()
return role
# Variables returned to the client
def __rolevars(self, role=None):
retval = {'role': role, 'addpkg': [], 'delpkg': []}
try:
db = Popen(['/sbin/e-smith/db', 'roles', 'getjson'], stdout=PIPE).communicate()[0]
for data in json.loads(db):
if data['name'] == role:
retval['addpkg'] = re.split(r'[\s,;]+', data['props']['Addpkg'])
retval['delpkg'] = re.split(r'[\s,;]+', data['props']['Delpkg'])
continue
except: pass
return retval
# Instance methods AKA routes
def mac2hostname(self):
# Ensure required parameters have been passed
if not request.query.mac:
return "Usage: GET /mac2hostname?mac=XX_XX_XX_XX_XX_XX[&base=YYY][&role=ZZZ]"
# Sets up variables for possible parameters
mac = self.__normalizemac(request.query.mac)
base = request.query.base or self.__defaultbase
role = request.query.role or self.__defaultrole
return self.__gethostname(mac, base, role)
def whatsmyhostname(self):
# No required parameters
ip = request.query.ip or request['REMOTE_ADDR']
base = request.query.base or self.__defaultbase
role = request.query.role or self.__defaultrole
return self.__gethostname(self.__getmac(ip), base, role)
def hosts(self):
# Default where clause: no where specifications
where = ''
# If a role parameter is passed, list the hosts for that role
if request.query.role:
where = "WHERE role = '%s'" % request.query.role
with self.__getcursor() as cursor:
return dumps([dict((meta[0], data)
for meta, data in zip(cursor.description, row))
for row in cursor.execute('SELECT role, hostname, mac FROM client '
+ where + 'ORDER BY role ASC, hostname ASC')], indent=4)
def domain(self):
with open(self.__domainfile, 'r') as f:
return f.read()
def mgmtkey(self):
with open(self.__mgmtfile, 'r') as f:
return f.read()
def vaultpass(self):
with open(self.__vaultpassfile, 'r') as f:
ip = request.query.ip or request['REMOTE_ADDR']
base = request.query.base or self.__defaultbase
role = request.query.role or self.__defaultrole
hostname = self.__gethostname(self.__getmac(ip), base, role)
# Encryption key
key = MD5.new(hostname).digest()
# Secret
secret = f.read().strip()
# Creating a secret that is multiple of 16 in length
i = 16 - (len(secret) % 16)
lengthy_secret = secret + i * 'x'
cipher = AES.new(key, AES.MODE_ECB)
crypted = cipher.encrypt(lengthy_secret)
# Encode in base64 because of unicode strings
return base64.b64encode(crypted)
def ansible_list(self):
# Faking calls
if request.query.role:
role = request.query.role
else:
role = self.__getrole()
result = {
'localhost': {
'hosts': [ 'localhost' ],
'vars': {
'ansible_connection': 'local'
}
},
'_meta': {
'hostvars': {
'localhost': self.__rolevars(role)
}
}
}
return dumps(result)
def ansible_host(self):
# Ignoring 'host' parameter, since it should always be 'localhost'.
# Instead, for testing purposes, if asked for a role, use it
if request.query.role:
role = request.query.role
else:
role = self.__getrole()
return dumps(self.__rolevars(role))
def epoptes_srv(self):
hostname = None
try:
with self.__getcursor() as cursor:
(hostname,) = cursor.execute('SELECT hostname FROM client WHERE role = \'docenti\' ORDER BY id DESC LIMIT 1').fetchone()
except TypeError:
return 'none'
return hostname
def start(self):
# Opens up a PID file
pid = os.getpid()
pidfile = open(self.__pidfile, 'w')
pidfile.write('%s' % pid)
pidfile.close()
# Runs Bottle, at last.
self.__app.run(host=self.__bindaddress, port=self.__port)
# Main body
if __name__ == '__main__':
cfgfile = '/etc/doraemon.ini'
if len(sys.argv) > 1:
cfgfile = sys.argv[1]
app = MyApp(cfgfile)
app.start()

391
doraemon/EsmithDatabase.php Normal file
View File

@ -0,0 +1,391 @@
<?php
/*
* Copyright (C) 2011 Nethesis S.r.l.
*
* This script is part of NethServer.
*
* NethServer is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NethServer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NethServer. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Read and write parameters into SME DB
*
* Ths class implements an interface to SME database executing the command /sbin/e-smith/db with sudo.
* The class needs /etc/sudoers configurazione. In the sudoers file you must have something like this:
* <code>
* Cmnd_Alias SME = /sbin/e-smith/db, /sbin/e-smith/signal-event
* www ALL=NOPASSWD: SME
* </code>
*
*
* @author Giacomo Sanchietti <giacomo.sanchietti@nethesis.it>
* @author Davide Principi <davide.principi@nethesis.it>
* @since 1.0
* @internal
*/
class EsmithDatabase
{
/**
* @var SME DB database command
* */
private $command = "/usr/bin/sudo /sbin/e-smith/db";
/**
* @var $db Database name, it's translated into the db file path. For example: /home/e-smith/db/testdb
* */
private $db;
/**
* This static class variable shares a socket connection to smwingsd
*
* @var resource
*/
private static $socket;
/**
* Construct an object to access a SME Configuration database file
* with $user's privileges.
*
* @param string $database Database name
*/
public function __construct($database)
{
if ( ! $database)
sprintf("%s: You must provide a valid database name.", get_class($this));
$this->db = $database;
}
public function getAll($type = NULL)
{
$output = "";
$ret = $this->dbRead('getjson', array(), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed!", __CLASS__);
}
$data = json_decode($output, TRUE);
if ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
$result = array();
foreach ($data as $item) {
// Apply type check filter:
if (isset($type) && $type !== $item['type']) {
continue;
}
$props = isset($item['props']) ? $item['props'] : array();
$result[$item['name']] = array_merge($props, array('type' => $item['type']));
}
return $result;
}
public function getAllByProp($propName = NULL, $propValue = NULL)
{
$output = "";
$ret = $this->dbRead('getjson', array(), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed!", __CLASS__);
}
$data = json_decode($output, TRUE);
if ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
$result = array();
foreach ($data as $item) {
$item['props']['type'] = $item['type'];
$item['props']['name'] = $item['name'];
if (isset($propName) && isset($propValue)) {
if (isset($item['props'][$propName]) && $propValue == $item['props'][$propName]) {
$result[$item['name']] = $item['props'];
}
} else {
$result[$item['name']] = $item['props'];
}
}
return $result;
}
public function getKey($key)
{
$output = '';
$ret = $this->dbRead('getjson', array($key), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed", __CLASS__);
}
$data = json_decode($output, TRUE);
if ($data === 1) {
// Key has not been found
return array();
} elseif ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
$data['props']['type'] = $data['type'];
$data['props']['name'] = $data['name'];
return $data['props'];
}
public function getKeyValue($key)
{
$output = '';
$ret = $this->dbRead('getjson', array($key), $output);
if ($ret !== 0) {
sprintf("%s: internal database command failed", __CLASS__);
}
$data = json_decode($output, TRUE);
if ($data === 1) {
// Key has not been found
return array();
} elseif ( ! is_array($data)) {
sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8));
}
return $data['type'];
}
public function setKey($key, $type, $props)
{
$output = NULL;
$ret = $this->dbExec('set', $this->prepareArguments($key, $type, $props), $output);
return ($ret == 0);
}
public function deleteKey($key)
{
$output = NULL;
$output = NULL;
$ret = $this->dbExec('delete', $this->prepareArguments($key), $output);
return ($ret == 0);
}
/**
* Return the type of a key
* Act like: /sbin/e-smith/db dbfile gettype key
*
* @param string $key the key to retrieve
* @access public
* @return string the type of the key
*/
public function getType($key)
{
$output = NULL;
$ret = $this->dbRead('gettype', array($key), $output);
return trim($output);
}
/**
* Set the type of a key
* Act like: /sbin/e-smith/db dbfile settype key type
*
* @param string $key the key to change
* @param string $type the new type
* @access public
* @return bool true on success, FALSE otherwise
*/
public function setType($key, $type)
{
$output = NULL;
$ret = $this->dbExec('settype', $this->prepareArguments($key, $type), $output);
return ($ret == 0);
}
public function getProp($key, $prop)
{
$output = NULL;
$ret = $this->dbRead('getprop', array($key, $prop), $output);
return trim($output);
}
public function setProp($key, $props)
{
$output = NULL;
$ret = $this->dbExec('setprop', $this->prepareArguments($key, $props), $output);
return ($ret == 0);
}
public function delProp($key, $props)
{
$output = NULL;
$ret = $this->dbExec('delprop', array_merge(array($key), array_values($props)), $output);
return ($ret == 0);
}
/**
* Execute the db command
* @param string $command The command to invoke
* @param array $args The command arguments
* @param string &$output The output from the command process
* @return int The command exit code
*/
private function dbExec($command, $args, &$output)
{
// prepend the database name and command
array_unshift($args, $this->db, $command);
$exitCode = 0;
$oArr = array();
exec($this->command . ' ' . implode(' ', array_map('escapeshellarg', $args)), $oArr, $exitCode);
$output = implode("\n", $oArr);
return $exitCode;
}
/**
* Read db data from memory cache daemon
* @param string $command The command to invoke
* @param array $args The command arguments
* @param string &$output The output from the read socket
* @return int The command exit code
*/
private function dbRead($command, $args, &$output)
{
if ( ! isset(self::$socket) ) {
$socketPath = '/var/run/smwingsd.sock';
$errno = 0;
$errstr = '';
self::$socket = fsockopen('unix://' . $socketPath, -1, $errno, $errstr);
if( ! is_resource(self::$socket)) {
sprintf("Invalid socket (%d): %s. Fall back to exec().", $errno, $errstr);
}
}
if ( ! is_resource(self::$socket)) {
return $this->dbExec($command, call_user_func_array(array($this, 'prepareArguments'), $args), $output);
}
// prepend the database name and command
array_unshift($args, $this->db, $command);
$ret = $this->sendMessage(self::$socket, 0x10, $args);
if ( ! $ret ) {
return 1;
}
$ret = $this->recvMessage(self::$socket);
if ( ! $ret ) {
return 1;
}
if ($command === 'getjson') {
$output = $ret;
} else {
$output = json_decode($ret, TRUE);
}
return 0;
}
private function sendMessage($socket, $type, $args = array())
{
$payload = json_encode($args);
$data = pack('CN', (int) $type, strlen($payload)) . $payload;
$written = fwrite($socket, $data);
if ($written !== strlen($data)) {
echo 'Socket write error';
}
return TRUE;
}
private function recvMessage($socket)
{
$buf = $this->safeRead($socket, 5);
if ($buf === FALSE) {
echo 'Socket read error';
return false;
}
$header = unpack('Ctype/Nsize', $buf);
if ( ! is_array($header)) {
echo 'Invalid message header';
return false;
}
$message = $this->safeRead($socket, $header['size']);
if ($message === FALSE) {
echo 'Socket read error';
return false;
}
if ($header['type'] & 0x02) {
return NULL;
}
return $message;
}
private function safeRead($socket, $size)
{
$buffer = "";
$count = 0;
while($count < $size) {
if(feof($socket)) {
return FALSE;
}
$chunk = fread($socket, $size - $count);
$count += strlen($chunk);
if($chunk === FALSE) {
return FALSE;
}
$buffer .= $chunk;
}
return $buffer;
}
/**
* Take arbitrary arguments and flattenize to an array
*
* @param mixed $_
* @return array
*/
private function prepareArguments()
{
$args = array();
foreach (func_get_args() as $arg) {
if (is_array($arg)) {
foreach ($arg as $propName => $propValue) {
$args[] = $propName;
$args[] = $propValue;
}
} else {
$args[] = (String) $arg;
}
}
return $args;
}
}

254
doraemon/index.php Normal file
View File

@ -0,0 +1,254 @@
<?php
include_once("EsmithDatabase.php");
define('CONFIG_KEY', 'doraemon');
$hosts_db = new EsmithDatabase('hosts');
$roles_db = new EsmithDatabase('roles');
$config_db = new EsmithDatabase('configuration');
$uri = $_SERVER['REQUEST_URI'];
// add leading '/' to uri string, works if $uri is empty
$uri = (substr($uri, 0, 1) == '/') ? $uri : '/' . $uri;
$pos = strpos($uri, '?');
if (false !== $pos) {
$route = substr($uri, 0, (int) $pos);
} else {
$route = $uri;
}
// remove trailing '/'
$route = rtrim($route, '/');
$query_string = $_SERVER['QUERY_STRING'];
$get_params = array();
parse_str($query_string, $get_params);
// determine internal function name (all lowercase, dash2underscore, named ROUTE_routename )
$function = preg_replace(':\-:', '_', strtolower($route));
$function = preg_replace(':^/:', 'ROUTE_', $function);
if (function_exists($function)) {
$function();
} else {
die('Go away.');
}
function varsForRole($role, $json=true) {
global $roles_db;
$result = $roles_db->getKey($role);
$return = array('role'=>$role,'Delpkg'=>'','Addpkg'=>'');
if (is_array($result)) {
if (is_string($result['Addpkg'])) $return['Addpkg'] = $result['Addpkg'];
if (is_string($result['Delpkg'])) $return['Delpkg'] = $result['Delpkg'];
}
if ($json) {
return json_encode($return);
} else {
return $return;
}
}
function getClient($mac=false) {
global $hosts_db;
if (!$mac) {
$mac = getClientMac();
if (!$mac) return false;
}
$hosts = $hosts_db->getAllByProp('MacAddress',$mac);
if (count($hosts)>0) {
return array_pop($hosts);
} else {
return false;
}
}
function getClientMac($ip=false) {
if (false === $ip) {
$ip = $_SERVER['REMOTE_ADDR'];
}
$result = pingHost($ip,1);
if (!$result) return false;
$macAddr=false;
$arptable=`arp -n $ip`;
$lines=explode("\n", $arptable);
foreach($lines as $line) {
$cols=preg_split('/\s+/', trim($line));
if ($cols[0]==$ip)
{
return $cols[2];
}
}
}
function pingHost($host, $timeout=1) {
$pingresult = exec("/bin/ping -c 1 -t 2 -W $timeout $host", $output, $retvar);
return (0 == $retvar);
}
function ROUTE_domain() {
global $config_db;
$theFile = $config_db->getProp(CONFIG_KEY,'DomainFile');
$content = file_get_contents($theFile);
if (false !== $content) {
echo $content;
}
}
function ROUTE_mgmtkey() {
global $config_db;
$theFile = $config_db->getProp(CONFIG_KEY,'ManagementKeyFile');
$content = file_get_contents($theFile);
if (false !== $content) {
echo $content;
}
}
function ROUTE_epoptes_srv() {
global $hosts_db;
$hosts = $hosts_db->getAllByProp('Role','docenti');
if (count($hosts) == 0) {
echo 'none';
} else {
$host = array_pop($hosts);
echo json_encode($host);
}
}
function ROUTE_ansible_host() {
global $get_params;
if (isset($get_params['role'])) {
$role = $get_params['role'];
} elseif ($client = getClient()) {
$role = $client['Role'];
} else {
$role = 'unknown';
}
echo varsForRole($role);
}
function ROUTE_hosts() {
global $hosts_db;
global $get_params;
if (isset($get_params['role'])) {
$items = $hosts_db->getAllByProp('Role', $get_params['role']);
} else {
$items = $hosts_db->getAll();
}
$results=array();
foreach ($items as $name=>$item) {
$results[]=array(
'mac'=>$item['MacAddress'],
'hostname'=>$name,
'role'=>$item['Role']
);
}
echo json_encode($results);
}
function ROUTE_ansible_list() {
global $get_params;
if (isset($get_params['role'])) {
$role = $get_params['role'];
} elseif ($client = getClient()) {
$role = $client['Role'];
} else {
$role = 'unknown';
}
$vars = varsForRole($role, false);
echo json_encode(array(
'localhost'=>array(
'hosts'=>array('localhost'),
'vars'=>array('ansible_connection'=>'local')
),
'_meta'=> array('hostvars'=>array('localhost'=>$vars))
));
}
function ROUTE_vaultpass() {
global $config_db;
$theFile = $config_db->getProp(CONFIG_KEY,'VaultPassFile');
$content = trim(file_get_contents($theFile));
if (false === $content) {
echo 'no file'; return;
}
if (!$client = getClient()) {
echo 'no client'; return;
}
$key = md5($client['name']);
$padlenght = 16 - (strlen($content) % 16);
$content_padded = str_repeat('x', $padlenght);
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $content_padded, MCRYPT_MODE_ECB);
echo base64_encode($ciphertext);
}
function ROUTE_whatsmyhostname() {
global $config_db;
$mac = getClientMac();
if (!$mac) return false;
$base = $config_db->getProp(CONFIG_KEY,'NamingBase');
$role = $config_db->getProp(CONFIG_KEY,'DefaultRole');
echo newHostname($mac, $base, $role);
}
function ROUTE_mac2hostname() {
global $config_db;
global $get_params;
$mac = getClientMac();
if (!$mac) {
echo 'Usage: GET /mac2hostname?mac=XX_XX_XX_XX_XX_XX[&base=YYY][&role=ZZZ]';
return;
}
if (isset($get_params['role'])) {
$role = $get_params['role'];
} else {
$role = $config_db->getProp(CONFIG_KEY,'DefaultRole');
}
if (isset($get_params['base'])) {
$base = $get_params['base'];
} else {
$base = $config_db->getProp(CONFIG_KEY,'NamingBase');
}
echo newHostname($mac, $base, $role);
}
function newHostname($mac, $base, $role) {
global $config_db;
global $hosts_db;
if(!preg_match('/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', $mac)) {
echo "invalid mac address";
return;
}
if ($client=getClient($mac)) {
echo $client['name'];
return;
}
$domainName = $config_db->getKeyValue('DomainName');
$digits = $config_db->getProp(CONFIG_KEY,'NamingDigits');
$formatstring = '%s-%0'.$digits.'d';
$nr=0;
$hostname = '';
do {
$nr++;
$hostname = sprintf($formatstring, $base, $nr);
if ($domainName != '') {
$hostname .= '.' . $domainname;
}
} while ($hosts_db->getKey($hostname));
$hosts_db->setKey($hostname, 'remote', array(
'MacAddress' => $mac,
'Role' => $role,
'Description' => ''
));
echo $hostname;
}

79
initrd
View File

@ -1,79 +0,0 @@
#!/bin/sh
# chkconfig: 2345 20 80
# description: Manage assignation of hostnames for clients
# pidfile: /var/run/doraemon.pid
# config: /etc/doraemon.ini
### BEGIN INIT INFO
# Provides: doraemon
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Helps client to join the domain and maintain itself
# Description: Helps client to join the domain and maintain itself
### END INIT INFO
# Change the next 3 lines to suit where you install your script and what you want to call it
DAEMON=/usr/bin/doraemon.py
DAEMON_NAME=doraemon
# Add any command line options for your daemon here
DAEMON_OPTS=""
# This next line determines what user the script runs as.
# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python.
#DAEMON_USER=mac2hostname
#DAEMON_GROUP=mac2hostname
# The process ID of the script when it runs is stored here:
LOGFILE=/var/log/$DAEMON_NAME.log
PIDFILE=/var/run/$DAEMON_NAME.pid
lockfile=/var/lock/subsys/$DAEMON_NAME
. /etc/rc.d/init.d/functions
. /etc/sysconfig/network
[ "$NETWORKING" = "no" ] && exit 0
do_start () {
echo -n $"Starting $DAEMON_NAME: "
daemon --pidfile=$PIDFILE "python $DAEMON >>$LOGFILE 2>/dev/null &"
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch $lockfile
return $RETVAL
}
do_stop () {
echo -n $"Stopping $DAEMON_NAME: "
killproc -p $PIDFILE python
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f $lockfile
return $RETVAL
}
case "$1" in
start|stop)
do_${1}
;;
restart|reload|force-reload)
do_stop
do_start
;;
status)
status -p $PIDFILE python
;;
*)
echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
exit 1
;;
esac
exit 0

View File

@ -0,0 +1 @@
client

View File

@ -0,0 +1 @@
/etc/domain.yml

View File

@ -0,0 +1 @@
/home/amgmt/.ssh/id_rsa.pub

View File

@ -0,0 +1 @@
lab

View File

@ -0,0 +1 @@
/home/amgmt/.ansible/vault.txt

View File

@ -0,0 +1 @@
public

View File

@ -0,0 +1,10 @@
{
#
# 00template_defaults
#
$localAccess = '';
$port = ${'doraemon'}{TCPPort} || "3000";
"";
}

View File

@ -0,0 +1,127 @@
{
$OUT .= <<HERE;
Listen $port
HostnameLookups off
ServerAdmin root@$DomainName
ServerRoot /etc/httpd
ServerTokens ProductOnly
User srvmgr
Group srvmgr
ErrorLog /var/log/doraemon/error_log
LogLevel warn
HERE
foreach (qw(
env
log_config
mime
negotiation
include
autoindex
dir
alias
rewrite
access
authz_host
expires
headers
setenvif
ssl
))
{
next unless -f "/usr/lib/httpd/modules/mod_${_}.so" ||
-f "/usr/lib64/httpd/modules/mod_${_}.so";
$OUT .= "LoadModule ${_}_module modules/mod_${_}.so\n";
}
$OUT .= <<HERE;
PidFile /var/run/doraemon.pid
ScoreBoardFile /var/run/doraemon.scoreboard
UseCanonicalName off
LogFormat "%h %l %u %t \\"%r\\" %>s %b" common
LogFormat "%{User-agent}i" agent
CustomLog /var/log/doraemon/access_log common
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 15
MaxClients 150
MaxRequestsPerChild 100
MinSpareServers 1
MaxSpareServers 5
StartServers 1
Timeout 300
DefaultIcon /icons/unknown.gif
DirectoryIndex index.htm index.html index.shtml index.cgi
IndexOptions FancyIndexing VersionSort NameWidth=*
IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t
AccessFileName .htaccess
AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
AddIconByType (TXT,/icons/text.gif) text/*
AddIconByType (IMG,/icons/image2.gif) image/*
AddIconByType (SND,/icons/sound2.gif) audio/*
AddIconByType (VID,/icons/movie.gif) video/*
DefaultType text/plain
TypesConfig /etc/mime.types
AddEncoding x-compress Z
AddEncoding x-gzip gz
AddIcon /icons/binary.gif .bin .exe
AddIcon /icons/binhex.gif .hqx
AddIcon /icons/tar.gif .tar
AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
AddIcon /icons/a.gif .ps .ai .eps
AddIcon /icons/layout.gif .html .shtml .htm .pdf
AddIcon /icons/text.gif .txt
AddIcon /icons/c.gif .c
AddIcon /icons/p.gif .pl .py
AddIcon /icons/f.gif .for
AddIcon /icons/dvi.gif .dvi
AddIcon /icons/uuencoded.gif .uu
AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
AddIcon /icons/tex.gif .tex
AddIcon /icons/bomb.gif core
AddIcon /icons/back.gif ..
AddIcon /icons/hand.right.gif README
AddIcon /icons/folder.gif ^^DIRECTORY^^
AddIcon /icons/blank.gif ^^BLANKICON^^
AddLanguage en .en
AddLanguage fr .fr
AddLanguage de .de
AddLanguage da .da
AddLanguage el .el
AddLanguage it .it
LanguagePriority en it de fr
AddType text/html .shtml
AddType application/x-pkcs7-crl .crl
AddType application/x-x509-ca-cert .crt
BrowserMatch "Mozilla/2" nokeepalive
BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0
BrowserMatch "RealPlayer 4\.0" force-response-1.0
BrowserMatch "Java/1\.0" force-response-1.0
BrowserMatch "JDK/1\.0" force-response-1.0
AddHandler cgi-script .cgi
AddHandler server-parsed .shtml
AddHandler imap-file map
HERE
}

View File

@ -0,0 +1,16 @@
# First, we configure the "default" to be a very restrictive set of
# permissions.
<Directory />
Options None
AllowOverride None
order deny,allow
deny from all
allow from none
</Directory>
<Directory /var/www/icons>
Allow from all
</Directory>

View File

@ -0,0 +1,23 @@
DocumentRoot /usr/share/doraemon
Alias /icons/ /var/www/icons/
<Directory /usr/share/doraemon/>
Options +Indexes
Order Deny,Allow
Deny from all
Allow from { $localAccess || 'all' }
AddType application/x-httpd-php .php
Options +FollowSymlinks
RewriteEngine on
RewriteBase /
RewriteRule ^(js/|fonts/|css/|images/|icons/|.+\.php) - [PT,L]
RewriteRule ^favicon.ico$ images/favicon.ico [L]
RewriteRule ^(.*)$ index.php/$1 [L]
php_value session.save_path /var/cache/doraemon
php_flag magic_quotes_gpc off
</Directory>

View File

@ -0,0 +1 @@
Include conf.d/php.conf

0
root/etc/httpd/doraemon/.gitignore vendored Normal file
View File

View File

@ -0,0 +1,9 @@
/var/log/doraemon/*log {
missingok
notifempty
sharedscripts
delaycompress
postrotate
/sbin/reload doraemon > /dev/null 2>/dev/null || true
endscript
}

View File

@ -0,0 +1,8 @@
#
# This file can be used to set additional environment variables for
# the httpd-admin process, or pass additional options to the
# httpd-admin executable. See httpd-admin.service unit.
#
LANG=C

View File

@ -0,0 +1,9 @@
[Unit]
Description=Graceful restart of doraemon service
Documentation=https://github.com/bglug-it/doraemon
Requisite=doraemon.service
[Service]
Type=oneshot
ExecStart=/usr/sbin/httpd -f /etc/httpd/doraemon/httpd.conf -k graceful

View File

@ -0,0 +1,9 @@
[Unit]
Description=Delayed graceful restart of doraemon service
Documentation=https://github.com/bglug-it/doraemon
Conflicts=doraemon-reload.service
[Timer]
OnActiveSec=5

View File

@ -0,0 +1,14 @@
[Unit]
Description=Doraemon httpd instance
Documentation=https://github.com/bglug-it/doraemon
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=simple
EnvironmentFile=/etc/sysconfig/doraemon
ExecStart=/usr/sbin/httpd -f /etc/httpd/doraemon/httpd.conf -c 'MaxConnectionsPerChild 12' -DFOREGROUND
ExecReload=/usr/sbin/httpd -f /etc/httpd/doraemon/httpd.conf -k graceful
LimitNOFILE=4096
[Install]
WantedBy=multi-user.target

1
root/usr/sbin/doraemon Symbolic link
View File

@ -0,0 +1 @@
httpd