added FSLogix share
This commit is contained in:
@@ -6,6 +6,7 @@ JOIN_PASSWORD=ReplaceWithLongRandomPassword
|
||||
DOMAIN_USERS_SID=S-1-5-21-1111111111-2222222222-3333333333-513
|
||||
DOMAIN_ADMINS_SID=S-1-5-21-1111111111-2222222222-3333333333-512
|
||||
PUBLIC_GROUP_SID=S-1-5-21-1111111111-2222222222-3333333333-513
|
||||
FSLOGIX_GROUP_SID=S-1-5-21-1111111111-2222222222-3333333333-513
|
||||
# SAMBA_HOSTNAME=adsambafsrv
|
||||
# NETBIOS_NAME=ADSAMBAFSRV
|
||||
# LDAP_URI=ldaps://example.com
|
||||
|
||||
@@ -19,7 +19,7 @@ RUN apt-get update \
|
||||
winbind \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /app /data/private /data/public /data/groups /state /etc/samba/generated
|
||||
RUN mkdir -p /app /data/private /data/public /data/fslogix /data/groups /state /etc/samba/generated
|
||||
|
||||
COPY app/reconcile_shares.py /app/reconcile_shares.py
|
||||
COPY app/init.sh /app/init.sh
|
||||
|
||||
11
README.md
11
README.md
@@ -8,6 +8,7 @@ This repository provides a production-oriented Samba file server container that
|
||||
- Static shares:
|
||||
- `\\server\Privat` -> `/data/private`
|
||||
- `\\server\Geteilt` -> `/data/public`
|
||||
- `\\server\FSLogix` -> `/data/fslogix`
|
||||
- Dynamic shares are generated from AD groups matching `FileShare_*` or `FS_*` and written to `/etc/samba/generated/shares.conf`.
|
||||
- Dynamic share records are persisted in SQLite at `/state/shares.db`.
|
||||
- Backing storage is GUID-based and stable across group rename:
|
||||
@@ -16,6 +17,7 @@ This repository provides a production-oriented Samba file server container that
|
||||
- Container hostname is fixed (`SAMBA_HOSTNAME`) to keep AD computer identity stable.
|
||||
- NetBIOS name defaults to `ADSAMBAFSRV` and is clamped to 15 characters (`NETBIOS_NAME` override supported).
|
||||
- Setup prompts for well-known authorization groups by SID (`DOMAIN_USERS_SID`, `DOMAIN_ADMINS_SID`) to avoid localized group names.
|
||||
- `FSLOGIX_GROUP_SID` controls who can access the default FSLogix share (defaults to `DOMAIN_USERS_SID`).
|
||||
- Startup resolves those SIDs to NSS group names via winbind, then uses those resolved groups in Samba `valid users` rules.
|
||||
- Share operations are audited with Samba `full_audit` (connect, list, read, write, create, delete, rename) and written to Samba log files.
|
||||
- Private home creation skips well-known/service accounts by default (including `krbtgt`, `msol_*`, `FileShare_ServiceAcc`).
|
||||
@@ -101,6 +103,7 @@ Kerberos requires close time alignment.
|
||||
- `DOMAIN_USERS_SID`
|
||||
- `DOMAIN_ADMINS_SID`
|
||||
- optional `PUBLIC_GROUP_SID` (defaults to `DOMAIN_USERS_SID`)
|
||||
- optional `FSLOGIX_GROUP_SID` (defaults to `DOMAIN_USERS_SID`)
|
||||
|
||||
Optional:
|
||||
- `SAMBA_HOSTNAME` (defaults to `adsambafsrv`)
|
||||
@@ -149,6 +152,14 @@ Kerberos requires close time alignment.
|
||||
- No guest access.
|
||||
- Permissions are reconciled recursively so all descendants remain homogeneous (dirs `2770`, files `0660`, shared group/admin ACLs).
|
||||
|
||||
### FSLogix
|
||||
|
||||
- Share: `\\server\FSLogix`
|
||||
- Path: `/data/fslogix`
|
||||
- Access for authenticated users in configurable `FSLOGIX_GROUP_SID` (default: `DOMAIN_USERS_SID`, resolved through winbind).
|
||||
- Semantics intentionally differ from `Geteilt`: only the share root is reconciled (`03770` + ACL defaults), while user-created profile container folders/files are not recursively normalized.
|
||||
- Samba masks are profile-container oriented (`create mask = 0600`, `directory mask = 0700`) so profile payload stays user-private by default.
|
||||
|
||||
### Dynamic Group Shares
|
||||
|
||||
- AD groups: `FileShare_*` and `FS_*`
|
||||
|
||||
10
app/init.sh
10
app/init.sh
@@ -115,9 +115,13 @@ resolve_share_groups_from_sids() {
|
||||
export PUBLIC_GROUP
|
||||
PUBLIC_GROUP="$(resolve_sid_to_group "$PUBLIC_GROUP_SID")"
|
||||
|
||||
export FSLOGIX_GROUP
|
||||
FSLOGIX_GROUP="$(resolve_sid_to_group "$FSLOGIX_GROUP_SID")"
|
||||
|
||||
log "Resolved DOMAIN_USERS_SID to '${DOMAIN_USERS_GROUP}'"
|
||||
log "Resolved DOMAIN_ADMINS_SID to '${DOMAIN_ADMINS_GROUP}'"
|
||||
log "Resolved PUBLIC_GROUP_SID to '${PUBLIC_GROUP}'"
|
||||
log "Resolved FSLOGIX_GROUP_SID to '${FSLOGIX_GROUP}'"
|
||||
}
|
||||
|
||||
render_krb5_conf() {
|
||||
@@ -156,9 +160,11 @@ write_runtime_env_file() {
|
||||
printf 'export DOMAIN_USERS_SID=%q\n' "$DOMAIN_USERS_SID"
|
||||
printf 'export DOMAIN_ADMINS_SID=%q\n' "$DOMAIN_ADMINS_SID"
|
||||
printf 'export PUBLIC_GROUP_SID=%q\n' "$PUBLIC_GROUP_SID"
|
||||
printf 'export FSLOGIX_GROUP_SID=%q\n' "$FSLOGIX_GROUP_SID"
|
||||
printf 'export DOMAIN_USERS_GROUP=%q\n' "$DOMAIN_USERS_GROUP"
|
||||
printf 'export DOMAIN_ADMINS_GROUP=%q\n' "$DOMAIN_ADMINS_GROUP"
|
||||
printf 'export PUBLIC_GROUP=%q\n' "$PUBLIC_GROUP"
|
||||
printf 'export FSLOGIX_GROUP=%q\n' "$FSLOGIX_GROUP"
|
||||
if [[ -n "${JOIN_USER:-}" ]]; then
|
||||
printf 'export JOIN_USER=%q\n' "$JOIN_USER"
|
||||
fi
|
||||
@@ -222,9 +228,11 @@ require_env DOMAIN_ADMINS_SID
|
||||
|
||||
export REALM WORKGROUP DOMAIN
|
||||
export PUBLIC_GROUP_SID="${PUBLIC_GROUP_SID:-${DOMAIN_USERS_SID}}"
|
||||
export FSLOGIX_GROUP_SID="${FSLOGIX_GROUP_SID:-${DOMAIN_USERS_SID}}"
|
||||
export DOMAIN_USERS_GROUP="${DOMAIN_USERS_SID}"
|
||||
export DOMAIN_ADMINS_GROUP="${DOMAIN_ADMINS_SID}"
|
||||
export PUBLIC_GROUP="${PUBLIC_GROUP_SID}"
|
||||
export FSLOGIX_GROUP="${FSLOGIX_GROUP_SID}"
|
||||
if [[ -n "${JOIN_USER:-}" ]]; then
|
||||
export JOIN_USER
|
||||
fi
|
||||
@@ -232,7 +240,7 @@ if [[ -n "${JOIN_PASSWORD:-}" ]]; then
|
||||
export JOIN_PASSWORD
|
||||
fi
|
||||
|
||||
mkdir -p /data/private /data/public /data/groups /state /etc/samba/generated /var/log/samba
|
||||
mkdir -p /data/private /data/public /data/fslogix /data/groups /state /etc/samba/generated /var/log/samba
|
||||
touch /etc/samba/generated/shares.conf /var/log/reconcile.log
|
||||
|
||||
append_winbind_to_nss
|
||||
|
||||
@@ -20,6 +20,7 @@ LOCK_PATH = "/state/reconcile.lock"
|
||||
GROUP_ROOT = "/data/groups"
|
||||
PRIVATE_ROOT = "/data/private"
|
||||
PUBLIC_ROOT = "/data/public"
|
||||
FSLOGIX_ROOT = "/data/fslogix"
|
||||
GENERATED_CONF = "/etc/samba/generated/shares.conf"
|
||||
|
||||
LDAP_FILTER = (
|
||||
@@ -715,6 +716,51 @@ def sync_public_directory() -> None:
|
||||
log(f"Unable to resolve GID for {group_display}; public ACLs unchanged")
|
||||
|
||||
|
||||
def sync_fslogix_directory() -> None:
|
||||
workgroup = os.environ["WORKGROUP"]
|
||||
fslogix_group = os.getenv("FSLOGIX_GROUP", "")
|
||||
fslogix_group_sid = os.getenv("FSLOGIX_GROUP_SID", "")
|
||||
qualified_group = fslogix_group
|
||||
|
||||
os.makedirs(FSLOGIX_ROOT, exist_ok=True)
|
||||
|
||||
gid = None
|
||||
if qualified_group:
|
||||
gid = resolve_group_gid_flexible(workgroup, qualified_group)
|
||||
if gid is None and fslogix_group_sid:
|
||||
gid = resolve_gid_from_sid(fslogix_group_sid)
|
||||
|
||||
if gid is None:
|
||||
group_display = qualified_group or fslogix_group_sid or "<unset>"
|
||||
log(f"Unable to resolve GID for {group_display}; fslogix ACLs unchanged")
|
||||
return
|
||||
|
||||
admin_group = os.getenv("DOMAIN_ADMINS_GROUP", "")
|
||||
admin_gid = None
|
||||
if admin_group:
|
||||
admin_gid = resolve_group_gid_flexible(workgroup, admin_group)
|
||||
if admin_gid is None:
|
||||
admin_gid = resolve_gid_from_sid(os.getenv("DOMAIN_ADMINS_SID", ""))
|
||||
|
||||
os.chown(FSLOGIX_ROOT, 0, gid)
|
||||
os.chmod(FSLOGIX_ROOT, 0o3770)
|
||||
run_command(["setfacl", "-b", FSLOGIX_ROOT], check=False)
|
||||
|
||||
acl_entries = [f"g:{gid}:rwx", f"d:g:{gid}:rwx"]
|
||||
if admin_gid is not None and admin_gid != gid:
|
||||
acl_entries.append(f"g:{admin_gid}:rwx")
|
||||
acl_entries.append(f"d:g:{admin_gid}:rwx")
|
||||
|
||||
result = run_command(
|
||||
["setfacl", "-m", ",".join(acl_entries), FSLOGIX_ROOT], check=False
|
||||
)
|
||||
if result.returncode != 0:
|
||||
log(
|
||||
"setfacl failed for fslogix root: "
|
||||
f"{result.stderr.strip() or result.stdout.strip()}"
|
||||
)
|
||||
|
||||
|
||||
def sync_private_directories() -> None:
|
||||
workgroup = os.environ["WORKGROUP"]
|
||||
admin_group = os.getenv("DOMAIN_ADMINS_GROUP", "")
|
||||
@@ -800,6 +846,7 @@ def with_lock() -> bool:
|
||||
conn.close()
|
||||
|
||||
sync_public_directory()
|
||||
sync_fslogix_directory()
|
||||
sync_private_directories()
|
||||
reload_samba()
|
||||
log("Reconciliation completed")
|
||||
|
||||
@@ -15,6 +15,7 @@ services:
|
||||
volumes:
|
||||
- private_data:/data/private
|
||||
- public_data:/data/public
|
||||
- fslogix_data:/data/fslogix
|
||||
- group_data:/data/groups
|
||||
- state_data:/state
|
||||
- samba_lib_data:/var/lib/samba
|
||||
@@ -28,6 +29,7 @@ services:
|
||||
volumes:
|
||||
private_data:
|
||||
public_data:
|
||||
fslogix_data:
|
||||
group_data:
|
||||
state_data:
|
||||
samba_lib_data:
|
||||
|
||||
@@ -71,3 +71,22 @@
|
||||
directory mask = 2770
|
||||
inherit permissions = yes
|
||||
access based share enum = yes
|
||||
|
||||
[FSLogix]
|
||||
path = /data/fslogix
|
||||
read only = no
|
||||
browseable = yes
|
||||
guest ok = no
|
||||
vfs objects = acl_xattr full_audit
|
||||
full_audit:prefix = %T|%u|%I|%m|%S
|
||||
full_audit:success = all
|
||||
full_audit:failure = all
|
||||
full_audit:syslog = false
|
||||
valid users = @"${FSLOGIX_GROUP}"
|
||||
force group = "${FSLOGIX_GROUP}"
|
||||
create mask = 0600
|
||||
directory mask = 0700
|
||||
force create mode = 0600
|
||||
force directory mode = 0700
|
||||
hide unreadable = yes
|
||||
access based share enum = yes
|
||||
|
||||
10
setup
10
setup
@@ -84,11 +84,13 @@ write_env_file() {
|
||||
local domain_users_sid=""
|
||||
local domain_admins_sid=""
|
||||
local public_group_sid=""
|
||||
local fslogix_group_sid=""
|
||||
local samba_hostname="adsambafsrv"
|
||||
local netbios_name="ADSAMBAFSRV"
|
||||
local service_password=""
|
||||
local service_account_sam=""
|
||||
local public_group_prompt=""
|
||||
local fslogix_group_prompt=""
|
||||
local samba_hostname_input=""
|
||||
local netbios_name_input=""
|
||||
local sanitized_netbios_name=""
|
||||
@@ -107,6 +109,12 @@ write_env_file() {
|
||||
public_group_sid="$domain_users_sid"
|
||||
fi
|
||||
|
||||
fslogix_group_prompt="FSLOGIX_GROUP_SID (press Enter to reuse DOMAIN_USERS_SID)"
|
||||
read -r -p "${fslogix_group_prompt}: " fslogix_group_sid
|
||||
if [[ -z "$fslogix_group_sid" ]]; then
|
||||
fslogix_group_sid="$domain_users_sid"
|
||||
fi
|
||||
|
||||
read -r -p "SAMBA_HOSTNAME [adsambafsrv]: " samba_hostname_input
|
||||
if [[ -n "${samba_hostname_input:-}" ]]; then
|
||||
samba_hostname="$samba_hostname_input"
|
||||
@@ -158,6 +166,7 @@ SERVICE_ACCOUNT_PASSWORD=${service_password}
|
||||
DOMAIN_USERS_SID=${domain_users_sid}
|
||||
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
||||
PUBLIC_GROUP_SID=${public_group_sid}
|
||||
FSLOGIX_GROUP_SID=${fslogix_group_sid}
|
||||
SAMBA_HOSTNAME=${samba_hostname}
|
||||
NETBIOS_NAME=${netbios_name}
|
||||
EOF
|
||||
@@ -209,6 +218,7 @@ JOIN_PASSWORD=${service_password}
|
||||
DOMAIN_USERS_SID=${domain_users_sid}
|
||||
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
||||
PUBLIC_GROUP_SID=${public_group_sid}
|
||||
FSLOGIX_GROUP_SID=${fslogix_group_sid}
|
||||
SAMBA_HOSTNAME=${samba_hostname}
|
||||
NETBIOS_NAME=${netbios_name}
|
||||
# Optional overrides:
|
||||
|
||||
Reference in New Issue
Block a user