Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.git
.github
.claude
docs
media
e2e/results
e2e/node_modules
web/.meteor/local
workspace
34 changes: 34 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: E2E Tests

on:
pull_request:
branches: [ main ]
workflow_dispatch:

jobs:
e2e:
name: Run E2E Tests
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run E2E tests
working-directory: e2e
run: ./run-tests.sh --compose
continue-on-error: true
id: tests

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: e2e-results
path: e2e/results/
retention-days: 14

- name: Check test result
if: steps.tests.outcome == 'failure'
run: exit 1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ Thumbs.db
# Playwright
e2e/test-results/
e2e/playwright-report/
e2e/results/

workspace/
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ samba-conductor/
│ └── screenshots/ # UI screenshots for documentation (generated via Playwright)
├── e2e/ # Playwright E2E tests and screenshot capture
│ ├── screenshots/ # Capture scripts (use data-e2e selectors)
│ ├── tests/ # E2E test suites and helpers
│ ├── run-tests.sh # Run E2E tests (host or Docker Compose mode)
│ └── update-screenshots.sh # Run to update docs screenshots (change detection)
├── .github/workflows/ # CI pipelines (E2E tests on PR)
└── CLAUDE.md # This file
```

Expand All @@ -37,7 +40,9 @@ Each subdirectory has its own `CLAUDE.md` with specific instructions.
- **Web UI:** Meteor 3.4 + React 19 + Tailwind CSS 4 (mobile-first, 3 themes: Wine/Classic/Light)
- **Primary Data Source:** Samba 4 AD DC (LDAPS + samba-tool)
- **Database:** MongoDB (app state, settings, DR snapshots — AD data is NOT stored here except for DR)
- **Deployment:** Docker (3 image variants, single `/data` volume)
- **Deployment:** Docker (all-in-one single container, standalone DC, or web + external DC)
- **Build:** Rspack + Babel (with `REMOVE_E2E_ATTRS=true` to strip test selectors in production)
- **CI:** GitHub Actions E2E workflow (`.github/workflows/e2e-tests.yml`)

## Architecture

Expand All @@ -46,6 +51,7 @@ Each subdirectory has its own `CLAUDE.md` with specific instructions.
- **Credential Store:** AES-256-GCM, random key per boot (session), PBKDF2-derived key (DR data)
- **Sync Account:** Auto-created AD service account for background sync jobs
- **S3 Backup:** Configurable mongodump + samba-tool domain backup → S3-compatible storage
- **Cron:** Background sync/backup jobs, disableable via `Meteor.settings.cron.enabled = false`

## Development Constraints

Expand Down
34 changes: 25 additions & 9 deletions docker/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Docker images for the Samba Conductor infrastructure. Three variants, all using

## Images

| Image | Base | Contents |
|----------------------------|------------------------|-------------------------------|
| **samba-ad-dc** | Fedora minimal | Samba 4 AD DC only |
| **web** (`web/Dockerfile`) | zcloud runtime | Meteor + MongoDB + samba-tool |
| **all-in-one** | zcloud runtime + Samba | Everything in one container |
| Image | Base | Contents |
|----------------------------|----------------------------------------------|---------------------------------------|
| **samba-ad-dc** | Fedora minimal | Samba 4 AD DC only |
| **web** (`web/Dockerfile`) | zcloud runtime | Meteor + MongoDB + samba-tool |
| **all-in-one** | Fedora minimal + Node.js/MongoDB from Meteor | Samba + Meteor + MongoDB + Supervisor |

## Structure

Expand All @@ -24,17 +24,21 @@ docker/
│ ├── entrypoint.sh # Sources samba-setup.sh
│ └── supervisord.conf
├── all-in-one/
│ ├── Dockerfile # zcloud runtime + Samba + Meteor app
│ ├── Dockerfile # Fedora minimal + Samba + Node.js + MongoDB + Meteor app
│ ├── docker-compose.yml
│ ├── entrypoint.sh # Sources samba-setup.sh + mongo/app env
│ └── supervisord.conf
│ ├── entrypoint.sh # Sources samba-setup.sh, assembles supervisor config
│ └── supervisor/ # Supervisor config templates
│ ├── supervisord.conf # Base config with [include]
│ ├── samba.conf # Always active
│ ├── mongodb.conf # Active when internal MongoDB needed
│ └── webapp.conf # Active when ROOT_URL is set
├── README.md
└── CLAUDE.md
```

