mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-07-04 08:02:02 +02:00
Add blacklisted usernames
This commit is contained in:
parent
9d1b44319c
commit
821275ce70
8
Makefile
8
Makefile
@ -6,6 +6,12 @@ USERS_SCRIPT := ./cli/generate_users.py
|
|||||||
INCLUDES_OUT := ./tasks/utils/docker-roles.yml
|
INCLUDES_OUT := ./tasks/utils/docker-roles.yml
|
||||||
INCLUDES_SCRIPT := ./cli/generate_playbook.py
|
INCLUDES_SCRIPT := ./cli/generate_playbook.py
|
||||||
|
|
||||||
|
EXTRA_USERS := $(shell \
|
||||||
|
find $(ROLES_DIR) -maxdepth 1 -type d -name 'docker*' -printf '%f\n' \
|
||||||
|
| sed -E 's/^docker[_-]?//' \
|
||||||
|
| paste -sd, - \
|
||||||
|
)
|
||||||
|
|
||||||
.PHONY: build install test
|
.PHONY: build install test
|
||||||
|
|
||||||
build:
|
build:
|
||||||
@ -15,7 +21,7 @@ build:
|
|||||||
@echo "✅ Applications defaults written to $(APPLICATIONS_OUT)\n"
|
@echo "✅ Applications defaults written to $(APPLICATIONS_OUT)\n"
|
||||||
@echo "🔧 Generating users defaults → $(USERS_OUT) from roles in $(ROLES_DIR)…"
|
@echo "🔧 Generating users defaults → $(USERS_OUT) from roles in $(ROLES_DIR)…"
|
||||||
@mkdir -p $(dir $(USERS_OUT))
|
@mkdir -p $(dir $(USERS_OUT))
|
||||||
python3 $(USERS_SCRIPT) --roles-dir $(ROLES_DIR) --output $(USERS_OUT)
|
python3 $(USERS_SCRIPT) --roles-dir $(ROLES_DIR) --output $(USERS_OUT) --extra-users "$(EXTRA_USERS)"
|
||||||
@echo "✅ Users defaults written to $(USERS_OUT)\n"
|
@echo "✅ Users defaults written to $(USERS_OUT)\n"
|
||||||
@echo "🔧 Generating Docker role includes → $(INCLUDES_OUT)…"
|
@echo "🔧 Generating Docker role includes → $(INCLUDES_OUT)…"
|
||||||
@mkdir -p $(dir $(INCLUDES_OUT))
|
@mkdir -p $(dir $(INCLUDES_OUT))
|
||||||
|
@ -25,7 +25,6 @@ def build_users(defs, primary_domain, start_id, become_pwd):
|
|||||||
"""
|
"""
|
||||||
users = OrderedDict()
|
users = OrderedDict()
|
||||||
used_uids = set()
|
used_uids = set()
|
||||||
used_gids = set()
|
|
||||||
|
|
||||||
# Pre-collect any provided uids/gids and check for duplicates
|
# Pre-collect any provided uids/gids and check for duplicates
|
||||||
for key, overrides in defs.items():
|
for key, overrides in defs.items():
|
||||||
@ -34,11 +33,6 @@ def build_users(defs, primary_domain, start_id, become_pwd):
|
|||||||
if uid in used_uids:
|
if uid in used_uids:
|
||||||
raise ValueError(f"Duplicate uid {uid} for user '{key}'")
|
raise ValueError(f"Duplicate uid {uid} for user '{key}'")
|
||||||
used_uids.add(uid)
|
used_uids.add(uid)
|
||||||
if 'gid' in overrides:
|
|
||||||
gid = overrides['gid']
|
|
||||||
if gid in used_gids:
|
|
||||||
raise ValueError(f"Duplicate gid {gid} for user '{key}'")
|
|
||||||
used_gids.add(gid)
|
|
||||||
|
|
||||||
next_free = start_id
|
next_free = start_id
|
||||||
def allocate_free_id():
|
def allocate_free_id():
|
||||||
@ -48,7 +42,6 @@ def build_users(defs, primary_domain, start_id, become_pwd):
|
|||||||
next_free += 1
|
next_free += 1
|
||||||
free = next_free
|
free = next_free
|
||||||
used_uids.add(free)
|
used_uids.add(free)
|
||||||
used_gids.add(free)
|
|
||||||
next_free += 1
|
next_free += 1
|
||||||
return free
|
return free
|
||||||
|
|
||||||
@ -68,13 +61,8 @@ def build_users(defs, primary_domain, start_id, become_pwd):
|
|||||||
if 'gid' in overrides:
|
if 'gid' in overrides:
|
||||||
gid = overrides['gid']
|
gid = overrides['gid']
|
||||||
else:
|
else:
|
||||||
# if gid not provided, default to uid (and ensure uniqueness)
|
# default GID to UID
|
||||||
if uid in used_gids:
|
gid = uid
|
||||||
# already added in allocate_free_id or pre-collect
|
|
||||||
gid = uid
|
|
||||||
else:
|
|
||||||
gid = uid
|
|
||||||
used_gids.add(gid)
|
|
||||||
|
|
||||||
entry = {
|
entry = {
|
||||||
'username': username,
|
'username': username,
|
||||||
@ -102,8 +90,6 @@ def build_users(defs, primary_domain, start_id, become_pwd):
|
|||||||
raise ValueError(f"Duplicate username '{un}' in merged users")
|
raise ValueError(f"Duplicate username '{un}' in merged users")
|
||||||
if em in seen_emails:
|
if em in seen_emails:
|
||||||
raise ValueError(f"Duplicate email '{em}' in merged users")
|
raise ValueError(f"Duplicate email '{em}' in merged users")
|
||||||
if gd in seen_gids:
|
|
||||||
raise ValueError(f"Duplicate gid '{gd}' in merged users")
|
|
||||||
seen_usernames.add(un)
|
seen_usernames.add(un)
|
||||||
seen_emails.add(em)
|
seen_emails.add(em)
|
||||||
seen_gids.add(gd)
|
seen_gids.add(gd)
|
||||||
@ -185,6 +171,11 @@ def parse_args():
|
|||||||
'--start-id', '-s', type=int, default=1001,
|
'--start-id', '-s', type=int, default=1001,
|
||||||
help='Starting uid/gid number (default: 1001).'
|
help='Starting uid/gid number (default: 1001).'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--extra-users', '-e',
|
||||||
|
help='Comma-separated list of additional usernames to include.',
|
||||||
|
default=None
|
||||||
|
)
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
@ -199,6 +190,17 @@ def main():
|
|||||||
print(f"Error merging user definitions: {e}", file=sys.stderr)
|
print(f"Error merging user definitions: {e}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Add extra users if any
|
||||||
|
if args.extra_users:
|
||||||
|
for name in args.extra_users.split(','):
|
||||||
|
user = name.strip()
|
||||||
|
if not user:
|
||||||
|
continue
|
||||||
|
if user in user_defs:
|
||||||
|
print(f"Warning: extra user '{user}' already defined; skipping.", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
user_defs[user] = {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
users = build_users(
|
users = build_users(
|
||||||
defs=user_defs,
|
defs=user_defs,
|
||||||
|
@ -3,7 +3,7 @@ images:
|
|||||||
users:
|
users:
|
||||||
administrator:
|
administrator:
|
||||||
username: "administrator"
|
username: "administrator"
|
||||||
crm:
|
contact:
|
||||||
description: "General contact account"
|
description: "General contact account"
|
||||||
username: "contact"
|
username: "contact"
|
||||||
features:
|
features:
|
||||||
|
126
roles/user/vars/configuration.yml
Normal file
126
roles/user/vars/configuration.yml
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
# Reserved usernames
|
||||||
|
users:
|
||||||
|
root:
|
||||||
|
username: root
|
||||||
|
uid: 0
|
||||||
|
gid: 0
|
||||||
|
description: "System superuser"
|
||||||
|
daemon:
|
||||||
|
username: daemon
|
||||||
|
description: "Daemon processes owner"
|
||||||
|
bin:
|
||||||
|
username: bin
|
||||||
|
description: "Owner of essential binaries"
|
||||||
|
sys:
|
||||||
|
username: sys
|
||||||
|
description: "System files owner"
|
||||||
|
sync:
|
||||||
|
username: sync
|
||||||
|
description: "Sync user for filesystem synchronization"
|
||||||
|
games:
|
||||||
|
username: games
|
||||||
|
description: "Games and educational software owner"
|
||||||
|
man:
|
||||||
|
username: man
|
||||||
|
description: "Manual pages viewer"
|
||||||
|
lp:
|
||||||
|
username: lp
|
||||||
|
description: "Printer spooler"
|
||||||
|
mail:
|
||||||
|
username: mail
|
||||||
|
description: "Mail system"
|
||||||
|
news:
|
||||||
|
username: news
|
||||||
|
description: "Network news system"
|
||||||
|
uucp:
|
||||||
|
username: uucp
|
||||||
|
description: "UUCP system"
|
||||||
|
proxy:
|
||||||
|
username: proxy
|
||||||
|
description: "Proxy user"
|
||||||
|
www-data:
|
||||||
|
username: www-data
|
||||||
|
description: "Web server user"
|
||||||
|
backup:
|
||||||
|
username: backup
|
||||||
|
description: "Backup operator"
|
||||||
|
list:
|
||||||
|
username: list
|
||||||
|
description: "Mailing list manager"
|
||||||
|
irc:
|
||||||
|
username: irc
|
||||||
|
description: "IRC services user"
|
||||||
|
gnats:
|
||||||
|
username: gnats
|
||||||
|
description: "GNATS bug-reporting system"
|
||||||
|
nobody:
|
||||||
|
username: nobody
|
||||||
|
description: "Unprivileged user"
|
||||||
|
messagebus:
|
||||||
|
username: messagebus
|
||||||
|
description: "D-Bus message bus system"
|
||||||
|
sshd:
|
||||||
|
username: sshd
|
||||||
|
description: "SSH daemon"
|
||||||
|
rpc:
|
||||||
|
username: rpc
|
||||||
|
description: "Rpcbind daemon"
|
||||||
|
ftp:
|
||||||
|
username: ftp
|
||||||
|
description: "FTP server"
|
||||||
|
postfix:
|
||||||
|
username: postfix
|
||||||
|
description: "Postfix mail transfer agent"
|
||||||
|
mysql:
|
||||||
|
username: mysql
|
||||||
|
description: "MySQL database server"
|
||||||
|
mongodb:
|
||||||
|
username: mongodb
|
||||||
|
description: "MongoDB database server"
|
||||||
|
admin:
|
||||||
|
username: admin
|
||||||
|
description: "Generic reserved username"
|
||||||
|
administrator:
|
||||||
|
username: administrator
|
||||||
|
user:
|
||||||
|
username: user
|
||||||
|
description: "Generic reserved username"
|
||||||
|
test:
|
||||||
|
username: test
|
||||||
|
description: "Generic reserved username"
|
||||||
|
guest:
|
||||||
|
username: guest
|
||||||
|
description: "Generic reserved username"
|
||||||
|
demo:
|
||||||
|
username: demo
|
||||||
|
description: "Generic reserved username"
|
||||||
|
info:
|
||||||
|
username: info
|
||||||
|
description: "Generic reserved username"
|
||||||
|
support:
|
||||||
|
username: support
|
||||||
|
description: "Generic reserved username"
|
||||||
|
helpdesk:
|
||||||
|
username: helpdesk
|
||||||
|
description: "Generic reserved username"
|
||||||
|
operator:
|
||||||
|
username: operator
|
||||||
|
description: "Generic reserved username"
|
||||||
|
staff:
|
||||||
|
username: staff
|
||||||
|
description: "Generic reserved username"
|
||||||
|
smtp:
|
||||||
|
username: smtp
|
||||||
|
description: "Generic reserved username"
|
||||||
|
imap:
|
||||||
|
username: imap
|
||||||
|
description: "Generic reserved username"
|
||||||
|
pop:
|
||||||
|
username: pop
|
||||||
|
description: "Generic reserved username"
|
||||||
|
webmaster:
|
||||||
|
username: webmaster
|
||||||
|
description: "Generic reserved username"
|
||||||
|
mailman:
|
||||||
|
username: mailman
|
||||||
|
description: "Generic reserved username"
|
97
tests/unit/test_generate_users.py
Normal file
97
tests/unit/test_generate_users.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
import yaml
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
# Add cli/ to import path
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../..", "cli")))
|
||||||
|
|
||||||
|
import generate_users
|
||||||
|
|
||||||
|
class TestGenerateUsers(unittest.TestCase):
|
||||||
|
def test_build_users_auto_increment_and_overrides(self):
|
||||||
|
defs = {
|
||||||
|
'alice': {},
|
||||||
|
'bob': {'uid': 2000, 'email': 'bob@custom.com', 'description': 'Custom user'},
|
||||||
|
'carol': {}
|
||||||
|
}
|
||||||
|
users = generate_users.build_users(
|
||||||
|
defs=defs,
|
||||||
|
primary_domain='example.com',
|
||||||
|
start_id=1001,
|
||||||
|
become_pwd='pw'
|
||||||
|
)
|
||||||
|
# alice should get uid/gid 1001
|
||||||
|
self.assertEqual(users['alice']['uid'], 1001)
|
||||||
|
self.assertEqual(users['alice']['gid'], 1001)
|
||||||
|
self.assertEqual(users['alice']['email'], 'alice@example.com')
|
||||||
|
# bob overrides
|
||||||
|
self.assertEqual(users['bob']['uid'], 2000)
|
||||||
|
self.assertEqual(users['bob']['gid'], 2000)
|
||||||
|
self.assertEqual(users['bob']['email'], 'bob@custom.com')
|
||||||
|
self.assertIn('description', users['bob'])
|
||||||
|
# carol should get next free id = 1002
|
||||||
|
self.assertEqual(users['carol']['uid'], 1002)
|
||||||
|
self.assertEqual(users['carol']['gid'], 1002)
|
||||||
|
|
||||||
|
def test_build_users_duplicate_override_uid(self):
|
||||||
|
defs = {
|
||||||
|
'u1': {'uid': 1001},
|
||||||
|
'u2': {'uid': 1001}
|
||||||
|
}
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
generate_users.build_users(defs, 'ex.com', 1001, 'pw')
|
||||||
|
|
||||||
|
def test_build_users_shared_gid_allowed(self):
|
||||||
|
# Allow two users to share the same GID when one overrides gid and the other uses that as uid
|
||||||
|
defs = {
|
||||||
|
'a': {'uid': 1500},
|
||||||
|
'b': {'gid': 1500}
|
||||||
|
}
|
||||||
|
users = generate_users.build_users(defs, 'ex.com', 1500, 'pw')
|
||||||
|
# Both should have gid 1500
|
||||||
|
self.assertEqual(users['a']['gid'], 1500)
|
||||||
|
self.assertEqual(users['b']['gid'], 1500)
|
||||||
|
|
||||||
|
def test_build_users_duplicate_username_email(self):
|
||||||
|
defs = {
|
||||||
|
'u1': {'username': 'same', 'email': 'same@ex.com'},
|
||||||
|
'u2': {'username': 'same'}
|
||||||
|
}
|
||||||
|
# second user with same username should raise
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
generate_users.build_users(defs, 'ex.com', 1001, 'pw')
|
||||||
|
|
||||||
|
def test_dictify_converts_ordereddict(self):
|
||||||
|
od = generate_users.OrderedDict([('a', 1), ('b', {'c': 2})])
|
||||||
|
result = generate_users.dictify(OrderedDict(od))
|
||||||
|
self.assertIsInstance(result, dict)
|
||||||
|
self.assertEqual(result, {'a': 1, 'b': {'c': 2}})
|
||||||
|
|
||||||
|
def test_load_user_defs_and_conflict(self):
|
||||||
|
# create temp roles structure
|
||||||
|
tmp = tempfile.mkdtemp()
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.join(tmp, 'role1/vars'))
|
||||||
|
os.makedirs(os.path.join(tmp, 'role2/vars'))
|
||||||
|
# role1 defines user x
|
||||||
|
with open(os.path.join(tmp, 'role1/vars/configuration.yml'), 'w') as f:
|
||||||
|
yaml.safe_dump({'users': {'x': {'email': 'x@a'}}}, f)
|
||||||
|
# role2 defines same user x with same value
|
||||||
|
with open(os.path.join(tmp, 'role2/vars/configuration.yml'), 'w') as f:
|
||||||
|
yaml.safe_dump({'users': {'x': {'email': 'x@a'}}}, f)
|
||||||
|
defs = generate_users.load_user_defs(tmp)
|
||||||
|
self.assertIn('x', defs)
|
||||||
|
# now conflict definition
|
||||||
|
with open(os.path.join(tmp, 'role2/vars/configuration.yml'), 'w') as f:
|
||||||
|
yaml.safe_dump({'users': {'x': {'email': 'x@b'}}}, f)
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
generate_users.load_user_defs(tmp)
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(tmp)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user