Skip to content

Commit

Permalink
[openSUSE] adapt to shellinabox package differences rockstor#2006
Browse files Browse the repository at this point in the history
A recently available OBS shells.repo hosted shellinabox package had
breaking differences from our prior epel CentOS pacakge. The following
code changes were made to support both packages concurrently:

- Move to re-configure service on every service enable and black format
the associated file: shellinaboxd_service.py, with minor refactoring.
- Remove build time 'initial' config file instantiation, along with the
prior listed change this establishes a single point, within code, where
our desired configuration is written. This also ensures we overwrite
package default config prior to initial service start.
- Establish distro specific configuration options: the proposed openSUSE
shells repo package has a different certificates location.
- Go with both package defaults re user/group for running the
shellinabox deamon.
- Account for differing default configuration file names.
- Account for differing systemd service file names by normalising on our
existing 'shellinaboxd' and establishing this if not found during the
initrock.py script run by rockstor-pre.service.
- Improve comments in prep_db.py pertaining to shellinaboxd + black
format.
- Remove unused status() and associated import from shell.py.
- Pass systemd service name to shell.py to reduce hardcoded instances.
- Move shell.py to string.format + black formatting.
- Fix existing bug where shellinabox config file options had no closing
quote and carriage return.
  • Loading branch information
phillxnet committed Mar 2, 2020
1 parent f3f6cd8 commit 8d6b17f
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 79 deletions.
6 changes: 0 additions & 6 deletions buildout.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ parts =
# postgres-conf
gunicorn
nginx-conf
shellinabox-conf
stop-shellinabox
django-settings-conf
test-settings-conf
Expand Down Expand Up @@ -60,11 +59,6 @@ recipe = collective.recipe.template
input = ${buildout:directory}/conf/nginx.conf.in
output = ${buildout:directory}/etc/nginx/nginx.conf

[shellinabox-conf]
recipe = collective.recipe.template
input = ${buildout:directory}/conf/shellinaboxd.in
output = /etc/sysconfig/shellinaboxd

[stop-shellinabox]
recipe = plone.recipe.command
command = systemctl stop shellinaboxd
Expand Down
20 changes: 14 additions & 6 deletions conf/settings.conf.in
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,20 @@ NUT_LISTEN_ON_IP = '0.0.0.0'
NUT_SYSTEM_SHUTDOWNCMD = '/sbin/shutdown -h +0'

# Shell In A Box base settings
SHELLINABOX = {
'user': 'root',
'group': 'root',
'port': '4200',
'certs': '/var/lib/shellinabox'
}
if distro.id() == 'rockstor':
SHELLINABOX = {
'user': 'shellinabox',
'group': 'shellinabox',
'port': '4200',
'certs': '/var/lib/shellinabox'
}
else:
SHELLINABOX = {
'user': 'shellinabox',
'group': 'shellinabox',
'port': '4200',
'certs': '/etc/shellinabox/certs'
}

