Skip to content

Commit

Permalink
udbsync: update both nfsroot_staging and nfsroot_ro in udbsync.passwd
Browse files Browse the repository at this point in the history
Fixes #169.

Tested:

$ machinectl shell myrhfs0
$ diff -u /export/nfsroot_{staging,ro}/etc/group
  ( empty )

$ systemd-nspawn -D /export/nfsroot_staging
  $ pacman -S minidlna
  ...
  Creating user minidlna (minidlna server) with uid 971 and gid 971.

$ diff -u /export/nfsroot_{staging,ro}/etc/group
--- /export/nfsroot_staging/etc/group   2020-03-15 21:36:38.539844642 +0100
+++ /export/nfsroot_ro/etc/group        2020-03-15 21:34:10.167058855 +0100
@@ -76,2 +76,1 @@
-minidlna:x:971:

$ systemctl restart udbsync_passwd_nfsroot

$ diff -u /export/nfsroot_{staging,ro}/etc/group
  ( empty )
  • Loading branch information
zopieux committed Mar 15, 2020
1 parent 5f5b1e6 commit 9364643
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 34 deletions.
11 changes: 9 additions & 2 deletions etc/systemd/system/udbsync_passwd_nfsroot.service
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
[Unit]
Description=/etc/{passwd,shadow,group} synchronization daemon for nfsroot
Description=/etc/{passwd,shadow,group} synchronization daemon for nfsroot_staging and nfsroot_ro
After=network-online.target

[Service]
Type=simple
User=root
ExecStart=/opt/prologin/venv/bin/python -m prologin.udbsync_clients.passwd /export/nfsroot_ro
# Update nfsroot_staging so the latest version is copied when admins use the
# commit_staging.sh script. But also copy to nfsroot_ro so changes are synced
# to machines immediately.
# This could be racy with package installation in nfsroot_staging, but it's
# rather unlikely.
ExecStart=/opt/prologin/venv/bin/python -m prologin.udbsync_clients.passwd \
--root /export/nfsroot_staging \
--exec-after '/usr/bin/cp --preserve=all /export/nfsroot_staging/etc/passwd /export/nfsroot_staging/etc/shadow /export/nfsroot_staging/etc/group /export/nfsroot_ro/etc'
Restart=always
RestartSec=2

Expand Down
67 changes: 36 additions & 31 deletions python-lib/prologin/udbsync_clients/passwd.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,21 @@
#
# You should have received a copy of the GNU General Public License
# along with Prologin-SADM. If not, see <http://www.gnu.org/licenses/>.

import argparse
import collections
import datetime
import functools
import logging
import os
import re
import shutil
import subprocess
import tempfile

import prologin.config
import prologin.log
import prologin.presenced.client
import prologin.udbsync.client
import re
import shutil
import subprocess
import sys

User = collections.namedtuple('User',
'login password uid gid name home shell'
Expand Down Expand Up @@ -66,27 +67,26 @@
HOME_DIR = '/home/{}'


class BufferFile:
"""Write to `filepath` using a temporary file as a buffer, so that the
destination file is changed instantly.
class AtomicFile:
"""Buffers writes in a temp file, then rename it atomically to `filepath`.
Allows for an atomic update of `filepath`.
"""
def __init__(self, filepath, perms):
self.filepath = filepath
self.temp_path = os.path.join(
'/tmp',
filepath.replace(os.path.sep, '-')
)
self.f = open(self.temp_path, 'w')
os.chmod(self.temp_path, perms)
self.temp_file = tempfile.NamedTemporaryFile(
'w', prefix='udbsync-passwd-', delete=False)
os.chmod(self.temp_file.name, perms)

def __enter__(self):
return self.f
return self.temp_file

def __exit__(self, type, value, traceback):
self.f.close()
shutil.move(self.temp_path, self.filepath)
def __exit__(self, exc_type, exc_val, exc_tb):
self.temp_file.close()
shutil.move(self.temp_file.name, self.filepath)

def callback(root_path, users, updates_metadata):

def callback(root_path: str, exec_after: str, users, updates_metadata):
logging.info('New updates: %r', updates_metadata)

passwd_users = {}
Expand Down Expand Up @@ -218,8 +218,8 @@ def callback(root_path, users, updates_metadata):
sorted_groups = list(groups.values())
sorted_groups.sort(key=lambda g: g.gid)

# Finally, output updated files.
with BufferFile(os.path.join(root_path, 'etc/passwd'), PASSWD_PERMS) as f:
# Finally, write updated files.
with AtomicFile(os.path.join(root_path, 'etc/passwd'), PASSWD_PERMS) as f:
for user in sorted_users:
print(
'{0.login}:{0.password}'
Expand All @@ -229,16 +229,15 @@ def callback(root_path, users, updates_metadata):
file=f
)

with BufferFile(os.path.join(root_path, 'etc/shadow'), SHADOW_PERMS) as f:
with AtomicFile(os.path.join(root_path, 'etc/shadow'), SHADOW_PERMS) as f:
for user in sorted_users:
try:
shadow = shadow_passwords[user.login]
print('{}:{}'.format(user.login, shadow), file=f)
except KeyError:
pass
else:
print('{}:{}'.format(user.login, shadow), file=f)

with BufferFile(os.path.join(root_path, 'etc/group'), GROUP_PERMS) as f:
with AtomicFile(os.path.join(root_path, 'etc/group'), GROUP_PERMS) as f:
for group in sorted_groups:
print(
'{0.name}:{0.password}:{0.gid}:{1}'.format(
Expand All @@ -249,12 +248,18 @@ def callback(root_path, users, updates_metadata):
file=f
)

if exec_after is not None:
# shell=True so we don't have to shlex.split() the command.
# Inherits stdout/stderr so errors goes into system logs.
subprocess.run(exec_after, shell=True, check=True)


if __name__ == '__main__':
if len(sys.argv) != 2:
root_path = '/'
else:
root_path = sys.argv[1]
prologin.log.setup_logging('udbsync_passwd({})'.format(root_path))
callback = functools.partial(callback, root_path)
p = argparse.ArgumentParser()
p.add_argument('--root', default='/', help="Root path prefix (default: /)")
p.add_argument('--exec-after', required=False,
help="Command to execute after a successful update")
args = p.parse_args()
prologin.log.setup_logging('udbsync_passwd({})'.format(args.root))
callback = functools.partial(callback, args.root, args.exec_after)
prologin.udbsync.client.connect().poll_updates(callback)
1 change: 0 additions & 1 deletion rfs/commit_staging.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ for serv in "$@"; do
echo "## restarting metadata services on $serv"
ssh -T root@"$serv" <<EOF
systemctl restart rootssh-copy
systemctl restart udbsync_passwd_nfsroot
EOF

done

0 comments on commit 9364643

Please sign in to comment.