diff --git a/.env.example b/.env.example new file mode 100644 index 00000000000..3406fdea543 --- /dev/null +++ b/.env.example @@ -0,0 +1,56 @@ +# Copy to .env, replace every REPLACE_ value, then chmod 600 .env. + +# Build and pinned images +FLEET_IMAGE_REPOSITORY=thinkhome/fleet +FLEET_IMAGE_TAG=local-b0223221 +FLEET_REVISION=b0223221a4c68ebc13e12e26e5236f09207d748d +MYSQL_IMAGE=mysql:8.0.44 +REDIS_IMAGE=redis:6.2 +ALPINE_IMAGE=alpine:3.23.4 +CADDY_IMAGE=caddy:2.11.4-alpine + +# Secrets: generate with `openssl rand -hex 32` and `openssl rand -base64 32`. +MYSQL_ROOT_PASSWORD=REPLACE_WITH_RANDOM_HEX +MYSQL_PASSWORD=REPLACE_WITH_RANDOM_HEX +FLEET_SERVER_PRIVATE_KEY=REPLACE_WITH_RANDOM_BASE64 + +# Initial setup credentials. Fleet consumes these only during first-run setup. +FLEET_ADMIN_EMAIL=samuel.paluba@thinkhome.org +FLEET_ADMIN_PASSWORD=REPLACE_WITH_12_CHARS_NUMBER_AND_SYMBOL + +# Public endpoint and reverse proxy +FLEET_DOMAIN=fleet.thinkhome.org +HTTP_PORT=80 +HTTPS_PORT=443 +FLEET_SERVER_ADDRESS=0.0.0.0:1337 +FLEET_SERVER_TLS=false +FLEET_SERVER_TRUSTED_PROXIES=1 + +# MySQL +MYSQL_DATABASE=fleet +MYSQL_USER=fleet +MYSQL_MAX_ALLOWED_PACKET=536870912 +MYSQL_BINLOG_EXPIRE_SECONDS=86400 + +# Fleet runtime +FLEET_LOGGING_JSON=true +FLEET_LOGGING_DEBUG=false +FLEET_SERVER_MAX_INSTALLER_SIZE=10GiB +FLEET_VULNERABILITIES_CURRENT_INSTANCE_CHECKS=yes +FLEET_VULNERABILITIES_DATABASES_PATH=/vulndb +FLEET_VULNERABILITIES_PERIODICITY=1h + +# Optional Fleet Premium license and S3-compatible software storage +FLEET_LICENSE_KEY= +FLEET_S3_SOFTWARE_INSTALLERS_BUCKET= +FLEET_S3_SOFTWARE_INSTALLERS_PREFIX= +FLEET_S3_SOFTWARE_INSTALLERS_ACCESS_KEY_ID= +FLEET_S3_SOFTWARE_INSTALLERS_SECRET_ACCESS_KEY= +FLEET_S3_SOFTWARE_INSTALLERS_REGION= +FLEET_S3_SOFTWARE_INSTALLERS_ENDPOINT_URL= +FLEET_S3_SOFTWARE_INSTALLERS_FORCE_S3_PATH_STYLE=false + +# MDM +# Windows WSTEP cert/key live in ./secrets/mdm and are mounted read-only. +# Android requires one-line/minified Google service-account JSON for Android Management API + Pub/Sub. +FLEET_MDM_ANDROID_GOOGLE_SERVICE_CREDENTIALS= diff --git a/.gitignore b/.gitignore index 9a08aaea9fd..a3da68a87e7 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,4 @@ custom-gcl # dibble (the seed slinger) — binary lives next to source tools/dibble/dibble +secrets/ diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 00000000000..bb65817dccc --- /dev/null +++ b/Caddyfile @@ -0,0 +1,4 @@ +{$FLEET_DOMAIN} { + encode zstd gzip + reverse_proxy fleet:1337 +} diff --git a/Dockerfile.local b/Dockerfile.local new file mode 100644 index 00000000000..5be4699392c --- /dev/null +++ b/Dockerfile.local @@ -0,0 +1,24 @@ +FROM node:24.10.0-bookworm-slim AS frontend +WORKDIR /src +COPY package.json yarn.lock .npmrc .yarnrc ./ +RUN yarn install --frozen-lockfile --network-timeout 600000 +COPY . . +RUN NODE_OPTIONS=--max-old-space-size=2048 NODE_ENV=production yarn run webpack --progress + +FROM golang:1.26.5-trixie AS builder +RUN apt-get update && apt-get install -y --no-install-recommends musl-tools && rm -rf /var/lib/apt/lists/* +WORKDIR /src +COPY . . +COPY --from=frontend /src/assets /src/assets +COPY --from=frontend /src/frontend/templates/react.tmpl /src/frontend/templates/react.tmpl +RUN go run github.com/kevinburke/go-bindata/go-bindata -pkg=bindata -tags full -o=server/bindata/generated.go frontend/templates/ assets/... server/mail/templates +ARG FLEET_VERSION=local +ARG FLEET_REVISION=unknown +RUN CC=musl-gcc CGO_ENABLED=1 GOMAXPROCS=2 go build -tags full,fts5,netgo -trimpath -o /fleet -ldflags "-s -w -extldflags '-static' -X github.com/fleetdm/fleet/v4/server/version.version=${FLEET_VERSION} -X github.com/fleetdm/fleet/v4/server/version.revision=${FLEET_REVISION}" ./cmd/fleet + +FROM alpine:3.23.4 +RUN apk --no-cache add ca-certificates jq && apk --no-cache upgrade openssl libcrypto3 libssl3 +RUN addgroup -S fleet && adduser -S fleet -G fleet +COPY --from=builder /fleet /usr/bin/fleet +USER fleet +CMD ["fleet", "serve"] diff --git a/Dockerfile.local.dockerignore b/Dockerfile.local.dockerignore new file mode 100644 index 00000000000..9382faefb3c --- /dev/null +++ b/Dockerfile.local.dockerignore @@ -0,0 +1,4 @@ +.git +.env +build +node_modules diff --git a/docker-compose.local.yml b/docker-compose.local.yml new file mode 100644 index 00000000000..05541e4c13e --- /dev/null +++ b/docker-compose.local.yml @@ -0,0 +1,121 @@ +name: fleet-local + +volumes: + mysql: + redis: + data: + logs: + vulndb: + caddy_data: + caddy_config: + +services: + mysql: + image: ${MYSQL_IMAGE:-mysql:8.0.44} + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?set MYSQL_ROOT_PASSWORD in .env} + MYSQL_DATABASE: ${MYSQL_DATABASE:-fleet} + MYSQL_USER: ${MYSQL_USER:-fleet} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:?set MYSQL_PASSWORD in .env} + volumes: + - mysql:/var/lib/mysql + command: ["mysqld", "--max_allowed_packet=${MYSQL_MAX_ALLOWED_PACKET:-536870912}", "--binlog-expire-logs-seconds=${MYSQL_BINLOG_EXPIRE_SECONDS:-86400}"] + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -u$$MYSQL_USER -p$$MYSQL_PASSWORD --silent"] + interval: 10s + timeout: 5s + retries: 20 + restart: unless-stopped + + redis: + image: ${REDIS_IMAGE:-redis:6.2} + command: ["redis-server", "--appendonly", "yes"] + volumes: + - redis:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 20 + restart: unless-stopped + + fleet-init: + image: ${ALPINE_IMAGE:-alpine:3.23.4} + volumes: + - logs:/logs + - data:/data + - vulndb:/vulndb + command: ["sh", "-c", "chown -R 100:101 /logs /data /vulndb"] + + fleet: + image: ${FLEET_IMAGE_REPOSITORY:-thinkhome/fleet}:${FLEET_IMAGE_TAG} + build: + context: . + dockerfile: Dockerfile.local + args: + FLEET_VERSION: ${FLEET_IMAGE_TAG} + FLEET_REVISION: ${FLEET_REVISION:-unknown} + depends_on: + mysql: + condition: service_healthy + redis: + condition: service_healthy + fleet-init: + condition: service_completed_successfully + command: ["sh", "-c", "/usr/bin/fleet prepare db --no-prompt && exec /usr/bin/fleet serve"] + environment: + FLEET_REDIS_ADDRESS: redis:6379 + FLEET_MYSQL_ADDRESS: mysql:3306 + FLEET_MYSQL_DATABASE: ${MYSQL_DATABASE:-fleet} + FLEET_MYSQL_USERNAME: ${MYSQL_USER:-fleet} + FLEET_MYSQL_PASSWORD: ${MYSQL_PASSWORD:?set MYSQL_PASSWORD in .env} + FLEET_SERVER_ADDRESS: ${FLEET_SERVER_ADDRESS:-0.0.0.0:1337} + FLEET_SERVER_TLS: ${FLEET_SERVER_TLS:-false} + FLEET_SERVER_TRUSTED_PROXIES: ${FLEET_SERVER_TRUSTED_PROXIES:-1} + FLEET_SERVER_PRIVATE_KEY: ${FLEET_SERVER_PRIVATE_KEY:?set FLEET_SERVER_PRIVATE_KEY in .env} + FLEET_SERVER_MAX_INSTALLER_SIZE: ${FLEET_SERVER_MAX_INSTALLER_SIZE:-10GiB} + FLEET_LOGGING_JSON: ${FLEET_LOGGING_JSON:-true} + FLEET_LOGGING_DEBUG: ${FLEET_LOGGING_DEBUG:-false} + FLEET_VULNERABILITIES_CURRENT_INSTANCE_CHECKS: ${FLEET_VULNERABILITIES_CURRENT_INSTANCE_CHECKS:-yes} + FLEET_VULNERABILITIES_DATABASES_PATH: ${FLEET_VULNERABILITIES_DATABASES_PATH:-/vulndb} + FLEET_VULNERABILITIES_PERIODICITY: ${FLEET_VULNERABILITIES_PERIODICITY:-1h} + FLEET_MDM_WINDOWS_WSTEP_IDENTITY_CERT: /run/secrets/fleet-mdm/windows-wstep.crt + FLEET_MDM_WINDOWS_WSTEP_IDENTITY_KEY: /run/secrets/fleet-mdm/windows-wstep.key + FLEET_MDM_ANDROID_GOOGLE_SERVICE_CREDENTIALS: ${FLEET_MDM_ANDROID_GOOGLE_SERVICE_CREDENTIALS:-} + FLEET_LICENSE_KEY: ${FLEET_LICENSE_KEY:-} + FLEET_S3_SOFTWARE_INSTALLERS_BUCKET: ${FLEET_S3_SOFTWARE_INSTALLERS_BUCKET:-} + FLEET_S3_SOFTWARE_INSTALLERS_PREFIX: ${FLEET_S3_SOFTWARE_INSTALLERS_PREFIX:-} + FLEET_S3_SOFTWARE_INSTALLERS_ACCESS_KEY_ID: ${FLEET_S3_SOFTWARE_INSTALLERS_ACCESS_KEY_ID:-} + FLEET_S3_SOFTWARE_INSTALLERS_SECRET_ACCESS_KEY: ${FLEET_S3_SOFTWARE_INSTALLERS_SECRET_ACCESS_KEY:-} + FLEET_S3_SOFTWARE_INSTALLERS_REGION: ${FLEET_S3_SOFTWARE_INSTALLERS_REGION:-} + FLEET_S3_SOFTWARE_INSTALLERS_ENDPOINT_URL: ${FLEET_S3_SOFTWARE_INSTALLERS_ENDPOINT_URL:-} + FLEET_S3_SOFTWARE_INSTALLERS_FORCE_S3_PATH_STYLE: ${FLEET_S3_SOFTWARE_INSTALLERS_FORCE_S3_PATH_STYLE:-false} + expose: + - "1337" + volumes: + - data:/fleet + - logs:/logs + - vulndb:/vulndb + - ./secrets/mdm:/run/secrets/fleet-mdm:ro + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:1337/healthz"] + interval: 10s + timeout: 5s + retries: 20 + restart: unless-stopped + + caddy: + image: ${CADDY_IMAGE:-caddy:2.11.4-alpine} + depends_on: + fleet: + condition: service_healthy + ports: + - "${HTTP_PORT:-80}:80" + - "${HTTPS_PORT:-443}:443" + environment: + FLEET_DOMAIN: ${FLEET_DOMAIN:-fleet.thinkhome.org} + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy_data:/data + - caddy_config:/config + restart: unless-stopped