## Conventions

- Base image: Fedora minimal (samba-ad-dc) or zcloud runtime (web, all-in-one)
- Base image: Fedora minimal (samba-ad-dc, all-in-one) or zcloud runtime (web)
- Use `microdnf` on Fedora, `apt-get` on Ubuntu (zcloud)
- Always clean package caches after install
- Single `/data` volume — standard paths symlinked into it
Expand All @@ -44,3 +48,15 @@ docker/
- Shared scripts in `scripts/samba-setup.sh` used by both samba-ad-dc and all-in-one
- Replica DC support via `SAMBA_JOIN_AS_DC=true` + `SAMBA_PRIMARY_DC=hostname`
- Dev compose includes replica as optional profile (`--profile replica`)

## All-in-One Service Management

Entrypoint conditionally assembles supervisor config from templates based on environment:

| `ROOT_URL` | `MONGO_URL` | Services started |
|------------|-------------|-----------------------------------|
| not set | — | Samba only |
| set | not set | Samba + internal MongoDB + webapp |
| set | set | Samba + webapp (external MongoDB) |

If `METEOR_SETTINGS` env var is provided, it is used as-is. Otherwise, settings are generated from `SAMBA_REALM`.
102 changes: 60 additions & 42 deletions docker/all-in-one/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,74 +1,92 @@
# =============================================================================
# Samba Conductor — All-in-One Image
# Base: zcloud meteor runtime (Node.js + MongoDB) + Samba 4 AD DC
# Single volume (/data) for all persistent state
# =============================================================================

# Stage 1: Build Meteor application
FROM zcloudws/meteor-build:3.4 AS builder

WORKDIR /app-source/source
USER root

RUN chown zcloud:zcloud -R /app-source
RUN mkdir -p /tmp/tools/{.node,.mongodb} && chown zcloud:zcloud -R /tmp/tools

USER zcloud

RUN export DEV_BUNDLE_DIR=/home/zcloud/.meteor/packages/meteor-tool/*.*.*/mt-os.linux.x86_64/dev_bundle && \
cp -r $DEV_BUNDLE_DIR/{bin,lib} /tmp/tools/.node && \
cp -r $DEV_BUNDLE_DIR/mongodb/bin /tmp/tools/.mongodb/

COPY --chown=zcloud:zcloud web /app-source/source

ENV METEOR_DISABLE_OPTIMISTIC_CACHING=1

RUN meteor npm i --no-audit --legacy-peer-deps \
&& meteor build --platforms web.browser --directory ../app-build

RUN cd /app-source/app-build/bundle/programs/server \
&& npm i --no-audit --legacy-peer-deps
# --legacy-peer-deps because of react-error-boundary
RUN meteor npm i --no-audit --legacy-peer-deps && \
meteor build --platforms web.browser --directory ../app-build

# Stage 2: All-in-one runtime (Node.js + MongoDB from zcloud + Samba)
FROM zcloudws/meteor-node-mongodb-runtime:3.4-with-tools
# Samba 4 Active Directory Domain Controller (standalone)
# Based on Fedora minimal for smallest possible image
FROM registry.fedoraproject.org/fedora-minimal:42

LABEL maintainer="samba-conductor"
LABEL description="Samba Conductor All-in-One: Samba 4 AD DC + Web UI + MongoDB"
LABEL description="Samba 4 AD DC on Fedora minimal"

USER root

