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