UPDATE_CHANNELS = {
'stable': {
Expand Down
7 changes: 0 additions & 7 deletions conf/shellinaboxd.in

This file was deleted.

24 changes: 24 additions & 0 deletions src/rockstor/scripts/initrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,29 @@ def require_postgres(logging):
return logging.info('systemd daemon reloaded')


def establish_shellinaboxd_service(logging):
"""
Normalise on shellinaboxd as service name for shellinabox package.
The https://download.opensuse.org/repositories/shells shellinabox package
( https://build.opensuse.org/package/show/shells/shellinabox ) uses a
systemd service name of shellinabox.
If we find no shellinaboxd service file and there exists a shellinabox one
create a copy to enable us to normalise on shellinaboxd and avoid carrying
another package just to implement this service name change as we are
heavily invested in the shellinaboxd service name.
:param logging: handle to logger.
:return: logger handle.
"""
logging.info("Normalising on shellinaboxd service file")
required_sysd_name = "/usr/lib/systemd/system/shellinaboxd.service"
opensuse_sysd_name = "/usr/lib/systemd/system/shellinabox.service"
if os.path.exists(required_sysd_name):
return logging.info("- shellinaboxd.service already exists")
if os.path.exists(opensuse_sysd_name):
shutil.copyfile(opensuse_sysd_name, required_sysd_name)
run_command([SYSCTL, "daemon-reload"])
return logging.info("- established shellinaboxd.service file")

def enable_rockstor_service(logging):
rs_dest = '/etc/systemd/system/rockstor.service'
rs_src = '%s/conf/rockstor.service' % BASE_DIR
Expand Down Expand Up @@ -436,6 +459,7 @@ def main():
shutil.copyfile('/etc/issue', '/etc/issue.rockstor')
init_update_issue(logging)

establish_shellinaboxd_service(logging)
enable_rockstor_service(logging)
enable_bootstrap_service(logging)

Expand Down
48 changes: 26 additions & 22 deletions src/rockstor/scripts/prep_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,46 @@

def register_services():
services = {
'AFP': 'netatalk',
'NFS': 'nfs',
'Samba': 'smb',
'NIS': 'nis',
'NTP': 'ntpd',
'Active Directory': 'active-directory',
'LDAP': 'ldap',
'SFTP': 'sftp',
'Replication': 'replication',
'SNMP': 'snmpd',
'Rock-on': 'docker',
'S.M.A.R.T': 'smartd',
'NUT-UPS': 'nut',
'ZTaskd': 'ztask-daemon',
'Bootstrap': 'rockstor-bootstrap',
'Shell In A Box': 'shellinaboxd',
'Rockstor': 'rockstor'
}
"AFP": "netatalk",
"NFS": "nfs",
"Samba": "smb",
"NIS": "nis",
"NTP": "ntpd",
"Active Directory": "active-directory",
"LDAP": "ldap",
"SFTP": "sftp",
"Replication": "replication",
"SNMP": "snmpd",
"Rock-on": "docker",
"S.M.A.R.T": "smartd",
"NUT-UPS": "nut",
"ZTaskd": "ztask-daemon",
"Bootstrap": "rockstor-bootstrap",
"Shell In A Box": "shellinaboxd",
"Rockstor": "rockstor",
}

# N.B. all other services have null as their default config with service.
# Consider bringing shellinaboxd in line with this now default behaviour.
services_configs = {
'shellinaboxd': ('{"detach": false, "css": "white-on-black", '
'"shelltype": "LOGIN"}')
}
"shellinaboxd": (
'{"detach": false, "css": "white-on-black", ' '"shelltype": "LOGIN"}'
)
}

for k, v in services.items():
try:
so = Service.objects.get(name=v)
so.display_name = k
# Apply any configuration defaults found in services_configs.
if v in services_configs:
so.config = services_configs[v]
except Service.DoesNotExist:
so = Service(display_name=k, name=v)
finally:
so.save()
for so in Service.objects.filter():
if (so.display_name not in services):
if so.display_name not in services:
so.delete()


Expand Down
41 changes: 24 additions & 17 deletions src/rockstor/smart_manager/views/shellinaboxd_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,37 +15,44 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

from rest_framework.response import Response
from system.services import systemctl
from base_service import BaseServiceDetailView
from smart_manager.models import Service
from system.shell import (update_shell_config, restart_shell)

from system.shell import update_shell_config, restart_shell
import json
import logging

logger = logging.getLogger(__name__)


class ShellInABoxServiceView(BaseServiceDetailView):
name = 'shellinaboxd'
service_name = "shellinaboxd"

def post(self, request, command):
service = Service.objects.get(name=self.name)
service = Service.objects.get(name=self.service_name)

if (command == 'config'):
config = request.data.get('config')
if command == "config":
config = request.data.get("config")
self._save_config(service, config)
shelltype = config.get('shelltype')
css = config.get('css')
shelltype = config.get("shelltype")
css = config.get("css")
update_shell_config(shelltype, css)
restart_shell()

elif (command == 'start'):
systemctl(self.name, 'enable')
systemctl(self.name, 'start')
restart_shell(self.service_name)

