diff --git a/projects/maison/architecture.d2 b/projects/maison/architecture.d2 index 4eacfc87..2b23b9ba 100644 --- a/projects/maison/architecture.d2 +++ b/projects/maison/architecture.d2 @@ -280,17 +280,16 @@ maison: { # - n8n n8n: { - class: [application; undeployed] + class: [application] icon: assets/icons/apps/n8n.svg link: https://n8n.io/ tooltip: Secure and AI-native workflow automation tool for technical people. } n8n <- _.system.Traefik: { - class: [undeployed] source-arrowhead: HTTP (5678) } n8n <- _.system.Tailscale: { - class: [connect-vpn; undeployed] + class: [connect-vpn] source-arrowhead: HTTP (5678) } diff --git a/projects/maison/assets/architecture.svg b/projects/maison/assets/architecture.svg index 23d5420a..01b05360 100644 --- a/projects/maison/assets/architecture.svg +++ b/projects/maison/assets/architecture.svg @@ -1,13 +1,13 @@ - @@ -923,7 +923,7 @@ of the maison platform in a visual way
with all the components and how they
interact with each other. \

-INTERNETLOCALNET☸ kubernetes.maison.chezmoi.shsystemmultimedialife-managementautomationothersCert-ManagerCert-Manager is a Kubernetes controller that automates the management and issuance of TLS certificates.ExternalDNSExternalDNS is a Kubernetes controller that configures DNS resources.TailscaleTailScale is a mesh VPN that makes it easy to connect your devices, wherever they are.FluxCDOpen and extensible continuous delivery solution for KubernetesTraefikTraefik is a modern HTTP reverse proxy and load balancer.External-SecretExternal-Secret is a Kubernetes controller that allows you to use external secret management systems.CloudNativePGCloudNativePG is a comprehensive platform designed to seamlessly manage PostgreSQL databases within Kubernetes environments.FileflowsFileFlows is a file processing application that can execute actions against a file in a tree flow structure.JellyfinJellyfin is the volunteer-built media solution that puts you in control of your media.JellyseerrFree and open source software application for managing requests for Jellyfin libraries.Actual-BudgetActual Budget is a personal finance app that helps you track your spending and save money.MealieIntuitive and easy to use recipe management app.DocspellDocspell is a personal document management system.n8nSecure and AI-native workflow automation tool for technical people.BudibaseA modern, open source low-code platform for building modern internal applications in minutes.LinkdingLinkding is a self-hosted bookmarking and link aggregation service. HTTPS (443) HTTPS (443) VPN HTTPS (443) HTTP (80)HTTPS (443) HTTP (19200) HTTP (19200) HTTP (8096) HTTP (8096) HTTP (8096) HTTP (5055) HTTP (5055) HTTP (5006) HTTP (5006) HTTP (9000) HTTP (9000) HTTP (7880) HTTP (7880) HTTP (5678) HTTP (5678) HTTP (8080) HTTP (8080) HTTP (9090) HTTP (9090)Cert-Manager is a Kubernetes controller that automates the management and issuance of TLS certificates. +INTERNETLOCALNET☸ kubernetes.maison.chezmoi.shsystemmultimedialife-managementautomationothersCert-ManagerCert-Manager is a Kubernetes controller that automates the management and issuance of TLS certificates.ExternalDNSExternalDNS is a Kubernetes controller that configures DNS resources.TailscaleTailScale is a mesh VPN that makes it easy to connect your devices, wherever they are.FluxCDOpen and extensible continuous delivery solution for KubernetesTraefikTraefik is a modern HTTP reverse proxy and load balancer.External-SecretExternal-Secret is a Kubernetes controller that allows you to use external secret management systems.CloudNativePGCloudNativePG is a comprehensive platform designed to seamlessly manage PostgreSQL databases within Kubernetes environments.FileflowsFileFlows is a file processing application that can execute actions against a file in a tree flow structure.JellyfinJellyfin is the volunteer-built media solution that puts you in control of your media.JellyseerrFree and open source software application for managing requests for Jellyfin libraries.Actual-BudgetActual Budget is a personal finance app that helps you track your spending and save money.MealieIntuitive and easy to use recipe management app.DocspellDocspell is a personal document management system.n8nSecure and AI-native workflow automation tool for technical people.BudibaseA modern, open source low-code platform for building modern internal applications in minutes.LinkdingLinkding is a self-hosted bookmarking and link aggregation service. HTTPS (443) HTTPS (443) VPN HTTPS (443) HTTP (80)HTTPS (443) HTTP (19200) HTTP (19200) HTTP (8096) HTTP (8096) HTTP (8096) HTTP (5055) HTTP (5055) HTTP (5006) HTTP (5006) HTTP (9000) HTTP (9000) HTTP (7880) HTTP (7880) HTTP (5678) HTTP (5678) HTTP (8080) HTTP (8080) HTTP (9090) HTTP (9090)Cert-Manager is a Kubernetes controller that automates the management and issuance of TLS certificates. @@ -1323,7 +1323,7 @@ interact with each other. - + diff --git a/projects/maison/src/apps/kustomization.yaml b/projects/maison/src/apps/kustomization.yaml index af7be8a8..cb5ae7d9 100644 --- a/projects/maison/src/apps/kustomization.yaml +++ b/projects/maison/src/apps/kustomization.yaml @@ -10,3 +10,4 @@ resources: - jellyseerr.yaml - linkding.yaml - mealie.yaml + - n8n.yaml diff --git a/projects/maison/src/apps/n8n.yaml b/projects/maison/src/apps/n8n.yaml new file mode 100644 index 00000000..8b0c7467 --- /dev/null +++ b/projects/maison/src/apps/n8n.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: kustomize.toolkit.fluxcd.io/v1 +kind: Kustomization +metadata: + name: n8n +spec: + interval: 12h0m0s + timeout: 30s + retryInterval: 0s + + sourceRef: + kind: GitRepository + name: flux-system + namespace: flux-system + path: ./projects/maison/src/apps/n8n + + prune: true + wait: true diff --git a/projects/maison/src/apps/n8n/n8n.database.yaml b/projects/maison/src/apps/n8n/n8n.database.yaml new file mode 100644 index 00000000..3a941283 --- /dev/null +++ b/projects/maison/src/apps/n8n/n8n.database.yaml @@ -0,0 +1,21 @@ +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: n8n + namespace: n8n + labels: + app.kubernetes.io/component: database + app.kubernetes.io/instance: n8n-database + app.kubernetes.io/name: n8n + app.kubernetes.io/part-of: n8n +spec: + bootstrap: + initdb: + database: n8n + owner: n8n + description: PostgreSQL database dedicated to n8n + instances: 1 + + storage: + size: 5Gi diff --git a/projects/maison/src/apps/n8n/n8n.deployment.yaml b/projects/maison/src/apps/n8n/n8n.deployment.yaml new file mode 100644 index 00000000..56b485f1 --- /dev/null +++ b/projects/maison/src/apps/n8n/n8n.deployment.yaml @@ -0,0 +1,247 @@ +--- +# trunk-ignore(checkov/CKV_K8S_11): DO NOT SET the CPU limit +# trunk-ignore(checkov/CKV_K8S_15,checkov/CKV_K8S_43): Not aggreed with theses policies about the ImagePullPolicy=Always and digest verification. +# trunk-ignore(checkov/CKV2_K8S_6) +apiVersion: apps/v1 +kind: Deployment +metadata: + name: n8n + namespace: n8n + labels: + app.kubernetes.io/name: n8n + app.kubernetes.io/instance: n8n + app.kubernetes.io/part-of: n8n +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: n8n + app.kubernetes.io/instance: n8n + template: + metadata: + labels: + app.kubernetes.io/name: n8n + app.kubernetes.io/instance: n8n + spec: + automountServiceAccountToken: false + containers: + # trunk-ignore(trivy/KSV011): DO NOT SET the CPU limit + - name: n8n + env: + - name: DB_POSTGRESDB_DATABASE_FILE + value: /run/secrets/n8n/postgres/dbname + - name: DB_POSTGRESDB_HOST_FILE + value: /run/secrets/n8n/postgres/host + - name: DB_POSTGRESDB_PASSWORD_FILE + value: /run/secrets/n8n/postgres/password + - name: DB_POSTGRESDB_PORT_FILE + value: /run/secrets/n8n/postgres/port + - name: DB_POSTGRESDB_USER_FILE + value: /run/secrets/n8n/postgres/user + - name: DB_TYPE + value: postgresdb + - name: N8N_DIAGNOSTICS_ENABLED + value: "false" + - name: N8N_EDITOR_BASE_URL + value: https://n8n.chezmoi.sh + - name: N8N_EMAIL_MODE + value: smtp + - name: N8N_HIDE_USAGE_PAGE + value: "true" + - name: N8N_HIRING_BANNER_ENABLED + value: "false" + - name: N8N_HOST + value: n8n.chezmoi.sh + - name: N8N_LISTEN_ADDRESS + value: 0.0.0.0 + - name: N8N_PORT + value: "5678" + - name: N8N_PROTOCOL + value: http + - name: N8N_SMTP_HOST_FILE + value: /run/secrets/n8n/smtp/aws_ses_host + - name: N8N_SMTP_PASS_FILE + value: /run/secrets/n8n/smtp/aws_ses_password + - name: N8N_SMTP_PORT_FILE + value: /run/secrets/n8n/smtp/aws_ses_port + - name: N8N_SMTP_SENDER_FILE + value: /run/secrets/n8n/smtp/aws_ses_sender + - name: N8N_SMTP_USER_FILE + value: /run/secrets/n8n/smtp/aws_ses_username + - name: N8N_TEMPLATES_ENABLED + value: "true" + - name: N8N_USER_FOLDER + value: /opt/n8n/data + - name: TZ + value: Europe/Paris + - name: VUE_APP_URL_BASE_API + value: https://n8n.chezmoi.sh + image: docker.n8n.io/n8nio/n8n:1.72.1 + livenessProbe: + httpGet: + path: / + port: http + ports: + - name: http + containerPort: 5678 + protocol: TCP + readinessProbe: + httpGet: + path: / + port: http + resources: + requests: + cpu: 100m + memory: 1Gi + limits: + memory: 1Gi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 16727 + runAsGroup: 16727 + seccompProfile: + type: RuntimeDefault + volumeMounts: + - name: cnpg-config + mountPath: /run/secrets/n8n/postgress + readOnly: true + - name: smtp-config + mountPath: /run/secrets/n8n/smtp + readOnly: true + - name: n8n-persistent + mountPath: /opt/n8n + - name: n8n-cache + mountPath: /opt/n8n/.cache + securityContext: + runAsNonRoot: true + runAsUser: 16727 + runAsGroup: 16727 + fsGroup: 16727 + volumes: + - name: cnpg-config + secret: + secretName: n8n-app + - name: smtp-config + secret: + secretName: n8n-smtp-credentials + - name: n8n-persistent + persistentVolumeClaim: + claimName: n8n-persistent + - name: n8n-cache + emptyDir: {} +--- +apiVersion: v1 +kind: Service +metadata: + name: n8n + namespace: n8n + labels: + app.kubernetes.io/name: n8n + app.kubernetes.io/instance: n8n + app.kubernetes.io/part-of: n8n +spec: + selector: + app.kubernetes.io/name: n8n + app.kubernetes.io/instance: n8n + ports: + - name: http + port: 80 + targetPort: http + protocol: TCP +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: n8n-persistent + namespace: n8n + labels: + app.kubernetes.io/name: n8n + app.kubernetes.io/instance: n8n + app.kubernetes.io/part-of: n8n +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + labels: + app.kubernetes.io/name: n8n + app.kubernetes.io/instance: n8n + app.kubernetes.io/part-of: n8n + name: n8n-smtp + namespace: n8n +spec: + data: + - remoteRef: + key: apps-n8n-aws-ses + property: username + secretKey: aws-ses-username + - remoteRef: + key: apps-n8n-aws-ses + property: password + secretKey: aws-ses-password + secretStoreRef: + kind: ClusterSecretStore + name: kubernetes.maison.chezmoi.sh + target: + name: n8n-smtp-credentials + template: + type: Opaque + engineVersion: v2 + data: + aws_ses_host: email-smtp.us-east-1.amazonaws.com + aws_ses_username: "{{ .aws-ses-username }}" + aws_ses_password: "{{ .aws-ses-password }}" + aws_ses_port: "587" + aws_ses_sender: n8n +# --- +# apiVersion: networking.k8s.io/v1 +# kind: NetworkPolicy +# metadata: +# name: n8n +# namespace: n8n +# labels: +# app.kubernetes.io/name: n8n +# app.kubernetes.io/instance: n8n +# app.kubernetes.io/part-of: n8n +# spec: +# podSelector: +# matchLabels: +# app.kubernetes.io/name: n8n +# app.kubernetes.io/instance: n8n +# policyTypes: +# - Ingress +# - Egress +# ingress: +# - from: +# - podSelector: {} +# - from: +# - namespaceSelector: +# matchLabels: +# kubernetes.io/metadata.name: traefik-system +# egress: +# - to: +# - namespaceSelector: {} +# podSelector: +# matchLabels: +# k8s-app: kube-dns +# ports: +# - port: 53 +# protocol: UDP +# - to: +# - ipBlock: +# cidr: 0.0.0.0/0 +# ports: +# - port: 443 +# - to: +# - podSelector: {} diff --git a/projects/maison/src/apps/n8n/n8n.httproute.yaml b/projects/maison/src/apps/n8n/n8n.httproute.yaml new file mode 100644 index 00000000..4ba7abd0 --- /dev/null +++ b/projects/maison/src/apps/n8n/n8n.httproute.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: n8n-websecure + namespace: n8n +spec: + parentRefs: + - name: default + namespace: default + hostnames: + - n8n.chezmoi.sh + rules: + - backendRefs: + - name: n8n + port: 80 diff --git a/projects/maison/src/apps/n8n/n8n.vpn.yaml b/projects/maison/src/apps/n8n/n8n.vpn.yaml new file mode 100644 index 00000000..27645d3a --- /dev/null +++ b/projects/maison/src/apps/n8n/n8n.vpn.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: n8n-tailscale + namespace: n8n +spec: + defaultBackend: + service: + name: n8n + port: + number: 80 + ingressClassName: tailscale + tls: + - hosts: + - n8n diff --git a/projects/maison/src/apps/n8n/namespace.yaml b/projects/maison/src/apps/n8n/namespace.yaml new file mode 100644 index 00000000..95a26b0b --- /dev/null +++ b/projects/maison/src/apps/n8n/namespace.yaml @@ -0,0 +1,5 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: n8n diff --git a/projects/maison/src/infrastructure/crossplane/aws.iam.n8n.yaml b/projects/maison/src/infrastructure/crossplane/aws.iam.n8n.yaml new file mode 100644 index 00000000..a2ac3214 --- /dev/null +++ b/projects/maison/src/infrastructure/crossplane/aws.iam.n8n.yaml @@ -0,0 +1,98 @@ +# AWS User for Authelia +# +# Description: +# These resources will create an AWS IAM User with the necessary permissions +# to send emails from the domain chezmoi.sh. +--- +apiVersion: iam.aws.upbound.io/v1beta1 +kind: Policy +metadata: + annotations: + crossplane.io/external-name: AutheliaSESSender + name: maison.chezmoi.sh-amazonses-n8n + namespace: kubevault-kvstore +spec: + forProvider: + path: /maison.chezmoi.sh/ + policy: | + { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "AllowSendingEmails", + "Effect": "Allow", + "Action": [ + "ses:SendEmail", + "ses:SendRawEmail" + ], + "Resource": "arn:aws:ses:us-east-1:*:identity/*" + } + ] + } +--- +apiVersion: iam.aws.upbound.io/v1beta1 +kind: User +metadata: + annotations: + crossplane.io/external-name: n8n + name: maison.chezmoi.sh-amazonses-n8n + namespace: kubevault-kvstore +spec: + forProvider: + path: /maison.chezmoi.sh/ +--- +apiVersion: iam.aws.upbound.io/v1beta1 +kind: UserPolicyAttachment +metadata: + name: maison.chezmoi.sh-amazonses-n8n + namespace: kubevault-kvstore +spec: + forProvider: + policyArnRef: + name: maison.chezmoi.sh-amazonses-n8n + userRef: + name: maison.chezmoi.sh-amazonses-n8n +--- +apiVersion: iam.aws.upbound.io/v1beta1 +kind: AccessKey +metadata: + name: maison.chezmoi.sh-amazonses-n8n + namespace: kubevault-kvstore +spec: + forProvider: + userRef: + name: maison.chezmoi.sh-amazonses-n8n + writeConnectionSecretToRef: + name: apps-n8n-aws-ses + namespace: kubevault-kvstore +--- +# trunk-ignore-all(trivy/KSV113,checkov/CKV2_K8S_5): accessing to this secret is required +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: kubevault:kubernetes.maison.chezmoi.sh:n8n-ses + namespace: kubevault-kvstore +rules: + - apiGroups: + - "" + resources: + - secrets + resourceNames: + - apps-n8n-aws-ses + verbs: + - get + - list + - watch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: kubevault:kubernetes.maison.chezmoi.sh:n8n-ses + namespace: kubevault-kvstore +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: kubevault:kubernetes.maison.chezmoi.sh:n8n-ses +subjects: + - kind: ServiceAccount + name: kubernetes.maison.chezmoi.sh diff --git a/projects/maison/src/infrastructure/crossplane/kustomization.yaml b/projects/maison/src/infrastructure/crossplane/kustomization.yaml index b111fc2f..d1c10c6e 100644 --- a/projects/maison/src/infrastructure/crossplane/kustomization.yaml +++ b/projects/maison/src/infrastructure/crossplane/kustomization.yaml @@ -8,3 +8,6 @@ resources: # Cloudflare credentials - cloudflare.iam.external-dns.yaml - cloudflare.iam.cert-manager.yaml + + # AWS IAM roles for n8n + - aws.iam.n8n.yaml