Commit 8b1623b4 authored by Paul Cammish's avatar Paul Cammish
Browse files

Merge branch 'rename_admin_user_to_sympl' into 'master'

Rename admin user to sympl, secure permissions

See merge request sympl/sympl!24
parents ca1ac43a b820b965
require 'symbiosis/hooks'
require 'symbiosis/utils'
require 'pathname'
module Symbiosis
# Manages copying a domain skeleton into a freshly-made domain
class DomainSkeleton
attr_reader :skel_dir
def initialize(skel_dir = Symbiosis.path_in_etc('sympl', 'skel.d'))
@skel_dir = skel_dir
end
def params
Dir.glob(File.join(@skel_dir, '**', '*'))
.select { |f| File.file?(f) }
end
def should_populate?(domain)
Dir.mkdir domain.config_dir
rescue Errno::EEXIST
false
end
# after running should_populate? the config directory will have the wrong
# uid, so set it right
def ensure_config_owner(domain)
File.lchown domain.uid, domain.gid, domain.config_dir
end
def path_relative_to_skel(path)
skel = Pathname.new(@skel_dir)
pathname = Pathname.new(path)
pathname.relative_path_from(skel).to_s
end
def read_file(rel_path)
verbose "Reading skeleton #{rel_path}"
src_path = File.join(@skel_dir, rel_path)
Symbiosis::Utils.safe_open(src_path, File::RDONLY, &:read)
end
def write_file!(new_path, domain, contents)
verbose "Writing #{new_path}"
Symbiosis::Utils.safe_open(new_path, File::WRONLY | File::CREAT,
mode: 0o644,
uid: domain.uid,
gid: domain.gid) do |fh|
fh.truncate(0)
fh.write(contents)
end
end
def copy_file!(rel_path, domain)
contents = read_file rel_path
new_path = File.join domain.directory, rel_path
new_dir = File.dirname new_path
verbose "Ensuring #{new_dir} exists"
Symbiosis::Utils.mkdir_p new_dir
write_file! new_path, domain, contents
end
def copy!(domain)
params.each do |path|
copy_file! path_relative_to_skel(path), domain
end
true
end
# returns an array of key-value pair arrays
# where the key is the domain name and the
# value is an error or nil. If nil the copy for that
# domain was successful.
def try_copy!(domains)
domains.map do |domain|
begin
ensure_config_owner(domain)
verbose "Copying skeleton to #{domain.directory}..."
copy! domain
verbose "Copy completed for #{domain.directory}"
[domain.name, nil]
rescue StandardError => e
warn "Error copying to #{domain.directory} - #{e}"
puts e.backtrace.join("\n")
[domain.name, e]
end
end
end
def populate!(domains)
verbose 'Checking which domains to populate...'
domains = domains.select { |domain| should_populate? domain }
verbose "Populating [#{domains.join(', ')}]"
# convert [ [key, value], ... ] from try_copy! to a hash
Hash[try_copy!(domains)]
end
def verbose(str)
warn str if $VERBOSELOCAL
end
# Hooks for DomainSkeleton
# by default these live in /etc/sympl/skel-hooks.d
class Hooks < Symbiosis::Hooks
HOOKS_DIR = File.join('sympl', 'skel-hooks.d')
def self.run!(event, domains)
Symbiosis::DomainSkeleton::Hooks.new.run!(event, domains)
end
def initialize(hooks_dir = Symbiosis.path_in_etc(HOOKS_DIR))
super hooks_dir
end
end
end
end
......@@ -251,9 +251,11 @@ module Symbiosis
parent_dir_stat = File.stat(parent_dir)
#
# Refuse to write to directories owned by UIDs < 1000.
# Refuse to write to directories not owned UID 33 (www-data) or UID >= 1000
#
raise ArgumentError, "Parent directory #{parent_dir} is owned by a system user." unless parent_dir_stat.uid >= 1000
if ( parent_dir_stat.uid < 1000 && parent_dir_stat.uid != 33 )
raise ArgumentError, "Parent directory #{parent_dir} is owned by a system user other than www-data."
end
if false == value or value.nil?
......
#!/bin/bash
# Fairly simple bash script to enforce filesystem permissions for sensitive
# directories used by Sympl.
#
# Copyright 2019, Paul Cammish <sympl@kelduum.net>
set -e
if [ -f /etc/sympl/do-not-secure ]; then exit 0; fi
function secure_domain_dir()
{
domain="$1"
if [ ! -d "${domain}" ]; then
echo "E: ${domain} doesnt exist"
exit 1
fi
if [ -d ${domain}/public ]; then
# Determine uid for public from config/public-user or default to 33 (www-data)
if [ -f "${domain}/config/public-user" ]; then
public_uid="$( cat "${domain}/config/public-user" | sed 's|#.*||' | head -n 1 | grep . )"
if id -u $uid > /dev/null 2&>1 ; then
public_uid="$( id -u $public_uid )"
else
public_uid=33
fi
else
public_uid=33
fi
# Determine gid for public from config/public-group or default to 33 (www-data)
if [ -f "${domain}/config/public-group" ]; then
public_gid="$( cat "${domain}/config/public-group" | sed 's|#.*||' | head -n 1 | grep . )"
if id -g $gid > /dev/null 2&>1 ; then
public_gid="$( id -g $public_gid )"
else
public_gid=33
fi
else
public_gid=33
fi
# Add sympl use to the public group if it's >= 1000 and not already in it
if [ "$public_gid" -ge "1000" ] && [ "$(id -Gn sympl | tr ' ' '\n' | grep -c "^$( id -gn $public_gid )$" )" == "0" ]; then
# sympl is not in the $public_gid group, adding
usermod -a -G $public_gid sympl
fi
# Enforce permissions for /srv/example.org/public
find "${domain}/public" \( -type f -o -type d \) \( ! -uid ${public_uid} -o ! -gid ${public_gid} \) -exec chown ${public_uid}:${public_gid} {} \;
setfacl -R -d -m u::rwx -m g::rwx -m o::rx "${domain}/public/"
fi
# Enforce permissions for /srv/example.com/config - exim requires directory traversal (+x) as steps thorugh to the target.
if [ -d ${domain}/config ]; then
find "${domain}/config" \( -type f -o -type d \) \( ! -user sympl -o ! -group sympl \) ! -path '*ssl/sets*' -exec chown sympl:sympl {} \;
find "${domain}/config/ssl/sets" \( ! -user sympl -o ! -group ssl-cert \) -exec chown sympl:ssl-cert {} \;
find "${domain}/config" \( -type f -a ! -perm 660 -exec chmod 660 {} \; \) -o \( -type d -a ! -perm 2771 -exec chmod 2771 {} \; \)
fi
# Enforce permissions for mailboxes directory
if [ -d ${domain}/mailboxes ]; then
find "${domain}/mailboxes" \( -type f -o -type d \) \( ! -user sympl -o ! -group sympl \) -exec chown sympl:sympl {} \;
find "${domain}/mailboxes" \( -type f -a ! -perm 660 -exec chmod 600 {} \; \) -o \( -type d -a ! -perm 2700 -exec chmod 2700 {} \; \)
fi
}
# Enforce permissions on /var/backups
if [ -d /var/backups ]; then
find "/var/backups" ! -type l \( ! -user sympl -o ! -group sympl \) -exec chown sympl:sympl {} \;
find "/var/backups" ! -type l ! -perm o-rwx \( -type f -exec chmod 660 {} \; -o -type d -exec chmod 770 {} \; \)
fi
# Enforce permisions on /etc/sympl
if [ -d /etc/sympl ]; then
find "/etc/sympl" ! -type l ! -path '*/test.d/*' \( ! -user sympl -o ! -group sympl \) -exec chown sympl:sympl {} \;
find "/etc/sympl" ! -type l ! -path '*/test.d/*' ! -perm o-w \( -type f -exec chmod o-w {} \; -o -type d -exec chmod 775 {} \; \)
fi
for domain in $( find /srv -maxdepth 1 -mindepth 1 ! -type l -type d -print | grep -v '^/srv/\.' | grep '\.' ); do
if [ ! -f ${domain}/config/do-not-secure ]; then
secure_domain_dir ${domain}
fi
done
exit 0
#
# This allows the Admin user sudo access.
# This allows the Sympl user sudo access.
#
admin ALL = (ALL) ALL
sympl ALL = (ALL) ALL
# encoding: UTF-8
require 'test/unit'
require 'tmpdir'
require 'symbiosis'
require 'symbiosis/domain'
require 'symbiosis/domain_skeleton'
require './helpers.rb'
# tests for DomainSkeleton - ensure hooks work, ensure skeleton copies correctly
class TestDomainSkeleton < Test::Unit::TestCase
def setup
Process.egid = 1000 if Process.gid.zero?
Process.euid = 1000 if Process.uid.zero?
Symbiosis.etc = File.realpath Dir.mktmpdir('etc')
Symbiosis.prefix = File.realpath Dir.mktmpdir('srv')
@verbose = $VERBOSELOCAL || $DEBUG ? ' --verbose ' : ''
testd = File.dirname(__FILE__)
@script = File.expand_path(File.join(testd,"..","bin","sympl-skel"))
@script = '/usr/bin/sympl-skel' unless File.exist?(@script)
@script += @verbose
make_skeleton
end
def make_skeleton
skelpath = Symbiosis.path_in_etc('sympl/skel.d')
Symbiosis::Utils.mkdir_p File.join(skelpath, 'test', 'deep')
Symbiosis::Utils.set_param 'test-param', 'this is a test', skelpath
Symbiosis::Utils.set_param 'test-param', 'also a test', File.join(skelpath, 'test')
Symbiosis::Utils.set_param 'test-param', 'deep test', File.join(skelpath, 'test', 'deep')
@skel = Symbiosis::DomainSkeleton.new skelpath
end
def teardown
unless $DEBUG
FileUtils.rm_rf Symbiosis.etc if File.directory?(Symbiosis.etc)
FileUtils.rm_rf Symbiosis.prefix if File.directory?(Symbiosis.prefix)
end
Process.euid = 0 if Process.uid.zero?
Process.egid = 0 if Process.gid.zero?
end
def test_copy
domain = Symbiosis::Domain.new(nil, Symbiosis.prefix)
domain.create
@skel.copy! domain
assert_equal 'this is a test', domain.get_param('test-param')
assert_equal 'also a test', domain.get_param('test/test-param')
assert_equal 'deep test', domain.get_param('test/deep/test-param')
end
def test_hooks
hooks_dir = Symbiosis.path_in_etc('sympl', 'skel-hooks.d')
result = TestHelpers.make_test_hook hooks_dir
domain = Symbiosis::Domain.new(nil, Symbiosis.prefix)
domain.create
success = Symbiosis::DomainSkeleton::Hooks.run! 'domains-populated', [domain.name]
assert_equal true, success
assert_equal "domains-populated\n", result.args
assert_equal "#{domain.name}\n", result.output
end
def test_symbiosis_skel
hooks_dir = Symbiosis.path_in_etc('sympl', 'skel-hooks.d')
result = TestHelpers.make_test_hook hooks_dir
domain = Symbiosis::Domain.new(nil, Symbiosis.prefix)
domain.create
FileUtils.rm_rf domain.config_dir
pid = spawn("#{@script} --etc=#{Symbiosis.etc} --prefix=#{Symbiosis.prefix}")
_, status = Process.wait2(pid)
assert_equal 0, status.exitstatus
assert_equal "domains-populated\n", result.args
assert_equal "#{domain.name}\n", result.output
end
end
......@@ -10,7 +10,7 @@ class TestUtils < Test::Unit::TestCase
def setup
#
# The prefix has to be in a directory admin can read..
# The prefix has to be in a directory sympl can read..
#
@prefix = Dir.mktmpdir("srv","/tmp")
@prefix.freeze
......
sympl-core (9.0.190604.0) stable; urgency=medium
sympl-core (9.0.190609.0) stable; urgency=medium
* Added Beta tag to MOTDs
* Added recommends for common apps: htop, vim, nano, wget, curl
-- Paul Cammish <sympl@kelduum.net> Sun, 09 Jun 2019 22:56:00 +0100
sympl-core (9.0.190604.0) stable; urgency=medium
* Replaced banners
* Replaced mentions of Symbiosis with Sympl.
* Renamed package to sympl-backup
* Renamed package to sympl-core
-- Paul Cammish <sympl@kelduum.net> Tue, 04 Jun 2019 23:00:00 +0100
......
......@@ -30,7 +30,7 @@ Recommends: sympl-backup,
sympl-phpmyadmin,
sympl-dns,
sympl-webmail,
openssh-server
openssh-server, nano, vim, htop, strace, wget, curl
Provides: bytemark-symbiosis
Conflicts: bytemark-symbiosis
Replaces: bytemark-symbiosis
......@@ -41,7 +41,8 @@ Description: Easy, complete, and Debian-friendly server administration system
Installing this package will completely configure your system for
the virtual hosting of multiple domains with:
.
* Webhosting
* Webhosting.
* MySQL/MariaDB.
* FTP hosting.
* Email hosting (and webmail).
......@@ -27,30 +27,13 @@ SCRIPTNAME=/etc/init.d/$NAME
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions
# Allows us to use COLOURS!
if log_use_fancy_output; then
B="$($TPUT bold)"
M="$($TPUT setaf 5)"
L="$($TPUT setaf 4)"
Y="$($TPUT setaf 3)"
W="$($TPUT setaf 1)"
N="$($TPUT op)"
U="$($TPUT sgr0)"
else
B=''
L=''
Y=''
N=''
U=''
fi
case "$1" in
'start')
log_action_msg "System configured with $DESC."
cat <<EOF
┌─────┐ v9.0
└─┐ │
└─┐ │ beta
┌─────────┐ ┌───┐ ┌───┐ ┌─────────────┐ ┌───────────┐ │ │
┌─┘ ┌───────┘ │ │ │ │ │ ┌─┐ ┌─┐ └─┐ │ ┌─────┐ └─┐ │ │
│ └─┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
......
......@@ -3,7 +3,7 @@
cat <<EOF
┌─────┐ v9.0
└─┐ │
└─┐ │ beta
┌─────────┐ ┌───┐ ┌───┐ ┌─────────────┐ ┌───────────┐ │ │
┌─┘ ┌───────┘ │ │ │ │ │ ┌─┐ ┌─┐ └─┐ │ ┌─────┐ └─┐ │ │
│ └─┐ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
......
sympl-firewall (9.0.190609.0) stable; urgency=medium
* Renamed 'admin' user to 'sympl'
-- Paul Cammish <sympl@kelduum.net> Sun, 09 Jun 2019 00:53:13 +0100
sympl-firewall (9.0.190604.0) stable; urgency=medium
* Replaced mentions of Symbiosis with Sympl.
......
......@@ -14,7 +14,7 @@ fi
#
PREFIX=/etc/sympl/firewall
chown -R admin:admin $PREFIX
chown -R sympl:sympl $PREFIX
#DEBHELPER#
......
sympl-ftp (9.0.190609.0) stable; urgency=medium
* FTP user now logs in as owner of the chrooted dir
* Added Umask so files are +rw by the group
-- Paul Cammish <sympl@kelduum.net> Sun, 09 Jun 2019 23:04:00 +0100
sympl-ftp (9.0.190604.0) stable; urgency=medium
* Replaced mentions of Symbiosis with Sympl.
......
......@@ -177,15 +177,22 @@ begin
exit PERMANENT_ERROR
end
# Override the uid/gid, based on the target chroot dir
# This ensures whatever user is used, they have the right
# ownership
uid_override = File.stat(user.chroot_dir).uid
gid_override = File.stat(user.chroot_dir).gid
#
# Work out results before printing.
#
results = [ "uid:#{user.uid}",
"gid:#{user.gid}",
results = [ "uid:#{uid_override}",
"gid:#{gid_override}",
"dir:#{user.chroot_dir}" ]
results << "user_quota_size:#{user.quota}" unless user.quota.nil? or user.quota == 0
#
# Woo-hoo success!
#
......
sympl-mail (9.0.190609.0) stable; urgency=medium
* Renamed admin user to sympl.
-- Paul Cammish <sympl@kelduum.net> Sun, 09 Jun 2019 00:53:13 +0100
sympl-mail (9.0.190605.0) stable; urgency=medium
* Renamed package to sympl-mail
......
......@@ -4,7 +4,7 @@ local_users_forward:
driver = redirect
domains = $primary_hostname
check_local_user
local_parts = ! root : ! admin
local_parts = ! root : ! sympl
local_part_suffix = +*
local_part_suffix_optional
# Make sure the files exists to avoid awkward failures
......
......@@ -10,7 +10,7 @@ local_users_forward_sieve:
# Set permissions for any actions we might take
user = $local_user_uid
group = $local_user_gid
local_parts = ! root : ! admin
local_parts = ! root : ! sympl
local_part_suffix = +*
local_part_suffix_optional
transport = dovecot_lda
......
......@@ -4,7 +4,7 @@ local_users_vacation:
driver = accept
domains = $primary_hostname
check_local_user
local_parts = ! root : ! admin
local_parts = ! root : ! sympl
local_part_suffix = +*
local_part_suffix_optional
# This condition is mostly cribbed from the default value for
......
......@@ -4,7 +4,7 @@ local_users_mailbox:
driver = redirect
domains = $primary_hostname
check_local_user
local_parts = ! root : ! admin
local_parts = ! root : ! sympl
local_part_suffix = +*
local_part_suffix_optional
data = ${home}/Maildir/${if eqi{$h_X-Spam-Status:}{spam}{.Spam/}{}}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment