Initial Phoenix Sankofa Cloud setup

- Complete project structure with Next.js frontend
- GraphQL API backend with Apollo Server
- Portal application with NextAuth
- Crossplane Proxmox provider
- GitOps configurations
- CI/CD pipelines
- Testing infrastructure (Vitest, Jest, Go tests)
- Error handling and monitoring
- Security hardening
- UI component library
- Documentation
This commit is contained in:
defiQUG
2025-11-28 12:54:33 -08:00
commit 6f28146ac3
229 changed files with 43136 additions and 0 deletions

84
cloudflare/README.md Normal file
View File

@@ -0,0 +1,84 @@
# Cloudflare Zero Trust Configuration
This directory contains all Cloudflare Zero Trust configurations for secure global access to the hybrid cloud control plane.
## Structure
```
cloudflare/
├── access-policies.yaml # Access policies for applications
├── tunnel-configs/ # Per-site tunnel configurations
├── gateway-policies.yaml # DNS and filtering policies
├── warp-config.json # WARP device enrollment
└── terraform/ # Terraform modules for Cloudflare
```
## Components
### Access Policies
Define who can access which applications based on:
- User identity (from Keycloak)
- Device posture
- IP address
- MFA requirements
- Time-based rules
### Tunnels
Outbound-only connections from Proxmox sites and control plane to Cloudflare:
- No public IPs required
- Automatic reconnection
- Load balancing across multiple tunnels
- Health checks
### Gateway Policies
DNS filtering and network security:
- Block malicious domains
- Log DNS queries
- Apply policies based on user/device
- Split DNS for internal services
### WARP
Device-level VPN for employees:
- Zero Trust network access
- Device posture checks
- Automatic enrollment
## Usage
### Apply Access Policies
```bash
# Using Cloudflare API
cloudflared access policy create --config access-policies.yaml
# Or via Terraform
cd terraform
terraform apply
```
### Deploy Tunnels
1. Create tunnel in Cloudflare dashboard
2. Copy tunnel token
3. Update tunnel config with token
4. Deploy cloudflared agent with config
```bash
cloudflared tunnel run --config tunnel-configs/site-1.yaml
```
### Configure WARP
1. Create WARP enrollment in Cloudflare dashboard
2. Update warp-config.json with enrollment details
3. Distribute config to devices
## Security Best Practices
- Use service tokens for API access
- Rotate tunnel tokens regularly
- Enable MFA for all access policies
- Use device posture checks
- Log all access attempts
- Review policies quarterly

View File

@@ -0,0 +1,263 @@
# Cloudflare Zero Trust Access Policies
# These policies control who can access which applications
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflare-access-policies
namespace: default
data:
# Portal Access Policy
portal-policy: |
{
"name": "Portal Access",
"application": {
"domain": "portal.yourdomain.com",
"name": "Hybrid Cloud Portal"
},
"policies": [
{
"name": "Allow Authenticated Users",
"decision": "allow",
"include": [
{
"email": {
"domain": "yourdomain.com"
}
}
],
"require": [
{
"email": {
"domain": "yourdomain.com"
}
}
],
"session_duration": "24h"
},
{
"name": "Require MFA for Admins",
"decision": "allow",
"include": [
{
"group": {
"name": "admins"
}
}
],
"require": [
{
"mfa": {}
}
],
"session_duration": "8h"
}
]
}
# Rancher Access Policy
rancher-policy: |
{
"name": "Rancher Access",
"application": {
"domain": "rancher.yourdomain.com",
"name": "Rancher UI"
},
"policies": [
{
"name": "Allow Admin Group",
"decision": "allow",
"include": [
{
"group": {
"name": "admins"
}
},
{
"group": {
"name": "platform-engineers"
}
}
],
"require": [
{
"mfa": {}
},
{
"device_posture": {
"check": "managed_device"
}
}
],
"session_duration": "4h"
}
]
}
# ArgoCD Access Policy
argocd-policy: |
{
"name": "ArgoCD Access",
"application": {
"domain": "argocd.yourdomain.com",
"name": "ArgoCD GitOps"
},
"policies": [
{
"name": "Allow Platform Engineers",
"decision": "allow",
"include": [
{
"group": {
"name": "platform-engineers"
}
},
{
"group": {
"name": "admins"
}
}
],
"require": [
{
"mfa": {}
}
],
"session_duration": "8h"
}
]
}
# Grafana Access Policy
grafana-policy: |
{
"name": "Grafana Access",
"application": {
"domain": "grafana.yourdomain.com",
"name": "Grafana Dashboards"
},
"policies": [
{
"name": "Allow All Authenticated",
"decision": "allow",
"include": [
{
"email": {
"domain": "yourdomain.com"
}
}
],
"session_duration": "24h"
}
]
}
# Vault Access Policy
vault-policy: |
{
"name": "Vault Access",
"application": {
"domain": "vault.yourdomain.com",
"name": "HashiCorp Vault"
},
"policies": [
{
"name": "Allow Admin Group Only",
"decision": "allow",
"include": [
{
"group": {
"name": "admins"
}
}
],
"require": [
{
"mfa": {}
},
{
"device_posture": {
"check": "managed_device"
}
}
],
"session_duration": "2h"
}
]
}
# Proxmox API Access Policy
proxmox-api-policy: |
{
"name": "Proxmox API Access",
"application": {
"domain": "proxmox-api.yourdomain.com",
"name": "Proxmox API"
},
"policies": [
{
"name": "Allow Service Accounts",
"decision": "allow",
"include": [
{
"service_token": {
"name": "crossplane-proxmox-token"
}
}
],
"session_duration": "1h"
},
{
"name": "Allow Platform Engineers",
"decision": "allow",
"include": [
{
"group": {
"name": "platform-engineers"
}
}
],
"require": [
{
"mfa": {}
}
],
"session_duration": "4h"
}
]
}
# Keycloak Access Policy
keycloak-policy: |
{
"name": "Keycloak Access",
"application": {
"domain": "keycloak.yourdomain.com",
"name": "Keycloak Admin"
},
"policies": [
{
"name": "Allow Admin Group Only",
"decision": "allow",
"include": [
{
"group": {
"name": "admins"
}
}
],
"require": [
{
"mfa": {}
},
{
"device_posture": {
"check": "managed_device"
}
}
],
"session_duration": "2h"
}
]
}

