diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..cb0f94b --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,66 @@ +name: Build and Push Moroz Docker Image + +on: + push: + branches: + - master + tags: + - 'v*' + pull_request: + branches: + - master + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + id-token: write + 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 + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + # set latest tag for master branch pushes + type=raw,value=latest,enable={{is_default_branch}} + # set PR tags for pull requests + type=ref,event=pr + # set version tags for releases + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + # set branch-sha for non-default branches + type=sha,prefix={{branch}}-,enable={{is_default_branch}} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 23cb2ec..a4ab58c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,26 @@ -FROM alpine:3.6 +FROM golang:1.23-alpine AS builder -RUN apk --update add \ - ca-certificates +WORKDIR /app +RUN apk --no-cache add git ca-certificates +ADD . . +RUN go mod download -COPY ./build/linux/moroz /usr/bin/moroz +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o moroz ./cmd/moroz -CMD ["moroz"] +FROM alpine:latest +RUN apk --no-cache add ca-certificates +RUN addgroup -g 1001 -S moroz && \ + adduser -u 1001 -S moroz -G moroz + +WORKDIR /app +COPY --from=builder /app/moroz . + +RUN chown -R moroz:moroz /app +USER moroz + +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +CMD ["/app/moroz"] \ No newline at end of file diff --git a/README.md b/README.md index 90e974a..d4bbb38 100644 --- a/README.md +++ b/README.md @@ -140,5 +140,13 @@ Assumes you have the `./server.crt` and `./server.key` files. moroz -configs /path/to/configs/folder ``` +# Kubernetes Deployment + +Deploy to Kubernetes using kustomize: + +```bash +kubectl apply -k deploy/ +``` + --- moroz icon by [Souvik Bhattacharjee](https://thenounproject.com/souvik502/) from the [Noun Project](https://thenounproject.com/). diff --git a/deploy/configmap.yaml b/deploy/configmap.yaml new file mode 100644 index 0000000..0bfb7d9 --- /dev/null +++ b/deploy/configmap.yaml @@ -0,0 +1,63 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: moroz-config +data: + global.toml: | + client_mode = "MONITOR" + # blocked_path_regex = "^(?:/Users)/.*" + # allowed_path_regex = "^(?:/Users)/.*" + batch_size = 100 + enable_all_event_upload = true + enable_bundles = false + enable_transitive_rules = true + clean_sync = true + full_sync_interval = 600 + + [[rules]] + rule_type = "BINARY" + policy = "BLOCKLIST" + identifier = "2dc104631939b4bdf5d6bccab76e166e37fe5e1605340cf68dab919df58b8eda" + custom_msg = "blocklist firefox" + + [[rules]] + rule_type = "TEAMID" + policy = "ALLOWLIST" + identifier = "EQHXZ8M8AV" + custom_msg = "allow google team id" + + [[rules]] + rule_type = "SIGNINGID" + policy = "ALLOWLIST" + identifier = "EQHXZ8M8AV:com.google.Chrome" + custom_msg = "allow google chrome signing id" + + [[rules]] + rule_type = "SIGNINGID" + policy = "BLOCKLIST" + identifier = "platform:com.apple.BluetoothFileExchange" + custom_msg = "block bluetooth file exchange.app" + + [[rules]] + rule_type = "BINARY" + policy = "ALLOWLIST_COMPILER" + identifier = "60d79d1763fefb56716e4a36284300523eb4335c3726fb9070fa83074b02279e" + custom_msg = "allowlist go compiler component" + + [[rules]] + rule_type = "BINARY" + policy = "ALLOWLIST_COMPILER" + identifier = "8e78770685d51324b78588fddc6afc2f8b6cef5231c27eeb97363cc437fec18a" + custom_msg = "allowlist go compiler component" + + [[rules]] + rule_type = "BINARY" + policy = "ALLOWLIST_COMPILER" + identifier = "e88617cfd62809fb10e213c459a52f48e028fae4321e41134c4797465af886b6" + custom_msg = "allowlist go compiler component" + + [[rules]] + rule_type = "BINARY" + policy = "ALLOWLIST_COMPILER" + identifier = "d867fca68bbd7db18e9ced231800e7535bc067852b1e530987bb7f57b5e3a02c" + custom_msg = "allowlist go compiler component" diff --git a/deploy/deployment.yaml b/deploy/deployment.yaml new file mode 100644 index 0000000..156bb69 --- /dev/null +++ b/deploy/deployment.yaml @@ -0,0 +1,79 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: moroz-server + labels: + app.kubernetes.io/name: moroz-server +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: moroz-server + template: + metadata: + name: moroz-server + labels: + app.kubernetes.io/name: moroz-server + spec: + containers: + - name: moroz-server + image: ghcr.io/pmdroid/moroz:latest + args: + - /app/moroz + - --configs=/configs + - --use-tls=false + - --debug + imagePullPolicy: IfNotPresent + ports: + - containerPort: 8080 + protocol: TCP + securityContext: + allowPrivilegeEscalation: false + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + resources: + requests: + memory: "128Mi" + cpu: "100m" + limits: + memory: "256Mi" + cpu: "200m" + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + volumeMounts: + - mountPath: /configs + name: configs + readOnly: true + restartPolicy: Always + securityContext: + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + seccompProfile: + type: RuntimeDefault + automountServiceAccountToken: false + volumes: + - name: configs + configMap: + name: moroz-config + defaultMode: 0440 \ No newline at end of file diff --git a/deploy/kustomization.yaml b/deploy/kustomization.yaml new file mode 100644 index 0000000..b90d24b --- /dev/null +++ b/deploy/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +metadata: + name: moroz +resources: + - deployment.yaml + - service.yaml + - configmap.yaml \ No newline at end of file diff --git a/deploy/service.yaml b/deploy/service.yaml new file mode 100644 index 0000000..e6f87e0 --- /dev/null +++ b/deploy/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: moroz-server +spec: + selector: + app.kubernetes.io/name: moroz-server + ports: + - protocol: TCP + port: 8080 + name: http + targetPort: 8080 + type: ClusterIP diff --git a/moroz/server.go b/moroz/server.go index 5779764..c5ecf02 100644 --- a/moroz/server.go +++ b/moroz/server.go @@ -53,6 +53,9 @@ func AddHTTPRoutes(r *mux.Router, e Endpoints, logger log.Logger) { options..., )) + r.Methods("GET").Path("/health").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) } // errBadRoute is used for mux errors