Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aabedb0f3f |
-20
@@ -1,20 +0,0 @@
|
|||||||
# This file is maintained automatically by "tofu init".
|
|
||||||
# Manual edits may be lost in future updates.
|
|
||||||
|
|
||||||
provider "registry.opentofu.org/hashicorp/vault" {
|
|
||||||
version = "4.4.0"
|
|
||||||
constraints = "4.4.0"
|
|
||||||
hashes = [
|
|
||||||
"h1:IhKDv0pTgpy89K3QYmDX872H75Wl7kZKR2scUQynuiA=",
|
|
||||||
"zh:0309ea8f81386e17ab13c06c5991ca959708c55c815b0cfba2bbcd865e0d606e",
|
|
||||||
"zh:40e56199ccd266bffa216e8ebbcdc2e29b6ef5145b39377be766e763cac759c8",
|
|
||||||
"zh:6fad1f073bd2e53e34736e000f98db581137e153ac80bbb5c4f1a1e38b46a1d2",
|
|
||||||
"zh:74564fd4759decccf7f3c952aa2feba1012f103a66ec354aa3b3292a2f1b2412",
|
|
||||||
"zh:7aae012c1a43e6e5dae6f608ec0f08cdb3f95fa121a32e413fe7ee37cb99947f",
|
|
||||||
"zh:7c83f508e164844b1dd9bafe9de0fe60c7be7b55a02e704a6e2f50cff38b7d96",
|
|
||||||
"zh:873a42322b68d9fba4a38217b97ee04a1eb617e811d7f9954016f5c3eb6cb0bc",
|
|
||||||
"zh:9db2b13472cf91a5f18f0a7c6ae532277c05b0980d87f492341426b981679f7b",
|
|
||||||
"zh:ac1cbd2926265db80efe3f1814bed82901f7d8a7d4e5b1e22592e1eef234b1c7",
|
|
||||||
"zh:f465a955cc96f640e7426a648ba672c169a4a2959bad6146fe61583d67642561",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
# minio-values.yaml - Configuration MinIO pour k3s
|
|
||||||
# À placer dans factory/argocd/apps/minio/
|
|
||||||
|
|
||||||
apiVersion: argoproj.io/v1alpha1
|
|
||||||
kind: Application
|
|
||||||
metadata:
|
|
||||||
name: minio
|
|
||||||
namespace: argocd
|
|
||||||
spec:
|
|
||||||
project: default
|
|
||||||
source:
|
|
||||||
repoURL: https://charts.bitnami.com/bitnami
|
|
||||||
chart: minio
|
|
||||||
targetRevision: 12.13.0
|
|
||||||
helm:
|
|
||||||
values: |
|
|
||||||
global:
|
|
||||||
storageClass: longhorn
|
|
||||||
|
|
||||||
auth:
|
|
||||||
rootUser: kadans-admin
|
|
||||||
rootPassword: "${MINIO_PASSWORD}" # À définir dans Vault
|
|
||||||
|
|
||||||
mode: standalone
|
|
||||||
|
|
||||||
persistence:
|
|
||||||
enabled: true
|
|
||||||
size: 50Gi
|
|
||||||
storageClass: longhorn
|
|
||||||
|
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
memory: 512Mi
|
|
||||||
cpu: 250m
|
|
||||||
limits:
|
|
||||||
memory: 1Gi
|
|
||||||
cpu: 500m
|
|
||||||
|
|
||||||
service:
|
|
||||||
type: ClusterIP
|
|
||||||
port: 9000
|
|
||||||
|
|
||||||
ingress:
|
|
||||||
enabled: true
|
|
||||||
hostname: minio.arcodange.lab
|
|
||||||
annotations:
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
traefik.ingress.kubernetes.io/router.tls: "true"
|
|
||||||
tls: true
|
|
||||||
|
|
||||||
buckets:
|
|
||||||
- name: kadans-videos
|
|
||||||
policy: none
|
|
||||||
purge: false
|
|
||||||
- name: kadans-thumbnails
|
|
||||||
policy: none
|
|
||||||
purge: false
|
|
||||||
|
|
||||||
destination:
|
|
||||||
server: https://kubernetes.default.svc
|
|
||||||
namespace: storage
|
|
||||||
|
|
||||||
syncPolicy:
|
|
||||||
automated:
|
|
||||||
prune: true
|
|
||||||
selfHeal: true
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
# ADR 20260407: CI/CD Architecture with ArgoCD, Gitea, and Vault
|
|
||||||
|
|
||||||
## Status
|
|
||||||
Proposed
|
|
||||||
|
|
||||||
## Context
|
|
||||||
The home lab requires a secure and automated CI/CD pipeline to deploy applications to the k3s cluster. The pipeline must integrate with:
|
|
||||||
- **Gitea**: For Git repository management and CI runners.
|
|
||||||
- **ArgoCD**: For GitOps-based continuous deployment.
|
|
||||||
- **Vault**: For secrets management and OIDC authentication.
|
|
||||||
- **Gitea Act Runner**: For executing CI jobs.
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
We will implement a **GitOps-driven CI/CD pipeline** with the following components:
|
|
||||||
|
|
||||||
### 1. Gitea OIDC Authentication with Vault
|
|
||||||
- Gitea is registered as an OIDC application in Vault.
|
|
||||||
- Vault issues short-lived tokens for Gitea users.
|
|
||||||
- The `gitea_oidc_auth.yml` playbook automates this setup using Playwright and OpenTofu.
|
|
||||||
- **OIDC Workflow**:
|
|
||||||
1. The `oidc_jwt_token.sh` script (base64-encoded in `secrets.vault_oauth__sh_b64`) handles the OIDC flow.
|
|
||||||
2. Gitea Act Runner executes the script to obtain an ID token from Gitea.
|
|
||||||
3. The ID token is used to authenticate with Vault and retrieve secrets.
|
|
||||||
|
|
||||||
### 2. Gitea Act Runner
|
|
||||||
- Deployed on `pi1` and `pi3` (not on the Gitea host, which is `pi2`).
|
|
||||||
- Uses Docker-in-Docker for job execution.
|
|
||||||
- **Custom Runner Image (`ubuntu-latest-ca`)**: Required due to the self-signed `.lab` domain. The custom image includes the local CA certificate to trust the Gitea instance (`gitea.arcodange.lab`).
|
|
||||||
- Managed via Docker Compose (`03_cicd.yml`).
|
|
||||||
|
|
||||||
### 3. ArgoCD
|
|
||||||
- Deployed on the k3s cluster (via HelmChart in `/var/lib/rancher/k3s/server/manifests/argocd.yaml`).
|
|
||||||
- Uses Gitea as the source of truth for GitOps.
|
|
||||||
- Synchronizes the `factory` repository to deploy applications.
|
|
||||||
- Configured with Traefik for TLS termination.
|
|
||||||
|
|
||||||
### 4. Vault Secrets Operator
|
|
||||||
- Deployed in the `tools` namespace.
|
|
||||||
- Manages secrets for applications deployed via ArgoCD.
|
|
||||||
- Integrates with Gitea OIDC for authentication.
|
|
||||||
- **Helm Chart Integration**:
|
|
||||||
- `VaultAuth`: Authenticates with Vault using Kubernetes service accounts.
|
|
||||||
- `VaultStaticSecret`: Retrieves static secrets (e.g., `kvv2/webapp/config`).
|
|
||||||
- `VaultDynamicSecret`: Generates dynamic secrets (e.g., PostgreSQL credentials).
|
|
||||||
|
|
||||||
### 5. Security
|
|
||||||
- **TLS**: Traefik terminates TLS using Let's Encrypt.
|
|
||||||
- **OIDC**: Gitea authentication via Vault.
|
|
||||||
- **Secrets**: Stored in Vault, injected via the Vault Secrets Operator.
|
|
||||||
|
|
||||||
## Architecture Diagram
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
|
||||||
graph TD
|
|
||||||
%% Styles
|
|
||||||
classDef gitea fill:#ffcc99,stroke:#cc9966,color:#333;
|
|
||||||
classDef argocd fill:#99ffcc,stroke:#66cc99,color:#333;
|
|
||||||
classDef vault fill:#ccccff,stroke:#6666cc,color:#333;
|
|
||||||
classDef k3s fill:#ff9999,stroke:#cc0000,color:#333;
|
|
||||||
classDef runner fill:#ffff99,stroke:#cccc00,color:#333;
|
|
||||||
|
|
||||||
%% Components
|
|
||||||
Gitea["Gitea (pi2)"]:::gitea
|
|
||||||
ArgoCD["ArgoCD (k3s)"]:::argocd
|
|
||||||
Vault["Vault (k3s/tools)"]:::vault
|
|
||||||
Runner1["Gitea Act Runner (pi1)"]:::runner
|
|
||||||
Runner2["Gitea Act Runner (pi3)"]:::runner
|
|
||||||
VaultOperator["Vault Secrets Operator (k3s/tools)"]:::vault
|
|
||||||
k3s["k3s Cluster"]:::k3s
|
|
||||||
|
|
||||||
%% Workflow
|
|
||||||
Gitea -->|OIDC Auth| Vault
|
|
||||||
Gitea -->|Trigger CI| Runner1
|
|
||||||
Gitea -->|Trigger CI| Runner2
|
|
||||||
Runner1 -->|Deploy to| k3s
|
|
||||||
Runner2 -->|Deploy to| k3s
|
|
||||||
ArgoCD -->|GitOps Sync| Gitea
|
|
||||||
ArgoCD -->|Deploy Apps| k3s
|
|
||||||
VaultOperator -->|Inject Secrets| k3s
|
|
||||||
Vault -->|Secrets| VaultOperator
|
|
||||||
|
|
||||||
%% Annotations
|
|
||||||
linkStyle 0,1,2,3,4,5,6,7 stroke:#999,stroke-width:1px;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
### Positive
|
|
||||||
- **Automated Deployments**: ArgoCD ensures the cluster state matches Git.
|
|
||||||
- **Secure Secrets**: Vault centralizes secret management.
|
|
||||||
- **Scalable CI**: Gitea Act Runners can be added to any host.
|
|
||||||
- **OIDC Integration**: Secure authentication via Vault.
|
|
||||||
|
|
||||||
### Negative
|
|
||||||
- **Complexity**: Multiple moving parts (Gitea, ArgoCD, Vault).
|
|
||||||
- **Dependency on Vault**: If Vault fails, CI/CD may be disrupted.
|
|
||||||
- **Learning Curve**: Requires familiarity with GitOps and Vault.
|
|
||||||
|
|
||||||
## Alternatives Considered
|
|
||||||
|
|
||||||
### Alternative 1: GitHub Actions
|
|
||||||
- **Rejected**: Self-hosted Gitea aligns better with the home lab's privacy goals.
|
|
||||||
|
|
||||||
### Alternative 2: Jenkins
|
|
||||||
- **Rejected**: ArgoCD + Gitea Act Runner is lighter and more GitOps-native.
|
|
||||||
|
|
||||||
### Alternative 3: No CI/CD
|
|
||||||
- **Rejected**: Manual deployments are error-prone and unscalable.
|
|
||||||
|
|
||||||
## Sequence Diagrams
|
|
||||||
|
|
||||||
### 1. CI/CD Workflow for OpenTofu/Terraform
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant Gitea
|
|
||||||
participant Runner as Gitea Act Runner (pi1/pi3)
|
|
||||||
participant Vault
|
|
||||||
participant WebApp as WebApp (k3s)
|
|
||||||
|
|
||||||
Gitea->>Runner: Trigger vault.yaml workflow
|
|
||||||
Runner->>Gitea: Execute vault_oauth__sh_b64 (OIDC)
|
|
||||||
Gitea-->>Runner: Return ID Token
|
|
||||||
Runner->>Vault: Authenticate with ID Token
|
|
||||||
Vault-->>Runner: Return Vault Token
|
|
||||||
Runner->>Runner: Run OpenTofu/Terraform
|
|
||||||
Runner->>Vault: Fetch Secrets (via Vault Action)
|
|
||||||
Vault-->>Runner: Return Secrets
|
|
||||||
Runner->>WebApp: Deploy Changes
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Vault Secrets Operator Workflow
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant ArgoCD
|
|
||||||
participant WebApp as WebApp (k3s)
|
|
||||||
participant VaultOperator as Vault Secrets Operator
|
|
||||||
participant Vault
|
|
||||||
|
|
||||||
ArgoCD->>WebApp: Deploy Helm Chart
|
|
||||||
WebApp->>VaultOperator: Create VaultAuth (K8s Auth)
|
|
||||||
VaultOperator->>Vault: Authenticate (K8s Service Account)
|
|
||||||
Vault-->>VaultOperator: Return Vault Token
|
|
||||||
WebApp->>VaultOperator: Create VaultStaticSecret (kvv2/webapp/config)
|
|
||||||
VaultOperator->>Vault: Fetch Static Secret
|
|
||||||
Vault-->>VaultOperator: Return Secret
|
|
||||||
VaultOperator->>WebApp: Inject Secret (secretkv)
|
|
||||||
WebApp->>VaultOperator: Create VaultDynamicSecret (postgres/creds/webapp)
|
|
||||||
VaultOperator->>Vault: Generate Dynamic Secret
|
|
||||||
Vault-->>VaultOperator: Return Credentials
|
|
||||||
VaultOperator->>WebApp: Inject Credentials (vso-db-credentials)
|
|
||||||
WebApp->>WebApp: Restart Pods (Rollout)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Success Metrics
|
|
||||||
- Gitea Act Runners successfully execute CI jobs.
|
|
||||||
- ArgoCD synchronizes the `factory` repository without errors.
|
|
||||||
- Vault Secrets Operator injects secrets into deployed applications.
|
|
||||||
@@ -1,334 +0,0 @@
|
|||||||
# ADR 20260407: Network Architecture
|
|
||||||
|
|
||||||
## Status
|
|
||||||
Proposed
|
|
||||||
|
|
||||||
## Context
|
|
||||||
The home lab requires a secure and resilient network architecture to support:
|
|
||||||
- Internal services (`.lab` domain).
|
|
||||||
- External services (`.arcodange.fr` domain).
|
|
||||||
- DNS resolution and ad-blocking (Pi-hole).
|
|
||||||
- TLS certificate management (Step CA).
|
|
||||||
- Ingress routing (Traefik).
|
|
||||||
- CDN and DDoS protection (Cloudflare).
|
|
||||||
|
|
||||||
## Decision
|
|
||||||
We will implement a **multi-layered network architecture** with the following components:
|
|
||||||
|
|
||||||
### 1. External Layer (Internet)
|
|
||||||
- **Cloudflare**: CDN, DDoS protection, and DNS for `.arcodange.fr`.
|
|
||||||
- **DuckDNS**: Dynamic DNS for external access.
|
|
||||||
- **Livebox**: ISP-provided gateway (NAT, DHCP, firewall).
|
|
||||||
|
|
||||||
### 2. Internal Layer (Home Lab)
|
|
||||||
- **Pi-hole (pi1, pi3)**: DNS sinkhole for ad-blocking and internal DNS resolution.
|
|
||||||
- **Step CA (pi1)**: Internal certificate authority for `.lab` domain.
|
|
||||||
- **Traefik (k3s)**: Ingress controller with TLS termination.
|
|
||||||
- **k3s Cluster**: Hosts internal services with Longhorn storage.
|
|
||||||
|
|
||||||
### 3. DNS Architecture
|
|
||||||
- **Pi-hole**: Primary DNS for internal clients.
|
|
||||||
- Forwards `.lab` queries to Step CA.
|
|
||||||
- Forwards external queries to Cloudflare (1.1.1.1).
|
|
||||||
- **Step CA**: Issues certificates for `.lab` services.
|
|
||||||
- **Cloudflare**: Manages `.arcodange.fr` DNS records.
|
|
||||||
|
|
||||||
### 4. Ingress and TLS
|
|
||||||
- **Traefik**: Terminates TLS for both `.lab` and `.arcodange.fr` domains.
|
|
||||||
- Uses Let's Encrypt for `.arcodange.fr`.
|
|
||||||
- Uses Step CA for `.lab`.
|
|
||||||
- **Helm Chart Annotations**:
|
|
||||||
- `traefik.ingress.kubernetes.io/router.entrypoints: websecure`
|
|
||||||
- `traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt`
|
|
||||||
- `traefik.ingress.kubernetes.io/router.middlewares: localIp@file`
|
|
||||||
|
|
||||||
### 5. Security
|
|
||||||
- **Cloudflare Tunnel**: Securely exposes internal services without port forwarding.
|
|
||||||
- **CrowdSec**: Intrusion detection and banning.
|
|
||||||
- **Traefik Middlewares**: IP filtering, rate limiting, and authentication.
|
|
||||||
- **Cloudflare Turnstile**: CAPTCHA protection for public-facing services.
|
|
||||||
|
|
||||||
## Architecture Diagrams
|
|
||||||
|
|
||||||
### 0. High-Level Network Architecture (Architecture Beta)
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'neutral', 'themeVariables': {
|
|
||||||
'primaryColor': '#f0f0f0',
|
|
||||||
'primaryBorderColor': '#333333',
|
|
||||||
'primaryTextColor': '#333333',
|
|
||||||
'lineColor': '#333333',
|
|
||||||
'tertiaryColor': '#e67e22'
|
|
||||||
}}}%%
|
|
||||||
architectureBeta
|
|
||||||
%% External Layer
|
|
||||||
box "Internet" #f9f9f9
|
|
||||||
component Cloudflare["Cloudflare\n(CDN/DNS)"] #f9f9f9
|
|
||||||
component DuckDNS["DuckDNS\n(DDNS)"] #f9f9f9
|
|
||||||
end
|
|
||||||
|
|
||||||
%% External Gateway
|
|
||||||
box "External Gateway" #e6e6e6
|
|
||||||
component Livebox["Livebox\n(NAT/Firewall)"] #e6e6e6
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Internal Layer
|
|
||||||
box "Internal Network\n(192.168.1.0/24)" #d4d4d4
|
|
||||||
%% DNS Layer
|
|
||||||
box "DNS" #ffff99
|
|
||||||
component PiHole1["Pi-hole\n(pi1)"] #ffff99
|
|
||||||
component PiHole3["Pi-hole\n(pi3)"] #ffff99
|
|
||||||
component StepCA["Step CA\n(pi1)"] #ccccff
|
|
||||||
end
|
|
||||||
|
|
||||||
%% k3s Layer
|
|
||||||
box "k3s Cluster" #ff9999
|
|
||||||
component Traefik["Traefik\n(Ingress)"] #ff9999
|
|
||||||
component CrowdSec["CrowdSec\n(Security)"] #ff9999
|
|
||||||
component Gitea["Gitea\n(pi2)"] #ffcc99
|
|
||||||
component Vault["Vault\n(Secrets)"] #ccccff
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Connections
|
|
||||||
Cloudflare --> Livebox : "DNS"
|
|
||||||
DuckDNS --> Livebox : "DDNS"
|
|
||||||
Livebox --> PiHole1 : "NAT"
|
|
||||||
Livebox --> PiHole3 : "NAT"
|
|
||||||
Livebox --> Traefik : "NAT"
|
|
||||||
PiHole1 --> StepCA : "Forward .lab"
|
|
||||||
PiHole1 --> Cloudflare : "Forward External"
|
|
||||||
PiHole3 --> StepCA : "Forward .lab"
|
|
||||||
PiHole3 --> Cloudflare : "Forward External"
|
|
||||||
Traefik --> Cloudflare : "TLS (Let's Encrypt)"
|
|
||||||
Traefik --> StepCA : "TLS (Step CA)"
|
|
||||||
CrowdSec --> Traefik : "Ban IPs"
|
|
||||||
Traefik --> Gitea : "Route"
|
|
||||||
Traefik --> Vault : "Route"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 1. High-Level Network Architecture
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#f89136'}}}%%
|
|
||||||
graph TD
|
|
||||||
%% Styles
|
|
||||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333;
|
|
||||||
classDef external fill:#e6e6e6,stroke:#555,color:#333;
|
|
||||||
classDef internal fill:#d4d4d4,stroke:#777,color:#333;
|
|
||||||
classDef security fill:#ff9999,stroke:#cc0000,color:#333;
|
|
||||||
classDef dns fill:#ffff99,stroke:#cccc00,color:#333;
|
|
||||||
classDef ca fill:#ccccff,stroke:#6666cc,color:#333;
|
|
||||||
|
|
||||||
%% Internet
|
|
||||||
subgraph "Internet"
|
|
||||||
Cloudflare["Cloudflare (CDN/DNS)"]:::internet
|
|
||||||
DuckDNS["DuckDNS (DDNS)"]:::internet
|
|
||||||
end
|
|
||||||
|
|
||||||
%% External Gateway
|
|
||||||
subgraph "External Gateway"
|
|
||||||
Livebox["Livebox (NAT/Firewall)"]:::external
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Internal Network
|
|
||||||
subgraph "Internal Network (192.168.1.0/24)"
|
|
||||||
%% Pi-hole DNS
|
|
||||||
PiHole1["Pi-hole (pi1)"]:::dns
|
|
||||||
PiHole3["Pi-hole (pi3)"]:::dns
|
|
||||||
|
|
||||||
%% Step CA
|
|
||||||
StepCA["Step CA (pi1)"]:::ca
|
|
||||||
|
|
||||||
%% k3s Cluster
|
|
||||||
k3s["k3s Cluster"]:::internal
|
|
||||||
Traefik["Traefik (k3s)"]:::internal
|
|
||||||
CrowdSec["CrowdSec (k3s)"]:::security
|
|
||||||
|
|
||||||
%% Services
|
|
||||||
Gitea["Gitea (pi2)"]:::internal
|
|
||||||
Vault["Vault (k3s)"]:::internal
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Connections
|
|
||||||
Cloudflare -->|DNS| Livebox
|
|
||||||
DuckDNS -->|DDNS| Livebox
|
|
||||||
Livebox -->|NAT| PiHole1
|
|
||||||
Livebox -->|NAT| PiHole3
|
|
||||||
Livebox -->|NAT| k3s
|
|
||||||
|
|
||||||
%% Internal DNS
|
|
||||||
PiHole1 -->|Forward .lab| StepCA
|
|
||||||
PiHole1 -->|Forward External| Cloudflare
|
|
||||||
PiHole3 -->|Forward .lab| StepCA
|
|
||||||
PiHole3 -->|Forward External| Cloudflare
|
|
||||||
|
|
||||||
%% Ingress
|
|
||||||
Traefik -->|"TLS (Let's Encrypt)"| Cloudflare
|
|
||||||
Traefik -->|"TLS (Step CA)"| StepCA
|
|
||||||
CrowdSec -->|Ban IPs| Traefik
|
|
||||||
|
|
||||||
%% Service Access
|
|
||||||
Traefik -->|Route| Gitea
|
|
||||||
Traefik -->|Route| Vault
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. DNS Resolution Flow
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant Client
|
|
||||||
participant PiHole
|
|
||||||
participant StepCA
|
|
||||||
participant Cloudflare
|
|
||||||
participant ExternalDNS
|
|
||||||
|
|
||||||
Client->>PiHole: Query example.lab
|
|
||||||
PiHole->>StepCA: Forward .lab query
|
|
||||||
StepCA-->>PiHole: Return A record
|
|
||||||
PiHole-->>Client: Return response
|
|
||||||
|
|
||||||
Client->>PiHole: Query example.com
|
|
||||||
PiHole->>Cloudflare: Forward to 1.1.1.1
|
|
||||||
Cloudflare->>ExternalDNS: Resolve externally
|
|
||||||
ExternalDNS-->>Cloudflare: Return response
|
|
||||||
Cloudflare-->>PiHole: Return response
|
|
||||||
PiHole-->>Client: Return response
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Ingress and TLS Flow
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant User
|
|
||||||
participant Cloudflare
|
|
||||||
participant Traefik
|
|
||||||
participant StepCA
|
|
||||||
participant Service
|
|
||||||
|
|
||||||
User->>Cloudflare: HTTPS Request (webapp.arcodange.fr)
|
|
||||||
Cloudflare->>Traefik: Forward to internal IP
|
|
||||||
Traefik->>Let's Encrypt: Request Certificate
|
|
||||||
Let's Encrypt-->>Traefik: Issue Certificate
|
|
||||||
Traefik->>Service: Route request
|
|
||||||
Service-->>Traefik: Return response
|
|
||||||
Traefik-->>Cloudflare: Return HTTPS response
|
|
||||||
Cloudflare-->>User: Return response
|
|
||||||
|
|
||||||
User->>Traefik: HTTPS Request (webapp.arcodange.lab)
|
|
||||||
Traefik->>StepCA: Request Certificate
|
|
||||||
StepCA-->>Traefik: Issue Certificate
|
|
||||||
Traefik->>Service: Route request
|
|
||||||
Service-->>Traefik: Return response
|
|
||||||
Traefik-->>User: Return HTTPS response
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Security Flow (CrowdSec + Traefik)
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant Attacker
|
|
||||||
participant Traefik
|
|
||||||
participant CrowdSec
|
|
||||||
participant BannedIPs
|
|
||||||
|
|
||||||
Attacker->>Traefik: Malicious Request
|
|
||||||
Traefik->>CrowdSec: Log suspicious activity
|
|
||||||
CrowdSec->>BannedIPs: Add IP to ban list
|
|
||||||
BannedIPs-->>Traefik: Update middleware
|
|
||||||
Traefik-->>Attacker: Block request (403)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Playbook and Role Analysis
|
|
||||||
|
|
||||||
### 1. Pi-hole Deployment
|
|
||||||
- **Playbook**: `playbooks/system/pihole.yml`
|
|
||||||
- **Role**: `arcodange.factory.pihole`
|
|
||||||
- **Configuration**:
|
|
||||||
- Upstream DNS: Cloudflare (1.1.1.1) and Step CA for `.lab`.
|
|
||||||
- Blocklists: Ad-blocking and malware domains.
|
|
||||||
|
|
||||||
### 2. Step CA Deployment
|
|
||||||
- **Playbook**: `playbooks/ssl/ssl.yml`
|
|
||||||
- **Role**: `step_ca`
|
|
||||||
- **Configuration**:
|
|
||||||
- Internal CA for `.lab` domain.
|
|
||||||
- Short-lived certificates (default: 24h).
|
|
||||||
|
|
||||||
### 3. Traefik Deployment
|
|
||||||
- **Playbook**: `playbooks/system/system_k3s.yml` (via k3s)
|
|
||||||
- **Helm Chart**: `traefik` (installed via k3s)
|
|
||||||
- **Key Annotations**:
|
|
||||||
```yaml
|
|
||||||
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
||||||
traefik.ingress.kubernetes.io/router.tls.certresolver: letsencrypt
|
|
||||||
traefik.ingress.kubernetes.io/router.middlewares: localIp@file
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. CrowdSec Deployment
|
|
||||||
- **Playbook**: `playbooks/tools/crowdsec.yml`
|
|
||||||
- **Role**: `arcodange.factory.crowdsec`
|
|
||||||
- **Configuration**:
|
|
||||||
- Bouncer integration with Traefik.
|
|
||||||
- Custom scenarios for brute-force and bot detection.
|
|
||||||
|
|
||||||
## Consequences
|
|
||||||
|
|
||||||
### Positive
|
|
||||||
- **Resilient DNS**: Pi-hole provides ad-blocking and internal DNS resolution.
|
|
||||||
- **Secure TLS**: Step CA for internal services, Let's Encrypt for external.
|
|
||||||
- **DDoS Protection**: Cloudflare absorbs external attacks.
|
|
||||||
- **Intrusion Detection**: CrowdSec bans malicious IPs automatically.
|
|
||||||
|
|
||||||
### Negative
|
|
||||||
- **Complexity**: Multiple layers require careful configuration.
|
|
||||||
- **Single Point of Failure**: Pi-hole is critical for internal DNS.
|
|
||||||
- **Certificate Management**: Step CA requires maintenance for `.lab` domain.
|
|
||||||
|
|
||||||
## Alternatives Considered
|
|
||||||
|
|
||||||
### Alternative 1: Public DNS for `.lab`
|
|
||||||
- **Rejected**: Exposing internal domains is a security risk.
|
|
||||||
|
|
||||||
### Alternative 2: No Ad-Blocking
|
|
||||||
- **Rejected**: Pi-hole provides essential security and privacy.
|
|
||||||
|
|
||||||
### Alternative 3: Self-Signed Certificates
|
|
||||||
- **Rejected**: Step CA provides better usability with short-lived certs.
|
|
||||||
|
|
||||||
### 5. Cloudflare Turnstile + CrowdSec Flow
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant User
|
|
||||||
participant Cloudflare
|
|
||||||
participant Turnstile
|
|
||||||
participant Traefik
|
|
||||||
participant CrowdSec
|
|
||||||
participant BannedIPs
|
|
||||||
|
|
||||||
User->>Cloudflare: Request protected endpoint
|
|
||||||
Cloudflare->>Turnstile: Challenge (CAPTCHA)
|
|
||||||
Turnstile-->>Cloudflare: Return token
|
|
||||||
Cloudflare->>Traefik: Forward request with token
|
|
||||||
|
|
||||||
alt Valid Token
|
|
||||||
Traefik->>Service: Route request
|
|
||||||
Service-->>Traefik: Return response
|
|
||||||
Traefik-->>Cloudflare: Return response
|
|
||||||
Cloudflare-->>User: Return success
|
|
||||||
else Invalid Token
|
|
||||||
Traefik->>CrowdSec: Log suspicious activity
|
|
||||||
CrowdSec->>BannedIPs: Add IP to ban list
|
|
||||||
BannedIPs-->>Traefik: Update middleware
|
|
||||||
Traefik-->>Cloudflare: Block request (403)
|
|
||||||
Cloudflare-->>User: Return "Access Denied"
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Success Metrics
|
|
||||||
- Pi-hole blocks >50% of ads and trackers.
|
|
||||||
- Step CA issues certificates without downtime.
|
|
||||||
- Traefik routes 100% of external traffic via Cloudflare.
|
|
||||||
- CrowdSec bans >10 malicious IPs per day.
|
|
||||||
- Cloudflare Turnstile blocks >90% of bot traffic.
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Script pour générer un token Gitea et configurer Hermes
|
|
||||||
# Usage: ./get_gitea_token.sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "🔑 Génération d'un token Gitea pour Hermes..."
|
|
||||||
|
|
||||||
# Créer l'inventaire
|
|
||||||
cat > ~/Work/Arcodange/factory/inventory.ini << 'EOF'
|
|
||||||
[gitea]
|
|
||||||
gitea.arcodange.lab ansible_host=192.168.1.202 ansible_user=git
|
|
||||||
|
|
||||||
[gitea:vars]
|
|
||||||
ansible_ssh_private_key_file=~/.ssh/id_rsa_arcodange
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Créer le playbook
|
|
||||||
cat > ~/Work/Arcodange/factory/get_token.yml << 'EOF'
|
|
||||||
- name: Générer un token Gitea pour Hermes
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
gather_facts: true
|
|
||||||
|
|
||||||
vars:
|
|
||||||
gitea_user_name: arcodange
|
|
||||||
gitea_token_name: hermes-{{ ansible_date_time.iso8601 }}
|
|
||||||
gitea_token_scopes: "write:repository,write:user"
|
|
||||||
gitea_token_fact_name: gitea_api_token
|
|
||||||
gitea_base_url: "https://gitea.arcodange.lab"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Inclure le role gitea_token
|
|
||||||
include_role:
|
|
||||||
name: .claude/worktrees/focused-dirac-151213/ansible/arcodange/factory/roles/gitea_token
|
|
||||||
|
|
||||||
- name: Afficher le token généré
|
|
||||||
debug:
|
|
||||||
var: gitea_api_token
|
|
||||||
when: gitea_api_token is defined and gitea_api_token != 'deleted'
|
|
||||||
|
|
||||||
- name: Sauvegarder le token dans un fichier temporaire
|
|
||||||
copy:
|
|
||||||
content: "{{ gitea_api_token }}"
|
|
||||||
dest: /tmp/gitea_token.txt
|
|
||||||
when: gitea_api_token is defined and gitea_api_token != 'deleted'
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Exécuter le playbook
|
|
||||||
echo "📜 Exécution du playbook Ansible..."
|
|
||||||
cd ~/Work/Arcodange/factory
|
|
||||||
ansible-playbook -i inventory.ini get_token.yml
|
|
||||||
|
|
||||||
# Récupérer le token depuis le fichier temporaire
|
|
||||||
if [ -f /tmp/gitea_token.txt ]; then
|
|
||||||
TOKEN=$(cat /tmp/gitea_token.txt)
|
|
||||||
echo "✅ Token généré avec succès !"
|
|
||||||
|
|
||||||
# Configurer Hermes
|
|
||||||
echo "🛠 Configuration de Hermes..."
|
|
||||||
hermes config set gitea.token "$TOKEN"
|
|
||||||
hermes config set gitea.base_url "https://gitea.arcodange.lab"
|
|
||||||
hermes config set gitea.username "arcodange"
|
|
||||||
|
|
||||||
# Nettoyer
|
|
||||||
rm -f /tmp/gitea_token.txt
|
|
||||||
rm -f inventory.ini
|
|
||||||
rm -f get_token.yml
|
|
||||||
|
|
||||||
echo "✨ Configuration terminée !"
|
|
||||||
echo "Token stocké dans la configuration Hermes."
|
|
||||||
else
|
|
||||||
echo "⚠️ Impossible de récupérer le token. Vérifie que :"
|
|
||||||
echo " 1. Le serveur Gitea est accessible"
|
|
||||||
echo " 2. La clé SSH est configurée (~/.ssh/id_rsa_arcodange)"
|
|
||||||
echo " 3. L'utilisateur 'git' a les permissions nécessaires"
|
|
||||||
fi
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Script pour générer un token Gitea via API REST et configurer Hermes
|
|
||||||
# Usage: ./get_gitea_token_rest.sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "🔑 Génération d'un token Gitea via API REST..."
|
|
||||||
|
|
||||||
# Demander les informations nécessaires
|
|
||||||
read -p "Nom d'utilisateur Gitea (arcodange) : " GITEA_USER
|
|
||||||
GITEA_USER=${GITEA_USER:-arcodange}
|
|
||||||
|
|
||||||
read -s -p "Mot de passe Gitea : " GITEA_PASSWORD
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
read -p "Nom du token (hermes-access) : " TOKEN_NAME
|
|
||||||
TOKEN_NAME=${TOKEN_NAME:-hermes-access}
|
|
||||||
|
|
||||||
read -p "Portées (write:repository,write:user) : " SCOPES
|
|
||||||
SCOPES=${SCOPES:-write:repository,write:user}
|
|
||||||
|
|
||||||
# Appeler l'API Gitea pour créer un token
|
|
||||||
echo "📡 Appel à l'API Gitea..."
|
|
||||||
RESPONSE=$(curl -s -X POST \
|
|
||||||
"https://gitea.arcodange.lab/api/v1/users/${GITEA_USER}/tokens" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-u "${GITEA_USER}:${GITEA_PASSWORD}" \
|
|
||||||
-d "{\"name\":\"${TOKEN_NAME}\",\"scopes\":[\"${SCOPES//,/\",\"}\"]}")
|
|
||||||
|
|
||||||
# Extraire le token de la réponse
|
|
||||||
TOKEN=$(echo "$RESPONSE" | jq -r '.sha1' 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
|
|
||||||
echo "❌ Échec de la création du token"
|
|
||||||
echo "Réponse de l'API :"
|
|
||||||
echo "$RESPONSE" | jq .
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Token généré avec succès !"
|
|
||||||
echo "Token: $TOKEN"
|
|
||||||
|
|
||||||
# Configurer Hermes
|
|
||||||
echo "🛠 Configuration de Hermes..."
|
|
||||||
hermes config set gitea.token "$TOKEN"
|
|
||||||
hermes config set gitea.base_url "https://gitea.arcodange.lab"
|
|
||||||
hermes config set gitea.username "$GITEA_USER"
|
|
||||||
|
|
||||||
echo "✨ Configuration terminée !"
|
|
||||||
echo "Token stocké dans la configuration Hermes."
|
|
||||||
echo ""
|
|
||||||
echo "Pour vérifier la configuration :"
|
|
||||||
echo " hermes config get gitea"
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Script pour générer un token Gitea via API REST avec fichier temporaire
|
|
||||||
# Usage: ./get_gitea_token_tmp.sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "🔑 Génération d'un token Gitea via API REST..."
|
|
||||||
|
|
||||||
# Utiliser le fichier temporaire existant
|
|
||||||
PASS_FILE="/tmp/gitea_pass.txt"
|
|
||||||
|
|
||||||
if [ ! -f "$PASS_FILE" ]; then
|
|
||||||
echo "❌ Fichier de mot de passe introuvable : $PASS_FILE"
|
|
||||||
echo "Créez-le avec : echo 'votre_mot_de_passe' > $PASS_FILE"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Variables
|
|
||||||
GITEA_USER="arcodange"
|
|
||||||
TOKEN_NAME="hermes-$(date +%Y%m%d-%H%M%S)"
|
|
||||||
SCOPES="write:repository,write:user"
|
|
||||||
|
|
||||||
# Appeler l'API Gitea pour créer un token
|
|
||||||
echo "📡 Appel à l'API Gitea..."
|
|
||||||
RESPONSE=$(curl -s -X POST \
|
|
||||||
"https://gitea.arcodange.lab/api/v1/users/${GITEA_USER}/tokens" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-u "${GITEA_USER}:$(cat $PASS_FILE)" \
|
|
||||||
-d "{\"name\":\"${TOKEN_NAME}\",\"scopes\":[\"${SCOPES//,/\",\"}\"]}")
|
|
||||||
|
|
||||||
# Nettoyer le fichier temporaire
|
|
||||||
rm -f "$PASS_FILE"
|
|
||||||
|
|
||||||
# Extraire le token de la réponse
|
|
||||||
TOKEN=$(echo "$RESPONSE" | jq -r '.sha1' 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -z "$TOKEN" ] || [ "$TOKEN" = "null" ]; then
|
|
||||||
echo "❌ Échec de la création du token"
|
|
||||||
echo "Réponse de l'API :"
|
|
||||||
echo "$RESPONSE" | jq .
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "✅ Token généré avec succès !"
|
|
||||||
echo "Token: $TOKEN"
|
|
||||||
|
|
||||||
# Configurer Hermes
|
|
||||||
echo "🛠 Configuration de Hermes..."
|
|
||||||
hermes config set gitea.token "$TOKEN"
|
|
||||||
hermes config set gitea.base_url "https://gitea.arcodange.lab"
|
|
||||||
hermes config set gitea.username "$GITEA_USER"
|
|
||||||
|
|
||||||
echo "✨ Configuration terminée !"
|
|
||||||
echo "Token stocké dans la configuration Hermes."
|
|
||||||
echo ""
|
|
||||||
echo "Pour vérifier la configuration :"
|
|
||||||
hermes config get gitea
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
- name: Générer un token Gitea pour Hermes
|
|
||||||
hosts: localhost
|
|
||||||
connection: local
|
|
||||||
gather_facts: true
|
|
||||||
|
|
||||||
vars:
|
|
||||||
gitea_user_name: arcodange
|
|
||||||
gitea_token_name: hermes-{{ ansible_date_time.iso8601 }}
|
|
||||||
gitea_token_scopes: "write:repository,write:user"
|
|
||||||
gitea_token_fact_name: gitea_api_token
|
|
||||||
gitea_base_url: "https://gitea.arcodange.lab"
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Inclure le role gitea_token
|
|
||||||
include_role:
|
|
||||||
name: .claude/worktrees/focused-dirac-151213/ansible/arcodange/factory/roles/gitea_token
|
|
||||||
|
|
||||||
- name: Afficher le token généré
|
|
||||||
debug:
|
|
||||||
var: gitea_api_token
|
|
||||||
when: gitea_api_token is defined and gitea_api_token != 'deleted'
|
|
||||||
|
|
||||||
- name: Sauvegarder le token dans un fichier temporaire
|
|
||||||
copy:
|
|
||||||
content: "{{ gitea_api_token }}"
|
|
||||||
dest: /tmp/gitea_token.txt
|
|
||||||
when: gitea_api_token is defined and gitea_api_token != 'deleted'
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
[gitea]
|
|
||||||
gitea.arcodange.lab ansible_host=192.168.1.202 ansible_user=git
|
|
||||||
|
|
||||||
[gitea:vars]
|
|
||||||
ansible_ssh_private_key_file=~/.ssh/id_rsa_arcodange
|
|
||||||
Generated
-59
@@ -1,59 +0,0 @@
|
|||||||
# This file is maintained automatically by "tofu init".
|
|
||||||
# Manual edits may be lost in future updates.
|
|
||||||
|
|
||||||
provider "registry.opentofu.org/cyrilgdn/postgresql" {
|
|
||||||
version = "1.24.0"
|
|
||||||
constraints = "1.24.0"
|
|
||||||
hashes = [
|
|
||||||
"h1:GDmie2p7Ii7BOPHeMUtM6bwSvr3YvsIWUQ30TsSc+5I=",
|
|
||||||
"zh:023153dbb689296f3edbc34cb6d57355bbfa8af6faa3e20ab71a71a909c8ff56",
|
|
||||||
"zh:0d222293c1c30b0af5b9ac9a662f7fd6bbbdca0859b02046527ad133838fafd8",
|
|
||||||
"zh:1234c6ec6bd49d8a2afc80fecc748ebdd01555ac4f1b4618dc7fce670bf18858",
|
|
||||||
"zh:18d047ffa30156da8281206b977d23dc9690e87137b0a9dd7049d2776e603ce6",
|
|
||||||
"zh:2f009822960405a20f2d304f70187f77338bf3e633f522da35021f4b83bc6a8f",
|
|
||||||
"zh:39e540af29165e9dc28712651d308fdf0fcac018850a0841f7c5380f58aafb64",
|
|
||||||
"zh:4930ac482b2b0f06681228932d0ed91ac4f34555b15285787f048e33ffa72d39",
|
|
||||||
"zh:4d33f4a9f449166a5cb495e764c8a09bf9710927162d30f5db46283a88023cad",
|
|
||||||
"zh:55d6584517ebaf7b4ecefedbf600af0d12077a00834b777f884b4631f77b7e13",
|
|
||||||
"zh:5f6ab45cbb3f2b62f82f0377c7f10d4aa0f35db7f426af7529915401097c252f",
|
|
||||||
"zh:5fac6d4d676425d9491a42b992b166bdbae5ecd1fe6815bc75435f7c3024015b",
|
|
||||||
"zh:a13f1a7cc763053da2317178c7ba1d57f5fddf14f9be282881da63fc28fd0e57",
|
|
||||||
"zh:d566eaae8ba33542f7b290e8fa0f2eb7f2cfb51d5ae7cafaae261f76f3f3e422",
|
|
||||||
"zh:ea4d50591767696b6f27d977c44b17390415d21b761e00f591d67c6248c18949",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
provider "registry.opentofu.org/hashicorp/random" {
|
|
||||||
version = "3.7.2"
|
|
||||||
hashes = [
|
|
||||||
"h1:cFGCdxTlsrteTiaOV/iOQdql7eJkD3F/vtJxenkj9IE=",
|
|
||||||
"zh:2ffeb1058bd7b21a9e15a5301abb863053a2d42dffa3f6cf654a1667e10f4727",
|
|
||||||
"zh:519319ed8f4312ed76519652ad6cd9f98bc75cf4ec7990a5684c072cf5dd0a5d",
|
|
||||||
"zh:7371c2cc28c94deb9dba62fbac2685f7dde47f93019273a758dd5a2794f72919",
|
|
||||||
"zh:9b0ac4c1d8e36a86b59ced94fa517ae9b015b1d044b3455465cc6f0eab70915d",
|
|
||||||
"zh:c6336d7196f1318e1cbb120b3de8426ce43d4cacd2c75f45dba2dbdba666ce00",
|
|
||||||
"zh:c71f18b0cb5d55a103ea81e346fb56db15b144459123f1be1b0209cffc1deb4e",
|
|
||||||
"zh:d2dc49a6cac2d156e91b0506d6d756809e36bf390844a187f305094336d3e8d8",
|
|
||||||
"zh:d5b5fc881ccc41b268f952dae303501d6ec9f9d24ee11fe2fa56eed7478e15d0",
|
|
||||||
"zh:db9723eaca26d58c930e13fde221d93501529a5cd036b1f167ef8cff6f1a03cc",
|
|
||||||
"zh:fe3359f733f3ab518c6f85f3a9cd89322a7143463263f30321de0973a52d4ad8",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
provider "registry.opentofu.org/hashicorp/vault" {
|
|
||||||
version = "4.4.0"
|
|
||||||
constraints = "4.4.0"
|
|
||||||
hashes = [
|
|
||||||
"h1:IhKDv0pTgpy89K3QYmDX872H75Wl7kZKR2scUQynuiA=",
|
|
||||||
"zh:0309ea8f81386e17ab13c06c5991ca959708c55c815b0cfba2bbcd865e0d606e",
|
|
||||||
"zh:40e56199ccd266bffa216e8ebbcdc2e29b6ef5145b39377be766e763cac759c8",
|
|
||||||
"zh:6fad1f073bd2e53e34736e000f98db581137e153ac80bbb5c4f1a1e38b46a1d2",
|
|
||||||
"zh:74564fd4759decccf7f3c952aa2feba1012f103a66ec354aa3b3292a2f1b2412",
|
|
||||||
"zh:7aae012c1a43e6e5dae6f608ec0f08cdb3f95fa121a32e413fe7ee37cb99947f",
|
|
||||||
"zh:7c83f508e164844b1dd9bafe9de0fe60c7be7b55a02e704a6e2f50cff38b7d96",
|
|
||||||
"zh:873a42322b68d9fba4a38217b97ee04a1eb617e811d7f9954016f5c3eb6cb0bc",
|
|
||||||
"zh:9db2b13472cf91a5f18f0a7c6ae532277c05b0980d87f492341426b981679f7b",
|
|
||||||
"zh:ac1cbd2926265db80efe3f1814bed82901f7d8a7d4e5b1e22592e1eef234b1c7",
|
|
||||||
"zh:f465a955cc96f640e7426a648ba672c169a4a2959bad6146fe61583d67642561",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
# Home lab
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
|
||||||
graph LR
|
|
||||||
%% Styles
|
|
||||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
|
||||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
|
||||||
classDef cluster fill:#d4d4d4,stroke:#777,color:#333,font-size:12px;
|
|
||||||
|
|
||||||
%% Internet
|
|
||||||
subgraph "Internet / Le monde"
|
|
||||||
direction TB
|
|
||||||
DuckDNS["🌐 DuckDNS<br>(duckdns.org)"]:::internet
|
|
||||||
GoogleCloud["☁️ Google Cloud<br>(cloud.google.com)"]:::internet
|
|
||||||
Cloudflare["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
|
||||||
OVHcloud["🖥️ OVHcloud<br>(ovhcloud.com)"]:::internet
|
|
||||||
Zoho["📧 Zoho<br>(zoho.eu)"]:::internet
|
|
||||||
CrowdSec["🔒 CrowdSec<br>(crowdsec.net)"]:::internet
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Bureaux Arcodange
|
|
||||||
subgraph "Bureaux Arcodange"
|
|
||||||
direction TB
|
|
||||||
Livebox["📡 Livebox<br>(gateway)"]:::arcodange
|
|
||||||
MacBook["💻 MacBook Pro M4"]:::arcodange
|
|
||||||
|
|
||||||
subgraph "Cluster Raspberry Pi"
|
|
||||||
direction LR
|
|
||||||
Pi1["🐧 pi1"]:::cluster
|
|
||||||
Pi2["🐧 pi2"]:::cluster
|
|
||||||
Pi3["🐧 pi3"]:::cluster
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Network
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
|
||||||
graph LR
|
|
||||||
%% Styles
|
|
||||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
|
||||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
|
||||||
classDef k3sServer fill:#ff9999,stroke:#cc0000,color:#333,font-size:12px;
|
|
||||||
classDef k3sAgent fill:#99ff99,stroke:#00cc00,color:#333,font-size:12px;
|
|
||||||
classDef traefik fill:#9999ff,stroke:#0000cc,color:#333,font-size:12px;
|
|
||||||
classDef dns fill:#ffff99,stroke:#cccc00,color:#333,font-size:12px;
|
|
||||||
classDef cloudflared fill:#ccccff,stroke:#6666cc,color:#333,font-size:12px;
|
|
||||||
|
|
||||||
%% Internet
|
|
||||||
subgraph "Internet / Le monde"
|
|
||||||
direction TB
|
|
||||||
DuckDNS["🌐 DuckDNS<br>(duckdns.org)"]:::internet
|
|
||||||
subgraph "Cloudflare"
|
|
||||||
CloudflareService["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
|
||||||
DNSNote@{ shape: text, label: "redirige *.arcodange.fr<br>→ Cloudflared Tunnel" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Bureaux Arcodange
|
|
||||||
subgraph "Bureaux Arcodange"
|
|
||||||
direction TB
|
|
||||||
Livebox["📡 Livebox<br>(passerelle)"]:::arcodange
|
|
||||||
MacBook["💻 MacBook Pro M4"]:::arcodange
|
|
||||||
|
|
||||||
%% Cluster Raspberry Pi
|
|
||||||
subgraph "Cluster Raspberry Pi"
|
|
||||||
direction LR
|
|
||||||
|
|
||||||
%% Noeud pi1
|
|
||||||
subgraph "pi1"
|
|
||||||
Pi1["🐧 pi1"]:::k3sServer
|
|
||||||
subgraph "K3s (Server)"
|
|
||||||
Traefik["🚀 Traefik"]:::traefik
|
|
||||||
Cloudflared["☁️ Cloudflared Tunnel"]:::cloudflared
|
|
||||||
end
|
|
||||||
PiHole1["🧹 Pi-hole"]:::dns
|
|
||||||
NS1@{ shape: text, label: "nameserver : pi3" }
|
|
||||||
DNSArcodange@{ shape: text, label: ".arcodange.lab<br>→ Traefik", color:#cccc00 }
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Noeud pi2
|
|
||||||
subgraph "pi2"
|
|
||||||
Pi2["🐧 pi2"]:::k3sAgent
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Noeud pi3
|
|
||||||
subgraph "pi3"
|
|
||||||
Pi3["🐧 pi3"]:::k3sAgent
|
|
||||||
PiHole3["🧹 Pi-hole"]:::dns
|
|
||||||
NS3@{ shape: text, label: "nameserver : pi1" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Connexions
|
|
||||||
%% Pi-hole → Livebox (DNS)
|
|
||||||
PiHole1 -.->|DNS| Livebox
|
|
||||||
PiHole3 -.->|DNS| Livebox
|
|
||||||
|
|
||||||
%% Cloudflare → Cloudflared Tunnel → Traefik
|
|
||||||
CloudflareService -.-> Cloudflared
|
|
||||||
Cloudflared --> Traefik
|
|
||||||
|
|
||||||
%% Livebox → Traefik (HTTPS)
|
|
||||||
Livebox -->|redirige HTTPS| Traefik
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## with Gitea
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
|
||||||
graph LR
|
|
||||||
%% Styles
|
|
||||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
|
||||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
|
||||||
classDef k3sServer fill:#ff9999,stroke:#cc0000,color:#333,font-size:12px;
|
|
||||||
classDef k3sAgent fill:#99ff99,stroke:#00cc00,color:#333,font-size:12px;
|
|
||||||
classDef traefik fill:#9999ff,stroke:#0000cc,color:#333,font-size:12px;
|
|
||||||
classDef dns fill:#ffff99,stroke:#cccc00,color:#333,font-size:12px;
|
|
||||||
classDef cloudflared fill:#ccccff,stroke:#6666cc,color:#333,font-size:12px;
|
|
||||||
classDef gitea fill:#ffcc99,stroke:#cc9966,color:#333,font-size:12px;
|
|
||||||
classDef cms fill:#99ffcc,stroke:#66cc99,color:#333,font-size:12px;
|
|
||||||
classDef domainFR fill:#99ccff,stroke:#0066cc,color:#333,font-size:12px;
|
|
||||||
classDef domainLAB fill:#ccff99,stroke:#66cc66,color:#333,font-size:12px;
|
|
||||||
|
|
||||||
%% Internet
|
|
||||||
subgraph "Internet / Le monde"
|
|
||||||
direction TB
|
|
||||||
DuckDNS["🌐 DuckDNS<br>(duckdns.org)"]:::internet
|
|
||||||
subgraph "Cloudflare"
|
|
||||||
CloudflareService["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
|
||||||
DNSNote@{ shape: text, label: "redirige *.arcodange.fr<br>→ Cloudflared Tunnel" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Bureaux Arcodange
|
|
||||||
subgraph "Bureaux Arcodange"
|
|
||||||
direction TB
|
|
||||||
Livebox["📡 Livebox<br>(passerelle)"]:::arcodange
|
|
||||||
MacBook["💻 MacBook Pro M4"]:::arcodange
|
|
||||||
|
|
||||||
%% Cluster Raspberry Pi
|
|
||||||
subgraph "Cluster Raspberry Pi"
|
|
||||||
direction LR
|
|
||||||
|
|
||||||
%% Noeud pi1
|
|
||||||
subgraph "pi1"
|
|
||||||
Pi1["🐧 pi1"]:::k3sServer
|
|
||||||
subgraph "K3s (Server)"
|
|
||||||
Traefik["🚀 Traefik"]:::traefik
|
|
||||||
Cloudflared["☁️ Cloudflared Tunnel"]:::cloudflared
|
|
||||||
end
|
|
||||||
PiHole1["🧹 Pi-hole"]:::dns
|
|
||||||
GiteaRunner1["🏃 Gitea Act Runner"]:::gitea
|
|
||||||
NS1@{ shape: text, label: "nameserver : pi3" }
|
|
||||||
DNSArcodange@{ shape: text, label: ".arcodange.lab<br>→ Traefik", color:#cccc00 }
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Noeud pi2
|
|
||||||
subgraph "pi2"
|
|
||||||
Pi2["🐧 pi2"]:::k3sAgent
|
|
||||||
Gitea["🦊 Gitea"]:::gitea
|
|
||||||
subgraph "K3s (Agent)"
|
|
||||||
CMS["📝 CMS"]:::cms
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Noeud pi3
|
|
||||||
subgraph "pi3"
|
|
||||||
Pi3["🐧 pi3"]:::k3sAgent
|
|
||||||
PiHole3["🧹 Pi-hole"]:::dns
|
|
||||||
GiteaRunner3["🏃 Gitea Act Runner"]:::gitea
|
|
||||||
NS3@{ shape: text, label: "nameserver : pi1" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Connexions Pi-hole → Livebox (DNS)
|
|
||||||
PiHole1 -.->|DNS| Livebox
|
|
||||||
PiHole3 -.->|DNS| Livebox
|
|
||||||
|
|
||||||
%% Cloudflare → Cloudflared Tunnel → Traefik
|
|
||||||
CloudflareService -.-> Cloudflared
|
|
||||||
Cloudflared --> Traefik
|
|
||||||
|
|
||||||
%% Livebox → Traefik (HTTPS)
|
|
||||||
Livebox -->|redirige HTTPS| Traefik
|
|
||||||
|
|
||||||
%% Gitea Act Runner → Gitea
|
|
||||||
GiteaRunner1 -.->|communication| Gitea
|
|
||||||
GiteaRunner3 -.->|communication| Gitea
|
|
||||||
|
|
||||||
%% Domains → CMS via Traefik
|
|
||||||
Traefik -->|cms-rec.arcodange.fr| CMS:::domainFR
|
|
||||||
Traefik -->|www.arcodange.lab| CMS:::domainLAB
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### with security
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#05e2ff1c'}}}%%
|
|
||||||
graph LR
|
|
||||||
%% Styles
|
|
||||||
classDef internet fill:#f9f9f9,stroke:#999,color:#333,font-size:12px;
|
|
||||||
classDef arcodange fill:#e6e6e6,stroke:#555,color:#333,font-size:12px;
|
|
||||||
classDef k3sServer fill:#ff9999,stroke:#cc0000,color:#333,font-size:12px;
|
|
||||||
classDef k3sAgent fill:#99ff99,stroke:#00cc00,color:#333,font-size:12px;
|
|
||||||
classDef traefik fill:#9999ff,stroke:#0000cc,color:#333,font-size:12px;
|
|
||||||
classDef dns fill:#ffff99,stroke:#cccc00,color:#333,font-size:12px;
|
|
||||||
classDef cloudflared fill:#ccccff,stroke:#6666cc,color:#333,font-size:12px;
|
|
||||||
classDef gitea fill:#ffcc99,stroke:#cc9966,color:#333,font-size:12px;
|
|
||||||
classDef cms fill:#99ffcc,stroke:#66cc99,color:#333,font-size:12px;
|
|
||||||
classDef domainFR fill:#99ccff,stroke:#0066cc,color:#333,font-size:12px;
|
|
||||||
classDef domainLAB fill:#ccff99,stroke:#66cc66,color:#333,font-size:12px;
|
|
||||||
classDef crowdsec fill:#ff99cc,stroke:#cc6699,color:#333,font-size:12px;
|
|
||||||
classDef security fill:#ffcccc,stroke:#cc9999,color:#333,font-size:12px;
|
|
||||||
|
|
||||||
%% Internet
|
|
||||||
subgraph "Internet / Le monde"
|
|
||||||
direction TB
|
|
||||||
subgraph "Cloudflare"
|
|
||||||
CloudflareService["🛡️ Cloudflare<br>(cloudflare.com)"]:::internet
|
|
||||||
DNSNote@{ shape: text, label: "redirige *.arcodange.fr<br>→ Cloudflared Tunnel" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Bureaux Arcodange
|
|
||||||
subgraph "Bureaux Arcodange"
|
|
||||||
direction TB
|
|
||||||
Livebox["📡 Livebox<br>(passerelle)"]:::arcodange
|
|
||||||
DNSArcodange@{ shape: text, label: ".arcodange.lab<br>→ Traefik" }
|
|
||||||
|
|
||||||
%% Cluster Raspberry Pi
|
|
||||||
subgraph "Cluster Raspberry Pi"
|
|
||||||
direction LR
|
|
||||||
|
|
||||||
%% Noeud pi1
|
|
||||||
subgraph "pi1"
|
|
||||||
Pi1["🐧 pi1"]:::k3sServer
|
|
||||||
subgraph "K3s (Server)"
|
|
||||||
Traefik["🚀 Traefik"]:::traefik
|
|
||||||
Cloudflared["☁️ Cloudflared Tunnel"]:::cloudflared
|
|
||||||
end
|
|
||||||
PiHole1["🧹 Pi-hole"]:::dns
|
|
||||||
GiteaRunner1["🏃 Gitea Act Runner"]:::gitea
|
|
||||||
NS1@{ shape: text, label: "nameserver : pi3" }
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Noeud pi2
|
|
||||||
subgraph "pi2"
|
|
||||||
Pi2["🐧 pi2"]:::k3sAgent
|
|
||||||
Gitea["🦊 Gitea"]:::gitea
|
|
||||||
subgraph "K3s (Agent)"
|
|
||||||
CMS["📝 CMS"]:::cms
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Noeud pi3
|
|
||||||
subgraph "pi3"
|
|
||||||
Pi3["🐧 pi3"]:::k3sAgent
|
|
||||||
PiHole3["🧹 Pi-hole"]:::dns
|
|
||||||
GiteaRunner3["🏃 Gitea Act Runner"]:::gitea
|
|
||||||
subgraph "K3s (Agent)"
|
|
||||||
CrowdSec["🔒 CrowdSec"]:::crowdsec
|
|
||||||
end
|
|
||||||
NS3@{ shape: text, label: "nameserver : pi1" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Sécurité combinée
|
|
||||||
subgraph "Sécurité"
|
|
||||||
Turnstile["🛡️ Cloudflared Turnstile<br>(CAPTCHA)"]:::security
|
|
||||||
BannedIPs["🚫 CrowdSec<br>(IP bannies)"]:::security
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
%% Connexions Pi-hole → Livebox (DNS)
|
|
||||||
PiHole1 -.->|DNS| Livebox
|
|
||||||
PiHole3 -.->|DNS| Livebox
|
|
||||||
|
|
||||||
%% Internet → Livebox
|
|
||||||
CloudflareService --> Livebox
|
|
||||||
|
|
||||||
%% Gitea Act Runner → Gitea
|
|
||||||
GiteaRunner1 -.->|communication| Gitea
|
|
||||||
GiteaRunner3 -.->|communication| Gitea
|
|
||||||
|
|
||||||
%% Cloudflare → Cloudflared Tunnel → Sécurité → Traefik
|
|
||||||
CloudflareService -.-> Cloudflared
|
|
||||||
Cloudflared --> Turnstile
|
|
||||||
Turnstile --> BannedIPs
|
|
||||||
BannedIPs --> Traefik
|
|
||||||
|
|
||||||
%% Livebox → Sécurité → Traefik
|
|
||||||
Livebox --> Turnstile
|
|
||||||
|
|
||||||
%% Domaines → Gitea/CMS via Traefik
|
|
||||||
Traefik -->|gitea.arcodange.fr| Gitea:::domainFR
|
|
||||||
Traefik -->|gitea.arcodange.lab| Gitea:::domainLAB
|
|
||||||
Traefik -->|cms-rec.arcodange.fr| CMS:::domainFR
|
|
||||||
Traefik -->|www.arcodange.lab| CMS:::domainLAB
|
|
||||||
|
|
||||||
```
|
|
||||||
## CI/CD Architecture
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
%%{init: {'theme': 'base', 'themeVariables': { 'primaryColor': '#333333', 'edgeLabelBackground':'#f0f0f0', 'tertiaryColor': '#e67e22'}}}%%
|
|
||||||
graph TD
|
|
||||||
%% Styles
|
|
||||||
classDef gitea fill:#ffcc99,stroke:#cc9966,color:#333;
|
|
||||||
classDef argocd fill:#99ffcc,stroke:#66cc99,color:#333;
|
|
||||||
classDef vault fill:#ccccff,stroke:#6666cc,color:#333;
|
|
||||||
classDef k3s fill:#ff9999,stroke:#cc0000,color:#333;
|
|
||||||
classDef runner fill:#ffff99,stroke:#cccc00,color:#333;
|
|
||||||
|
|
||||||
%% Components
|
|
||||||
Gitea["Gitea (pi2)"]:::gitea
|
|
||||||
ArgoCD["ArgoCD (k3s)"]:::argocd
|
|
||||||
Vault["Vault (k3s/tools)"]:::vault
|
|
||||||
Runner1["Gitea Act Runner (pi1)"]:::runner
|
|
||||||
Runner2["Gitea Act Runner (pi3)"]:::runner
|
|
||||||
VaultOperator["Vault Secrets Operator (k3s/tools)"]:::vault
|
|
||||||
k3s["k3s Cluster"]:::k3s
|
|
||||||
WebApp["WebApp (k3s)"]:::k3s
|
|
||||||
|
|
||||||
%% Workflow
|
|
||||||
Gitea -->|OIDC Auth| Vault
|
|
||||||
Gitea -->|Trigger CI| Runner1
|
|
||||||
Gitea -->|Trigger CI| Runner2
|
|
||||||
Runner1 -->|Deploy to| k3s
|
|
||||||
Runner2 -->|Deploy to| k3s
|
|
||||||
ArgoCD -->|GitOps Sync| Gitea
|
|
||||||
ArgoCD -->|Deploy Apps| k3s
|
|
||||||
VaultOperator -->|Inject Secrets| WebApp
|
|
||||||
Vault -->|Secrets| VaultOperator
|
|
||||||
WebApp -->|Uses| VaultOperator
|
|
||||||
|
|
||||||
%% Annotations
|
|
||||||
linkStyle 0,1,2,3,4,5,6,7 stroke:#999,stroke-width:1px;
|
|
||||||
|
|
||||||
%% Notes
|
|
||||||
note1["Custom runner image includes<br>.lab CA cert"]
|
|
||||||
Runner1 --> note1
|
|
||||||
Runner2 --> note1
|
|
||||||
|
|
||||||
%% OIDC Workflow
|
|
||||||
note2["OIDC Flow:<br>1. Runner executes vault_oauth__sh_b64<br>2. Obtains ID token from Gitea<br>3. Authenticates with Vault"]
|
|
||||||
Runner1 --> note2
|
|
||||||
Runner2 --> note2
|
|
||||||
```
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user