View File

@@ -0,0 +1,149 @@
# Cloudflare Gateway Policies
# DNS filtering and network security policies
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflare-gateway-policies
namespace: default
data:
# DNS Policies
dns-policies: |
{
"policies": [
{
"name": "Block Malicious Domains",
"action": "block",
"precedence": 1,
"filters": [
{
"type": "dns",
"categories": [
"malware",
"phishing",
"command-and-control",
"ransomware",
"spyware"
]
}
]
},
{
"name": "Block Adult Content",
"action": "block",
"precedence": 2,
"filters": [
{
"type": "dns",
"categories": [
"adult"
]
}
],
"identity": {
"groups": [
{
"name": "employees"
}
]
}
},
{
"name": "Allow All for Admins",
"action": "allow",
"precedence": 100,
"identity": {
"groups": [
{
"name": "admins"
}
]
}
}
]
}
# Network Policies
network-policies: |
{
"policies": [
{
"name": "Block High Risk Ports",
"action": "block",
"precedence": 1,
"rules": [
{
"protocol": "tcp",
"ports": [
"22",
"23",
"135",
"139",
"445",
"1433",
"3306",
"3389",
"5432"
]
}
],
"identity": {
"groups": [
{
"name": "employees"
}
}
}
},
{
"name": "Allow Admin Access",
"action": "allow",
"precedence": 100,
"identity": {
"groups": [
{
"name": "admins"
},
{
"name": "platform-engineers"
}
}
}
}
]
}
# Logging Configuration
logging-config: |
{
"dns": {
"enabled": true,
"log_all": true,
"log_blocks": true
},
"network": {
"enabled": true,
"log_all": true,
"log_blocks": true
},
"retention": {
"days": 30
}
}
# Split DNS Configuration
split-dns: |
{
"domains": [
"yourdomain.com",
"*.yourdomain.com",
"*.svc.cluster.local",
"*.local"
],
"dns_servers": [
"10.0.0.53",
"10.1.0.53",
"10.2.0.53"
]
}

13
cloudflare/terraform/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
# Terraform files
*.tfstate
*.tfstate.*
.terraform/
.terraform.lock.hcl
terraform.tfvars
*.tfvars
crash.log
override.tf
override.tf.json
*_override.tf
*_override.tf.json

View File