elif command == "start":
# Assert config from db to re-establish our config file.
# Avoids using package default config on first enable.
# TODO: config assert every time is a little heavy / overkill.
config = json.loads(service.config)
shelltype = config.get("shelltype")
css = config.get("css")
update_shell_config(shelltype, css)
systemctl(self.service_name, "enable")
systemctl(self.service_name, "start")

elif (command == 'stop'):
systemctl(self.name, 'stop')
systemctl(self.name, 'disable')
elif command == "stop":
systemctl(self.service_name, "stop")
systemctl(self.service_name, "disable")

return Response()
45 changes: 24 additions & 21 deletions src/rockstor/system/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,50 @@
"""

from osi import run_command
from services import service_status
import shutil
from tempfile import mkstemp
from django.conf import settings

SHELL_CONFIG = '/etc/sysconfig/shellinaboxd'
SYSTEMCTL = '/usr/bin/systemctl'
# Config file for /repositories/shells/.../shells.repo shellinabox package.
# This package references the following config file in it's service file.
SHELL_CONFIG = "/etc/sysconfig/shellinabox"
PREFIX = "SHELLINABOXD_"
# The to-be-legacy CentOS shellinabox package config
if settings.OS_DISTRO_ID == "rockstor":
SHELL_CONFIG = "/etc/sysconfig/shellinaboxd"
PREFIX = ""
SYSTEMCTL = "/usr/bin/systemctl"


def update_shell_config(shelltype='LOGIN', css='white-on-black'):
def update_shell_config(shelltype="LOGIN", css="white-on-black"):
fh, npath = mkstemp()
with open(npath, 'w') as tfo:
with open(npath, "w") as tfo:
# Write shellinaboxd default config
tfo.write('# Shell In A Box configured by Rockstor\n\n')
tfo.write('USER=%s\n' % settings.SHELLINABOX.get('user'))
tfo.write('GROUP=%s\n' % settings.SHELLINABOX.get('group'))
tfo.write('CERTDIR=%s\n' % settings.SHELLINABOX.get('certs'))
tfo.write('PORT=%s\n' % settings.SHELLINABOX.get('port'))
tfo.write("# Shell In A Box configured by Rockstor\n\n")
tfo.write("{}USER={}\n".format(PREFIX, settings.SHELLINABOX.get("user")))
tfo.write("{}GROUP={}\n".format(PREFIX, settings.SHELLINABOX.get("group")))
tfo.write("{}CERTDIR={}\n".format(PREFIX, settings.SHELLINABOX.get("certs")))
tfo.write("{}PORT={}\n".format(PREFIX, settings.SHELLINABOX.get("port")))
# Add config customization for css and shelltype
# IMPORTANT
# --localhost-only to block shell direct access and because behind
# nginx --disable-ssl because already on rockstor ssl --no-beep to
# avoid sounds and possible crashes under FF - see man pages
tfo.write('OPTS="--no-beep --localhost-only --disable-ssl ')
tfo.write('{}OPTS="--no-beep --localhost-only --disable-ssl '.format(PREFIX))
# Switch between LOGIN connection and SSH connection LOGIN connection
# only uid > 1000 allowed, su required to become root\n SSH connection
# root login allowed too
tfo.write('-s /:%s ' % shelltype)
tfo.write("-s /:{} ".format(shelltype))
# If white on black add --css option Default black on white --css
# option not required / shellinaboxd fails
if (css == 'white-on-black'):
tfo.write('--css %s.css' % css)

if css == "white-on-black":
tfo.write("--css {}.css".format(css))
# And we finally close quote and carriage return the compound OPTS line:
tfo.write('"\n')
shutil.move(npath, SHELL_CONFIG)


def restart_shell():
def restart_shell(sysd_name):
# simply restart shellinaboxd service No return code checks because rc!=0
# documented also for nicely running state
return run_command([SYSTEMCTL, 'restart', 'shellinaboxd'])


def status():
return service_status('shellinaboxd')
return run_command([SYSTEMCTL, "restart", sysd_name])

0 comments on commit 8d6b17f

Please sign in to comment.