diff --git a/.devcontainer/.gitignore b/.devcontainer/.gitignore deleted file mode 100644 index 52c762e..0000000 --- a/.devcontainer/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.kube/** \ No newline at end of file diff --git a/.devcontainer/.kube/.gitignore b/.devcontainer/.kube/.gitignore new file mode 100644 index 0000000..d29675e --- /dev/null +++ b/.devcontainer/.kube/.gitignore @@ -0,0 +1,2 @@ +** +!.gitignore \ No newline at end of file diff --git a/.gitea/app-continous-deploy.yaml b/.gitea/app-continous-deploy.yaml new file mode 100644 index 0000000..9be8bcb --- /dev/null +++ b/.gitea/app-continous-deploy.yaml @@ -0,0 +1,70 @@ +on: + schedule: + - cron: '0 9 * * 0' # every sunday 9 am + push: + branches: + - main + pull_request: + branches: + - main +jobs: + continuous-deploy: + runs-on: ["deploy", "kubectl"] + env: + GITHUB_TEMP: ${{ runner.temp }} # fix missing GITHUB_TEMP on gitea + steps: + + - name: Checkout code + uses: actions/checkout@v3 + + - name: limbo public actions + env: + WORKSPACE: "${{ gitea.workspace }}" + run: | + curl -fsSL https://git.limbosolutions.com/kb/gitea/raw/branch/main/cloud-scripts/setup-limbo-actions.sh | bash 2>&1 + + + # limbo custom actions required https://git.limbosolutions.com/kb/gitea/raw/branch/main + - name: Configure kubectl config + uses: ./.gitea/limbo_actions/kubectl-setup + with: + kube_server: ${{ secrets.HOSTING_KUBE_SERVER }} + kube_ca_base64: ${{ secrets.HOSTING_KUBE_CA_BASE64 }} + kube_token: ${{ secrets.HOSTING_KUBE_TOKEN }} + + - name: Deploy + shell: bash + env: + # cron jobs env + MARIADB_USER: ${{ secrets.MARIADB_USER }} + MARIADB_PASSWORD: ${{ secrets.MARIADB_PASSWORD }} + MARIADB_ROOT_PASSWORD: ${{ secrets.MARIADB_ROOT_PASSWORD }} + MARIADB_DATABASE: ${{ secrets.MARIADB_DATABASE }} + PBS_REPOSITORY: ${{ secrets.PBS_REPOSITORY }} + PBS_PASSWORD: ${{ secrets.PBS_PASSWORD }} + PBS_FINGERPRINT: ${{ secrets.PBS_FINGERPRINT }} + + + run: | + set -euo pipefail + + # ensure cleanup always runs + trap 'rm -f \ + deploy/app/.env.d/*' EXIT + + # setup secrets files + + echo "MARIADB_USER=${MARIADB_USER}" >> deploy/app/.env.d/nextcloud-mariadb.env + echo "MARIADB_PASSWORD=${MARIADB_USER}" >> deploy/app/.env.d/nextcloud-mariadb.env + echo "MARIADB_DATABASE=${MARIADB_DATABASE}" >> deploy/app/.env.d/nextcloud-mariadb.env + + echo "PBS_REPOSITORY=${PBS_REPOSITORY}" >> deploy/app/.env.d/pbs.env + echo "PBS_PASSWORD=${PBS_PASSWORD}" >> deploy/app/.env.d/pbs.env + echo "PBS_FINGERPRINT=${PBS_FINGERPRINT}" >> deploy/app/.env.d/pbs.env + + + # enforce secrets files security + chmod 600 deploy/app/.env.d/* + + # invoke deploy script + ops-scripts/apply-app.sh diff --git a/.gitignore b/.gitignore index 0c47e60..7ebd553 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ -**.env.** -.env -localSettings -archive -tmp/** +**.tmp** +**.cache** +**tmp** +**.cache** diff --git a/README.md b/README.md index 5cf4063..e87089d 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ Using [NextCloud](https://nextcloud.com/) - [preview generator](#preview-generator) - [repair tree](#repair-tree) - [delete file locks](#delete-file-locks) -- [Setup](#setup) -- [Requirements - infra](#requirements---infra) +- [Setup and Deploy](#setup-and-deploy) + - [App](#app) + - [Infra](#infra) - [mariadb database](#mariadb-database) ## command @@ -61,21 +62,45 @@ su -s /bin/bash www-data -c "php occ files:repair-tree" DELETE FROM oc_file_locks WHERE 1; ``` -## Setup +## Setup and Deploy -- deploy mariadb -- nextcloud helm chart +### App + +**Security context:** + +This script is intended to be executed only by low‑privilege deployment identities, such as the **continuous‑deploy** ServiceAccount or an application maintainer with equivalent permissions. ```bash ./ops-scripts/apply-app.sh ``` -## Requirements - infra +**Responsibilities:** + +- Database deployment +- Persistent Volume Claims (storage.limbosolutions.com) +- Nextcloud Helm chart deployment +- Backup job deployment + +**Requirements:** + +- [infra](#infra) + +### Infra + +**Security context:** +This script requires elevated cluster‑level permissions and must be executed only by platform maintainers, not by the continuous‑deploy identity. ```bash ./ops-scripts/apply-infra.sh ``` +**Responsibilities:** + +- Ingress controller deployment +- Persistent storage provisioning (storage.limbosolutions.com) +- services accounts: + - Continuous deploy - Deployment RBAC (ServiceAccount + Role + RoleBinding) + ## mariadb database **Connect to maria db:** diff --git a/deploy/app/.env.d/.gitignore b/deploy/app/.env.d/.gitignore new file mode 100644 index 0000000..9621539 --- /dev/null +++ b/deploy/app/.env.d/.gitignore @@ -0,0 +1,3 @@ +** +!.gitignore +!**.example \ No newline at end of file diff --git a/deploy/app/.env.d/nextcloud-mariadb.env.example b/deploy/app/.env.d/nextcloud-mariadb.env.example new file mode 100644 index 0000000..db35a83 --- /dev/null +++ b/deploy/app/.env.d/nextcloud-mariadb.env.example @@ -0,0 +1,4 @@ +MARIADB_USER=???? +MARIADB_PASSWORD=???? +MARIADB_ROOT_PASSWORD=??? +MARIADB_DATABASE=??? \ No newline at end of file diff --git a/deploy/app/.env.d/nextcloud.secrets.env.example b/deploy/app/.env.d/nextcloud.secrets.env.example new file mode 100644 index 0000000..8ce5d3a --- /dev/null +++ b/deploy/app/.env.d/nextcloud.secrets.env.example @@ -0,0 +1,3 @@ +NEXTCLOUD_HOST=???? +NEXTCLOUD_USERNAME=???? +NEXTCLOUD_PASSWORD=??? diff --git a/deploy/app/.env.d/pbs.env.example b/deploy/app/.env.d/pbs.env.example new file mode 100644 index 0000000..372cde9 --- /dev/null +++ b/deploy/app/.env.d/pbs.env.example @@ -0,0 +1,3 @@ +PBS_REPOSITORY=???? +PBS_PASSWORD=???? +PBS_FINGERPRINT=??? \ No newline at end of file diff --git a/deploy/app/.gitignore b/deploy/app/.gitignore new file mode 100644 index 0000000..a3002c6 --- /dev/null +++ b/deploy/app/.gitignore @@ -0,0 +1,2 @@ +**.local.** +**.private.** \ No newline at end of file diff --git a/deploy/app/backup-cronjob.yaml b/deploy/app/backups/backup-pbs-cronjob.yaml similarity index 100% rename from deploy/app/backup-cronjob.yaml rename to deploy/app/backups/backup-pbs-cronjob.yaml diff --git a/deploy/helm/values.yaml b/deploy/app/helm-values.yaml similarity index 56% rename from deploy/helm/values.yaml rename to deploy/app/helm-values.yaml index 9f7bf60..5c0ad50 100644 --- a/deploy/helm/values.yaml +++ b/deploy/app/helm-values.yaml @@ -6,10 +6,51 @@ image: replicaCount: 1 +livenessProbe: + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + +readinessProbe: + initialDelaySeconds: 60 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 12 + +# ------------------------------------------------------------------------------ +# Nextcloud maintenance mode & Kubernetes probes +# ------------------------------------------------------------------------------ +# When performing upgrades or running `occ upgrade`, Nextcloud may return 503 +# on /status.php until the database migration is complete. During this period, +# Kubernetes will repeatedly kill the pod because the readiness/liveness probes +# fail before the upgrade finishes. +# +# To prevent Kubernetes from terminating the pod during maintenance or upgrades, +# temporarily disable both probes. This allows the container to stay alive long +# enough for you to exec into it and run: +# +# php occ upgrade +# +# After the upgrade completes, re‑enable the probes. +# +# Example: +# +# livenessProbe: +# enabled: false +# +# readinessProbe: +# enabled: false +# ------------------------------------------------------------------------------ + + internalDatabase: enabled: false +mariadb: + enabled: false + externalDatabase: enabled: true type: mysql @@ -19,27 +60,15 @@ externalDatabase: database: "???" port: 3306 - -mariadb: - enabled: false - -service: - type: ClusterIP - port: 8080 - loadBalancerIP: "" - nodePort: - persistence: enabled: true accessMode: ReadWriteOnce size: 8Gi nextcloudData: - enabled: true - subPath: - annotations: {} accessMode: ReadWriteOnce size: 8Gi + resources: limits: cpu: "1" @@ -55,27 +84,6 @@ resources: cronjob: enabled: true - ## Cronjob sidecar resource requests and limits - ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ - ## - resources: {} - - # Allow configuration of lifecycle hooks - # ref: https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/ - lifecycle: {} - # postStartCommand: [] - # preStopCommand: [] - # Set securityContext parameters. For example, you may need to define runAsNonRoot directive - securityContext: {} - # runAsUser: 33 - # runAsGroup: 33 - # runAsNonRoot: true - # readOnlyRootFilesystem: true - - # The command the cronjob container executes. - command: - - /cron.sh - nextcloud: extraVolumes: - name: mf-documents @@ -139,48 +147,8 @@ nextcloud: $CONFIG = array ( 'maintenance_window_start' => 1, ); -# nextcloud: -# configs: -# logging.config.php: |- -# 'file', -# 'logfile' => 'nextcloud.log', -# 'loglevel' => 0, -# 'logdateformat' => 'F d, Y H:i:s' -# ); - - ingress: - enabled: true - className: traefik - annotations: - kubernetes.io/ingress.class: traefik - cert-manager.io/cluster-issuer: "letsencrypt-prod" - traefik.ingress.kubernetes.io/router.entrypoints: websecure,public-https - - # traefik.public-https.redirect.permanent: 'true' - # traefik.public-https.redirect.regex: 'https://(.*)/.well-known/(?:card|cal)dav' - # traefik.public-https.redirect.replacement: 'https://$$1/remote.php/dav' - # traefik.public-https.headers.STSPreload: 'true' - # traefik.public-https.headers.STSSeconds: '15552000' - # traefik.public-https.routers.nextcloud.middlewares: 'nextcloudHeader' - - # traefik.web-secure.routers.nextcloud.middlewares: 'nextcloudHeader' - # traefik.websecure.headers.STSPreload: 'true' - # traefik.websecure.headers.STSSeconds: '15552000' - # traefik.websecure.redirect.permanent: 'true' - # traefik.websecure.redirect.regex: 'https://(.*)/.well-known/(?:card|cal)dav' - # traefik.websecure.redirect.replacement: 'https://$$1/remote.php/dav' - # hosts: - # - host: cloud.limbosolutions.com - # paths: - # - path: / - # pathType: Prefix - tls: - - secretName: cloud-limbosolutions-com-secret-tls - hosts: - - "cloud.limbosolutions.com" + enabled: false diff --git a/deploy/app/kustomization.yaml b/deploy/app/kustomization.yaml index d6d19a7..021782c 100644 --- a/deploy/app/kustomization.yaml +++ b/deploy/app/kustomization.yaml @@ -4,16 +4,16 @@ kind: Kustomization secretGenerator: - name: nextcloud-mariadb envs: - - ./.env.d/nextcloud-mariadb.secrets + - ./.env.d/nextcloud-mariadb.env - name: backup-secret envs: - - ./.env.d/pbs.secrets + - ./.env.d/pbs.env resources: - - ./persistent-volumes-claims.yaml + - ./storage-limbosolutions-com/pvc.yaml - ./mariadb-deploy.yaml - - ./backup-cronjob.yaml + - ./backups/backup-pbs-cronjob.yaml generatorOptions: disableNameSuffixHash: true diff --git a/deploy/app/mariadb-deploy.yaml b/deploy/app/mariadb-deploy.yaml index c4956f1..7fc5bba 100644 --- a/deploy/app/mariadb-deploy.yaml +++ b/deploy/app/mariadb-deploy.yaml @@ -3,7 +3,6 @@ apiVersion: v1 kind: Service metadata: name: nextcloud-mariadb - namespace: cloud-limbosolutions-com spec: clusterIP: None selector: diff --git a/deploy/app/persistent-volumes-claims.yaml b/deploy/app/storage-limbosolutions-com/pvc.yaml similarity index 88% rename from deploy/app/persistent-volumes-claims.yaml rename to deploy/app/storage-limbosolutions-com/pvc.yaml index 71425d5..64ba89a 100644 --- a/deploy/app/persistent-volumes-claims.yaml +++ b/deploy/app/storage-limbosolutions-com/pvc.yaml @@ -3,7 +3,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mf-documents-limbosolutions-com - namespace: cloud-limbosolutions-com spec: storageClassName: storage-limbosolutions-com volumeName: mf-documents-limbosolutions-com-nextcloud @@ -18,7 +17,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: media-music-limbosolutions-com - namespace: cloud-limbosolutions-com spec: storageClassName: storage-limbosolutions-com volumeName: media-music-limbosolutions-com-nextcloud @@ -34,7 +32,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: media-videos-limbosolutions-com - namespace: cloud-limbosolutions-com spec: storageClassName: storage-limbosolutions-com volumeName: media-videos-limbosolutions-com-nextcloud @@ -50,7 +47,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: media-gaming-limbosolutions-com - namespace: cloud-limbosolutions-com spec: storageClassName: storage-limbosolutions-com volumeName: media-gaming-limbosolutions-com-nextcloud @@ -66,7 +62,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: it-storage-limbosolutions-com - namespace: cloud-limbosolutions-com spec: storageClassName: storage-limbosolutions-com volumeName: it-storage-limbosolutions-com-nextcloud @@ -81,10 +76,11 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mf-gallery-limbosolutions-com - namespace: cloud-limbosolutions-com spec: storageClassName: storage-limbosolutions-com volumeName: mf-gallery-limbosolutions-com-nextcloud + + accessModes: - ReadWriteMany resources: @@ -97,7 +93,6 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mf-nextcloud-limbosolutions-com - namespace: cloud-limbosolutions-com spec: storageClassName: storage-limbosolutions-com volumeName: mf-nextcloud-limbosolutions-com-nextcloud diff --git a/deploy/helm/.gitignore b/deploy/helm/.gitignore deleted file mode 100644 index ecf0016..0000000 --- a/deploy/helm/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**.local**.yaml \ No newline at end of file diff --git a/deploy/infra/cd-serviceaccount.yaml b/deploy/infra/cd-serviceaccount.yaml new file mode 100644 index 0000000..46cb133 --- /dev/null +++ b/deploy/infra/cd-serviceaccount.yaml @@ -0,0 +1,50 @@ + +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: continuous-deploy +rules: +- apiGroups: [""] + resources: ["pods", "services", "secrets", "configmaps", "persistentvolumeclaims"] + verbs: ["get", "watch", "list", "create", "update", "patch", "delete"] + +- apiGroups: ["apps"] + resources: ["deployments", "statefulsets"] + verbs: ["get", "watch", "list", "create", "update", "patch", "delete"] + +- apiGroups: ["batch"] + resources: ["cronjobs", "jobs"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: continuous-deploy + +--- + +apiVersion: v1 +kind: Secret +metadata: + name: continuous-deploy + annotations: + kubernetes.io/service-account.name: continuous-deploy +type: kubernetes.io/service-account-token + +--- + + +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: continuous-deploy +subjects: +- kind: ServiceAccount + name: continuous-deploy +roleRef: + kind: Role + name: continuous-deploy + apiGroup: rbac.authorization.k8s.io + diff --git a/deploy/infra/ingress.yaml b/deploy/infra/ingress.yaml new file mode 100644 index 0000000..c39914c --- /dev/null +++ b/deploy/infra/ingress.yaml @@ -0,0 +1,26 @@ + +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: cloud-limbosolutions-com + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + kubernetes.io/ingress.class: traefik + traefik.ingress.kubernetes.io/router.entrypoints: websecure, public-https +spec: + ingressClassName: traefik + rules: + - host: cloud.limbosolutions.com + http: + paths: + - backend: + service: + name: nextcloud + port: + number: 8080 + path: / + pathType: Prefix + tls: + - secretName: cloud-limbosolutions-com-secret-tls + hosts: + - cloud.limbosolutions.com diff --git a/deploy/infra/kustomization.yaml b/deploy/infra/kustomization.yaml index 8ccaf3c..4f4f7a1 100644 --- a/deploy/infra/kustomization.yaml +++ b/deploy/infra/kustomization.yaml @@ -2,6 +2,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - namespace.yaml - + - cd-serviceaccount.yaml + - ingress.yaml + - storage-limbosolutions-com/pv.yaml generatorOptions: disableNameSuffixHash: true \ No newline at end of file diff --git a/deploy/hosting/persistent-volumes.yaml b/deploy/infra/storage-limbosolutions-com/pv.yaml similarity index 100% rename from deploy/hosting/persistent-volumes.yaml rename to deploy/infra/storage-limbosolutions-com/pv.yaml diff --git a/ops-scripts/apply-app.sh b/ops-scripts/apply-app.sh index a08fc69..ec046f8 100755 --- a/ops-scripts/apply-app.sh +++ b/ops-scripts/apply-app.sh @@ -4,12 +4,32 @@ echo "Executing app deploy." kubectl kustomize deploy/app | kubectl apply -f - +load_env_file() { + local file="$1" -helm repo add nextcloud https://nextcloud.github.io/helm/ 2>/dev/null || true -helm repo update nextcloud + if [ -f "$file" ]; then + echo "Loading environment variables from: $file" + set -a + . "$file" + set +a + else + echo "Skipping missing env file: $file" + fi +} +helm repo add nextcloud https://nextcloud.github.io/helm/ --force-update + +load_env_file "deploy/app/.env.d/nextcloud-mariadb.env" +load_env_file "deploy/app/.env.d/nextcloud.env" + + helm upgrade --install nextcloud nextcloud/nextcloud \ - --values ./deploy/helm/values.yaml \ - --values ./deploy/helm/values.local.yaml \ + --values ./deploy/app/helm-values.yaml \ + --set externalDatabase.user=${MARIADB_USER} \ + --set externalDatabase.password=${MARIADB_PASSWORD} \ + --set externalDatabase.database=${MARIADB_DATABASE} \ + --set nextcloud.host=${NEXTCLOUD_HOST} \ + --set nextcloud.username=${NEXTCLOUD_USERNAME} \ + --set nextcloud.password=${NEXTCLOUD_PASSWORD} \ --namespace cloud-limbosolutions-com \ No newline at end of file diff --git a/ops-scripts/apply-infra.sh b/ops-scripts/apply-infra.sh index e74bf60..91c06ec 100755 --- a/ops-scripts/apply-infra.sh +++ b/ops-scripts/apply-infra.sh @@ -2,6 +2,6 @@ set -e echo "Executing infra deploy." -kubectl kustomize deploy/infra | kubectl apply -f - +kubectl kustomize deploy/infra | kubectl -n cloud-limbosolutions-com apply -f -