Docker support

This commit is contained in:
pmb
2025-07-02 17:11:31 -07:00
parent 24b614b97d
commit 4d97255d3a
6 changed files with 630 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
# Build directories
builddir/
testbuild/
# Git
.git/
.gitignore
# Documentation
README.md
*.md
# CI/CD
.github/
# IDE files
.vscode/
.idea/
*.swp
*.swo
*~
# OS files
.DS_Store
Thumbs.db
# Docker files
Dockerfile
.dockerignore
docker-compose.yml
# Coverage reports
.codecov.yml
coverage/
*.gcov
*.gcda
*.gcno
# Temporary files
*.tmp
*.temp
*.log
+124
View File
@@ -0,0 +1,124 @@
name: Build and Publish Docker Image
on:
push:
branches: [ "main" ]
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "main" ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
meson \
ninja-build \
pkg-config \
libboost-all-dev \
libgtest-dev \
libgmock-dev
- name: Setup build directory
run: meson setup builddir --buildtype=release
- name: Build project
run: meson compile -C builddir
- name: Run tests
run: meson test -C builddir
- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: builddir/meson-logs/testlog.txt
build-and-push-image:
runs-on: ubuntu-latest
needs: build-and-test
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Generate artifact attestation
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v1
with:
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
subject-digest: ${{ steps.build.outputs.digest }}
push-to-registry: true
security-scan:
runs-on: ubuntu-latest
needs: build-and-push-image
if: github.event_name != 'pull_request'
permissions:
contents: read
security-events: write
steps:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
+365
View File
@@ -0,0 +1,365 @@
# Docker Setup and Deployment
This document describes how to build, run, and deploy the finger service using Docker and GitHub Actions.
## Quick Start
### Using Docker Compose (Recommended)
1. **Clone the repository:**
```bash
git clone https://github.com/waffle2k/finger.git
cd finger
```
2. **Start the service:**
```bash
docker compose up -d
```
3. **Test the service:**
```bash
# Test with the example user
finger john@localhost
# Or using telnet
telnet localhost 79
# Then type: john
```
4. **Add your own users:**
```bash
# Create a status file for a user
echo "Your status message here" > users/yourusername
# Test it
finger yourusername@localhost
```
### Using Docker directly
1. **Build the image:**
```bash
docker build -t finger-service .
```
2. **Run the container:**
```bash
docker run -d \
--name finger \
-p 79:79 \
-v $(pwd)/users:/var/finger/users \
finger-service
```
### Using Pre-built Images
You can also use the automatically built images from GitHub Container Registry:
```bash
docker run -d \
--name finger \
-p 79:79 \
-v $(pwd)/users:/var/finger/users \
ghcr.io/waffle2k/finger:latest
```
## Docker Architecture
### Multi-stage Build
The Dockerfile uses a multi-stage build approach:
1. **Builder Stage (Ubuntu 24.04):**
- Installs all build dependencies (meson, ninja, boost, gtest, etc.)
- Compiles the C++20 source code
- Runs all tests to ensure quality
- Creates a statically linked binary
2. **Runtime Stage (Alpine Linux):**
- Minimal base image (~5MB)
- Only includes runtime dependencies
- Runs as non-root user for security
- Includes health checks
### Security Features
- **Non-root execution:** Runs as user `finger` (UID 1000)
- **Minimal attack surface:** Alpine Linux base with minimal packages
- **Health checks:** Built-in container health monitoring
- **Read-only filesystem:** Application doesn't write to filesystem
### Image Size
- **Final image:** ~15MB (Alpine + binary + minimal runtime deps)
- **Build image:** ~2GB (includes all build tools, discarded after build)
## GitHub Actions CI/CD
### Automated Workflow
The repository includes a comprehensive GitHub Actions workflow (`.github/workflows/docker-publish.yml`) that:
1. **Build and Test:**
- Builds the project with meson
- Runs all unit tests
- Uploads test results as artifacts
2. **Multi-platform Docker Build:**
- Builds for `linux/amd64` and `linux/arm64`
- Uses Docker Buildx for cross-platform support
- Implements build caching for faster builds
3. **Container Registry Publishing:**
- Publishes to GitHub Container Registry (`ghcr.io`)
- Tags with multiple strategies:
- `latest` for main branch
- `v1.2.3` for semantic version tags
- `main-abc1234` for commit SHA
- `pr-123` for pull requests
4. **Security Scanning:**
- Runs Trivy vulnerability scanner
- Uploads results to GitHub Security tab
- Fails on high-severity vulnerabilities
5. **Supply Chain Security:**
- Generates SLSA build provenance attestations
- Signs container images
- Provides build transparency
### Triggering Builds
The workflow triggers on:
- **Push to main branch:** Builds and publishes `latest` tag
- **Version tags:** Builds and publishes semantic version tags (`v1.0.0`)
- **Pull requests:** Builds but doesn't publish (security)
### Using Published Images
Images are available at: `ghcr.io/waffle2k/finger`
Available tags:
- `latest` - Latest stable build from main branch
- `v1.0.0` - Specific version releases
- `main-abc1234` - Specific commit builds
## Configuration
### Environment Variables
The container supports these environment variables:
- `FINGER_PORT`: Port to listen on (default: 79)
- `FINGER_DATA_DIR`: Directory for user files (default: /var/finger/users)
### Volume Mounts
- `/var/finger/users`: Directory containing user status files
- Mount your local `users/` directory here
- Each file represents a user (filename = username)
- File contents = user's status message
### Health Checks
The container includes built-in health checks:
- **Check:** TCP connection to port 79
- **Interval:** Every 30 seconds
- **Timeout:** 10 seconds
- **Retries:** 3 attempts
- **Start period:** 40 seconds
## Development
### Local Development with Docker
1. **Build development image:**
```bash
docker build --target builder -t finger-dev .
```
2. **Run tests in container:**
```bash
docker run --rm finger-dev meson test -C builddir
```
3. **Interactive development:**
```bash
docker run -it --rm \
-v $(pwd):/app \
-w /app \
finger-dev bash
```
### Debugging
1. **View container logs:**
```bash
docker logs finger
```
2. **Execute into running container:**
```bash
docker exec -it finger sh
```
3. **Check health status:**
```bash
docker inspect finger | grep -A 10 Health
```
## Production Deployment
### Docker Swarm
```yaml
version: '3.8'
services:
finger:
image: ghcr.io/waffle2k/finger:latest
ports:
- "79:79"
volumes:
- finger_data:/var/finger/users
deploy:
replicas: 2
restart_policy:
condition: on-failure
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "79"]
interval: 30s
timeout: 10s
retries: 3
volumes:
finger_data:
```
### Kubernetes
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: finger-service
spec:
replicas: 3
selector:
matchLabels:
app: finger
template:
metadata:
labels:
app: finger
spec:
containers:
- name: finger
image: ghcr.io/waffle2k/finger:latest
ports:
- containerPort: 79
volumeMounts:
- name: user-data
mountPath: /var/finger/users
livenessProbe:
tcpSocket:
port: 79
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: user-data
configMap:
name: finger-users
---
apiVersion: v1
kind: Service
metadata:
name: finger-service
spec:
selector:
app: finger
ports:
- port: 79
targetPort: 79
type: LoadBalancer
```
## Troubleshooting
### Common Issues
1. **Permission denied on user files:**
```bash
# Fix file permissions
chmod 644 users/*
```
2. **Port 79 requires root:**
```bash
# Use a different port
docker run -p 8079:79 finger-service
```
3. **Container won't start:**
```bash
# Check logs
docker logs finger
# Check if port is available
netstat -ln | grep :79
```
4. **Health check failing:**
```bash
# Test manually
docker exec finger nc -z localhost 79
# Check if service is running
docker exec finger ps aux
```
### Performance Tuning
1. **Resource limits:**
```yaml
services:
finger:
deploy:
resources:
limits:
memory: 64M
cpus: '0.1'
```
2. **Connection limits:**
- The service handles concurrent connections efficiently
- Default OS limits should be sufficient for most use cases
- Monitor with `docker stats` for resource usage
## Security Considerations
1. **Network Security:**
- Finger protocol sends data in plain text
- Consider using behind a reverse proxy with TLS
- Restrict access with firewall rules
2. **Data Security:**
- User files are readable by the finger user
- Don't store sensitive information in status files
- Consider file permissions on the host
3. **Container Security:**
- Runs as non-root user
- Uses minimal base image
- Regular security scanning in CI/CD
- Keep images updated
## Contributing
When contributing Docker-related changes:
1. Test locally with `docker build`
2. Ensure all tests pass in the container
3. Update this documentation if needed
4. The CI/CD pipeline will automatically test your changes
For more information, see the main [README.md](README.md).
+68
View File
@@ -0,0 +1,68 @@
# Multi-stage build for C++ finger service
# Build stage - use Ubuntu with all development dependencies
FROM ubuntu:24.04 AS builder
# Avoid interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies
RUN apt-get update && apt-get install -y \
build-essential \
meson \
ninja-build \
pkg-config \
libboost-all-dev \
libgtest-dev \
libgmock-dev \
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy source files
COPY . .
# Build the project
RUN meson setup builddir --buildtype=release
RUN meson compile -C builddir
# Run tests to ensure quality
RUN meson test -C builddir
# Runtime stage - minimal Alpine Linux
FROM alpine:latest
# Install runtime dependencies (if any)
RUN apk add --no-cache \
libstdc++ \
&& addgroup -g 1000 finger \
&& adduser -D -s /bin/sh -u 1000 -G finger finger
# Copy the compiled binary from builder stage
COPY --from=builder /app/builddir/finger /usr/local/bin/finger
# Make the binary executable
RUN chmod +x /usr/local/bin/finger
# Create the directory for user data with proper permissions
RUN mkdir -p /var/finger/users && \
chown -R finger:finger /var/finger
# Switch to non-root user
USER finger
# Expose port 79 (finger protocol)
EXPOSE 79
# Add health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD nc -z localhost 79 || exit 1
# Set metadata labels
LABEL org.opencontainers.image.title="finger"
LABEL org.opencontainers.image.description="A silly finger service written in C++20"
LABEL org.opencontainers.image.source="https://github.com/waffle2k/finger"
LABEL org.opencontainers.image.licenses="MIT"
# Set the binary as the default command
CMD ["finger"]
+25
View File
@@ -0,0 +1,25 @@
version: '3.8'
services:
finger:
build: .
ports:
- "79:79"
volumes:
- ./users:/var/finger/users
restart: unless-stopped
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "79"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Example using published image instead of building locally
# finger:
# image: ghcr.io/waffle2k/finger:latest
# ports:
# - "79:79"
# volumes:
# - ./users:/var/finger/users
# restart: unless-stopped
+6
View File
@@ -0,0 +1,6 @@
John Doe is currently working on the finger service project.
He's available for collaboration and can be reached via email.
Current status: Online and coding! 🚀
Last updated: January 2025