# Install Samba AD DC and dependencies on Ubuntu 22.04
RUN apt-get update && apt-get install -y --no-install-recommends \
# Install Samba AD DC and required dependencies
RUN microdnf install -y \
samba \
samba-dsdb-modules \
samba-vfs-modules \
winbind \
krb5-user \
ldap-utils \
samba-dc \
samba-client \
samba-winbind \
samba-winbind-clients \
krb5-workstation \
bind-utils \
supervisor \
tdb-tools \
ldb-tools \
findutils \
procps-ng \
openssl \
&& rm -rf /var/lib/apt/lists/*

# Copy built Meteor app
COPY --from=builder --chown=zcloud:zcloud /app-source/app-build/bundle /home/zcloud/app

# Shared samba scripts
COPY docker/scripts/samba-setup.sh /usr/local/lib/samba-setup.sh
RUN chmod +x /usr/local/lib/samba-setup.sh

# Image-specific files
COPY docker/all-in-one/entrypoint.sh /usr/local/bin/entrypoint.sh
COPY docker/all-in-one/supervisord.conf /etc/supervisord.conf
RUN chmod +x /usr/local/bin/entrypoint.sh

# Single persistent volume
VOLUME ["/data"]
&& microdnf clean all \
&& rm -rf /var/cache/dnf

# Samba ports
EXPOSE 53/tcp 53/udp
EXPOSE 88/tcp 88/udp
EXPOSE 135/tcp
EXPOSE 137/udp
EXPOSE 138/udp
EXPOSE 139/tcp
EXPOSE 389/tcp 389/udp
EXPOSE 445/tcp
EXPOSE 464/tcp 464/udp
EXPOSE 636/tcp
EXPOSE 3268/tcp
EXPOSE 3269/tcp

# Web UI
EXPOSE 3000/tcp
EXPOSE 27017/tcp
EXPOSE 49152-65535/tcp

# Non-root user for MongoDB
RUN useradd -r -s /sbin/nologin -d /opt/app conductor

# Shared samba scripts
COPY docker/scripts/samba-setup.sh /usr/local/lib/samba-setup.sh
RUN chmod +x /usr/local/lib/samba-setup.sh

# Image-specific files
COPY docker/all-in-one/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# Supervisor config templates
COPY docker/all-in-one/supervisor/supervisord.conf /etc/supervisord.conf
COPY docker/all-in-one/supervisor/ /etc/supervisor/templates/
RUN mkdir -p /etc/supervisor/conf.d

# Meteor tools
COPY --from=builder /tmp/tools/.node /opt/nodejs/
COPY --from=builder /tmp/tools/.mongodb /opt/mongodb/
COPY --from=builder /app-source/app-build/bundle /opt/app

# --legacy-peer-deps because of react-error-boundary
RUN cd /opt/app/programs/server && PATH=$PATH:/opt/nodejs/bin npm i --no-audit --legacy-peer-deps

# Single persistent volume
VOLUME ["/data"]

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
80 changes: 63 additions & 17 deletions docker/all-in-one/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,43 @@ SAMBA_DNS_FORWARDER=${SAMBA_DNS_FORWARDER:-"8.8.8.8"}
SAMBA_SERVER_ROLE=${SAMBA_SERVER_ROLE:-"dc"}
DATA_DIR=${DATA_DIR:-"/data"}

ROOT_URL=${ROOT_URL:-"http://localhost:3000"}
PORT=${PORT:-3000}

TEMPLATES_DIR="/etc/supervisor/templates"
CONF_DIR="/etc/supervisor/conf.d"

# =============================================================================
# Determine which services to start
# =============================================================================
# ROOT_URL set → start webapp
# MONGO_URL set → use external MongoDB (skip internal)
# MONGO_URL unset → start internal MongoDB if webapp is enabled
ENABLE_WEBAPP=false
ENABLE_MONGODB=false

if [ -n "${ROOT_URL}" ]; then
ENABLE_WEBAPP=true
if [ -z "${MONGO_URL}" ]; then
ENABLE_MONGODB=true
MONGO_URL="mongodb://127.0.0.1:27017/samba-conductor"
fi
fi

# =============================================================================
# Setup MongoDB directories
# Setup directories
# =============================================================================
MONGO_DATA="${DATA_DIR}/mongodb"
MONGO_LOGS="${DATA_DIR}/logs/mongodb"
APP_LOGS="${DATA_DIR}/logs/app"
SAMBA_LOGS="${DATA_DIR}/logs/samba"
mkdir -p "${SAMBA_LOGS}"

if [ "${ENABLE_MONGODB}" = true ]; then
mkdir -p "${DATA_DIR}/mongodb" "${DATA_DIR}/logs/mongodb"
chown -R conductor:conductor "${DATA_DIR}/mongodb" "${DATA_DIR}/logs/mongodb"
fi

mkdir -p "${MONGO_DATA}" "${MONGO_LOGS}" "${APP_LOGS}"
chown -R zcloud:zcloud "${MONGO_DATA}" "${MONGO_LOGS}" "${APP_LOGS}"
if [ "${ENABLE_WEBAPP}" = true ]; then
mkdir -p "${DATA_DIR}/logs/app"
chown -R conductor:conductor "${DATA_DIR}/logs/app"
fi

# =============================================================================
# Run shared Samba setup (provision, kerberos, TLS)
Expand All @@ -31,22 +56,43 @@ source /usr/local/lib/samba-setup.sh
setup_samba

# =============================================================================
# Generate environment file for the web app
# Assemble supervisor config from templates
# =============================================================================
SAMBA_REALM_LOWER=$(echo "${SAMBA_REALM}" | tr '[:upper:]' '[:lower:]')
BASE_DN="DC=$(echo "${SAMBA_REALM_LOWER}" | sed 's/\./,DC=/g')"
rm -f "${CONF_DIR}"/*.conf

cat > /etc/samba-conductor.env <<ENVEOF
# Samba always runs
cp "${TEMPLATES_DIR}/samba.conf" "${CONF_DIR}/"

if [ "${ENABLE_MONGODB}" = true ]; then
cp "${TEMPLATES_DIR}/mongodb.conf" "${CONF_DIR}/"
fi

if [ "${ENABLE_WEBAPP}" = true ]; then
cp "${TEMPLATES_DIR}/webapp.conf" "${CONF_DIR}/"

# Generate environment file for the web app
if [ -z "${METEOR_SETTINGS}" ]; then
SAMBA_REALM_LOWER=$(echo "${SAMBA_REALM}" | tr '[:upper:]' '[:lower:]')
BASE_DN="DC=$(echo "${SAMBA_REALM_LOWER}" | sed 's/\./,DC=/g')"
METEOR_SETTINGS='{"samba":{"ldapUrl":"ldaps://127.0.0.1:636","baseDn":"'"${BASE_DN}"'","realm":"'"${SAMBA_REALM}"'","tlsRejectUnauthorized":false,"sessionTtlMinutes":30},"public":{"appInfo":{"name":"Samba Conductor"}}}'
fi

cat > /etc/samba-conductor.env <<ENVEOF
export ROOT_URL="${ROOT_URL}"
export PORT="${PORT}"
export MONGO_URL="mongodb://127.0.0.1:27017/samba-conductor"
export METEOR_SETTINGS='{"samba":{"ldapUrl":"ldaps://127.0.0.1:636","baseDn":"${BASE_DN}","realm":"${SAMBA_REALM}","tlsRejectUnauthorized":false,"sessionTtlMinutes":30},"public":{"appInfo":{"name":"Samba Conductor"}}}'
export MONGO_URL="${MONGO_URL}"
export METEOR_SETTINGS='${METEOR_SETTINGS}'
ENVEOF
fi

# =============================================================================
# Start
# =============================================================================
echo "=== Samba Conductor All-in-One ==="
echo " Realm: ${SAMBA_REALM}"
echo " Web UI: ${ROOT_URL}"
echo " Data: ${DATA_DIR}"
echo " Realm: ${SAMBA_REALM}"
echo " Services: samba$([ "${ENABLE_MONGODB}" = true ] && echo ', mongodb')$([ "${ENABLE_WEBAPP}" = true ] && echo ', webapp')"
[ "${ENABLE_WEBAPP}" = true ] && echo " Web UI: ${ROOT_URL}"
[ "${ENABLE_MONGODB}" = true ] && echo " MongoDB: internal" || ([ "${ENABLE_WEBAPP}" = true ] && echo " MongoDB: ${MONGO_URL}")
echo " Data: ${DATA_DIR}"

# Start all services via supervisor
exec /usr/bin/supervisord -c /etc/supervisord.conf
9 changes: 9 additions & 0 deletions docker/all-in-one/supervisor/mongodb.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[program:mongodb]
command=/opt/mongodb/bin/mongod --dbpath /data/mongodb --bind_ip 127.0.0.1 --port 27017 --logpath /data/logs/mongodb/mongod.log --quiet
user=conductor
autostart=true
autorestart=true
priority=20
environment=PATH="/opt/mongodb/bin:%(ENV_PATH)s"
stdout_logfile=/data/logs/mongodb/mongod-stdout.log
stderr_logfile=/data/logs/mongodb/mongod-stderr.log
7 changes: 7 additions & 0 deletions docker/all-in-one/supervisor/samba.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[program:samba]
command=/usr/sbin/samba --foreground --no-process-group
autostart=true
autorestart=true
priority=10
stdout_logfile=/data/logs/samba/samba-stdout.log
stderr_logfile=/data/logs/samba/samba-stderr.log
Loading
Loading