From 3cf3ac2caacec49371b1a982abc157a59bf4177e Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Wed, 10 Dec 2025 13:51:04 +0100 Subject: [PATCH] try plausible CE for web analytics --- .gitea/workflows/plausible.yaml | 62 +++++++ chart/values.yaml | 3 +- hashicorp-vault/iac/terraform.tfvars | 4 + plausible/add-initcontainer.yaml | 38 +++++ plausible/iac/backend.tf | 6 + plausible/iac/main.tf | 30 ++++ plausible/iac/providers.tf | 16 ++ plausible/kustomization.yaml | 56 ++++++ plausible/plausibleValues.yaml | 178 ++++++++++++++++++++ plausible/resources/configmap.yaml | 14 ++ plausible/resources/geoipsecret.yaml | 24 +++ plausible/resources/vaultauth.yaml | 14 ++ plausible/resources/vaultdynamicsecret.yaml | 25 +++ plausible/resources/vaultsecret.yaml | 24 +++ 14 files changed, 493 insertions(+), 1 deletion(-) create mode 100644 .gitea/workflows/plausible.yaml create mode 100644 plausible/add-initcontainer.yaml create mode 100644 plausible/iac/backend.tf create mode 100644 plausible/iac/main.tf create mode 100644 plausible/iac/providers.tf create mode 100644 plausible/kustomization.yaml create mode 100644 plausible/plausibleValues.yaml create mode 100644 plausible/resources/configmap.yaml create mode 100644 plausible/resources/geoipsecret.yaml create mode 100644 plausible/resources/vaultauth.yaml create mode 100644 plausible/resources/vaultdynamicsecret.yaml create mode 100644 plausible/resources/vaultsecret.yaml diff --git a/.gitea/workflows/plausible.yaml b/.gitea/workflows/plausible.yaml new file mode 100644 index 0000000..4535fee --- /dev/null +++ b/.gitea/workflows/plausible.yaml @@ -0,0 +1,62 @@ +--- +# template source: https://github.com/bretfisher/docker-build-workflow/blob/main/templates/call-docker-build.yaml +name: Plausible + +on: #[push,pull_request] + workflow_dispatch: {} + push: &plausiblePaths + paths: + - 'plausible/**/*.tf' + pull_request: *plausiblePaths + +# cancel any previously-started, yet still active runs of this workflow on the same branch +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +.vault_step: &vault_step + name: read vault secret + uses: https://gitea.arcodange.duckdns.org/arcodange-org/vault-action.git@main + id: vault-secrets + with: + url: https://vault.arcodange.duckdns.org + jwtGiteaOIDC: ${{ needs.gitea_vault_auth.outputs.gitea_vault_jwt }} + role: gitea_cicd_plausible + method: jwt + path: gitea_jwt + secrets: | + kvv1/google/credentials credentials | GOOGLE_BACKEND_CREDENTIALS ; + kvv1/gitea/tofu_module_reader ssh_private_key | TERRAFORM_SSH_KEY ; + +jobs: + gitea_vault_auth: + name: Auth with gitea for vault + runs-on: ubuntu-latest + outputs: + gitea_vault_jwt: ${{steps.gitea_vault_jwt.outputs.id_token}} + steps: + + - name: Auth with gitea for vault + id: gitea_vault_jwt + run: | + echo -n "${{ secrets.vault_oauth__sh_b64 }}" | base64 -d | bash + + tofu: + name: Tofu - plausible IAC + needs: + - gitea_vault_auth + runs-on: ubuntu-latest + env: + OPENTOFU_VERSION: 1.8.2 + TERRAFORM_VAULT_AUTH_JWT: ${{ needs.gitea_vault_auth.outputs.gitea_vault_jwt }} + steps: + - *vault_step + - uses: actions/checkout@v4 + # - uses: dflook/terraform-plan@v1 + # with: + # path: hashicorp-vault/iac + - name: terraform apply + uses: dflook/terraform-apply@v1 + with: + path: plausible/iac + auto_approve: true \ No newline at end of file diff --git a/chart/values.yaml b/chart/values.yaml index 27f40fa..a26bc89 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -6,4 +6,5 @@ tools: crowdsec: {} redis: {} clickhouse: {} - grafana: {} \ No newline at end of file + grafana: {} + plausible: {} \ No newline at end of file diff --git a/hashicorp-vault/iac/terraform.tfvars b/hashicorp-vault/iac/terraform.tfvars index 1945736..da3ca5f 100644 --- a/hashicorp-vault/iac/terraform.tfvars +++ b/hashicorp-vault/iac/terraform.tfvars @@ -10,4 +10,8 @@ applications = [ name = "crowdsec" service_account_namespaces = ["tools"] }, + { + name = "plausible" + service_account_namespaces = ["tools"] + }, ] \ No newline at end of file diff --git a/plausible/add-initcontainer.yaml b/plausible/add-initcontainer.yaml new file mode 100644 index 0000000..ded3e9e --- /dev/null +++ b/plausible/add-initcontainer.yaml @@ -0,0 +1,38 @@ +- op: add + path: /spec/template/spec/initContainers/0 + value: + name: build-database-url + image: alpine:3.19 + command: ["/bin/sh", "-c"] + args: + - | + echo "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}" > /run/secrets/DATABASE_URL + volumeMounts: + - name: generated-secrets + mountPath: /run/secrets + env: + - name: DB_USER + valueFrom: + secretKeyRef: + name: plausible-db-credentials + key: username + - name: DB_PASS + valueFrom: + secretKeyRef: + name: plausible-db-credentials + key: password + - name: DB_HOST + valueFrom: + configMapKeyRef: + name: plausible-config + key: DB_HOST + - name: DB_PORT + valueFrom: + configMapKeyRef: + name: plausible-config + key: DB_PORT + - name: DB_NAME + valueFrom: + configMapKeyRef: + name: plausible-config + key: DB_NAME diff --git a/plausible/iac/backend.tf b/plausible/iac/backend.tf new file mode 100644 index 0000000..662c1df --- /dev/null +++ b/plausible/iac/backend.tf @@ -0,0 +1,6 @@ +terraform { + backend "gcs" { + bucket = "arcodange-tf" + prefix = "tools/plausible/main" + } +} \ No newline at end of file diff --git a/plausible/iac/main.tf b/plausible/iac/main.tf new file mode 100644 index 0000000..b3e979f --- /dev/null +++ b/plausible/iac/main.tf @@ -0,0 +1,30 @@ +module "app_roles" { + source = "git::ssh://git@192.168.1.202:2222/arcodange-org/tools.git//hashicorp-vault/iac/modules/app_roles?depth=1&ref=main" + name = "plausible" + service_account_namespaces = ["tools"] +} + +# https://github.com/plausible/community-edition/wiki/configuration#database + +#SECRET_KEY_BASE (openssl rand -base64 48) +# + +resource "random_password" "secret" { + for_each = toset("48","32") + length = tonumber(each.value) + special = false +} +locals { + config = { + SECRET_KEY_BASE = base64encode(random_password.secret["48"].result) + TOTP_VAULT_KEY = base64encode(random_password.secret["32"].result) + } +} + +resource "vault_kv_secret_v2" "config" { + mount = "kvv2" + name = "plausible/config" + cas = 1 +# delete_all_versions = true + data_json = jsonencode(local.config) +} \ No newline at end of file diff --git a/plausible/iac/providers.tf b/plausible/iac/providers.tf new file mode 100644 index 0000000..c0d2716 --- /dev/null +++ b/plausible/iac/providers.tf @@ -0,0 +1,16 @@ +terraform { + required_providers { + vault = { + source = "vault" + version = "4.4.0" + } + } +} + +provider "vault" { + address = "https://vault.arcodange.duckdns.org" + auth_login_jwt { # TERRAFORM_VAULT_AUTH_JWT environment variable + mount = "gitea_jwt" + role = "gitea_cicd_plausible" + } +} \ No newline at end of file diff --git a/plausible/kustomization.yaml b/plausible/kustomization.yaml new file mode 100644 index 0000000..4c2a1de --- /dev/null +++ b/plausible/kustomization.yaml @@ -0,0 +1,56 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +helmCharts: +- name: plausible + repo: https://charts.pascaliske.dev + version: 2.0.0 + releaseName: plausible + valuesFile: plausibleValues.yaml + +patches: + - target: + kind: IngressRoute + name: plausible-route + patch: |- + - op: add + path: /spec/tls + value: + certResolver: letsencrypt + domains: + - main: arcodange.duckdns.org + sans: + - analytics.arcodange.duckdns.org + +resources: + - resources/vaultauth.yaml + - resources/vaultdynamicsecret.yaml + - resources/vaultsecret.yaml + - resources/configmap.yaml + - resources/geoipsecret.yaml + +patchesJson6902: + - target: + version: v1 + kind: Deployment + name: plausible + patch: |- + - op: replace + path: /spec/template/spec/containers/1/env/2 + value: + name: GEOIPUPDATE_LICENSE_KEY + valueFrom: + secretKeyRef: + name: plausible-geoip + key: LICENSE_KEY + - op: add + path: /spec/template/spec/volumes/- + value: + name: generated-secrets + emptyDir: + medium: Memory + - target: + version: v1 + kind: Deployment + name: plausible + path: add-initcontainer.yaml \ No newline at end of file diff --git a/plausible/plausibleValues.yaml b/plausible/plausibleValues.yaml new file mode 100644 index 0000000..4bf7257 --- /dev/null +++ b/plausible/plausibleValues.yaml @@ -0,0 +1,178 @@ +image: + # -- The registry to pull the image from. + registry: ghcr.io + # -- The repository to pull the image from. + repository: plausible/community-edition + # -- The docker tag, if left empty chart's appVersion will be used. + # @default -- `.Chart.AppVersion` + tag: '' + # -- The pull policy for the controller. + pullPolicy: IfNotPresent + +nameOverride: '' +fullnameOverride: '' + +controller: + # -- Create a workload for this chart. + enabled: true + # -- Type of the workload object. + kind: Deployment + # -- The number of replicas. + replicas: 1 + # -- Additional annotations for the controller object. + annotations: {} + # -- Additional labels for the controller object. + labels: {} + +service: + # -- Create a service for exposing this chart. + enabled: true + # -- The service type used. + type: ClusterIP + # -- ClusterIP used if service type is `ClusterIP`. + clusterIP: '' + # -- LoadBalancerIP if service type is `LoadBalancer`. + loadBalancerIP: '' + # -- Allowed addresses when service type is `LoadBalancer`. + loadBalancerSourceRanges: [] + # -- Additional annotations for the service object. + annotations: {} + # -- Additional labels for the service object. + labels: {} + +serviceMonitor: + # -- Create a service monitor for prometheus operator. + enabled: false + # -- How frequently the exporter should be scraped. + interval: 30s + # -- Timeout value for individual scrapes. + timeout: 10s + # -- Additional annotations for the service monitor object. + annotations: {} + # -- Additional labels for the service monitor object. + labels: {} + +ingressRoute: + # -- Create an IngressRoute object for exposing this chart. + create: true + # -- List of [entry points](https://doc.traefik.io/traefik/routing/routers/#entrypoints) on which the ingress route will be available. + entryPoints: [websecure] + # -- [Matching rule](https://doc.traefik.io/traefik/routing/routers/#rule) for the underlying router. + rule: 'Host(analytics.arcodange.duckdns.org)' + # -- List of [middleware objects](https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/#kind-middleware) for the ingress route. + middlewares: [localIp@file] + # -- Use an existing secret containing the TLS certificate. + tlsSecretName: '' + # -- Additional annotations for the ingress route object. + annotations: {} + # -- Additional labels for the ingress route object. + labels: {} + +certificate: + # -- Create an Certificate object for the exposed chart. + create: false + # -- List of subject alternative names for the certificate. + dnsNames: [] + # -- Name of the secret in which the certificate will be stored. Defaults to the first item in dnsNames. + secretName: '' + issuerRef: + # -- Type of the referenced certificate issuer. Can be "Issuer" or "ClusterIssuer". + kind: ClusterIssuer + # -- Name of the referenced certificate issuer. + name: '' + # -- Additional annotations for the certificate object. + annotations: {} + # -- Additional labels for the certificate object. + labels: {} + +env: + # -- Timezone for the container. + - name: TZ + value: Europe/Paris + +ports: + http: + # -- Enable the port inside the `Deployment` and `Service` objects. + enabled: true + # -- The port used as internal port and cluster-wide port if `.service.type` == `ClusterIP`. + port: 8000 + # -- The external port used if `.service.type` == `NodePort`. + nodePort: null + # -- The protocol used for the service. + protocol: TCP + +secret: + # -- Create a new secret object. + create: false + # -- Use an existing secret object. + existingSecret: 'plausible-config' + # -- Secret values used when not using an existing secret. Helm templates are supported for values. + values: + # -- Secret key for session tokens. + SECRET_KEY_BASE: '{{ randAlphaNum 42 | b64enc }}' + # -- Encryption token for TOTP secrets. + TOTP_VAULT_KEY: '{{ randAlphaNum 32 | b64enc }}' + # -- Additional annotations for the secret object. + annotations: {} + # -- Additional labels for the secret object. + labels: {} + +geoip: + # -- Enable support for MaxMinds GeoLite2 database. + enabled: true + image: + # -- The repository for the geoip image. + repository: ghcr.io/maxmind/geoipupdate + # -- The docker tag for the geoip image. + tag: v7.1.1 + # -- Required. MaxMind account ID. + accountId: '1266329' + # -- Required. Case-sensitive MaxMind license key. + # licenseKey: 'kvv2/data/plausible/geoip LICENSE_KEY' + # -- Optional. Database update frequency. Defaults to "168" which equals 7 days. + frequency: 168 + # -- Optional. Specify the database mount path inside the containers. + mountPath: /geoip + +serviceAccount: + # -- Create a `ServiceAccount` object. + create: true + # -- Specify the service account used for the controller. + name: '' + # -- Additional annotations for the service account object. + annotations: {} + # -- Additional labels for the service account object. + labels: {} + +# -- Pod-level security attributes. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#security-context). +securityContext: {} + # fsGroup: 1000 + # runAsNonRoot: true + # runAsGroup: 1000 + # runAsUser: 1000 + +# -- Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). +resources: {} + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +# -- Pod-level affinity. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling). +affinity: {} + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/hostname + # operator: In + # values: + # - my-node-xyz + +# -- Pod-level tolerations. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling). +tolerations: [] + # - key: node-role.kubernetes.io/control-plane + # operator: Exists + # effect: NoSchedule \ No newline at end of file diff --git a/plausible/resources/configmap.yaml b/plausible/resources/configmap.yaml new file mode 100644 index 0000000..72b9d94 --- /dev/null +++ b/plausible/resources/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: plausible-config + namespace: tools +# Doc: https://github.com/plausible/community-edition/wiki/Configuration +data: + DB_HOST: pgbouncer.tools + DB_PORT: !!str 5432 + DB_NAME: plausible + + BASE_URL: https://analytics.arcodange.duckdns.org + + CLICKHOUSE_DATABASE_URL: http://plausible:plausiblearcodange@plausible.tools:8123/plausible \ No newline at end of file diff --git a/plausible/resources/geoipsecret.yaml b/plausible/resources/geoipsecret.yaml new file mode 100644 index 0000000..0f4b7dc --- /dev/null +++ b/plausible/resources/geoipsecret.yaml @@ -0,0 +1,24 @@ +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: plausible-geoip + namespace: tools +spec: + type: kv-v2 + + # mount path + mount: kvv2 + + # path of the secret + path: plausible/geoip + + # dest k8s secret + destination: + name: plausible-geoip + create: true + + # static secret refresh interval + refreshAfter: 30s + + # Name of the CRD to authenticate to Vault + vaultAuthRef: plausible \ No newline at end of file diff --git a/plausible/resources/vaultauth.yaml b/plausible/resources/vaultauth.yaml new file mode 100644 index 0000000..b9f4398 --- /dev/null +++ b/plausible/resources/vaultauth.yaml @@ -0,0 +1,14 @@ +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultAuth +metadata: + name: plausible + namespace: tools +spec: + vaultConnectionRef: default + method: kubernetes + mount: kubernetes + kubernetes: + role: plausible + serviceAccount: plausible + audiences: + - vault \ No newline at end of file diff --git a/plausible/resources/vaultdynamicsecret.yaml b/plausible/resources/vaultdynamicsecret.yaml new file mode 100644 index 0000000..c4d5e99 --- /dev/null +++ b/plausible/resources/vaultdynamicsecret.yaml @@ -0,0 +1,25 @@ +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultDynamicSecret +metadata: + name: plausible-db-credentials + namespace: tools +spec: + + # Mount path of the secrets backend + mount: postgres + + # Path to the secret + path: creds/plausible + + # Where to store the secrets, VSO will create the secret + destination: + create: true + name: plausible-db-credentials + + # Restart these pods when secrets rotated + rolloutRestartTargets: + - kind: Deployment + name: plausible + + # Name of the CRD to authenticate to Vault + vaultAuthRef: plausible \ No newline at end of file diff --git a/plausible/resources/vaultsecret.yaml b/plausible/resources/vaultsecret.yaml new file mode 100644 index 0000000..21a066b --- /dev/null +++ b/plausible/resources/vaultsecret.yaml @@ -0,0 +1,24 @@ +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: plausible + namespace: tools +spec: + type: kv-v2 + + # mount path + mount: kvv2 + + # path of the secret + path: plausible/config + + # dest k8s secret + destination: + name: plausible-config + create: true + + # static secret refresh interval + refreshAfter: 30s + + # Name of the CRD to authenticate to Vault + vaultAuthRef: plausible \ No newline at end of file