@@ -0,0 +1,250 @@
terraform {
required_version = ">= 1.0"
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4.0"
}
}
backend "s3" {
# Configure your backend here
# bucket = "your-terraform-state"
# key = "cloudflare/terraform.tfstate"
# region = "us-east-1"
}
}
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
# Variables
variable "cloudflare_api_token" {
description = "Cloudflare API token"
type = string
sensitive = true
}
variable "zone_id" {
description = "Cloudflare Zone ID"
type = string
}
variable "account_id" {
description = "Cloudflare Account ID"
type = string
}
# Access Applications
resource "cloudflare_access_application" "portal" {
zone_id = var.zone_id
name = "Hybrid Cloud Portal"
domain = "portal.yourdomain.com"
session_duration = "24h"
cors_headers {
allowed_methods = ["GET", "POST", "PUT", "DELETE"]
allowed_origins = ["https://portal.yourdomain.com"]
allow_credentials = true
}
}
resource "cloudflare_access_application" "rancher" {
zone_id = var.zone_id
name = "Rancher UI"
domain = "rancher.yourdomain.com"
session_duration = "4h"
}
resource "cloudflare_access_application" "argocd" {
zone_id = var.zone_id
name = "ArgoCD GitOps"
domain = "argocd.yourdomain.com"
session_duration = "8h"
}
resource "cloudflare_access_application" "grafana" {
zone_id = var.zone_id
name = "Grafana Dashboards"
domain = "grafana.yourdomain.com"
session_duration = "24h"
}
resource "cloudflare_access_application" "vault" {
zone_id = var.zone_id
name = "HashiCorp Vault"
domain = "vault.yourdomain.com"
session_duration = "2h"
}
resource "cloudflare_access_application" "keycloak" {
zone_id = var.zone_id
name = "Keycloak Admin"
domain = "keycloak.yourdomain.com"
session_duration = "2h"
}
# Access Policies
resource "cloudflare_access_policy" "portal_authenticated" {
application_id = cloudflare_access_application.portal.id
zone_id = var.zone_id
name = "Allow Authenticated Users"
decision = "allow"
precedence = 1
include {
email_domain = "yourdomain.com"
}
}
resource "cloudflare_access_policy" "portal_admin_mfa" {
application_id = cloudflare_access_application.portal.id
zone_id = var.zone_id
name = "Require MFA for Admins"
decision = "allow"
precedence = 2
include {
group = cloudflare_access_group.admins.id
}
require {
mfa = true
}
}
# Access Groups
resource "cloudflare_access_group" "admins" {
account_id = var.account_id
name = "admins"
include {
email_domain = "yourdomain.com"
}
require {
email = ["admin@yourdomain.com"]
}
}
resource "cloudflare_access_group" "platform_engineers" {
account_id = var.account_id
name = "platform-engineers"
include {
email_domain = "yourdomain.com"
}
}
resource "cloudflare_access_group" "employees" {
account_id = var.account_id
name = "employees"
include {
email_domain = "yourdomain.com"
}
}
# Tunnels
resource "cloudflare_tunnel" "control_plane" {
account_id = var.account_id
name = "control-plane-tunnel"
secret = var.tunnel_secret_control_plane
}
resource "cloudflare_tunnel" "proxmox_site_1" {
account_id = var.account_id
name = "proxmox-site-1-tunnel"
secret = var.tunnel_secret_site_1
}
resource "cloudflare_tunnel" "proxmox_site_2" {
account_id = var.account_id
name = "proxmox-site-2-tunnel"
secret = var.tunnel_secret_site_2
}
resource "cloudflare_tunnel" "proxmox_site_3" {
account_id = var.account_id
name = "proxmox-site-3-tunnel"
secret = var.tunnel_secret_site_3
}
# Tunnel Routes
resource "cloudflare_tunnel_route" "control_plane" {
account_id = var.account_id
tunnel_id = cloudflare_tunnel.control_plane.id
network = "10.0.0.0/16"
comment = "Control plane network"
}
resource "cloudflare_tunnel_route" "site_1" {
account_id = var.account_id
tunnel_id = cloudflare_tunnel.proxmox_site_1.id
network = "10.1.0.0/16"
comment = "Proxmox site 1 network"
}
resource "cloudflare_tunnel_route" "site_2" {
account_id = var.account_id
tunnel_id = cloudflare_tunnel.proxmox_site_2.id
network = "10.2.0.0/16"
comment = "Proxmox site 2 network"
}
resource "cloudflare_tunnel_route" "site_3" {
account_id = var.account_id
tunnel_id = cloudflare_tunnel.proxmox_site_3.id
network = "10.3.0.0/16"
comment = "Proxmox site 3 network"
}
# Gateway Policies
resource "cloudflare_teams_list" "blocked_domains" {
account_id = var.account_id
name = "Blocked Domains"
type = "DOMAIN"
items = [
"malware.example.com",
"phishing.example.com"
]
}
resource "cloudflare_teams_rule" "block_malicious" {
account_id = var.account_id
name = "Block Malicious Domains"
description = "Block known malicious domains"
precedence = 1
action = "block"
filters = ["dns"]
rule_settings {
block_page_enabled = true
block_reason = "This domain is blocked by security policy"
}
}
# Outputs
output "tunnel_ids" {
value = {
control_plane = cloudflare_tunnel.control_plane.id
site_1 = cloudflare_tunnel.proxmox_site_1.id
site_2 = cloudflare_tunnel.proxmox_site_2.id
site_3 = cloudflare_tunnel.proxmox_site_3.id
}
}
output "application_ids" {
value = {
portal = cloudflare_access_application.portal.id
rancher = cloudflare_access_application.rancher.id
argocd = cloudflare_access_application.argocd.id
grafana = cloudflare_access_application.grafana.id
vault = cloudflare_access_application.vault.id
keycloak = cloudflare_access_application.keycloak.id
}
}

