diff --git a/.containerignore b/.containerignore new file mode 100644 index 0000000..082a78a --- /dev/null +++ b/.containerignore @@ -0,0 +1,36 @@ +# Git +.git +.gitignore + +# Documentation +*.md +!README.md +LICENSE + +# Build artifacts +argparse-builder +argparse-builder-* +*.exe +*.dll +*.so +*.dylib + +# Test files +*_test.go +test_*.go + +# Development +.vscode +.idea +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Temp files +tmp/ +temp/ +*.tmp diff --git a/Makefile.container b/Makefile.container new file mode 100644 index 0000000..7bf0579 --- /dev/null +++ b/Makefile.container @@ -0,0 +1,54 @@ +.PHONY: help build run docker-build docker-run docker-push clean + +BINARY_NAME=argparse-builder +DOCKER_IMAGE=argparse-builder +DOCKER_TAG=latest +PORT=8080 + +help: + @echo "Available targets:" + @echo " build - Build Go binary" + @echo " run - Run locally" + @echo " docker-build - Build Docker image" + @echo " docker-run - Run Docker container" + @echo " docker-push - Push to registry" + @echo " docker-clean - Remove Docker images" + @echo " clean - Clean all artifacts" + +build: + go build -ldflags="-s -w" -o $(BINARY_NAME) . + +run: build + ./$(BINARY_NAME) + +docker-build: + docker build -t $(DOCKER_IMAGE):$(DOCKER_TAG) . + +docker-build-alpine: + docker build -f Dockerfile.alpine -t $(DOCKER_IMAGE):alpine . + +docker-run: + docker run -d -p $(PORT):8080 --name $(BINARY_NAME) $(DOCKER_IMAGE):$(DOCKER_TAG) + +docker-stop: + docker stop $(BINARY_NAME) && docker rm $(BINARY_NAME) + +docker-logs: + docker logs -f $(BINARY_NAME) + +docker-push: + docker push $(DOCKER_IMAGE):$(DOCKER_TAG) + +docker-clean: + docker rmi $(DOCKER_IMAGE):$(DOCKER_TAG) || true + +compose-up: + docker-compose up -d + +compose-down: + docker-compose down + +clean: + rm -f $(BINARY_NAME) + docker-compose down || true + docker rm -f $(BINARY_NAME) || true diff --git a/README.md b/README.md index 9407db7..4eedbfe 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,8 @@ go build -o argparse-builder ./argparse-builder ``` -Server starts on port 8080. Open http://localhost:8080 in your browser. -Demo available at: https://pages.git.pynezz.dev/argparse-builder +Server starts on port 8080. Open in your browser. +Demo available at: ## Usage @@ -89,3 +89,51 @@ Generates a complete bash script with: - No shell execution - Template-based HTML generation - CORS headers not set (localhost only by default) + +## Run in container + +### File Structure + +``` +. +├── assets/ +│ ├── Containerfile # OCI production (scratch, ~7MB) +│ └── Containerfile.alpine # Development (Alpine, ~15MB) +│ +├── docs/ +│ ├── container.md # Primary Podman guide ⭐ +│ ├── docker/ +│ │ ├── DOCKER_GUIDE.md # Complete Docker reference +│ │ ├── Dockerfile # Docker production +│ │ ├── Dockerfile.alpine # Docker development +│ │ ├── docker-compose.yml +│ │ ├── docker.md # Docker vs Podman +│ │ └── README.md +│ └── README.md # Documentation hub +│ +├── Makefile.container # Podman build targets +├── podman-compose.yml # OCI compose with SELinux +├── CONTAINER_QUICKREF.md # One-page reference +├── PODMAN_SUMMARY.md # This summary +└── k8s-deployment.yaml # Kubernetes manifest (TODO) +``` + +### Usage + +```bash +# Podman (primary) +podman build -f assets/Containerfile -t argparse-builder . +podman run -d -p 8080:8080 --security-opt label=type:container_t argparse-builder + +# Docker (compatibility) +docker build -f assets/Containerfile -t argparse-builder . +docker run -d -p 8080:8080 argparse-builder +``` + +``` + +``` + +``` + +``` diff --git a/assets/.compose b/assets/.compose new file mode 100644 index 0000000..35002f4 --- /dev/null +++ b/assets/.compose @@ -0,0 +1,43 @@ +version: '3.8' + +services: + argparse-builder: + build: + context: . + dockerfile: Dockerfile + container_name: argparse-builder + ports: + - "8080:8080" + restart: unless-stopped + + # Resource limits + deploy: + resources: + limits: + cpus: '0.5' + memory: 128M + reservations: + cpus: '0.1' + memory: 32M + + # Security + read_only: true + cap_drop: + - ALL + security_opt: + - no-new-privileges:true + + # Health check + healthcheck: + test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1"] + interval: 30s + timeout: 3s + retries: 3 + start_period: 5s + + # Logging + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" diff --git a/assets/Containerfile b/assets/Containerfile new file mode 100644 index 0000000..4dd69ff --- /dev/null +++ b/assets/Containerfile @@ -0,0 +1,50 @@ +# OCI-compliant multi-stage build for Podman +# Handles SELinux contexts and rootless operation + +FROM docker.io/library/golang:1.23-alpine AS builder + +RUN apk add --no-cache git ca-certificates tzdata + +WORKDIR /build + +COPY go.mod go.sum* ./ +RUN go mod download + +COPY . . + +# Build static binary with no CGO +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -ldflags='-w -s -extldflags "-static"' \ + -a -installsuffix cgo \ + -o argparse-builder \ + . + +# Minimal runtime with proper labels +FROM scratch + +LABEL maintainer="your-email@example.com" \ + org.opencontainers.image.title="Argparse Builder" \ + org.opencontainers.image.description="Interactive bash argument parser generator" \ + org.opencontainers.image.version="1.0.0" \ + org.opencontainers.image.authors="pynezz" \ + org.opencontainers.image.url="https://git.pynezz.dev/pynezz/argparser" \ + org.opencontainers.image.source="https://git.pynezz.dev/pynezz/argparser" + +# Copy certificates and timezone data +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo + +# Copy binary +COPY --from=builder /build/argparse-builder /argparse-builder + +# Expose port +EXPOSE 8080 + +# Run as nobody user +USER 65534:65534 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD ["/argparse-builder", "health"] || exit 1 + +ENTRYPOINT ["/argparse-builder"] diff --git a/assets/Containerfile.alpine b/assets/Containerfile.alpine new file mode 100644 index 0000000..79eda1c --- /dev/null +++ b/assets/Containerfile.alpine @@ -0,0 +1,41 @@ +# Alpine-based with shell for debugging +# SELinux compatible, rootless ready + +FROM docker.io/library/golang:1.23-alpine AS builder + +RUN apk add --no-cache git ca-certificates + +WORKDIR /build + +COPY go.mod go.sum* ./ +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 go build \ + -ldflags='-w -s' \ + -o argparse-builder \ + . + +FROM docker.io/library/alpine:3.19 + +LABEL maintainer="your-email@example.com" \ + org.opencontainers.image.title="Argparse Builder (Alpine)" \ + org.opencontainers.image.description="Interactive bash argument parser generator" \ + org.opencontainers.image.version="1.0.0" + +RUN apk add --no-cache ca-certificates tzdata && \ + adduser -D -u 1000 -h /app argparse + +WORKDIR /app + +COPY --from=builder --chown=argparse:argparse /build/argparse-builder . + +USER argparse + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +ENTRYPOINT ["/app/argparse-builder"] diff --git a/assets/Dockerfile b/assets/Dockerfile new file mode 100644 index 0000000..8828fac --- /dev/null +++ b/assets/Dockerfile @@ -0,0 +1,48 @@ +# Multi-stage build for minimal final image +FROM golang:1.23-alpine AS builder + +# Install build dependencies +RUN apk add --no-cache git ca-certificates tzdata + +WORKDIR /build + +# Copy go mod files +COPY go.mod go.sum* ./ + +# Download dependencies (cached layer) +RUN go mod download + +# Copy source code +COPY . . + +# Build with optimizations +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ + -ldflags='-w -s -extldflags "-static"' \ + -a -installsuffix cgo \ + -o argparse-builder \ + . + +# Final stage - minimal image +FROM scratch + +# Copy CA certificates for HTTPS (if needed) +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ + +# Copy timezone data +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo + +# Copy binary +COPY --from=builder /build/argparse-builder /argparse-builder + +# Expose port +EXPOSE 8080 + +# Run as non-root (numeric UID for scratch) +USER 65534:65534 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD ["/argparse-builder", "health"] || exit 1 + +# Run the application +ENTRYPOINT ["/argparse-builder"] diff --git a/assets/Dockerfile.alpine b/assets/Dockerfile.alpine new file mode 100644 index 0000000..e524106 --- /dev/null +++ b/assets/Dockerfile.alpine @@ -0,0 +1,38 @@ +# Alpine-based image for debugging and shell access +FROM golang:1.23-alpine AS builder + +RUN apk add --no-cache git ca-certificates + +WORKDIR /build + +COPY go.mod go.sum* ./ +RUN go mod download + +COPY . . + +RUN CGO_ENABLED=0 go build \ + -ldflags='-w -s' \ + -o argparse-builder \ + . + +# Alpine base for shell access and debugging +FROM alpine:3.19 + +RUN apk add --no-cache ca-certificates tzdata && \ + adduser -D -u 1000 argparse + +WORKDIR /app + +COPY --from=builder /build/argparse-builder . + +# Change ownership +RUN chown argparse:argparse /app/argparse-builder + +USER argparse + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 + +ENTRYPOINT ["/app/argparse-builder"] diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..63f97e3 --- /dev/null +++ b/compose.yml @@ -0,0 +1,49 @@ +version: "3" + +services: + argparse-builder: + build: + context: . + dockerfile: assets/Containerfile + container_name: argparse-builder + ports: + - "8080:8080" + restart: unless-stopped + + # SELinux + security_opt: + - label=type:container_t + - no-new-privileges:true + + # Security + read_only: true + cap_drop: + - ALL + + # Resources + deploy: + resources: + limits: + cpus: "0.5" + memory: 128M + reservations: + cpus: "0.1" + memory: 32M + + # Health + healthcheck: + test: + [ + "CMD-SHELL", + "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1", + ] + interval: 30s + timeout: 3s + retries: 3 + start_period: 5s + + # Logging + logging: + driver: journald + options: + tag: argparse-builder diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..76e0187 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,98 @@ +# Documentation + +> This project is OCI container (podman)-first. Docker specific documentation is provided in docker/docker.md + +# Container Quick Reference (Podman/OCI) + +## Structure + +``` +assets/ +├── Containerfile ← OCI production (~7MB) +└── Containerfile.alpine ← Development (~15MB) + +docs/ +├── container.md ← Main Podman guide ⭐ +└── docker/ ← Docker specifics +``` + +## Quick Commands + +```bash +# Build +podman build -t argparse-builder . + +# Run rootless +podman run -d -p 8080:8080 argparse-builder + +# With SELinux +podman run -d -p 8080:8080 --security-opt label=type:container_t argparse-builder + +# Hardened +podman run -d -p 8080:8080 \ + --read-only \ + --cap-drop=ALL \ + --security-opt=no-new-privileges \ + --memory=128m \ + argparse-builder +``` + +## SELinux Volumes + +```bash +# Private label (recommended) +podman run -v ./data:/data:Z argparse-builder + +# Shared label +podman run -v ./shared:/shared:z argparse-builder +``` + +## Systemd Integration + +```bash +# Generate unit +podman generate systemd --new --name argparse-builder > ~/.config/systemd/user/argparse-builder.service + +# Enable +systemctl --user enable --now argparse-builder +``` + +## Key Features + +- ✅ Rootless by default +- ✅ SELinux native support +- ✅ Daemonless operation +- ✅ OCI compliant +- ✅ Systemd integration +- ✅ ~7MB image size + +## Makefile + +```bash +make container-build # Build image +make container-run # Run rootless +make container-run-hardened # Security hardened +make container-systemd # Generate systemd unit +``` + +See **docs/container.md** for complete guide. + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` diff --git a/docs/container.md b/docs/container.md new file mode 100644 index 0000000..bca7f7f --- /dev/null +++ b/docs/container.md @@ -0,0 +1,478 @@ +# Docker Deployment Guide + +## Quick Start + +```bash +# Build and run +docker build -t argparse-builder . +docker run -d -p 8080:8080 --name argparse-builder argparse-builder + +# Or use docker-compose +docker-compose up -d + +# Access at http://localhost:8080 +``` + +## Dockerfile Options + +### 1. Dockerfile (scratch-based) - RECOMMENDED + +**Size: ~7-8 MB** + +- Minimal attack surface +- No shell, no package manager +- Best for production + +```bash +docker build -t argparse-builder:scratch . +``` + +### 2. Dockerfile.alpine (Alpine-based) + +**Size: ~15 MB** + +- Has shell for debugging +- Includes wget for healthchecks +- Better for development + +```bash +docker build -f Dockerfile.alpine -t argparse-builder:alpine . +``` + +## Build Commands + +### Standard Build + +```bash +docker build -t argparse-builder:latest . +``` + +### Multi-platform Build + +```bash +docker buildx build \ + --platform linux/amd64,linux/arm64 \ + -t argparse-builder:multi \ + --push . +``` + +### Build with Cache + +```bash +docker build \ + --cache-from argparse-builder:latest \ + -t argparse-builder:latest . +``` + +### Optimized Build Args + +```bash +docker build \ + --build-arg GO_VERSION=1.23 \ + --build-arg CGO_ENABLED=0 \ + -t argparse-builder:optimized . +``` + +## Run Options + +### Basic Run + +```bash +docker run -d -p 8080:8080 argparse-builder +``` + +### With Custom Port + +```bash +docker run -d -p 3000:8080 argparse-builder +``` + +### With Environment Variables + +```bash +docker run -d \ + -p 8080:8080 \ + -e PORT=8080 \ + -e LOG_LEVEL=debug \ + argparse-builder +``` + +### With Resource Limits + +```bash +docker run -d \ + -p 8080:8080 \ + --memory="128m" \ + --cpus="0.5" \ + argparse-builder +``` + +### With Read-only Root Filesystem + +```bash +docker run -d \ + -p 8080:8080 \ + --read-only \ + --cap-drop=ALL \ + --security-opt=no-new-privileges:true \ + argparse-builder +``` + +## Docker Compose + +### Start Services + +```bash +docker-compose up -d +``` + +### View Logs + +```bash +docker-compose logs -f +``` + +### Stop Services + +```bash +docker-compose down +``` + +### Rebuild and Restart + +```bash +docker-compose up -d --build +``` + +### Scale (if needed) + +```bash +docker-compose up -d --scale argparse-builder=3 +``` + +## Health Checks + +### Check Container Health + +```bash +docker ps --filter "name=argparse-builder" +``` + +### Manual Health Check + +```bash +# For Alpine image +docker exec argparse-builder wget -qO- http://localhost:8080/ + +# For scratch image (no shell) +curl http://localhost:8080/ +``` + +### View Health Status + +```bash +docker inspect --format='{{json .State.Health}}' argparse-builder | jq +``` + +## Image Management + +### List Images + +```bash +docker images | grep argparse-builder +``` + +### Tag Image + +```bash +docker tag argparse-builder:latest registry.example.com/argparse-builder:v1.0 +``` + +### Push to Registry + +```bash +docker push registry.example.com/argparse-builder:v1.0 +``` + +### Clean Up + +```bash +# Remove stopped containers +docker container prune + +# Remove unused images +docker image prune -a + +# Remove everything +docker system prune -a +``` + +## Kubernetes Deployment + +### Basic Deployment + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: argparse-builder +spec: + replicas: 2 + selector: + matchLabels: + app: argparse-builder + template: + metadata: + labels: + app: argparse-builder + spec: + containers: + - name: argparse-builder + image: argparse-builder:latest + ports: + - containerPort: 8080 + resources: + limits: + memory: "128Mi" + cpu: "500m" + requests: + memory: "32Mi" + cpu: "100m" + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 3 + periodSeconds: 5 +--- +apiVersion: v1 +kind: Service +metadata: + name: argparse-builder +spec: + type: LoadBalancer + ports: + - port: 80 + targetPort: 8080 + selector: + app: argparse-builder +``` + +### Deploy to Kubernetes + +```bash +kubectl apply -f k8s-deployment.yaml +kubectl get pods -l app=argparse-builder +kubectl get svc argparse-builder +``` + +## Security Best Practices + +### 1. Use Specific Version Tags + +```dockerfile +FROM golang:1.23-alpine AS builder +``` + +### 2. Run as Non-root User + +```dockerfile +USER 65534:65534 +``` + +### 3. Drop All Capabilities + +```bash +docker run --cap-drop=ALL argparse-builder +``` + +### 4. Use Read-only Root Filesystem + +```bash +docker run --read-only argparse-builder +``` + +### 5. Scan for Vulnerabilities + +```bash +docker scan argparse-builder:latest +``` + +## Performance Tuning + +### Build-time Optimizations + +```dockerfile +# Static linking +-ldflags='-w -s -extldflags "-static"' + +# No CGO +CGO_ENABLED=0 + +# Strip debug info +-w -s +``` + +### Runtime Optimizations + +```bash +# Set GOMAXPROCS (if multi-core) +docker run -e GOMAXPROCS=2 argparse-builder + +# Adjust memory limits +docker run --memory="64m" --memory-swap="128m" argparse-builder +``` + +## Monitoring + +### Container Stats + +```bash +docker stats argparse-builder +``` + +### Logs + +```bash +docker logs -f --tail 100 argparse-builder +``` + +### Resource Usage + +```bash +docker container inspect argparse-builder | jq '.[0].HostConfig.Memory' +``` + +## Troubleshooting + +### Container Won't Start + +```bash +# Check logs +docker logs argparse-builder + +# Inspect container +docker inspect argparse-builder + +# Try running interactively (Alpine image) +docker run -it --rm argparse-builder:alpine sh +``` + +### Port Already in Use + +```bash +# Find process using port +lsof -i :8080 + +# Use different port +docker run -p 3000:8080 argparse-builder +``` + +### Permission Denied + +```bash +# Check file permissions +ls -la argparse-builder + +# Ensure binary is executable +chmod +x argparse-builder +``` + +### Out of Memory + +```bash +# Increase memory limit +docker run --memory="256m" argparse-builder + +# Check current limits +docker inspect argparse-builder | jq '.[0].HostConfig' +``` + +## CI/CD Integration + +### GitHub Actions + +```yaml +name: Docker Build and Push + +on: + push: + branches: [main] + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: ghcr.io/${{ github.repository }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max +``` + +### GitLab CI + +```yaml +build: + stage: build + image: docker:latest + services: + - docker:dind + script: + - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA +``` + +## Image Size Comparison + +| Dockerfile | Base Image | Size | Use Case | +| ----------------- | ----------- | ------- | --------------- | +| Dockerfile | scratch | ~7 MB | Production | +| Dockerfile.alpine | alpine:3.19 | ~15 MB | Development | +| (no optimization) | golang:1.23 | ~900 MB | Not recommended | + +## Registry Options + +### GitHub Container Registry + +```bash +docker tag argparse-builder:latest ghcr.io/username/argparse-builder:latest +docker push ghcr.io/username/argparse-builder:latest +``` + +### Docker Hub + +```bash +docker tag argparse-builder:latest username/argparse-builder:latest +docker push username/argparse-builder:latest +``` + +### Private Registry + +```bash +docker tag argparse-builder:latest registry.example.com/argparse-builder:latest +docker push registry.example.com/argparse-builder:latest +``` diff --git a/docs/docker/README.md b/docs/docker/README.md new file mode 100644 index 0000000..9524cb7 --- /dev/null +++ b/docs/docker/README.md @@ -0,0 +1,12 @@ +# Docker quick start + +```zsh +# Build +docker build -t argparse-builder . + +# Run +docker run -d -p 8080:8080 argparse-builder + +# Or use compose +docker-compose up -d +``` diff --git a/docs/docker/docker-revised.md b/docs/docker/docker-revised.md new file mode 100644 index 0000000..21d6d34 --- /dev/null +++ b/docs/docker/docker-revised.md @@ -0,0 +1,86 @@ +# Docker-Specific Guide + +## Docker vs Podman + +This project prioritizes **Podman** (OCI-compliant, rootless, daemonless). For Docker, convert commands: + +```bash +# Podman → Docker +podman build -t name . → docker build -t name . +podman run -d name → docker run -d name +``` + +## Key Differences + +### SELinux + +**Podman**: Native support, use `:Z` or `:z` for volumes + +```bash +podman run -v ./data:/data:Z name +``` + +**Docker**: Requires `selinux` mount option + +```bash +docker run -v ./data:/data:Z name # May not work +docker run --security-opt label=type:container_t name +``` + +### Rootless + +**Podman**: Default rootless operation + +```bash +podman run -d -p 8080:8080 name # Works as user +``` + +**Docker**: Requires rootless daemon setup + +```bash +dockerd-rootless-setuptool.sh install +``` + +### Systemd + +**Podman**: Native integration + +```bash +podman generate systemd --new name +``` + +**Docker**: Use third-party solutions + +## Docker Files + +Use `Dockerfile` instead of `Containerfile`: + +```bash +docker build -f assets/Dockerfile.alpine -t argparse-builder . +``` + +## Docker Compose + +Standard `docker-compose.yml` works, but note SELinux limitations: + +```yaml +services: + app: + volumes: + - ./data:/data # No :Z support in Docker Compose +``` + +Workaround: + +```bash +chcon -Rt container_file_t ./data # Pre-label directory +``` + +## Migration to Podman + +1. Replace `docker` with `podman` in commands +2. Add `:Z` to volume mounts for SELinux +3. Use rootless by default +4. Generate systemd units with Podman + +See `docs/container.md` for full Podman guide. diff --git a/docs/docker/docker.md b/docs/docker/docker.md new file mode 100644 index 0000000..40eb0f9 --- /dev/null +++ b/docs/docker/docker.md @@ -0,0 +1,181 @@ +# Docker Quick Reference + +## 🚀 Quick Start + +```bash +# Build and run (one command) +docker build -t argparse-builder . && docker run -d -p 8080:8080 argparse-builder + +# Or with docker-compose +docker-compose up -d +``` + +## 📦 Files Included + +``` +Dockerfile ← Production (scratch-based, ~7MB) +Dockerfile.alpine ← Development (Alpine, ~15MB) +docker-compose.yml ← Easy deployment +.dockerignore ← Build optimization +k8s-deployment.yaml ← Kubernetes manifest +DOCKER_GUIDE.md ← Complete documentation +Makefile.docker ← Build automation +main_enhancements.go ← Health check & config code +``` + +## 🔨 Build Options + +```bash +# Production (minimal) +docker build -t argparse-builder . + +# Development (with shell) +docker build -f Dockerfile.alpine -t argparse-builder:alpine . + +# Multi-platform +docker buildx build --platform linux/amd64,linux/arm64 -t argparse-builder . +``` + +## ▶️ Run Options + +```bash +# Basic +docker run -d -p 8080:8080 argparse-builder + +# With limits +docker run -d -p 8080:8080 --memory=128m --cpus=0.5 argparse-builder + +# Secure +docker run -d -p 8080:8080 --read-only --cap-drop=ALL argparse-builder +``` + +## 🎯 Image Sizes + +| Image | Size | Use | +| ------- | ------- | ------------ | +| scratch | ~7 MB | Production | +| alpine | ~15 MB | Development | +| no-opt | ~900 MB | ❌ Don't use | + +## 🔍 Health Check + +```bash +# Check health +curl http://localhost:8080/health + +# Docker health status +docker ps --filter "health=healthy" +``` + +## 📊 Monitoring + +```bash +# Logs +docker logs -f argparse-builder + +# Stats +docker stats argparse-builder + +# Inspect +docker inspect argparse-builder +``` + +## 🎪 Kubernetes + +```bash +# Deploy +kubectl apply -f k8s-deployment.yaml + +# Check status +kubectl get pods -n argparse-builder +kubectl get svc -n argparse-builder + +# Logs +kubectl logs -f deployment/argparse-builder -n argparse-builder +``` + +## 🛠️ Makefile Commands + +```bash +make docker-build # Build image +make docker-run # Run container +make docker-logs # View logs +make compose-up # Start with compose +make clean # Clean everything +``` + +## 🔒 Security Features + +- Non-root user (UID 65534) +- Read-only filesystem +- No capabilities +- Static binary +- Minimal attack surface + +## 📝 Key Points + +✅ Dockerfile uses multi-stage build (builder + scratch) +✅ Healthcheck endpoint at /health +✅ Environment vars: PORT, LOG_LEVEL +✅ Graceful shutdown support +✅ Resource limits configured +✅ Security hardened by default + +## 🌐 Registry Push + +```bash +# GitHub Container Registry +docker tag argparse-builder ghcr.io/username/argparse-builder +docker push ghcr.io/username/argparse-builder + +# Docker Hub +docker tag argparse-builder username/argparse-builder +docker push username/argparse-builder +``` + +## 🔄 CI/CD Ready + +GitHub Actions example included in DOCKER_GUIDE.md for: + +- Automated builds on push +- Multi-platform support +- Registry push +- Cache optimization + +See DOCKER_GUIDE.md for complete instructions. + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` + +``` diff --git a/docs/podman.md b/docs/podman.md new file mode 100644 index 0000000..405f908 --- /dev/null +++ b/docs/podman.md @@ -0,0 +1,620 @@ +# Podman (OCI Container) Guide + +## Quick Start + +```bash +# Build +podman build -t argparse-builder . + +# Run rootless +podman run -d -p 8080:8080 argparse-builder + +# Run with SELinux +podman run -d -p 8080:8080 --security-opt label=type:container_t argparse-builder +``` + +## Podman vs Docker + +| Feature | Podman | Docker | +| ------- | ------------------- | ----------------- | +| Daemon | Daemonless | Requires daemon | +| Root | Rootless by default | Needs root/group | +| SELinux | Native support | Additional config | +| Systemd | Native integration | Third-party | +| OCI | Full compliance | Mostly compliant | + +## Build Commands + +### Standard Build + +```bash +podman build -t argparse-builder . +``` + +### Using Containerfile + +```bash +podman build -f Containerfile -t argparse-builder . +``` + +### Multi-architecture + +```bash +podman build --platform linux/amd64,linux/arm64 -t argparse-builder --manifest argparse-builder . +``` + +### Build with cache + +```bash +podman build --layers -t argparse-builder . +``` + +## Rootless Operation + +### Setup Rootless Podman + +```bash +# Install +sudo dnf install podman + +# Configure subuid/subgid (automatic on most systems) +grep $(whoami) /etc/subuid +grep $(whoami) /etc/subgid + +# Run rootless +podman run -d -p 8080:8080 argparse-builder + +# Verify +podman ps +``` + +### Port Binding < 1024 (Rootless) + +```bash +# Option 1: Use high port and forward +podman run -d -p 8080:8080 argparse-builder +sudo firewall-cmd --add-forward-port=port=80:proto=tcp:toport=8080 + +# Option 2: Set sysctl +sudo sysctl net.ipv4.ip_unprivileged_port_start=80 + +# Option 3: Use systemd socket activation +# See systemd section below +``` + +## SELinux Configuration + +### Run with SELinux + +```bash +# Default (uses process label) +podman run -d -p 8080:8080 argparse-builder + +# Specific label +podman run -d -p 8080:8080 --security-opt label=type:container_t argparse-builder + +# Disable SELinux for container (not recommended) +podman run -d -p 8080:8080 --security-opt label=disable argparse-builder +``` + +### Volume Mounts with SELinux + +```bash +# Auto-label volume (recommended) +podman run -d -p 8080:8080 -v ./data:/data:Z argparse-builder + +# Shared label (multiple containers) +podman run -d -p 8080:8080 -v ./data:/data:z argparse-builder + +# Private label (single container) +podman run -d -p 8080:8080 -v ./data:/data:Z argparse-builder +``` + +### Check SELinux Context + +```bash +# Container process +podman top label + +# Volume labels +ls -Z ./data +``` + +### SELinux Troubleshooting + +```bash +# Check denials +sudo ausearch -m avc -ts recent + +# Generate policy +sudo ausearch -m avc | audit2allow -M my-argparse +sudo semodule -i my-argparse.pp + +# Check booleans +getsebool -a | grep container +``` + +## Systemd Integration + +### Generate Systemd Unit + +```bash +# Create container +podman run -d --name argparse-builder -p 8080:8080 argparse-builder + +# Generate unit file +podman generate systemd --new --name argparse-builder > ~/.config/systemd/user/argparse-builder.service + +# Enable and start +systemctl --user daemon-reload +systemctl --user enable --now argparse-builder +``` + +### Systemd Unit (Manual) + +```ini +# ~/.config/systemd/user/argparse-builder.service +[Unit] +Description=Argparse Builder Container +Wants=network-online.target +After=network-online.target + +[Service] +Restart=always +ExecStartPre=/usr/bin/podman pull argparse-builder:latest +ExecStart=/usr/bin/podman run \ + --rm \ + --name argparse-builder \ + -p 8080:8080 \ + --security-opt label=type:container_t \ + argparse-builder:latest +ExecStop=/usr/bin/podman stop -t 10 argparse-builder +ExecStopPost=/usr/bin/podman rm -f argparse-builder + +[Install] +WantedBy=default.target +``` + +### System-wide Service (requires root) + +```bash +# Generate for system +sudo podman generate systemd --new --name argparse-builder > /etc/systemd/system/argparse-builder.service + +# Enable and start +sudo systemctl daemon-reload +sudo systemctl enable --now argparse-builder +``` + +## Pod Management + +### Create Pod + +```bash +# Create pod with port mapping +podman pod create --name argparse-pod -p 8080:8080 + +# Run container in pod +podman run -d --pod argparse-pod argparse-builder + +# List pods +podman pod ps + +# Inspect pod +podman pod inspect argparse-pod +``` + +### Pod with Multiple Containers + +```bash +podman pod create --name argparse-stack -p 8080:8080 -p 9090:9090 + +# Application +podman run -d --pod argparse-stack argparse-builder + +# Monitoring (example) +podman run -d --pod argparse-stack prom/prometheus +``` + +### Pod Systemd Service + +```bash +podman generate systemd --new --name argparse-pod > argparse-pod.service +``` + +## Security Hardening + +### Read-only Root + +```bash +podman run -d -p 8080:8080 --read-only argparse-builder +``` + +### No New Privileges + +```bash +podman run -d -p 8080:8080 --security-opt=no-new-privileges argparse-builder +``` + +### Drop Capabilities + +```bash +podman run -d -p 8080:8080 --cap-drop=ALL argparse-builder +``` + +### Resource Limits + +```bash +podman run -d -p 8080:8080 \ + --memory=128m \ + --cpus=0.5 \ + --pids-limit=100 \ + argparse-builder +``` + +### Combined Hardening + +```bash +podman run -d -p 8080:8080 \ + --read-only \ + --security-opt=no-new-privileges \ + --cap-drop=ALL \ + --security-opt label=type:container_t \ + --memory=128m \ + --cpus=0.5 \ + argparse-builder +``` + +## Networking + +### Port Mapping + +```bash +# Basic +podman run -d -p 8080:8080 argparse-builder + +# Different host port +podman run -d -p 3000:8080 argparse-builder + +# Specific interface +podman run -d -p 127.0.0.1:8080:8080 argparse-builder +``` + +### Custom Network + +```bash +# Create network +podman network create argparse-net + +# Run with network +podman run -d --network argparse-net -p 8080:8080 argparse-builder + +# Inspect network +podman network inspect argparse-net +``` + +### DNS + +```bash +# Custom DNS +podman run -d -p 8080:8080 --dns=8.8.8.8 argparse-builder + +# Add hosts +podman run -d -p 8080:8080 --add-host=api.local:192.168.1.100 argparse-builder +``` + +## Storage and Volumes + +### Named Volume + +```bash +# Create volume +podman volume create argparse-data + +# Use volume with SELinux +podman run -d -p 8080:8080 -v argparse-data:/data:Z argparse-builder + +# Inspect volume +podman volume inspect argparse-data +``` + +### Bind Mount + +```bash +# Auto-label (Z) +podman run -d -p 8080:8080 -v ./logs:/app/logs:Z argparse-builder + +# Shared label (z) - multiple containers +podman run -d -p 8080:8080 -v ./shared:/shared:z argparse-builder +``` + +### tmpfs Mount + +```bash +podman run -d -p 8080:8080 --tmpfs /tmp:rw,size=64m,mode=1777 argparse-builder +``` + +## Health and Monitoring + +### Health Check + +```bash +# Check health status +podman healthcheck run argparse-builder + +# View logs +podman logs argparse-builder + +# Stats +podman stats argparse-builder + +# Top processes +podman top argparse-builder +``` + +### Inspect Container + +```bash +# Full inspect +podman inspect argparse-builder + +# Specific field +podman inspect --format='{{.State.Health.Status}}' argparse-builder +``` + +## Registry Operations + +### Login + +```bash +# Docker Hub +podman login docker.io + +# GitHub Container Registry +podman login ghcr.io + +# Private registry +podman login registry.example.com +``` + +### Tag and Push + +```bash +# Tag +podman tag argparse-builder ghcr.io/username/argparse-builder:latest + +# Push +podman push ghcr.io/username/argparse-builder:latest +``` + +### Pull + +```bash +# Pull image +podman pull ghcr.io/username/argparse-builder:latest + +# Pull specific platform +podman pull --platform linux/arm64 ghcr.io/username/argparse-builder:latest +``` + +## Podman Compose + +### Install + +```bash +pip3 install podman-compose +``` + +### podman-compose.yml + +```yaml +version: "3" + +services: + argparse-builder: + build: . + ports: + - "8080:8080" + restart: unless-stopped + security_opt: + - label=type:container_t + - no-new-privileges + read_only: true + cap_drop: + - ALL + resources: + limits: + cpus: "0.5" + memory: 128M +``` + +### Usage + +```bash +podman-compose up -d +podman-compose logs -f +podman-compose down +``` + +## Kubernetes with Podman + +### Generate K8s YAML + +```bash +# From container +podman generate kube argparse-builder > argparse-k8s.yaml + +# From pod +podman generate kube argparse-pod > argparse-pod-k8s.yaml +``` + +### Play Kubernetes YAML + +```bash +# Create from YAML +podman play kube argparse-k8s.yaml + +# Remove +podman play kube --down argparse-k8s.yaml +``` + +## Migration from Docker + +### Pull Docker Images + +```bash +podman pull docker.io/library/nginx +``` + +### Docker Compatibility + +```bash +# Use docker command (alias) +alias docker=podman + +# Docker socket emulation +podman system service --time=0 unix:///tmp/podman.sock +export DOCKER_HOST=unix:///tmp/podman.sock +``` + +### Convert docker-compose + +```bash +# Most docker-compose files work with podman-compose +podman-compose -f docker-compose.yml up +``` + +## Troubleshooting + +### Permission Denied (Rootless) + +```bash +# Check subuid/subgid +grep $(whoami) /etc/subuid /etc/subgid + +# Verify user namespaces +podman unshare cat /proc/self/uid_map + +# Reset storage +podman system reset +``` + +### SELinux Denials + +```bash +# Check denials +sudo ausearch -m avc -ts recent | grep podman + +# Temporary permissive mode (troubleshooting) +sudo setenforce 0 + +# Re-enable +sudo setenforce 1 +``` + +### Port Already in Use + +```bash +# Find what's using port +ss -tulpn | grep 8080 + +# Use different port +podman run -d -p 8081:8080 argparse-builder +``` + +### Storage Issues + +```bash +# Check storage +podman system df + +# Clean up +podman system prune -a + +# Reset storage +podman system reset +``` + +## Performance Tuning + +### Storage Driver + +```bash +# Check current driver +podman info --format '{{.Store.GraphDriverName}}' + +# Use overlay (default, best performance) +# Already configured in /etc/containers/storage.conf +``` + +### Network Performance + +```bash +# Use host network (best performance, less isolation) +podman run -d --network host argparse-builder +``` + +### Build Cache + +```bash +# Use buildah cache +podman build --layers -t argparse-builder . +``` + +## CI/CD Integration + +### GitLab CI + +```yaml +build: + image: quay.io/podman/stable + services: + - podman:dind + script: + - podman build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . + - podman push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA +``` + +### GitHub Actions + +```yaml +- name: Build with Podman + run: | + podman build -t argparse-builder . + podman tag argparse-builder ghcr.io/${{ github.repository }}:latest + podman push ghcr.io/${{ github.repository }}:latest +``` + +## Best Practices + +1. **Use Rootless** - Run as regular user by default +2. **SELinux** - Keep enabled, use :Z/:z for volumes +3. **Systemd** - Use `podman generate systemd` for services +4. **Security** - Use `--read-only`, `--cap-drop=ALL`, labels +5. **OCI** - Use Containerfile (compatible with Dockerfile) +6. **Pods** - Group related containers in pods +7. **Health Checks** - Include in Containerfile +8. **Resources** - Set limits with `--memory`, `--cpus` + +## Quick Reference + +```bash +# Build +podman build -t name . + +# Run +podman run -d -p 8080:8080 name + +# SELinux volume +podman run -v ./data:/data:Z name + +# Systemd +podman generate systemd --new name + +# Rootless +podman run --userns=keep-id name + +# Pod +podman pod create -p 8080:8080 +podman run --pod podname name +``` diff --git a/main.go b/main.go index 79ddc41..6e68e12 100644 --- a/main.go +++ b/main.go @@ -6,13 +6,18 @@ import ( "io/fs" "log" "net/http" + "os" "strings" "text/template" ) //go:embed static/* templates/* -var content embed.FS +var content embed.FS // Environment configuration +type Config struct { + Port string + LogLevel string +} type Argument struct { Params string `json:"params"` Command string `json:"command"` @@ -36,11 +41,34 @@ type GenerateRequestV2 struct { Subcommands []Subcommand `json:"subcommands"` } +func loadConfig() Config { + port := os.Getenv("PORT") + if port == "" { + port = "8080" + } + + return Config{ + Port: port, + LogLevel: os.Getenv("LOG_LEVEL"), + } +} + +// Add health check endpoint +func healthHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(map[string]string{ + "status": "healthy", + "version": "1.0.0", + }) +} + func main() { staticFS, err := fs.Sub(content, "static") if err != nil { log.Fatal(err) } + config := loadConfig() templatesFS, err := fs.Sub(content, "templates") if err != nil { @@ -49,6 +77,7 @@ func main() { tmpl := template.Must(template.ParseFS(templatesFS, "*.html")) + http.HandleFunc("/health", healthHandler) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { @@ -97,8 +126,9 @@ func main() { w.Write([]byte(bashCode)) }) - log.Println("Server starting on :8080") - log.Fatal(http.ListenAndServe(":8080", nil)) + addr := ":" + config.Port + log.Printf("Server starting on %s", addr) + log.Fatal(http.ListenAndServe(addr, nil)) } func generateBashScript(req GenerateRequest) string { @@ -222,6 +252,7 @@ func generateBashScript(req GenerateRequest) string { return sb.String() } + func generateBashScriptV2(req GenerateRequestV2) string { // If no subcommands, use flat generation if len(req.Subcommands) == 0 {