feat(ci,iac): Consolidate Forgejo CI workflows and add Talos Terraform IaC
Consolidate three separate Forgejo Actions (argocd-sync, security-scan, validate-k8s) into single cluster-ci workflow for cleaner CI/CD pipeline with proper job sequencing and reduced auth overhead. Add Terraform configuration for Talos cluster machine configs: - Provider setup for Talos - Centralized variables for CP and worker configs - Template-based config generation for controlplane.yaml and worker-*.yaml - Sensitive data separated in terraform.tfvars (gitignored) - Local state tracking for infrastructure
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
# Terraform state
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
.terraform/
|
||||
.terraform.lock.hcl
|
||||
|
||||
# Variables with secrets
|
||||
terraform.tfvars
|
||||
*.auto.tfvars
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
@@ -0,0 +1,126 @@
|
||||
# Generate Talos machine configurations
|
||||
|
||||
locals {
|
||||
pod_cidr = var.cluster_config.pod_subnets[0]
|
||||
service_cidr = var.cluster_config.service_subnets[0]
|
||||
controlplane_ip = var.cluster_config.controlplane_ip
|
||||
cluster_dns_ip = "10.96.0.10"
|
||||
kubelet_image = "ghcr.io/siderolabs/kubelet:${var.kubernetes_version}"
|
||||
kube_apiserver_img = "registry.k8s.io/kube-apiserver:${var.kubernetes_version}"
|
||||
controller_mgr_img = "registry.k8s.io/kube-controller-manager:${var.kubernetes_version}"
|
||||
kube_proxy_img = "registry.k8s.io/kube-proxy:${var.kubernetes_version}"
|
||||
scheduler_img = "registry.k8s.io/kube-scheduler:${var.kubernetes_version}"
|
||||
|
||||
factory_image = "factory.talos.dev/installer/613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245:${var.talos_version}"
|
||||
}
|
||||
|
||||
# Control plane machine configuration
|
||||
resource "local_file" "controlplane_config" {
|
||||
filename = "${path.module}/../cluster-config/controlplane.yaml"
|
||||
|
||||
content = templatefile("${path.module}/templates/controlplane.tftpl", {
|
||||
version = "v1alpha1"
|
||||
hostname = var.controlplane_config.hostname
|
||||
token = var.machine_token
|
||||
ca_crt = var.machine_ca_crt
|
||||
ca_key = var.machine_ca_key
|
||||
lan_ip = var.controlplane_config.lan_ip
|
||||
lan_subnet = var.controlplane_config.lan_subnet
|
||||
lan_gateway = var.controlplane_config.lan_gateway
|
||||
wg0_ip = var.controlplane_config.wg0_ip
|
||||
wg0_subnet = var.controlplane_config.wg0_subnet
|
||||
wg0_port = var.controlplane_config.wg0_port
|
||||
wg0_private_key = var.controlplane_config.wg0_private_key
|
||||
wg0_peers = var.controlplane_config.wg0_peers
|
||||
wg1_ip = var.controlplane_config.wg1_ip
|
||||
wg1_subnet = var.controlplane_config.wg1_subnet
|
||||
wg1_port = var.controlplane_config.wg1_port
|
||||
wg1_private_key = var.controlplane_config.wg1_private_key
|
||||
wg1_peers = var.controlplane_config.wg1_peers
|
||||
kubelet_image = local.kubelet_image
|
||||
cluster_dns_ip = local.cluster_dns_ip
|
||||
install_disk = var.controlplane_config.install_disk
|
||||
factory_image = local.factory_image
|
||||
longhorn_disks = var.controlplane_config.longhorn_disks
|
||||
dns_servers = var.cluster_config.dns_servers
|
||||
forgejo_registry_ip = var.forgejo_registry_ip
|
||||
forgejo_hostname = var.forgejo_hostname
|
||||
|
||||
# Cluster config
|
||||
cluster_id = var.cluster_id
|
||||
cluster_secret = var.cluster_secret
|
||||
controlplane_ip = local.controlplane_ip
|
||||
cluster_name = var.cluster_name
|
||||
pod_subnets = var.cluster_config.pod_subnets
|
||||
service_subnets = var.cluster_config.service_subnets
|
||||
dns_domain = var.cluster_config.dns_domain
|
||||
bootstrap_token = var.bootstrap_token
|
||||
|
||||
# Kubernetes certs
|
||||
kubernetes_ca_crt = var.kubernetes_ca_crt
|
||||
kubernetes_ca_key = var.kubernetes_ca_key
|
||||
etcd_ca_crt = var.etcd_ca_crt
|
||||
etcd_ca_key = var.etcd_ca_key
|
||||
aggregator_ca_crt = var.aggregator_ca_crt
|
||||
aggregator_ca_key = var.aggregator_ca_key
|
||||
service_account_key = var.service_account_key
|
||||
secretbox_encryption_secret = var.secretbox_encryption_secret
|
||||
|
||||
# Component images
|
||||
kube_apiserver_img = local.kube_apiserver_img
|
||||
controller_mgr_img = local.controller_mgr_img
|
||||
kube_proxy_img = local.kube_proxy_img
|
||||
scheduler_img = local.scheduler_img
|
||||
})
|
||||
}
|
||||
|
||||
# Worker machine configurations
|
||||
resource "local_file" "worker_configs" {
|
||||
for_each = var.worker_configs
|
||||
|
||||
filename = "${path.module}/../cluster-config/${each.key}.yaml"
|
||||
|
||||
content = templatefile("${path.module}/templates/worker.tftpl", {
|
||||
version = "v1alpha1"
|
||||
hostname = each.value.hostname
|
||||
token = var.machine_token
|
||||
ca_crt = var.machine_ca_crt
|
||||
lan_ip = each.value.lan_ip
|
||||
lan_subnet = each.value.lan_subnet
|
||||
lan_gateway = each.value.lan_gateway
|
||||
kubelet_image = local.kubelet_image
|
||||
cluster_dns_ip = local.cluster_dns_ip
|
||||
install_disk = each.value.install_disk
|
||||
factory_image = local.factory_image
|
||||
node_labels = each.value.node_labels
|
||||
|
||||
# Cluster config
|
||||
cluster_id = var.cluster_id
|
||||
cluster_secret = var.cluster_secret
|
||||
controlplane_ip = local.controlplane_ip
|
||||
cluster_name = var.cluster_name
|
||||
pod_subnets = var.cluster_config.pod_subnets
|
||||
service_subnets = var.cluster_config.service_subnets
|
||||
dns_domain = var.cluster_config.dns_domain
|
||||
bootstrap_token = var.bootstrap_token
|
||||
|
||||
# Kubernetes certs
|
||||
kubernetes_ca_crt = var.kubernetes_ca_crt
|
||||
|
||||
# Component images
|
||||
kube_proxy_img = local.kube_proxy_img
|
||||
})
|
||||
}
|
||||
|
||||
# Output paths for reference
|
||||
output "controlplane_config_path" {
|
||||
value = local_file.controlplane_config.filename
|
||||
description = "Path to generated controlplane config"
|
||||
}
|
||||
|
||||
output "worker_config_paths" {
|
||||
value = {
|
||||
for k, v in local_file.worker_configs : k => v.filename
|
||||
}
|
||||
description = "Paths to generated worker configs"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
talos = {
|
||||
source = "siderolabs/talos"
|
||||
version = "~> 0.7"
|
||||
}
|
||||
}
|
||||
|
||||
required_version = ">= 1.0"
|
||||
}
|
||||
|
||||
provider "talos" {}
|
||||
@@ -0,0 +1,179 @@
|
||||
version: ${version}
|
||||
debug: false
|
||||
persist: true
|
||||
|
||||
machine:
|
||||
type: controlplane
|
||||
token: ${token}
|
||||
ca:
|
||||
crt: ${ca_crt}
|
||||
key: ${ca_key}
|
||||
certSANs:
|
||||
- ${lan_ip}
|
||||
- ${wg0_ip}
|
||||
network:
|
||||
hostname: ${hostname}
|
||||
interfaces:
|
||||
- interface: eno1
|
||||
addresses:
|
||||
- ${lan_ip}/24
|
||||
routes:
|
||||
- network: 0.0.0.0/0
|
||||
gateway: ${lan_gateway}
|
||||
dhcp: false
|
||||
dhcpOptions:
|
||||
ipv6: false
|
||||
- interface: wg0
|
||||
addresses:
|
||||
- ${wg0_ip}/24
|
||||
wireguard:
|
||||
privateKey: "${wg0_private_key}"
|
||||
listenPort: ${wg0_port}
|
||||
peers:
|
||||
%{ for peer in wg0_peers ~}
|
||||
- publicKey: "${peer.public_key}"
|
||||
allowedIPs:
|
||||
%{ for ip in peer.allowed_ips ~}
|
||||
- ${ip}
|
||||
%{ endfor ~}
|
||||
%{ endfor ~}
|
||||
- interface: wg1
|
||||
addresses:
|
||||
- ${wg1_ip}/24
|
||||
wireguard:
|
||||
privateKey: "${wg1_private_key}"
|
||||
listenPort: ${wg1_port}
|
||||
peers:
|
||||
%{ for peer in wg1_peers ~}
|
||||
- publicKey: "${peer.public_key}"
|
||||
allowedIPs:
|
||||
%{ for ip in peer.allowed_ips ~}
|
||||
- ${ip}
|
||||
%{ endfor ~}
|
||||
persistentKeepaliveInterval: ${peer.persistent_keepalive_secs}s
|
||||
%{ endfor ~}
|
||||
nameservers:
|
||||
%{ for ns in dns_servers ~}
|
||||
- ${ns}
|
||||
%{ endfor ~}
|
||||
extraHostEntries:
|
||||
- ip: ${forgejo_registry_ip}
|
||||
aliases:
|
||||
- ${forgejo_hostname}
|
||||
kubelet:
|
||||
image: ${kubelet_image}
|
||||
defaultRuntimeSeccompProfileEnabled: true
|
||||
disableManifestsDirectory: true
|
||||
clusterDNS:
|
||||
- ${cluster_dns_ip}
|
||||
extraArgs:
|
||||
rotate-server-certificates: true
|
||||
nodeIP:
|
||||
validSubnets:
|
||||
- 192.168.1.0/24
|
||||
install:
|
||||
disk: ${install_disk}
|
||||
image: factory.talos.dev/installer/613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245:${talos_version}
|
||||
wipe: true
|
||||
grubUseUKICmdline: true
|
||||
disks:
|
||||
%{ for disk in longhorn_disks ~}
|
||||
- device: ${disk.device}
|
||||
partitions:
|
||||
- mountpoint: ${disk.mountpoint}
|
||||
%{ endfor ~}
|
||||
features:
|
||||
diskQuotaSupport: true
|
||||
kubePrism:
|
||||
enabled: true
|
||||
port: 7445
|
||||
hostDNS:
|
||||
enabled: false
|
||||
nodeLabels:
|
||||
node.kubernetes.io/exclude-from-external-load-balancers: ""
|
||||
topology.kubernetes.io/region: homelab
|
||||
topology.kubernetes.io/zone: az-a
|
||||
|
||||
cluster:
|
||||
id: ${cluster_id}
|
||||
secret: ${cluster_secret}
|
||||
controlPlane:
|
||||
endpoint: https://${controlplane_ip}:6443
|
||||
clusterName: ${cluster_name}
|
||||
allowSchedulingOnControlPlanes: true
|
||||
network:
|
||||
dnsDomain: ${dns_domain}
|
||||
podSubnets:
|
||||
%{ for subnet in pod_subnets ~}
|
||||
- ${subnet}
|
||||
%{ endfor ~}
|
||||
serviceSubnets:
|
||||
%{ for subnet in service_subnets ~}
|
||||
- ${subnet}
|
||||
%{ endfor ~}
|
||||
cni:
|
||||
name: none
|
||||
token: ${bootstrap_token}
|
||||
secretboxEncryptionSecret: ${secretbox_encryption_secret}
|
||||
ca:
|
||||
crt: ${kubernetes_ca_crt}
|
||||
key: ${kubernetes_ca_key}
|
||||
aggregatorCA:
|
||||
crt: ${aggregator_ca_crt}
|
||||
key: ${aggregator_ca_key}
|
||||
serviceAccount:
|
||||
key: ${service_account_key}
|
||||
apiServer:
|
||||
certSANs:
|
||||
- ${controlplane_ip}
|
||||
- ${wg0_ip}
|
||||
image: ${kube_apiserver_img}
|
||||
admissionControl:
|
||||
- name: PodSecurity
|
||||
configuration:
|
||||
apiVersion: pod-security.admission.config.k8s.io/v1alpha1
|
||||
defaults:
|
||||
audit: restricted
|
||||
audit-version: latest
|
||||
enforce: baseline
|
||||
enforce-version: latest
|
||||
warn: restricted
|
||||
warn-version: latest
|
||||
exemptions:
|
||||
namespaces:
|
||||
- kube-system
|
||||
runtimeClasses: []
|
||||
usernames: []
|
||||
kind: PodSecurityConfiguration
|
||||
auditPolicy:
|
||||
apiVersion: audit.k8s.io/v1
|
||||
kind: Policy
|
||||
rules:
|
||||
- level: Metadata
|
||||
controllerManager:
|
||||
image: ${controller_mgr_img}
|
||||
proxy:
|
||||
image: ${kube_proxy_img}
|
||||
disabled: true
|
||||
scheduler:
|
||||
image: ${scheduler_img}
|
||||
discovery:
|
||||
enabled: true
|
||||
registries:
|
||||
kubernetes:
|
||||
disabled: true
|
||||
service: {}
|
||||
etcd:
|
||||
ca:
|
||||
crt: ${etcd_ca_crt}
|
||||
key: ${etcd_ca_key}
|
||||
extraManifests:
|
||||
- https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml
|
||||
- https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||
inlineManifests:
|
||||
- name: cilium
|
||||
contents: |
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: kube-system
|
||||
@@ -0,0 +1,83 @@
|
||||
version: ${version}
|
||||
debug: false
|
||||
persist: true
|
||||
|
||||
machine:
|
||||
type: worker
|
||||
token: ${token}
|
||||
ca:
|
||||
crt: ${ca_crt}
|
||||
key: ""
|
||||
certSANs: []
|
||||
network:
|
||||
hostname: ${hostname}
|
||||
interfaces:
|
||||
- interface: eno1
|
||||
addresses:
|
||||
- ${lan_ip}/24
|
||||
routes:
|
||||
- network: 0.0.0.0/0
|
||||
gateway: ${lan_gateway}
|
||||
dhcp: false
|
||||
nameservers:
|
||||
- ${cluster_dns_ip}
|
||||
- 8.8.8.8
|
||||
- 1.1.1.1
|
||||
kubelet:
|
||||
image: ${kubelet_image}
|
||||
defaultRuntimeSeccompProfileEnabled: true
|
||||
disableManifestsDirectory: true
|
||||
extraArgs:
|
||||
rotate-server-certificates: true
|
||||
install:
|
||||
disk: ${install_disk}
|
||||
image: factory.talos.dev/installer/613e1592b2da41ae5e265e8789429f22e121aab91cb4deb6bc3c0b6262961245:${talos_version}
|
||||
wipe: true
|
||||
grubUseUKICmdline: true
|
||||
registries: {}
|
||||
features:
|
||||
diskQuotaSupport: true
|
||||
kubePrism:
|
||||
enabled: true
|
||||
port: 7445
|
||||
hostDNS:
|
||||
enabled: true
|
||||
forwardKubeDNSToHost: true
|
||||
nodeLabels:
|
||||
%{ for k, v in node_labels ~}
|
||||
${k}: ${v}
|
||||
%{ endfor ~}
|
||||
|
||||
cluster:
|
||||
id: ${cluster_id}
|
||||
secret: ${cluster_secret}
|
||||
controlPlane:
|
||||
endpoint: https://${controlplane_ip}:6443
|
||||
clusterName: ${cluster_name}
|
||||
network:
|
||||
dnsDomain: ${dns_domain}
|
||||
podSubnets:
|
||||
%{ for subnet in pod_subnets ~}
|
||||
- ${subnet}
|
||||
%{ endfor ~}
|
||||
serviceSubnets:
|
||||
%{ for subnet in service_subnets ~}
|
||||
- ${subnet}
|
||||
%{ endfor ~}
|
||||
cni:
|
||||
name: none
|
||||
token: ${bootstrap_token}
|
||||
ca:
|
||||
crt: ${kubernetes_ca_crt}
|
||||
key: ""
|
||||
discovery:
|
||||
enabled: true
|
||||
registries:
|
||||
kubernetes:
|
||||
disabled: true
|
||||
service: {}
|
||||
proxy:
|
||||
disabled: true
|
||||
extraManifests:
|
||||
- https://raw.githubusercontent.com/alex1989hu/kubelet-serving-cert-approver/main/deploy/standalone-install.yaml
|
||||
- https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
|
||||
@@ -0,0 +1,168 @@
|
||||
variable "talos_version" {
|
||||
type = string
|
||||
default = "v1.13.3"
|
||||
description = "Talos version"
|
||||
}
|
||||
|
||||
variable "kubernetes_version" {
|
||||
type = string
|
||||
default = "v1.36.1"
|
||||
description = "Kubernetes version"
|
||||
}
|
||||
|
||||
variable "cluster_name" {
|
||||
type = string
|
||||
default = "homelab-cluster"
|
||||
description = "Cluster name"
|
||||
}
|
||||
|
||||
variable "cluster_id" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Globally unique cluster ID (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "cluster_secret" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Shared cluster secret (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "bootstrap_token" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Bootstrap token for joining cluster"
|
||||
}
|
||||
|
||||
variable "machine_token" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Machine PKI token"
|
||||
}
|
||||
|
||||
variable "machine_ca_crt" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Machine CA certificate (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "machine_ca_key" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Machine CA private key (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "kubernetes_ca_crt" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Kubernetes CA certificate (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "kubernetes_ca_key" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Kubernetes CA private key (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "etcd_ca_crt" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Etcd CA certificate (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "etcd_ca_key" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Etcd CA private key (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "aggregator_ca_crt" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Aggregator CA certificate (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "aggregator_ca_key" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Aggregator CA private key (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "service_account_key" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Service account private key (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "secretbox_encryption_secret" {
|
||||
type = string
|
||||
sensitive = true
|
||||
description = "Secretbox encryption secret (base64 encoded)"
|
||||
}
|
||||
|
||||
variable "controlplane_config" {
|
||||
type = object({
|
||||
hostname = string
|
||||
lan_ip = string
|
||||
lan_subnet = string
|
||||
lan_gateway = string
|
||||
wg0_ip = string
|
||||
wg0_subnet = string
|
||||
wg0_port = number
|
||||
wg0_peers = list(object({
|
||||
public_key = string
|
||||
allowed_ips = list(string)
|
||||
}))
|
||||
wg1_ip = string
|
||||
wg1_subnet = string
|
||||
wg1_port = number
|
||||
wg1_peers = list(object({
|
||||
public_key = string
|
||||
allowed_ips = list(string)
|
||||
persistent_keepalive_secs = number
|
||||
}))
|
||||
wg0_private_key = string
|
||||
wg1_private_key = string
|
||||
install_disk = string
|
||||
longhorn_disks = list(object({
|
||||
device = string
|
||||
mountpoint = string
|
||||
}))
|
||||
})
|
||||
description = "Control plane machine configuration"
|
||||
}
|
||||
|
||||
variable "worker_configs" {
|
||||
type = map(object({
|
||||
hostname = string
|
||||
lan_ip = string
|
||||
lan_subnet = string
|
||||
lan_gateway = string
|
||||
install_disk = string
|
||||
node_labels = map(string)
|
||||
}))
|
||||
description = "Worker machine configurations"
|
||||
}
|
||||
|
||||
variable "cluster_config" {
|
||||
type = object({
|
||||
controlplane_ip = string
|
||||
pod_subnets = list(string)
|
||||
service_subnets = list(string)
|
||||
dns_servers = list(string)
|
||||
dns_domain = string
|
||||
})
|
||||
description = "Cluster-wide configuration"
|
||||
}
|
||||
|
||||
variable "forgejo_registry_ip" {
|
||||
type = string
|
||||
default = "10.107.155.96"
|
||||
description = "Forgejo registry (container repo) ClusterIP for host DNS rewrite"
|
||||
}
|
||||
|
||||
variable "forgejo_hostname" {
|
||||
type = string
|
||||
default = "forgejo.riotpiao.homelab.com"
|
||||
description = "Forgejo external hostname"
|
||||
}
|
||||
Reference in New Issue
Block a user