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_USERS_SID=S-1-5-21-1111111111-2222222222-3333333333-513
|
||||||
DOMAIN_ADMINS_SID=S-1-5-21-1111111111-2222222222-3333333333-512
|
DOMAIN_ADMINS_SID=S-1-5-21-1111111111-2222222222-3333333333-512
|
||||||
PUBLIC_GROUP_SID=S-1-5-21-1111111111-2222222222-3333333333-513
|
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
|
# SAMBA_HOSTNAME=adsambafsrv
|
||||||
# NETBIOS_NAME=ADSAMBAFSRV
|
# NETBIOS_NAME=ADSAMBAFSRV
|
||||||
# LDAP_URI=ldaps://example.com
|
# LDAP_URI=ldaps://example.com
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ RUN apt-get update \
|
|||||||
winbind \
|
winbind \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& 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/reconcile_shares.py /app/reconcile_shares.py
|
||||||
COPY app/init.sh /app/init.sh
|
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:
|
- Static shares:
|
||||||
- `\\server\Privat` -> `/data/private`
|
- `\\server\Privat` -> `/data/private`
|
||||||
- `\\server\Geteilt` -> `/data/public`
|
- `\\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 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`.
|
- Dynamic share records are persisted in SQLite at `/state/shares.db`.
|
||||||
- Backing storage is GUID-based and stable across group rename:
|
- 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.
|
- 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).
|
- 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.
|
- 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.
|
- 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.
|
- 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`).
|
- 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_USERS_SID`
|
||||||
- `DOMAIN_ADMINS_SID`
|
- `DOMAIN_ADMINS_SID`
|
||||||
- optional `PUBLIC_GROUP_SID` (defaults to `DOMAIN_USERS_SID`)
|
- optional `PUBLIC_GROUP_SID` (defaults to `DOMAIN_USERS_SID`)
|
||||||
|
- optional `FSLOGIX_GROUP_SID` (defaults to `DOMAIN_USERS_SID`)
|
||||||
|
|
||||||
Optional:
|
Optional:
|
||||||
- `SAMBA_HOSTNAME` (defaults to `adsambafsrv`)
|
- `SAMBA_HOSTNAME` (defaults to `adsambafsrv`)
|
||||||
@@ -149,6 +152,14 @@ Kerberos requires close time alignment.
|
|||||||
- No guest access.
|
- No guest access.
|
||||||
- Permissions are reconciled recursively so all descendants remain homogeneous (dirs `2770`, files `0660`, shared group/admin ACLs).
|
- 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
|
### Dynamic Group Shares
|
||||||
|
|
||||||
- AD groups: `FileShare_*` and `FS_*`
|
- 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
|
export PUBLIC_GROUP
|
||||||
PUBLIC_GROUP="$(resolve_sid_to_group "$PUBLIC_GROUP_SID")"
|
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_USERS_SID to '${DOMAIN_USERS_GROUP}'"
|
||||||
log "Resolved DOMAIN_ADMINS_SID to '${DOMAIN_ADMINS_GROUP}'"
|
log "Resolved DOMAIN_ADMINS_SID to '${DOMAIN_ADMINS_GROUP}'"
|
||||||
log "Resolved PUBLIC_GROUP_SID to '${PUBLIC_GROUP}'"
|
log "Resolved PUBLIC_GROUP_SID to '${PUBLIC_GROUP}'"
|
||||||
|
log "Resolved FSLOGIX_GROUP_SID to '${FSLOGIX_GROUP}'"
|
||||||
}
|
}
|
||||||
|
|
||||||
render_krb5_conf() {
|
render_krb5_conf() {
|
||||||
@@ -156,9 +160,11 @@ write_runtime_env_file() {
|
|||||||
printf 'export DOMAIN_USERS_SID=%q\n' "$DOMAIN_USERS_SID"
|
printf 'export DOMAIN_USERS_SID=%q\n' "$DOMAIN_USERS_SID"
|
||||||
printf 'export DOMAIN_ADMINS_SID=%q\n' "$DOMAIN_ADMINS_SID"
|
printf 'export DOMAIN_ADMINS_SID=%q\n' "$DOMAIN_ADMINS_SID"
|
||||||
printf 'export PUBLIC_GROUP_SID=%q\n' "$PUBLIC_GROUP_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_USERS_GROUP=%q\n' "$DOMAIN_USERS_GROUP"
|
||||||
printf 'export DOMAIN_ADMINS_GROUP=%q\n' "$DOMAIN_ADMINS_GROUP"
|
printf 'export DOMAIN_ADMINS_GROUP=%q\n' "$DOMAIN_ADMINS_GROUP"
|
||||||
printf 'export PUBLIC_GROUP=%q\n' "$PUBLIC_GROUP"
|
printf 'export PUBLIC_GROUP=%q\n' "$PUBLIC_GROUP"
|
||||||
|
printf 'export FSLOGIX_GROUP=%q\n' "$FSLOGIX_GROUP"
|
||||||
if [[ -n "${JOIN_USER:-}" ]]; then
|
if [[ -n "${JOIN_USER:-}" ]]; then
|
||||||
printf 'export JOIN_USER=%q\n' "$JOIN_USER"
|
printf 'export JOIN_USER=%q\n' "$JOIN_USER"
|
||||||
fi
|
fi
|
||||||
@@ -222,9 +228,11 @@ require_env DOMAIN_ADMINS_SID
|
|||||||
|
|
||||||
export REALM WORKGROUP DOMAIN
|
export REALM WORKGROUP DOMAIN
|
||||||
export PUBLIC_GROUP_SID="${PUBLIC_GROUP_SID:-${DOMAIN_USERS_SID}}"
|
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_USERS_GROUP="${DOMAIN_USERS_SID}"
|
||||||
export DOMAIN_ADMINS_GROUP="${DOMAIN_ADMINS_SID}"
|
export DOMAIN_ADMINS_GROUP="${DOMAIN_ADMINS_SID}"
|
||||||
export PUBLIC_GROUP="${PUBLIC_GROUP_SID}"
|
export PUBLIC_GROUP="${PUBLIC_GROUP_SID}"
|
||||||
|
export FSLOGIX_GROUP="${FSLOGIX_GROUP_SID}"
|
||||||
if [[ -n "${JOIN_USER:-}" ]]; then
|
if [[ -n "${JOIN_USER:-}" ]]; then
|
||||||
export JOIN_USER
|
export JOIN_USER
|
||||||
fi
|
fi
|
||||||
@@ -232,7 +240,7 @@ if [[ -n "${JOIN_PASSWORD:-}" ]]; then
|
|||||||
export JOIN_PASSWORD
|
export JOIN_PASSWORD
|
||||||
fi
|
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
|
touch /etc/samba/generated/shares.conf /var/log/reconcile.log
|
||||||
|
|
||||||
append_winbind_to_nss
|
append_winbind_to_nss
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ LOCK_PATH = "/state/reconcile.lock"
|
|||||||
GROUP_ROOT = "/data/groups"
|
GROUP_ROOT = "/data/groups"
|
||||||
PRIVATE_ROOT = "/data/private"
|
PRIVATE_ROOT = "/data/private"
|
||||||
PUBLIC_ROOT = "/data/public"
|
PUBLIC_ROOT = "/data/public"
|
||||||
|
FSLOGIX_ROOT = "/data/fslogix"
|
||||||
GENERATED_CONF = "/etc/samba/generated/shares.conf"
|
GENERATED_CONF = "/etc/samba/generated/shares.conf"
|
||||||
|
|
||||||
LDAP_FILTER = (
|
LDAP_FILTER = (
|
||||||
@@ -715,6 +716,51 @@ def sync_public_directory() -> None:
|
|||||||
log(f"Unable to resolve GID for {group_display}; public ACLs unchanged")
|
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:
|
def sync_private_directories() -> None:
|
||||||
workgroup = os.environ["WORKGROUP"]
|
workgroup = os.environ["WORKGROUP"]
|
||||||
admin_group = os.getenv("DOMAIN_ADMINS_GROUP", "")
|
admin_group = os.getenv("DOMAIN_ADMINS_GROUP", "")
|
||||||
@@ -800,6 +846,7 @@ def with_lock() -> bool:
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
sync_public_directory()
|
sync_public_directory()
|
||||||
|
sync_fslogix_directory()
|
||||||
sync_private_directories()
|
sync_private_directories()
|
||||||
reload_samba()
|
reload_samba()
|
||||||
log("Reconciliation completed")
|
log("Reconciliation completed")
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- private_data:/data/private
|
- private_data:/data/private
|
||||||
- public_data:/data/public
|
- public_data:/data/public
|
||||||
|
- fslogix_data:/data/fslogix
|
||||||
- group_data:/data/groups
|
- group_data:/data/groups
|
||||||
- state_data:/state
|
- state_data:/state
|
||||||
- samba_lib_data:/var/lib/samba
|
- samba_lib_data:/var/lib/samba
|
||||||
@@ -28,6 +29,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
private_data:
|
private_data:
|
||||||
public_data:
|
public_data:
|
||||||
|
fslogix_data:
|
||||||
group_data:
|
group_data:
|
||||||
state_data:
|
state_data:
|
||||||
samba_lib_data:
|
samba_lib_data:
|
||||||
|
|||||||
@@ -71,3 +71,22 @@
|
|||||||
directory mask = 2770
|
directory mask = 2770
|
||||||
inherit permissions = yes
|
inherit permissions = yes
|
||||||
access based share enum = 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_users_sid=""
|
||||||
local domain_admins_sid=""
|
local domain_admins_sid=""
|
||||||
local public_group_sid=""
|
local public_group_sid=""
|
||||||
|
local fslogix_group_sid=""
|
||||||
local samba_hostname="adsambafsrv"
|
local samba_hostname="adsambafsrv"
|
||||||
local netbios_name="ADSAMBAFSRV"
|
local netbios_name="ADSAMBAFSRV"
|
||||||
local service_password=""
|
local service_password=""
|
||||||
local service_account_sam=""
|
local service_account_sam=""
|
||||||
local public_group_prompt=""
|
local public_group_prompt=""
|
||||||
|
local fslogix_group_prompt=""
|
||||||
local samba_hostname_input=""
|
local samba_hostname_input=""
|
||||||
local netbios_name_input=""
|
local netbios_name_input=""
|
||||||
local sanitized_netbios_name=""
|
local sanitized_netbios_name=""
|
||||||
@@ -107,6 +109,12 @@ write_env_file() {
|
|||||||
public_group_sid="$domain_users_sid"
|
public_group_sid="$domain_users_sid"
|
||||||
fi
|
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
|
read -r -p "SAMBA_HOSTNAME [adsambafsrv]: " samba_hostname_input
|
||||||
if [[ -n "${samba_hostname_input:-}" ]]; then
|
if [[ -n "${samba_hostname_input:-}" ]]; then
|
||||||
samba_hostname="$samba_hostname_input"
|
samba_hostname="$samba_hostname_input"
|
||||||
@@ -158,6 +166,7 @@ SERVICE_ACCOUNT_PASSWORD=${service_password}
|
|||||||
DOMAIN_USERS_SID=${domain_users_sid}
|
DOMAIN_USERS_SID=${domain_users_sid}
|
||||||
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
||||||
PUBLIC_GROUP_SID=${public_group_sid}
|
PUBLIC_GROUP_SID=${public_group_sid}
|
||||||
|
FSLOGIX_GROUP_SID=${fslogix_group_sid}
|
||||||
SAMBA_HOSTNAME=${samba_hostname}
|
SAMBA_HOSTNAME=${samba_hostname}
|
||||||
NETBIOS_NAME=${netbios_name}
|
NETBIOS_NAME=${netbios_name}
|
||||||
EOF
|
EOF
|
||||||
@@ -209,6 +218,7 @@ JOIN_PASSWORD=${service_password}
|
|||||||
DOMAIN_USERS_SID=${domain_users_sid}
|
DOMAIN_USERS_SID=${domain_users_sid}
|
||||||
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
DOMAIN_ADMINS_SID=${domain_admins_sid}
|
||||||
PUBLIC_GROUP_SID=${public_group_sid}
|
PUBLIC_GROUP_SID=${public_group_sid}
|
||||||
|
FSLOGIX_GROUP_SID=${fslogix_group_sid}
|
||||||
SAMBA_HOSTNAME=${samba_hostname}
|
SAMBA_HOSTNAME=${samba_hostname}
|
||||||
NETBIOS_NAME=${netbios_name}
|
NETBIOS_NAME=${netbios_name}
|
||||||
# Optional overrides:
|
# Optional overrides:
|
||||||
|
|||||||
Reference in New Issue
Block a user