View File

@@ -0,0 +1,13 @@
# Copy this file to terraform.tfvars and fill in your values
# terraform.tfvars should be in .gitignore
cloudflare_api_token = "your-cloudflare-api-token"
zone_id = "your-zone-id"
account_id = "your-account-id"
# Generate secrets with: openssl rand -base64 32
tunnel_secret_control_plane = "your-control-plane-tunnel-secret"
tunnel_secret_site_1 = "your-site-1-tunnel-secret"
tunnel_secret_site_2 = "your-site-2-tunnel-secret"
tunnel_secret_site_3 = "your-site-3-tunnel-secret"

View File

@@ -0,0 +1,40 @@
variable "cloudflare_api_token" {
description = "Cloudflare API token with appropriate permissions"
type = string
sensitive = true
}
variable "zone_id" {
description = "Cloudflare Zone ID for yourdomain.com"
type = string
}
variable "account_id" {
description = "Cloudflare Account ID"
type = string
}
variable "tunnel_secret_control_plane" {
description = "Secret for control plane tunnel (generate with: openssl rand -base64 32)"
type = string
sensitive = true
}
variable "tunnel_secret_site_1" {
description = "Secret for Proxmox site 1 tunnel"
type = string
sensitive = true
}
variable "tunnel_secret_site_2" {
description = "Secret for Proxmox site 2 tunnel"
type = string
sensitive = true
}
variable "tunnel_secret_site_3" {
description = "Secret for Proxmox site 3 tunnel"
type = string
sensitive = true
}

View File

@@ -0,0 +1,77 @@
# Cloudflare Tunnel Configuration for Control Plane
# This tunnel connects the Kubernetes control plane to Cloudflare
tunnel: control-plane-tunnel
credentials-file: /etc/cloudflared/control-plane-tunnel.json
ingress:
# Portal
- hostname: portal.yourdomain.com
service: http://portal.portal.svc.cluster.local:80
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tcpKeepAlive: 30s
keepAliveConnections: 100
keepAliveTimeout: 90s
# Rancher
- hostname: rancher.yourdomain.com
service: http://rancher.rancher-system.svc.cluster.local:80
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# ArgoCD
- hostname: argocd.yourdomain.com
service: http://argocd-server.argocd.svc.cluster.local:80
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# Grafana
- hostname: grafana.yourdomain.com
service: http://kube-prometheus-stack-grafana.monitoring.svc.cluster.local:80
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# Vault
- hostname: vault.yourdomain.com
service: http://vault.vault.svc.cluster.local:8200
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# Keycloak
- hostname: keycloak.yourdomain.com
service: http://keycloak.keycloak.svc.cluster.local:8080
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# Kubernetes API (restricted)
- hostname: k8s-api.yourdomain.com
service: https://kubernetes.default.svc.cluster.local:443
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: false
# Catch-all rule (must be last)
- service: http_status:404
# Logging
loglevel: info
logfile: /var/log/cloudflared/control-plane-tunnel.log
# Metrics
metrics: 0.0.0.0:9090
# Health check
health-probe:
enabled: true
path: /health
port: 8080

View File

@@ -0,0 +1,70 @@
# Cloudflare Tunnel Configuration for Proxmox Site 1 (US-East)
# This tunnel connects Proxmox cluster to Cloudflare
tunnel: proxmox-site-1-tunnel
credentials-file: /etc/cloudflared/proxmox-site-1-tunnel.json
ingress:
# Proxmox Web UI
- hostname: pve1.yourdomain.com
service: https://pve1.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve1.local:8006
# Proxmox API
- hostname: pve1-api.yourdomain.com
service: https://pve1.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve1.local:8006
# Proxmox Node 2
- hostname: pve2.yourdomain.com
service: https://pve2.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve2.local:8006
# Proxmox Node 3
- hostname: pve3.yourdomain.com
service: https://pve3.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve3.local:8006
# Prometheus Exporter
- hostname: pve1-metrics.yourdomain.com
service: http://localhost:9221
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# Catch-all rule (must be last)
- service: http_status:404
# Logging
loglevel: info
logfile: /var/log/cloudflared/proxmox-site-1-tunnel.log
# Metrics
metrics: 0.0.0.0:9091
# Health check
health-probe:
enabled: true
path: /health
port: 8080

