diff --git a/.github/workflows/docker-publish-dockerhub.yml b/.github/workflows/docker-publish-dockerhub.yml index 872aac1..2990b9b 100644 --- a/.github/workflows/docker-publish-dockerhub.yml +++ b/.github/workflows/docker-publish-dockerhub.yml @@ -26,6 +26,24 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 # Needed to get all tags for versioning + + # Set version information + - name: Set Version + id: version + run: | + if [[ "${{ github.event.inputs.manual_version }}" != "" ]]; then + echo "VERSION=${{ github.event.inputs.manual_version }}" >> $GITHUB_ENV + elif [[ "${{ github.ref }}" == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/} + echo "VERSION=$VERSION" >> $GITHUB_ENV + else + VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev")-$(git rev-parse --short HEAD) + echo "VERSION=$VERSION" >> $GITHUB_ENV + fi + echo "BUILD_TIME=$(date -u +'%Y-%m-%d_%H:%M:%S')" >> $GITHUB_ENV + echo "COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV # Set up Docker Buildx for efficient builds - name: Set up Docker Buildx @@ -61,5 +79,11 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ env.VERSION }} + BUILD_TIME=${{ env.BUILD_TIME }} + COMMIT=${{ env.COMMIT }} + UID=1000 + GID=1000 cache-from: type=gha cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 66433ab..7fc262c 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -27,6 +27,24 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 # Needed to get all tags for versioning + + # Set version information + - name: Set Version + id: version + run: | + if [[ "${{ github.event.inputs.manual_version }}" != "" ]]; then + echo "VERSION=${{ github.event.inputs.manual_version }}" >> $GITHUB_ENV + elif [[ "${{ github.ref }}" == refs/tags/* ]]; then + VERSION=${GITHUB_REF#refs/tags/} + echo "VERSION=$VERSION" >> $GITHUB_ENV + else + VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev")-$(git rev-parse --short HEAD) + echo "VERSION=$VERSION" >> $GITHUB_ENV + fi + echo "BUILD_TIME=$(date -u +'%Y-%m-%d_%H:%M:%S')" >> $GITHUB_ENV + echo "COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV # Set up Docker Buildx for efficient builds - name: Set up Docker Buildx @@ -62,5 +80,11 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + build-args: | + VERSION=${{ env.VERSION }} + BUILD_TIME=${{ env.BUILD_TIME }} + COMMIT=${{ env.COMMIT }} + UID=1000 + GID=1000 cache-from: type=gha cache-to: type=gha,mode=max \ No newline at end of file diff --git a/.github/workflows/go-release.yml b/.github/workflows/go-release.yml index 92c07e6..60aa2c3 100644 --- a/.github/workflows/go-release.yml +++ b/.github/workflows/go-release.yml @@ -57,23 +57,29 @@ jobs: echo "VERSION=$VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_OUTPUT fi + # Also set build timestamp for versioning + echo "BUILD_TIME=$(date -u +'%Y-%m-%d_%H:%M:%S')" >> $GITHUB_ENV + echo "COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - name: Build for multiple platforms run: | mkdir -p dist + # Define common ldflags with version information + LDFLAGS="-X github.com/starfleetcptn/gomft/components.AppVersion=$VERSION -X main.Version=$VERSION -X main.BuildTime=$BUILD_TIME -X main.Commit=$COMMIT" + # Linux builds - GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-X main.version=$VERSION" -o dist/gomft-$VERSION-linux-amd64 . - GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-X main.version=$VERSION" -o dist/gomft-$VERSION-linux-arm64 . - GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build -ldflags="-X main.version=$VERSION" -o dist/gomft-$VERSION-linux-armv7 . + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/gomft-$VERSION-linux-amd64 . + GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/gomft-$VERSION-linux-arm64 . + GOOS=linux GOARCH=arm GOARM=7 CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/gomft-$VERSION-linux-armv7 . # macOS builds - GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-X main.version=$VERSION" -o dist/gomft-$VERSION-darwin-amd64 . - GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-X main.version=$VERSION" -o dist/gomft-$VERSION-darwin-arm64 . + GOOS=darwin GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/gomft-$VERSION-darwin-amd64 . + GOOS=darwin GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/gomft-$VERSION-darwin-arm64 . # Windows builds - GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-X main.version=$VERSION" -o dist/gomft-$VERSION-windows-amd64.exe . - GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="-X main.version=$VERSION" -o dist/gomft-$VERSION-windows-arm64.exe . + GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/gomft-$VERSION-windows-amd64.exe . + GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build -ldflags="$LDFLAGS" -o dist/gomft-$VERSION-windows-arm64.exe . # Create checksums cd dist diff --git a/Dockerfile b/Dockerfile index 8a1fe6d..57f166a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,11 @@ FROM golang:1.24-alpine AS builder WORKDIR /app +# Accept build arguments for version information +ARG VERSION=dev +ARG BUILD_TIME=unknown +ARG COMMIT=unknown + # Install build dependencies RUN apk add --no-cache git build-base @@ -18,8 +23,10 @@ COPY . . # Generate template files from .templ files RUN templ generate -# Build the application -RUN CGO_ENABLED=1 GOOS=linux go build -o gomft +# Compile the application with version information +RUN CGO_ENABLED=0 GOOS=linux go build \ + -ldflags "-X github.com/starfleetcptn/gomft/components.AppVersion=${VERSION} -X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME} -X main.Commit=${COMMIT} -X github.com/starfleetcptn/gomft/components.BuildTime=${BUILD_TIME} -X github.com/starfleetcptn/gomft/components.Commit=${COMMIT}" \ + -o /app/gomft # Install rclone RUN apk add --no-cache curl unzip && \ @@ -34,10 +41,21 @@ RUN apk add --no-cache curl unzip && \ # Create a smaller runtime image FROM alpine:3.19 +# Add arguments for UID and GID with defaults +ARG UID=1000 +ARG GID=1000 +ARG USERNAME=gomft + WORKDIR /app # Install runtime dependencies -RUN apk add --no-cache ca-certificates tzdata sqlite bash +RUN apk add --no-cache ca-certificates tzdata sqlite bash shadow su-exec \ + && apk add --no-cache --virtual .user-deps \ + shadow curl xz + +# Create user and group with specified IDs +RUN addgroup -g ${GID} ${USERNAME} && \ + adduser -D -u ${UID} -G ${USERNAME} -s /bin/sh ${USERNAME} # Copy the binary from the builder stage COPY --from=builder /app/gomft /app/ @@ -47,14 +65,27 @@ COPY --from=builder /usr/local/bin/rclone /usr/local/bin/rclone COPY static/ /app/static/ COPY components/ /app/components/ +# Copy entrypoint script +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + # Create data and backup directories RUN mkdir -p /app/data /app/backups +# Create a placeholder .env file with proper permissions +RUN touch /app/.env && chmod 644 /app/.env && chown ${USERNAME}:${USERNAME} /app/.env + # Set executable permissions RUN chmod +x /app/gomft +# Set ownership of application files +RUN chown -R ${USERNAME}:${USERNAME} /app + # Expose the application port EXPOSE 8080 +# Use our entrypoint script +ENTRYPOINT ["/entrypoint.sh"] + # Run the application CMD ["/app/gomft"] diff --git a/README.md b/README.md index 503c1cc..421ec81 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ GoMFT is a web-based managed file transfer application built with Go, leveraging > [!WARNING] > This application is actively under development. As such, any aspect of the application—including configurations, data structures, and database fields—may change rapidly and without prior notice. Please review all release notes thoroughly before updating. +--- ## Screenshots @@ -29,6 +30,8 @@ GoMFT is a web-based managed file transfer application built with Go, leveraging +--- + ## Features - **Multiple Storage Support**: Leverage rclone's extensive support for cloud storage providers: @@ -80,12 +83,16 @@ GoMFT is a web-based managed file transfer application built with Go, leveraging - **Docker Support**: Easy deployment with Docker images and Docker Compose support - **Portable Deployment**: Run on any platform that supports Docker or Go +--- + ## Prerequisites - Go 1.21 or later - rclone installed and configured - SQLite 3 +--- + ## Installation ### Standard Installation @@ -116,6 +123,8 @@ docker pull starfleetcptn/gomft:latest ``` 2. Run the container: + +#### Basic run ```bash docker run -d \ --name gomft \ @@ -125,6 +134,42 @@ docker run -d \ starfleetcptn/gomft:latest ``` +#### Run with specific user ID and group ID (using environment variables) +```bash +docker run -d \ + --name gomft \ + -p 8080:8080 \ + -v /path/to/data:/app/data \ + -v /path/to/backups:/app/backups \ + -e PUID=$(id -u) \ + -e PGID=$(id -g) \ + starfleetcptn/gomft:latest +``` + +#### Or specify user IDs directly +```bash +docker run -d \ + --name gomft \ + -p 8080:8080 \ + -v /path/to/data:/app/data \ + -v /path/to/backups:/app/backups \ + -e PUID=1001 \ + -e PGID=1001 \ + starfleetcptn/gomft:latest +``` + +#### Using a .env file for configuration +```bash +docker run -d \ + --name gomft \ + -p 8080:8080 \ + -v /path/to/data:/app/data \ + -v /path/to/backups:/app/backups \ + -v /path/to/.env:/app/.env \ + -e PUID=$(id -u) \ + -e PGID=$(id -g) \ + starfleetcptn/gomft:latest +``` 3. Access the web interface at `http://localhost:8080` #### Docker Compose Example @@ -143,19 +188,19 @@ services: volumes: - ./data:/app/data - ./backups:/app/backups + - ./.env:/app/.env environment: + - PUID=1000 + - PGID=1000 - TZ=UTC - SERVER_ADDRESS=:8080 - DATA_DIR=/app/data - BACKUP_DIR=/app/backups - JWT_SECRET=change_this_to_a_secure_random_string - BASE_URL=http://localhost:8080 - # Google OAuth configuration (optional) - GOOGLE_CLIENT_ID=your_google_client_id - GOOGLE_CLIENT_SECRET=your_google_client_secret - # Two-Factor Authentication configuration - - TOTP_ENCRYPTION_KEY=your_32_byte_secure_encryption_key - # Email configuration + - TOTP_ENCRYPTION_KEY=your_32_byte_encryption_key_here - EMAIL_ENABLED=true - EMAIL_HOST=smtp.example.com - EMAIL_PORT=587 @@ -165,34 +210,14 @@ services: - EMAIL_REQUIRE_AUTH=true - EMAIL_USERNAME=smtp_username - EMAIL_PASSWORD=smtp_password - # Logging configuration - LOGS_DIR=/app/data/logs - LOG_MAX_SIZE=10 - LOG_MAX_BACKUPS=5 - LOG_MAX_AGE=30 - LOG_COMPRESS=true - LOG_LEVEL=info + # The user directive is no longer needed when using PUID/PGID environment variables ``` - -Alternatively, you can mount your own .env file to the container: - -```yaml -version: '3' -services: - gomft: - image: starfleetcptn/gomft:latest - container_name: gomft - restart: unless-stopped - ports: - - "8080:8080" - volumes: - - ./data:/app/data - - ./backups:/app/backups - - ./.env:/app/.env - environment: - - TZ=UTC -``` - Save this as `docker-compose.yml` and run: ```bash @@ -201,11 +226,14 @@ docker-compose up -d For more information and available tags, visit the [GoMFT Docker Hub page](https://hub.docker.com/r/starfleetcptn/gomft). +--- + ## Configuration GoMFT uses an environment file located at `.env` in the root directory of the application. On first run, a default configuration will be created: -``` +```ini +# Basic configuration SERVER_ADDRESS=:8080 DATA_DIR=/app/data BACKUP_DIR=/app/backups @@ -230,6 +258,10 @@ EMAIL_PASSWORD=smtp_password # Two-Factor Authentication configuration TOTP_ENCRYPTION_KEY=your_32_byte_encryption_key_here + +# UserID and GroupID +PUID=1000 +PGID=1000 ``` ### Configuration Options @@ -278,6 +310,8 @@ GoMFT provides configurable logging with rotation support through the following Log files contain detailed information about file transfers, job execution, and system operations, which can be useful for troubleshooting and auditing. +--- + ## Usage 1. Start the server: @@ -552,6 +586,8 @@ The Admin Tools interface also includes database management capabilities: - View system statistics - Optimize the database with maintenance tools +--- + ## Development ### Project Structure @@ -602,6 +638,8 @@ templ generate air ``` +--- + ## Contributing 1. Fork the repository @@ -610,6 +648,8 @@ air 4. Push to the branch 5. Create a Pull Request +--- + ## Directory Structure GoMFT uses the following directory structure: @@ -630,6 +670,83 @@ volumes: These paths can be customized using the environment variables `DATA_DIR`, `BACKUP_DIR`, and `LOGS_DIR`. +--- + +## Security Considerations + +### Running as a Non-Root User + +By default, Docker containers run as the root user, which can pose security risks. GoMFT supports running as a non-root user, which is recommended for production environments. + +#### Benefits of Running as Non-Root + +- **Improved Security**: Limits the potential damage if the container is compromised +- **Better File Permissions**: Files created by the container will match your host user permissions +- **Compliance**: Many security policies and best practices require containers to run as non-root + +#### Methods to Run as Non-Root + +1. **Using PUID/PGID environment variables (recommended)**: + ```bash + # Using current user's ID + docker run -e PUID=$(id -u) -e PGID=$(id -g) starfleetcptn/gomft:latest + + # Or in docker-compose.yml + environment: + - PUID=1000 + - PGID=1000 + ``` + This is the most flexible method as it allows changing the user at runtime without rebuilding the image. + +2. **Using the `--user` flag with Docker run**: + ```bash + docker run --user $(id -u):$(id -g) starfleetcptn/gomft:latest + ``` + +3. **Using Docker Compose with environment variables for `user` directive**: + ```yaml + services: + gomft: + image: starfleetcptn/gomft:latest + user: "${UID:-1000}:${GID:-1000}" + ``` + +4. **Building a custom image with specified UID/GID**: + ```yaml + services: + gomft: + build: + context: . + args: + UID: ${UID:-1000} + GID: ${GID:-1000} + ``` + +#### Environment Variables for User Management + +| Variable | Description | Default | +|----------|-------------|---------| +| `PUID` | User ID to run as | Built-in user ID (1000) | +| `PGID` | Group ID to run as | Built-in group ID (1000) | +| `USERNAME` | Username to use | `gomft` | + +These environment variables allow you to change the user/group IDs at runtime without rebuilding the image. + +#### Volume Permissions + +When mounting volumes, ensure that the directories on the host have appropriate permissions for the container user: + +```bash +# Create directories with correct ownership +mkdir -p data backups +chown -R $(id -u):$(id -g) data backups + +# Or adjust permissions to allow the container user to write +mkdir -p data backups +chmod -R 777 data backups # Less secure, but easier for testing +``` + +--- ## License diff --git a/components/admin_tools.templ b/components/admin_tools.templ index dae3fee..583c721 100644 --- a/components/admin_tools.templ +++ b/components/admin_tools.templ @@ -513,11 +513,96 @@ templ AdminTools(ctx context.Context, data AdminToolsData) { { fmt.Sprint(data.TotalJobs) } + +
GoMFT © { getCurrentYear() } | Secure File Transfer Solution
++ + GitHub + +
+ +