167 lines
5.8 KiB
Markdown
167 lines
5.8 KiB
Markdown
# AAD Local File Share (Samba + Web UI)
|
|
|
|
Dockerized SMB/CIFS file sharing system for a single on-prem Linux host. Authentication is via on-prem AD DS (Samba `security = ADS` with winbind). Authorization and share management live exclusively in local SQLite, driven by the web UI.
|
|
|
|
## Architecture highlights
|
|
|
|
- **Authentication:** Samba joins AD as a member server (`security = ADS`). SMB1 is disabled; SMB2+ only. NTLMv2 is allowed for non-domain-joined devices.
|
|
- **Authorization:** SQLite is the source of truth (no AD groups). Share membership is expanded to per-user lists in a generated Samba config file.
|
|
- **Share model:**
|
|
- `\\server\private` is a single share that maps to `/data/private/%U` with per-user content.
|
|
- `\\server\<shareName>` are separate shares pointing to `/data/shares/<shareName>`.
|
|
- **No per-folder ACLs:** Samba config uses `force user = filesvc` and `force group = filesvc` for shared areas. `nt acl support = no` and related toggles prevent ACL edits from clients.
|
|
- **Dynamic share reload:** Web UI regenerates `/etc/samba/shares.generated.conf` atomically and Samba reloads via `smbcontrol all reload-config` on change.
|
|
- **Backups:** Scheduled ISO backups with a tar-based payload (`data.tar.zst`) + SQLite `.backup` file + `manifest.json` containing hashes/sizes.
|
|
|
|
## Directory layout
|
|
|
|
Host directories:
|
|
|
|
- `/srv/files/data` -> `/data`
|
|
- `/data/private/<username>`
|
|
- `/data/shares/<shareName>`
|
|
- `/srv/files/remote-backup` -> `/remote` (bind-mounted remote share)
|
|
|
|
SQLite location:
|
|
|
|
- `/var/lib/webui/app.db` (inside `webui` container, persisted via volume)
|
|
|
|
## Quick start
|
|
|
|
1. **Clone repo and create env file**
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
```
|
|
|
|
2. **Generate self-signed TLS certs for Traefik**
|
|
|
|
```bash
|
|
./scripts/generate-self-signed.sh ./traefik/certs files.example.com
|
|
```
|
|
|
|
3. **Create host directories**
|
|
|
|
```bash
|
|
sudo mkdir -p /srv/files/data /srv/files/remote-backup
|
|
```
|
|
|
|
4. **Start the stack**
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
5. **Open the Web UI**
|
|
|
|
- `https://files.example.com/`
|
|
|
|
## Samba security notes
|
|
|
|
- **SMB1 is disabled** (`server min protocol = SMB2`).
|
|
- **NTLMv2** is allowed for non-domain-joined clients (`ntlm auth = ntlmv2-only`).
|
|
- Recommended hardening: prefer Kerberos and domain-joined devices, set `server signing = mandatory`, and enable `smb encrypt = desired` or `required` based on performance and compliance needs.
|
|
- **No ACL editing**: `nt acl support = no`, `dos filemode = no`, and `unix extensions = no` reduce client-side permission manipulation.
|
|
|
|
## Share behavior
|
|
|
|
- **Private share:** `\\server\private` maps to `/data/private/%U` and creates the user directory on first connect. Directory is `0700` and owned by the user (mapped via winbind).
|
|
- **Shared shares:** Each share is a distinct Samba stanza in `/etc/samba/shares.generated.conf`. Users are allowed via per-user lists from SQLite:
|
|
- `valid users = <owner+rw+ro users>`
|
|
- `write list = <owner+rw users>`
|
|
- `read only = yes` (writes allowed via `write list`)
|
|
|
|
**Important:** Shared area permissions are enforced by Samba config (lists), not filesystem ACLs. All files are owned by `filesvc` inside the Samba container due to `force user/group`.
|
|
|
|
The `filesvc` UID/GID is `10050` by default in both `webui` and `samba` containers so ownership stays consistent on the host bind mount.
|
|
|
|
## Web UI
|
|
|
|
The Web UI authenticates via Entra ID OIDC (authorization code flow) and stores sessions in an HTTP-only cookie. Users are authorized if their UPN suffix matches `ALLOWED_UPN_SUFFIX`.
|
|
|
|
### Admin page
|
|
|
|
`/admin` shows access logs and aggregated statistics with Chart.js (daily activity and action breakdown). Access is restricted to the admin user via a bcrypt hash in `.env`.
|
|
|
|
To generate a bcrypt hash for the admin UPN:
|
|
|
|
```bash
|
|
node -e "const bcrypt=require('bcryptjs'); const upn=process.argv[1]; console.log(bcrypt.hashSync(upn, 10));" "admin@example.com"
|
|
```
|
|
|
|
Set the output in `ADMIN_UPN_BCRYPT`.
|
|
|
|
### Pages
|
|
|
|
- `/` list shares, create share
|
|
- `/shares/:id` manage membership
|
|
- `/admin` logs and statistics
|
|
|
|
### APIs
|
|
|
|
- `POST /api/shares` create share
|
|
- `GET /api/shares` list shares visible to current user
|
|
- `GET /api/shares/:id` share detail
|
|
- `POST /api/shares/:id/members` add/remove users or local groups
|
|
- `DELETE /api/shares/:id` disable share
|
|
|
|
Local groups are managed in the UI on each share page and can be assigned roles per share.
|
|
|
|
### Create share flow
|
|
|
|
1. Validate share name (strict regex, length limits, reserved names).
|
|
2. Insert into SQLite with state `creating`.
|
|
3. Create `/data/shares/<shareName>`.
|
|
4. `chown root:filesvc` and `chmod 2770`.
|
|
5. Regenerate `/samba-generated/shares.generated.conf` atomically.
|
|
6. Mark share `ready`.
|
|
|
|
## Backup job
|
|
|
|
Backups run on the `BACKUP_CRON` schedule and produce:
|
|
|
|
- `data.tar.zst` (tar archive of `/data` with xattrs/acl metadata)
|
|
- `app.db` (SQLite `.backup`)
|
|
- `manifest.json` (timestamp, sizes, sha256)
|
|
- `backup-<timestamp>.iso` containing the above, then compressed to `.iso.zst` (or `.iso.gz`)
|
|
|
|
**Note:** The ISO file itself does not preserve POSIX ACLs; the tar file inside does.
|
|
|
|
### Restore steps
|
|
|
|
1. Copy the compressed ISO back from `/remote`.
|
|
2. Decompress it.
|
|
3. Extract the ISO contents.
|
|
4. Decompress `data.tar.zst` and restore `/data` on the host.
|
|
5. Restore `app.db` to the web UI volume.
|
|
6. Redeploy compose. Re-join AD if the Samba secrets volume is lost.
|
|
|
|
## Connecting clients
|
|
|
|
### Windows
|
|
|
|
- `\\server\private`
|
|
- `\\server\shareName`
|
|
|
|
Use `DOMAIN\user` or `user@domain` when prompted. NTLMv2 is supported for non-domain-joined devices.
|
|
|
|
### macOS
|
|
|
|
- Finder -> Go -> Connect to Server
|
|
- `smb://server/private`
|
|
- `smb://server/shareName`
|
|
|
|
### Linux
|
|
|
|
```bash
|
|
smbclient -U user@domain //server/private
|
|
smbclient -U user@domain //server/shareName
|
|
```
|
|
|
|
## Files of interest
|
|
|
|
- `docker-compose.yml`
|
|
- `samba/smb.conf.template`
|
|
- `webui/src/index.js`
|
|
- `backupd/backup.sh`
|