View File

@@ -0,0 +1,70 @@
# Cloudflare Tunnel Configuration for Proxmox Site 2 (EU-West)
# This tunnel connects Proxmox cluster to Cloudflare
tunnel: proxmox-site-2-tunnel
credentials-file: /etc/cloudflared/proxmox-site-2-tunnel.json
ingress:
# Proxmox Web UI
- hostname: pve4.yourdomain.com
service: https://pve4.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve4.local:8006
# Proxmox API
- hostname: pve4-api.yourdomain.com
service: https://pve4.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve4.local:8006
# Proxmox Node 2
- hostname: pve5.yourdomain.com
service: https://pve5.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve5.local:8006
# Proxmox Node 3
- hostname: pve6.yourdomain.com
service: https://pve6.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve6.local:8006
# Prometheus Exporter
- hostname: pve4-metrics.yourdomain.com
service: http://localhost:9221
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# Catch-all rule (must be last)
- service: http_status:404
# Logging
loglevel: info
logfile: /var/log/cloudflared/proxmox-site-2-tunnel.log
# Metrics
metrics: 0.0.0.0:9092
# Health check
health-probe:
enabled: true
path: /health
port: 8080

View File

@@ -0,0 +1,60 @@
# Cloudflare Tunnel Configuration for Proxmox Site 3 (APAC)
# This tunnel connects Proxmox cluster to Cloudflare
tunnel: proxmox-site-3-tunnel
credentials-file: /etc/cloudflared/proxmox-site-3-tunnel.json
ingress:
# Proxmox Web UI
- hostname: pve7.yourdomain.com
service: https://pve7.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve7.local:8006
# Proxmox API
- hostname: pve7-api.yourdomain.com
service: https://pve7.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve7.local:8006
# Proxmox Node 2
- hostname: pve8.yourdomain.com
service: https://pve8.local:8006
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
tls:
skipVerify: true
httpHostHeader: pve8.local:8006
# Prometheus Exporter
- hostname: pve7-metrics.yourdomain.com
service: http://localhost:9221
originRequest:
noHappyEyeballs: true
connectTimeout: 30s
# Catch-all rule (must be last)
- service: http_status:404
# Logging
loglevel: info
logfile: /var/log/cloudflared/proxmox-site-3-tunnel.log
# Metrics
metrics: 0.0.0.0:9093
# Health check
health-probe:
enabled: true
path: /health
port: 8080

129
cloudflare/warp-config.json Normal file
View File

@@ -0,0 +1,129 @@
{
"organization": {
"name": "Your Organization",
"auth_domain": "yourdomain.com"
},
"enrollment": {
"enabled": true,
"mode": "automatic",
"require_mfa": true,
"device_posture_checks": [
"managed_device",
"os_version",
"disk_encryption"
]
},
"policies": [
{
"name": "Default WARP Policy",
"description": "Default policy for all WARP devices",
"rules": [
{
"action": "allow",
"match": "any",
"identity": {
"groups": [
{
"name": "employees"
}
]
}
}
]
},
{
"name": "Admin WARP Policy",
"description": "Enhanced access for administrators",
"rules": [
{
"action": "allow",
"match": "any",
"identity": {
"groups": [
{
"name": "admins"
},
{
"name": "platform-engineers"
}
]
},
"require": [
{
"mfa": {}
}
]
}
]
}
],
"device_posture": {
"checks": [
{
"name": "managed_device",
"type": "os_version",
"enabled": true,
"rules": [
{
"os": "windows",
"min_version": "10.0.19041"
},
{
"os": "macos",
"min_version": "11.0"
},
{
"os": "linux",
"min_version": "5.4"
}
]
},
{
"name": "disk_encryption",
"type": "disk_encryption",
"enabled": true,
"require": true
},
{
"name": "firewall_enabled",
"type": "firewall",
"enabled": true,
"require": true
}
]
},
"settings": {
"gateway_proxy": {
"enabled": true,
"tcp_port": 4000,
"udp_port": 4001
},
"split_tunnels": {
"enabled": true,
"exclude": [
"*.yourdomain.com",
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16"
]
},
"dns": {
"servers": [
"1.1.1.1",
"1.0.0.1"
],
"split_dns": [
{
"domains": [
"yourdomain.com",
"*.yourdomain.com"
],
"servers": [
"10.0.0.53"
]
}
]
}
}
}