mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-04-29 10:10:19 +00:00
Rewrite plinth to use sudo and action scripts instead of exmachina for privileged accesss.
This commit is contained in:
parent
0954d9d383
commit
444365ec78
4
Makefile
4
Makefile
@ -29,9 +29,9 @@ install: default
|
|||||||
mkdir -p $(DESTDIR)$(PYDIR) $(DESTDIR)$(DATADIR) $(DESTDIR)/usr/bin \
|
mkdir -p $(DESTDIR)$(PYDIR) $(DESTDIR)$(DATADIR) $(DESTDIR)/usr/bin \
|
||||||
$(DESTDIR)/usr/share/doc/plinth $(DESTDIR)/usr/share/man/man1
|
$(DESTDIR)/usr/share/doc/plinth $(DESTDIR)/usr/share/man/man1
|
||||||
cp -a static themes $(DESTDIR)$(DATADIR)/
|
cp -a static themes $(DESTDIR)$(DATADIR)/
|
||||||
|
cp -a actions $(DESTDIR)$(DATADIR)/
|
||||||
|
cp -a sudoers.d $(DESTDIR)/etc/sudoers.d
|
||||||
cp -a *.py modules templates $(DESTDIR)$(PYDIR)/
|
cp -a *.py modules templates $(DESTDIR)$(PYDIR)/
|
||||||
mkdir -p $(DESTDIR)$(PYDIR)/exmachina
|
|
||||||
cp -a vendor/exmachina/exmachina.py $(DESTDIR)$(PYDIR)/exmachina/.
|
|
||||||
cp share/init.d/plinth $(DESTDIR)/etc/init.d
|
cp share/init.d/plinth $(DESTDIR)/etc/init.d
|
||||||
install plinth $(DESTDIR)/usr/bin/
|
install plinth $(DESTDIR)/usr/bin/
|
||||||
mkdir -p $(DESTDIR)/var/lib/plinth/cherrypy_sessions $(DESTDIR)/var/log/plinth $(DESTDIR)/var/run
|
mkdir -p $(DESTDIR)/var/lib/plinth/cherrypy_sessions $(DESTDIR)/var/log/plinth $(DESTDIR)/var/run
|
||||||
|
|||||||
3
README
3
README
@ -43,9 +43,6 @@ get down into the details and configure things the average user never
|
|||||||
thinks about. For example, experts can turn off ntp or switch ntp
|
thinks about. For example, experts can turn off ntp or switch ntp
|
||||||
servers. Basic users should never even know those options exist.
|
servers. Basic users should never even know those options exist.
|
||||||
|
|
||||||
See comments in exmachina/exmachina.py for more details about the
|
|
||||||
configuration management process seperation scheme.
|
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
See the INSTALL file for additional details. Run:
|
See the INSTALL file for additional details. Run:
|
||||||
|
|||||||
10
actions/hostname-change
Executable file
10
actions/hostname-change
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
hostname="$1"
|
||||||
|
|
||||||
|
echo "$hostname" > /etc/hostname
|
||||||
|
if [ -x /etc/init.d/hostname.sh ] ; then
|
||||||
|
service hostname.sh start
|
||||||
|
else
|
||||||
|
service hostname start
|
||||||
|
fi
|
||||||
14
actions/timezone-change
Executable file
14
actions/timezone-change
Executable file
@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
zonename="$1"
|
||||||
|
|
||||||
|
tzpath="/usr/share/zoneinfo/$zonename"
|
||||||
|
|
||||||
|
if [ -e "$tzpath" ] ; then
|
||||||
|
cp "$tzpath" /etc/localtime
|
||||||
|
echo "$zonename" > /etc/timezone
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "timezone not valid" 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@ -9,6 +9,7 @@ from gettext import gettext as _
|
|||||||
from filedict import FileDict
|
from filedict import FileDict
|
||||||
from modules.auth import require
|
from modules.auth import require
|
||||||
from plugin_mount import PagePlugin, FormPlugin
|
from plugin_mount import PagePlugin, FormPlugin
|
||||||
|
from privilegedactions import privilegedaction_run
|
||||||
import cfg
|
import cfg
|
||||||
from forms import Form
|
from forms import Form
|
||||||
from model import User
|
from model import User
|
||||||
@ -48,20 +49,14 @@ def get_hostname():
|
|||||||
|
|
||||||
def set_hostname(hostname):
|
def set_hostname(hostname):
|
||||||
"Sets machine hostname to hostname"
|
"Sets machine hostname to hostname"
|
||||||
cfg.log.info("Writing '%s' to /etc/hostname with exmachina" % hostname)
|
cfg.log.info("Changing hostname to '%s'" % hostname)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cfg.exmachina.augeas.set("/files/etc/hostname/*", hostname)
|
privilegedaction_run("hostname-change", [hostname])
|
||||||
cfg.exmachina.augeas.save()
|
|
||||||
# don't persist/cache change unless it was saved successfuly
|
# don't persist/cache change unless it was saved successfuly
|
||||||
sys_store = filedict_con(cfg.store_file, 'sys')
|
sys_store = filedict_con(cfg.store_file, 'sys')
|
||||||
sys_store['hostname'] = hostname
|
sys_store['hostname'] = hostname
|
||||||
if platform.linux_distribution()[0]=="Ubuntu" :
|
|
||||||
cfg.exmachina.service.start("hostname")
|
|
||||||
else:
|
|
||||||
cfg.exmachina.initd.start("hostname.sh") # is hostname.sh debian-only?
|
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
raise cherrypy.HTTPError(500, "Hostname restart failed: %s" % e)
|
raise cherrypy.HTTPError(500, "Updating hostname failed: %s" % e)
|
||||||
|
|
||||||
class general(FormPlugin, PagePlugin):
|
class general(FormPlugin, PagePlugin):
|
||||||
url = ["/sys/config"]
|
url = ["/sys/config"]
|
||||||
@ -79,7 +74,7 @@ class general(FormPlugin, PagePlugin):
|
|||||||
return '<p>' + _('Only members of the expert group are allowed to see and modify the system setup.') + '</p>'
|
return '<p>' + _('Only members of the expert group are allowed to see and modify the system setup.') + '</p>'
|
||||||
|
|
||||||
sys_store = filedict_con(cfg.store_file, 'sys')
|
sys_store = filedict_con(cfg.store_file, 'sys')
|
||||||
hostname = cfg.exmachina.augeas.get("/files/etc/hostname/*")
|
hostname = get_hostname()
|
||||||
# this layer of persisting configuration in sys_store could/should be
|
# this layer of persisting configuration in sys_store could/should be
|
||||||
# removed -BLN
|
# removed -BLN
|
||||||
defaults = {'time_zone': "slurp('/etc/timezone').rstrip()",
|
defaults = {'time_zone': "slurp('/etc/timezone').rstrip()",
|
||||||
@ -139,10 +134,10 @@ class general(FormPlugin, PagePlugin):
|
|||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
message += msg
|
message += msg
|
||||||
|
time_zone = time_zone.strip()
|
||||||
if time_zone != sys_store['time_zone']:
|
if time_zone != sys_store['time_zone']:
|
||||||
src = os.path.join("/usr/share/zoneinfo", time_zone)
|
|
||||||
cfg.log.info("Setting timezone to %s" % time_zone)
|
cfg.log.info("Setting timezone to %s" % time_zone)
|
||||||
cfg.exmachina.misc.set_timezone(time_zone)
|
privilegedaction_run("timezone-change", [time_zone])
|
||||||
sys_store['time_zone'] = time_zone
|
sys_store['time_zone'] = time_zone
|
||||||
return message or "Settings updated."
|
return message or "Settings updated."
|
||||||
|
|
||||||
|
|||||||
23
plinth.py
23
plinth.py
@ -18,7 +18,6 @@ from logger import Logger
|
|||||||
#from modules.auth import AuthController, require, member_of, name_is
|
#from modules.auth import AuthController, require, member_of, name_is
|
||||||
|
|
||||||
from withsqlite.withsqlite import sqlite_db
|
from withsqlite.withsqlite import sqlite_db
|
||||||
from exmachina.exmachina import ExMachinaClient
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
__version__ = "0.2.14"
|
__version__ = "0.2.14"
|
||||||
@ -84,8 +83,6 @@ def parse_arguments():
|
|||||||
parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.')
|
parser = argparse.ArgumentParser(description='Plinth web interface for the FreedomBox.')
|
||||||
parser.add_argument('--pidfile', default="",
|
parser.add_argument('--pidfile', default="",
|
||||||
help='specify a file in which the server may write its pid')
|
help='specify a file in which the server may write its pid')
|
||||||
parser.add_argument('--listen-exmachina-key', default=False, action='store_true',
|
|
||||||
help='listen for JSON-RPC shared secret key on stdin at startup')
|
|
||||||
args=parser.parse_args()
|
args=parser.parse_args()
|
||||||
if args.pidfile:
|
if args.pidfile:
|
||||||
cfg.pidfile = args.pidfile
|
cfg.pidfile = args.pidfile
|
||||||
@ -96,13 +93,6 @@ def parse_arguments():
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
cfg.pidfile = "plinth.pid"
|
cfg.pidfile = "plinth.pid"
|
||||||
|
|
||||||
if args.listen_exmachina_key:
|
|
||||||
# this is where we optionally try to read in a shared secret key to
|
|
||||||
# authenticate connections to exmachina
|
|
||||||
cfg.exmachina_secret_key = sys.stdin.readline().strip()
|
|
||||||
else:
|
|
||||||
cfg.exmachina_secret_key = None
|
|
||||||
|
|
||||||
def setup():
|
def setup():
|
||||||
parse_arguments()
|
parse_arguments()
|
||||||
|
|
||||||
@ -113,19 +103,6 @@ def setup():
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
try:
|
|
||||||
from vendor.exmachina.exmachina import ExMachinaClient
|
|
||||||
except ImportError:
|
|
||||||
cfg.exmachina = None
|
|
||||||
print "unable to import exmachina client library, but continuing anyways..."
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
cfg.exmachina = ExMachinaClient(
|
|
||||||
secret_key=cfg.exmachina_secret_key or None)
|
|
||||||
except socket.error:
|
|
||||||
cfg.exmachina = None
|
|
||||||
print "couldn't connect to exmachina daemon, but continuing anyways..."
|
|
||||||
|
|
||||||
os.chdir(cfg.python_root)
|
os.chdir(cfg.python_root)
|
||||||
cherrypy.config.update({'error_page.404': error_page_404})
|
cherrypy.config.update({'error_page.404': error_page_404})
|
||||||
cherrypy.config.update({'error_page.500': error_page_500})
|
cherrypy.config.update({'error_page.500': error_page_500})
|
||||||
|
|||||||
15
privilegedactions.py
Normal file
15
privilegedactions.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
import cfg
|
||||||
|
|
||||||
|
def privilegedaction_run(action, options):
|
||||||
|
cmd = ['sudo', '-n', "/usr/share/plinth/actions/%s" % action]
|
||||||
|
if options:
|
||||||
|
cmd.extend(options)
|
||||||
|
cfg.log.info('running: %s ' % ' '.join(cmd))
|
||||||
|
|
||||||
|
output, error = \
|
||||||
|
subprocess.Popen(cmd,
|
||||||
|
stdout = subprocess.PIPE,
|
||||||
|
stderr= subprocess.PIPE).communicate()
|
||||||
|
return output, error
|
||||||
@ -7,21 +7,17 @@
|
|||||||
# Default-Stop: 0 1 6
|
# Default-Stop: 0 1 6
|
||||||
# Short-Description: plinth web frontend
|
# Short-Description: plinth web frontend
|
||||||
# Description:
|
# Description:
|
||||||
# Control the exmachina privileged execution daemon and the plinth
|
# Control the plinth web frontend.
|
||||||
# web frontend.
|
|
||||||
### END INIT INFO
|
### END INIT INFO
|
||||||
|
|
||||||
# This file is /etc/init.d/plinth
|
# This file is /etc/init.d/plinth
|
||||||
DAEMON=/usr/local/bin/plinth.py
|
DAEMON=/usr/local/bin/plinth.py
|
||||||
EXMACHINA_DAEMON=/usr/local/bin/exmachina.py
|
|
||||||
PID_FILE=/var/run/plinth.pid
|
PID_FILE=/var/run/plinth.pid
|
||||||
EXMACHINA_PID_FILE=/var/run/exmachina.pid
|
|
||||||
|
|
||||||
PLINTH_USER=www-data
|
PLINTH_USER=www-data
|
||||||
PLINTH_GROUP=www-data
|
PLINTH_GROUP=www-data
|
||||||
|
|
||||||
test -x $DAEMON || exit 0
|
test -x $DAEMON || exit 0
|
||||||
test -x $EXMACHINA_DAEMON || exit 0
|
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@ -31,17 +27,9 @@ start_plinth (){
|
|||||||
if [ -f $PID_FILE ]; then
|
if [ -f $PID_FILE ]; then
|
||||||
echo Already running with a pid of `cat $PID_FILE`.
|
echo Already running with a pid of `cat $PID_FILE`.
|
||||||
else
|
else
|
||||||
if [ -f $EXMACHINA_PID_FILE ]; then
|
|
||||||
echo exmachina was already running with a pid of `cat $EXMACHINA_PID_FILE`.
|
|
||||||
kill -15 `cat $EXMACHINA_PID_FILE`
|
|
||||||
rm -rf $EXMACHINA_PID_FILE
|
|
||||||
fi
|
|
||||||
SHAREDKEY=`$EXMACHINA_DAEMON --random-key`
|
|
||||||
touch $PID_FILE
|
touch $PID_FILE
|
||||||
chown $PLINTH_USER:$PLINTH_GROUP $PID_FILE
|
chown $PLINTH_USER:$PLINTH_GROUP $PID_FILE
|
||||||
echo $SHAREDKEY | $EXMACHINA_DAEMON --pidfile=$EXMACHINA_PID_FILE --group=$PLINTH_GROUP || rm $PID_FILE
|
sudo -u $PLINTH_USER -g $PLINTH_GROUP $DAEMON --pidfile=$PID_FILE
|
||||||
sleep 0.5
|
|
||||||
echo $SHAREDKEY | sudo -u $PLINTH_USER -g $PLINTH_GROUP $DAEMON --pidfile=$PID_FILE
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,13 +41,6 @@ stop_plinth () {
|
|||||||
else
|
else
|
||||||
echo "No pid file at $PID_FILE suggests plinth is not running."
|
echo "No pid file at $PID_FILE suggests plinth is not running."
|
||||||
fi
|
fi
|
||||||
if [ -f $EXMACHINA_PID_FILE ]; then
|
|
||||||
kill -15 `cat $EXMACHINA_PID_FILE` || true
|
|
||||||
rm -rf $EXMACHINA_PID_FILE
|
|
||||||
echo "killed exmachina"
|
|
||||||
else
|
|
||||||
echo "No pid file at $EXMACHINA_PID_FILE suggests exmachina is not running."
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test -x $DAEMON || exit 0
|
test -x $DAEMON || exit 0
|
||||||
|
|||||||
5
start.sh
5
start.sh
@ -1,10 +1,7 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
|
|
||||||
#PYTHONPATH=vendor/exmachina:$PYTHONPATH
|
#PYTHONPATH=$PYTHONPATH
|
||||||
|
|
||||||
export PYTHONPATH
|
export PYTHONPATH
|
||||||
|
|
||||||
sudo killall exmachina.py
|
|
||||||
sudo vendor/exmachina/exmachina.py -v &
|
|
||||||
python plinth.py
|
python plinth.py
|
||||||
sudo killall exmachina.py
|
|
||||||
|
|||||||
1
sudoers.d/plinth
Normal file
1
sudoers.d/plinth
Normal file
@ -0,0 +1 @@
|
|||||||
|
plinth ALL=(ALL:ALL) NOPASSWD:/usr/share/plinth/actions/*
|
||||||
Loading…
x
Reference in New Issue
Block a user