-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Define HOME_BASEDIR node variable #772
Merged
Merged
Changes from 4 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
6d931f5
Define HOME_BASEDIR node variable
DavidePrincipi a52d756
docs. Document HOME_BASEDIR variable
DavidePrincipi c2b9b1e
Add configure-home-basedir node helper command
DavidePrincipi bc5ec76
add-user fails, if HOME_BASEDIR is not a dir
DavidePrincipi 2f80502
Improve helper argument parsing
DavidePrincipi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 122 additions & 0 deletions
122
core/imageroot/var/lib/nethserver/node/bin/configure-home-basedir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
#!/usr/bin/env python3 | ||
|
||
# | ||
# Copyright (C) 2025 Nethesis S.r.l. | ||
# SPDX-License-Identifier: GPL-3.0-or-later | ||
# | ||
|
||
import agent | ||
import sys | ||
import os | ||
import stat | ||
import argparse | ||
import subprocess | ||
import json | ||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
description="Validate and configure the base path for home directories of NS8 modules.", | ||
epilog="DIR is the base path for new home directories." | ||
) | ||
parser.add_argument( | ||
"-c", | ||
"--check-only", | ||
dest="check_only", | ||
action="store_true", | ||
help="Check only, do not save the configuration.", | ||
) | ||
parser.add_argument( | ||
"DIR", | ||
metavar="DIR", | ||
help="Base path for new home directories.", | ||
) | ||
|
||
args = parser.parse_args() | ||
if args.DIR == "" and not args.check_only: | ||
agent.unset_env('HOME_BASEDIR') | ||
print("The base path for home directories has been reset to OS default.") | ||
else: | ||
home_basedir = validate_home_basedir(args.DIR) | ||
if not args.check_only: | ||
store_configuration(home_basedir) | ||
print("The base path for home directories has been updated.") | ||
|
||
def store_configuration(path): | ||
if os.path.isdir("/sys/fs/selinux"): | ||
# On systems with SELinux, configure path as /home equivalent | ||
update_selinux_customization(path) | ||
agent.set_env('HOME_BASEDIR', path) | ||
print("HOME_BASEDIR=" + path) | ||
|
||
def update_selinux_customization(path): | ||
ocurrent = subprocess.check_output(['semanage', 'fcontext', '-l', '-C'], text=True) | ||
if not f"\n{path} = /home\n" in ocurrent: | ||
subprocess.check_call(['semanage', 'fcontext', '-a', '-e', '/home', path]) | ||
subprocess.check_call(['restorecon', '-v', path]) | ||
update_parentdir_selinux_type(path) | ||
|
||
def update_parentdir_selinux_type(path): | ||
"""If needed, set home_root_t on parent dir as required by semanage-fcontext manpage.""" | ||
parentdir = os.path.dirname(path) | ||
if parentdir != "/": | ||
ols = subprocess.check_output(['ls', '-Zd', parentdir], text=True) | ||
if ":default_t:" in ols: | ||
subprocess.check_call(['semanage', 'fcontext', '-a', '-t', 'home_root_t', parentdir]) | ||
subprocess.check_call(['restorecon', '-v', parentdir]) | ||
|
||
def validate_home_basedir(path): | ||
"""Validate the given path and return it in canonicalized form.""" | ||
if os.path.islink(path) or not os.path.isdir(path): | ||
print(f"Error: {path} is not a directory.", file=sys.stderr) | ||
sys.exit(2) | ||
# Canonicalize the path value: | ||
home_basedir = os.path.abspath(path) | ||
if os.path.realpath(home_basedir) != home_basedir: | ||
print(f"Error: {path} contains one or more symlink components.", file=sys.stderr) | ||
sys.exit(2) | ||
check_permissions(home_basedir) | ||
check_unique_mountpoint(home_basedir) | ||
return home_basedir | ||
|
||
def get_mountpoint_device(path): | ||
"""Check if path is a mountpoint and return its source device.""" | ||
try: | ||
joutput = subprocess.check_output(['findmnt', '--json', '--mountpoint', path]) | ||
ofindmnt = json.loads(joutput) | ||
return ofindmnt["filesystems"][0]["source"] # source device path | ||
except subprocess.CalledProcessError: | ||
print(f"Error: {path} is not a filesystem mountpoint.", file=sys.stderr) | ||
sys.exit(2) | ||
|
||
def check_unique_mountpoint(path): | ||
"""Check if path is a mountpoint, and its source device is not | ||
mounted elsewhere.""" | ||
device = get_mountpoint_device(path) | ||
joutput = subprocess.check_output(['findmnt', '-o', 'TARGET', '--json', '--source', device]) | ||
gsanchietti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ofindmnt = json.loads(joutput) | ||
for ofs in ofindmnt["filesystems"]: | ||
if ofs['target'] != path: | ||
print(f"Error: device {device} has multiple mountpoints. It is mounted also on {ofs['target']}. Unmount it, persist the change, and retry.", file=sys.stderr) | ||
sys.exit(2) | ||
gsanchietti marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def check_permissions(dir_path): | ||
"""Checks if the given directory and its ancestors have octal 5 | ||
(world-readable and executable) or higher permissions.""" | ||
current_path = dir_path | ||
while current_path != "/": | ||
try: | ||
st = os.stat(current_path) | ||
# Extract other permissions (last 3 bits) | ||
other_perms = stat.S_IMODE(st.st_mode) & 0o007 | ||
# Check if permissions are at least 5 (r-x) | ||
if other_perms < 0o5: | ||
print(f"Error: invalid permissions on {current_path}. Fix with chmod -c +rx {current_path}, or higher.", file=sys.stderr) | ||
sys.exit(2) | ||
# Move to the parent directory | ||
current_path = os.path.dirname(current_path) | ||
except Exception as ex: | ||
print(f"Error: {current_path}:", ex, file=sys.stderr) | ||
sys.exit(2) | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, it is safer if we add an extra flag to reset the configuration of the path to the system one, eg.
-r
.