mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 08:15:17 +00:00
fix(docker): address security and reproducibility issues
Fixes from maintainer review: .dockerignore: - Add .env* files to prevent secret leakage (CRITICAL) - Add tests/, docs/, IDE files to reduce build context - Add organized comments for maintainability Dockerfile: - Pin bun version (ARG BUN_VERSION=1.2.2) for reproducible builds - Add build artifact validation step - Add npm cache clean to reduce image size - Add section comments for readability docker-compose.yml: - Add grok_home volume for grok-cli persistence - Add start_period to healthcheck for slow starts - Add resource limits (1G RAM, 2 CPUs) with reservations - Add documentation comments entrypoint.sh: - Improve chown error handling with warning message - Add usage help when no command provided README.md: - Add Resource Limits section with examples - Add Graceful Shutdown documentation - Add Troubleshooting section (permissions, ports, restart loops) - Add Security Notes section - Update persistence docs to include grok-cli Co-authored-by: opastorello <nicolas@pastorello-lab.com.br>
This commit is contained in:
1 parent
a14c7f3f6b
commit
b38641002f
5 files changed
+160
-5
No files matched your search
@@ -1,17 +1,47 @@
|
|||||||
|
# Git and GitHub
|
||||||
.git
|
.git
|
||||||
.github
|
.github
|
||||||
.husky
|
.husky
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
node_modules
|
node_modules
|
||||||
ui/node_modules
|
ui/node_modules
|
||||||
|
|
||||||
|
# Build outputs (rebuilt in container)
|
||||||
dist
|
dist
|
||||||
ui/dist
|
ui/dist
|
||||||
ui/.vite
|
ui/.vite
|
||||||
|
|
||||||
|
# Test and coverage
|
||||||
coverage
|
coverage
|
||||||
ui/coverage
|
ui/coverage
|
||||||
|
tests
|
||||||
|
**/__tests__
|
||||||
|
|
||||||
|
# Environment files (SECURITY: prevent secret leakage)
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
.env.local
|
||||||
|
.env.production
|
||||||
|
.env.development
|
||||||
|
|
||||||
|
# Documentation (not needed for runtime)
|
||||||
|
docs
|
||||||
|
*.md
|
||||||
|
!README.md
|
||||||
|
!LICENSE
|
||||||
|
|
||||||
|
# Logs and OS files
|
||||||
*.log
|
*.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# IDE and editor
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
*.tgz
|
||||||
|
*.tar.gz
|
||||||
+19
-2
@@ -1,9 +1,14 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Build stage: compile TypeScript and build UI
|
||||||
|
# =============================================================================
|
||||||
FROM node:20-bookworm-slim AS build
|
FROM node:20-bookworm-slim AS build
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-lc"]
|
SHELL ["/bin/bash", "-lc"]
|
||||||
|
|
||||||
|
# Pin bun version for reproducible builds
|
||||||
|
ARG BUN_VERSION=1.2.2
|
||||||
ENV BUN_INSTALL=/usr/local/bun
|
ENV BUN_INSTALL=/usr/local/bun
|
||||||
ENV PATH="$BUN_INSTALL/bin:$PATH"
|
ENV PATH="$BUN_INSTALL/bin:$PATH"
|
||||||
|
|
||||||
@@ -11,7 +16,8 @@ RUN apt-get update \
|
|||||||
&& apt-get install -y --no-install-recommends curl ca-certificates unzip \
|
&& apt-get install -y --no-install-recommends curl ca-certificates unzip \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN curl -fsSL https://bun.sh/install | bash
|
# Install specific bun version
|
||||||
|
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -26,11 +32,18 @@ COPY . .
|
|||||||
|
|
||||||
RUN bun run build:all
|
RUN bun run build:all
|
||||||
|
|
||||||
|
# Validate build artifacts exist
|
||||||
|
RUN test -d dist && test -d lib && echo "[OK] Build artifacts validated"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Runtime stage: minimal production image
|
||||||
|
# =============================================================================
|
||||||
FROM node:20-bookworm-slim AS runtime
|
FROM node:20-bookworm-slim AS runtime
|
||||||
|
|
||||||
SHELL ["/bin/bash", "-lc"]
|
SHELL ["/bin/bash", "-lc"]
|
||||||
|
|
||||||
|
# Pin bun version for reproducible builds
|
||||||
|
ARG BUN_VERSION=1.2.2
|
||||||
ENV BUN_INSTALL=/usr/local/bun
|
ENV BUN_INSTALL=/usr/local/bun
|
||||||
ENV PATH="$BUN_INSTALL/bin:/home/node/.opencode/bin:$PATH"
|
ENV PATH="$BUN_INSTALL/bin:/home/node/.opencode/bin:$PATH"
|
||||||
|
|
||||||
@@ -38,7 +51,8 @@ RUN apt-get update \
|
|||||||
&& apt-get install -y --no-install-recommends curl ca-certificates unzip \
|
&& apt-get install -y --no-install-recommends curl ca-certificates unzip \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
RUN curl -fsSL https://bun.sh/install | bash
|
# Install specific bun version
|
||||||
|
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
@@ -55,8 +69,11 @@ COPY --from=build /app/scripts ./scripts
|
|||||||
COPY --from=build /app/README.md ./README.md
|
COPY --from=build /app/README.md ./README.md
|
||||||
COPY --from=build /app/LICENSE ./LICENSE
|
COPY --from=build /app/LICENSE ./LICENSE
|
||||||
|
|
||||||
|
# Install AI CLI tools (using latest - pin versions in production if needed)
|
||||||
|
# These are optional tools for docker exec usage
|
||||||
RUN npm install -g @google/gemini-cli @vibe-kit/grok-cli @anthropic-ai/claude-code \
|
RUN npm install -g @google/gemini-cli @vibe-kit/grok-cli @anthropic-ai/claude-code \
|
||||||
&& npm install -g @kaitranntt/ccs --force \
|
&& npm install -g @kaitranntt/ccs --force \
|
||||||
|
&& npm cache clean --force \
|
||||||
&& su -s /bin/bash node -c 'curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path' \
|
&& su -s /bin/bash node -c 'curl -fsSL https://opencode.ai/install | bash -s -- --no-modify-path' \
|
||||||
&& ln -sf /app/dist/ccs.js /usr/local/bin/ccs
|
&& ln -sf /app/dist/ccs.js /usr/local/bin/ccs
|
||||||
|
|
||||||
|
|||||||
+83
-1
@@ -93,7 +93,83 @@ docker-compose -f docker/docker-compose.yml down
|
|||||||
|
|
||||||
- CCS stores data in `/home/node/.ccs` inside the container.
|
- CCS stores data in `/home/node/.ccs` inside the container.
|
||||||
- The examples use a named volume (`ccs_home`) to persist that data.
|
- The examples use a named volume (`ccs_home`) to persist that data.
|
||||||
- Compose also persists `/home/node/.claude` and `/home/node/.opencode` via named volumes.
|
- Compose also persists `/home/node/.claude`, `/home/node/.opencode`, and `/home/node/.grok-cli` via named volumes.
|
||||||
|
|
||||||
|
## Resource Limits
|
||||||
|
|
||||||
|
For production deployments, limit container resources:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name ccs-dashboard \
|
||||||
|
--restart unless-stopped \
|
||||||
|
--memory=1g \
|
||||||
|
--cpus=2 \
|
||||||
|
-p 3000:3000 \
|
||||||
|
-p 8317:8317 \
|
||||||
|
-v ccs_home:/home/node/.ccs \
|
||||||
|
ccs-dashboard:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Docker Compose includes default limits (1GB RAM, 2 CPUs). Adjust in `docker-compose.yml` under `deploy.resources`.
|
||||||
|
|
||||||
|
## Graceful Shutdown
|
||||||
|
|
||||||
|
CCS handles `SIGTERM` gracefully. When stopping the container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker stop ccs-dashboard # Sends SIGTERM, waits 10s, then SIGKILL
|
||||||
|
docker stop -t 30 ccs-dashboard # Wait 30s for graceful shutdown
|
||||||
|
```
|
||||||
|
|
||||||
|
The `init: true` in docker-compose.yml ensures proper signal forwarding.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Permission Errors (EACCES)
|
||||||
|
|
||||||
|
If you see permission errors on startup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check volume permissions
|
||||||
|
docker exec ccs-dashboard ls -la /home/node/.ccs
|
||||||
|
|
||||||
|
# Fix by recreating volumes
|
||||||
|
docker-compose down -v
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Already in Use
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check what's using the port
|
||||||
|
lsof -i :3000
|
||||||
|
lsof -i :8317
|
||||||
|
|
||||||
|
# Use different ports
|
||||||
|
docker run -p 4000:3000 -p 9317:8317 ...
|
||||||
|
|
||||||
|
# Or with compose
|
||||||
|
CCS_DASHBOARD_PORT=4000 CCS_CLIPROXY_PORT=9317 docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
### Container Keeps Restarting
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check logs for errors
|
||||||
|
docker logs ccs-dashboard --tail 50
|
||||||
|
|
||||||
|
# Check container health
|
||||||
|
docker inspect ccs-dashboard --format='{{.State.Health.Status}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug Mode
|
||||||
|
|
||||||
|
Enable verbose logging:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -e CCS_DEBUG=1 ...
|
||||||
|
```
|
||||||
|
|
||||||
## Examples: Claude + Gemini inside Docker
|
## Examples: Claude + Gemini inside Docker
|
||||||
|
|
||||||
@@ -121,3 +197,9 @@ If you need to configure credentials, do it according to each CLI's docs:
|
|||||||
docker exec -it ccs-dashboard claude --help
|
docker exec -it ccs-dashboard claude --help
|
||||||
docker exec -it ccs-dashboard gemini --help
|
docker exec -it ccs-dashboard gemini --help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- **Secrets**: For sensitive values like `CCS_PROXY_AUTH_TOKEN`, consider using Docker secrets or a `.env` file (not committed to git).
|
||||||
|
- **Network**: The container exposes ports 3000 and 8317. In production, use a reverse proxy (nginx, traefik) with TLS.
|
||||||
|
- **Updates**: Regularly rebuild the image to get security patches: `docker-compose build --pull`
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
# CCS Dashboard Docker Compose
|
||||||
|
# See docker/README.md for documentation
|
||||||
services:
|
services:
|
||||||
ccs-dashboard:
|
ccs-dashboard:
|
||||||
image: ccs-dashboard:latest
|
image: ccs-dashboard:latest
|
||||||
@@ -26,13 +28,26 @@ services:
|
|||||||
- ccs_home:/home/node/.ccs
|
- ccs_home:/home/node/.ccs
|
||||||
- claude_home:/home/node/.claude
|
- claude_home:/home/node/.claude
|
||||||
- opencode_home:/home/node/.opencode
|
- opencode_home:/home/node/.opencode
|
||||||
|
- grok_home:/home/node/.grok-cli
|
||||||
|
# Healthcheck uses internal ports (3000/8317) which are fixed
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD-SHELL", "curl -fsS --max-time 2 http://localhost:3000/ >/dev/null && curl -sS --max-time 2 http://127.0.0.1:8317/ >/dev/null"]
|
test: ["CMD-SHELL", "curl -fsS --max-time 2 http://localhost:3000/ >/dev/null && curl -sS --max-time 2 http://127.0.0.1:8317/ >/dev/null"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 12
|
retries: 12
|
||||||
|
start_period: 30s
|
||||||
|
# Resource limits (adjust based on workload)
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 1G
|
||||||
|
cpus: '2'
|
||||||
|
reservations:
|
||||||
|
memory: 256M
|
||||||
|
cpus: '0.5'
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
ccs_home:
|
ccs_home:
|
||||||
claude_home:
|
claude_home:
|
||||||
opencode_home:
|
opencode_home:
|
||||||
|
grok_home:
|
||||||
+13
-2
@@ -5,19 +5,30 @@ ccs_home_dir="${CCS_HOME_DIR:-/home/node/.ccs}"
|
|||||||
|
|
||||||
mkdir -p "$ccs_home_dir"
|
mkdir -p "$ccs_home_dir"
|
||||||
|
|
||||||
|
# Fix volume permissions if running as root
|
||||||
if [ "$(id -u)" = "0" ]; then
|
if [ "$(id -u)" = "0" ]; then
|
||||||
chown -R node:node "$ccs_home_dir" || true
|
if ! chown -R node:node "$ccs_home_dir" 2>/dev/null; then
|
||||||
|
echo "[!] Warning: Could not change ownership of $ccs_home_dir (read-only volume?)" >&2
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Show usage if no command provided
|
||||||
if [ "$#" -eq 0 ]; then
|
if [ "$#" -eq 0 ]; then
|
||||||
echo "[X] No command provided" >&2
|
echo "[X] No command provided" >&2
|
||||||
|
echo "" >&2
|
||||||
|
echo "Usage: docker run ccs-dashboard <command>" >&2
|
||||||
|
echo "" >&2
|
||||||
|
echo "Examples:" >&2
|
||||||
|
echo " docker run ccs-dashboard node dist/ccs.js config" >&2
|
||||||
|
echo " docker run ccs-dashboard ccs --help" >&2
|
||||||
|
echo "" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Drop privileges from root to node user
|
||||||
if [ "$(id -u)" = "0" ]; then
|
if [ "$(id -u)" = "0" ]; then
|
||||||
cmd="$(printf '%q ' "$@")"
|
cmd="$(printf '%q ' "$@")"
|
||||||
exec su -s /bin/bash node -c "exec ${cmd}"
|
exec su -s /bin/bash node -c "exec ${cmd}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exec "$@"
|
exec "$@"
|
||||||
|
|
||||||
Reference in new issue
Block a user