From ec239abcfc19627d91b93a6386577be9d7043051 Mon Sep 17 00:00:00 2001 From: "noussair.hamrit" Date: Fri, 4 Sep 2026 13:17:50 +0200 Subject: [PATCH] feat: add PostgreSQL production cluster GitOps configuration --- cluster.yaml | 32 ++++++ init/init.sql | 237 +++++++++++++++++++++++++++++++++++++++++++ kustomization.yaml | 16 +++ postgresql-prod.yaml | 26 +++++ 4 files changed, 311 insertions(+) create mode 100644 cluster.yaml create mode 100644 init/init.sql create mode 100644 kustomization.yaml create mode 100644 postgresql-prod.yaml diff --git a/cluster.yaml b/cluster.yaml new file mode 100644 index 0000000..d027b04 --- /dev/null +++ b/cluster.yaml @@ -0,0 +1,32 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster + +metadata: + name: postgresql-prod + +spec: + instances: 1 + + imageName: ghcr.io/cloudnative-pg/postgresql:18.4-system-trixie + + storage: + storageClass: longhorn + size: 2Gi + + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2" + memory: "4Gi" + + bootstrap: + initdb: + database: radius + owner: radius + + postInitApplicationSQLRefs: + configMapRefs: + - name: postgresql-init + key: init.sql \ No newline at end of file diff --git a/init/init.sql b/init/init.sql new file mode 100644 index 0000000..3eec320 --- /dev/null +++ b/init/init.sql @@ -0,0 +1,237 @@ +CREATE TABLE radacct ( + radacctid BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + acctsessionid VARCHAR(64) NOT NULL DEFAULT '', + acctuniqueid VARCHAR(32) NOT NULL DEFAULT '', + username VARCHAR(64) NOT NULL DEFAULT '', + groupname VARCHAR(64) NOT NULL DEFAULT '', + realm VARCHAR(64) DEFAULT '', + nasipaddress INET, + nasportid VARCHAR(32), + nasporttype VARCHAR(32), + acctstarttime TIMESTAMP, + acctupdatetime TIMESTAMP, + acctstoptime TIMESTAMP, + acctinterval INTEGER, + acctsessiontime BIGINT, + acctauthentic VARCHAR(32), + connectinfo_start VARCHAR(50), + connectinfo_stop VARCHAR(50), + acctinputoctets BIGINT, + acctoutputoctets BIGINT, + calledstationid VARCHAR(50) NOT NULL DEFAULT '', + callingstationid VARCHAR(50) NOT NULL DEFAULT '', + acctterminatecause VARCHAR(32) NOT NULL DEFAULT '', + servicetype VARCHAR(32), + framedprotocol VARCHAR(32), + framedipaddress INET, + framedipv6address INET, + framedipv6prefix CIDR, + framedinterfaceid VARCHAR(44) NOT NULL DEFAULT '', + delegatedipv6prefix CIDR, + class VARCHAR(64), + + CONSTRAINT radacct_acctuniqueid_key UNIQUE (acctuniqueid) +); + +CREATE INDEX idx_radacct_username ON radacct (username); +CREATE INDEX idx_radacct_framedipaddress ON radacct (framedipaddress); +CREATE INDEX idx_radacct_framedipv6address ON radacct (framedipv6address); +CREATE INDEX idx_radacct_acctsessionid ON radacct (acctsessionid); +CREATE INDEX idx_radacct_acctstarttime ON radacct (acctstarttime); +CREATE INDEX idx_radacct_acctstoptime ON radacct (acctstoptime); +CREATE INDEX idx_radacct_nasipaddress ON radacct (nasipaddress); + +CREATE INDEX idx_radacct_bulk_close +ON radacct (acctstoptime, nasipaddress, acctstarttime); + +CREATE TABLE radcheck ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + username VARCHAR(64) NOT NULL DEFAULT '', + attribute VARCHAR(64) NOT NULL DEFAULT '', + op CHAR(2) NOT NULL DEFAULT '==', + value VARCHAR(253) NOT NULL DEFAULT '' +); + +CREATE INDEX idx_radcheck_username +ON radcheck (username); + + +CREATE TABLE radgroupcheck ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + groupname VARCHAR(64) NOT NULL DEFAULT '', + attribute VARCHAR(64) NOT NULL DEFAULT '', + op CHAR(2) NOT NULL DEFAULT '==', + value VARCHAR(253) NOT NULL DEFAULT '' +); + +CREATE INDEX idx_radgroupcheck_groupname +ON radgroupcheck (groupname); + + +CREATE TABLE radgroupreply ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + groupname VARCHAR(64) NOT NULL DEFAULT '', + attribute VARCHAR(64) NOT NULL DEFAULT '', + op CHAR(2) NOT NULL DEFAULT '=', + value VARCHAR(253) NOT NULL DEFAULT '' +); + +CREATE INDEX idx_radgroupreply_groupname +ON radgroupreply (groupname); + + +CREATE TABLE radreply ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + username VARCHAR(64) NOT NULL DEFAULT '', + attribute VARCHAR(64) NOT NULL DEFAULT '', + op CHAR(2) NOT NULL DEFAULT '=', + value VARCHAR(253) NOT NULL DEFAULT '' +); + +CREATE INDEX idx_radreply_username +ON radreply (username); + + +CREATE TABLE radusergroup ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + username VARCHAR(64) NOT NULL DEFAULT '', + groupname VARCHAR(64) NOT NULL DEFAULT '', + priority INTEGER NOT NULL DEFAULT 1 +); + +CREATE INDEX idx_radusergroup_username +ON radusergroup (username); + +CREATE TABLE radpostauth ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + username VARCHAR(64) NOT NULL DEFAULT '', + reply VARCHAR(32) NOT NULL DEFAULT '', + authdate TIMESTAMP(6) NOT NULL DEFAULT CURRENT_TIMESTAMP, + reason VARCHAR(255), + class VARCHAR(64) NOT NULL DEFAULT '' +); + + +CREATE TABLE nas ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + nasname VARCHAR(128) NOT NULL, + shortname VARCHAR(32), + type VARCHAR(30) DEFAULT 'other', + ports INTEGER, + secret VARCHAR(60) NOT NULL DEFAULT 'secret', + server VARCHAR(64), + community VARCHAR(50), + description VARCHAR(200) DEFAULT 'RADIUS Client' +); + +CREATE INDEX idx_nas_nasname ON nas (nasname); + + +CREATE TABLE nasreload ( + nasipaddress INET PRIMARY KEY, + reloadtime TIMESTAMP NOT NULL +); + + +CREATE TABLE radippool ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + pool_name VARCHAR(128) NOT NULL, + framedipaddress INET NOT NULL, + nasipaddress INET NOT NULL, + calledstationid VARCHAR(30) NOT NULL DEFAULT '', + callingstationid VARCHAR(30) NOT NULL DEFAULT '', + expiry_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + username VARCHAR(128) NOT NULL DEFAULT '', + pool_key VARCHAR(64) NOT NULL DEFAULT '', + tenant_id VARCHAR(64) NOT NULL DEFAULT '', + + CONSTRAINT radippool_framedipaddress_unique + UNIQUE (framedipaddress) +); + +CREATE INDEX idx_radippool_pool_lookup +ON radippool (pool_name, expiry_time); + +CREATE INDEX idx_radippool_node_lookup +ON radippool (nasipaddress, pool_key, framedipaddress); + +CREATE TABLE node_assignments ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + username VARCHAR(128) NOT NULL UNIQUE, + tenant_id VARCHAR(64) NOT NULL, + node_ip INET NOT NULL, + vpn_ip INET, + vpn_subnet CIDR NOT NULL, + client_type VARCHAR(20) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT node_assignments_client_type_check + CHECK (client_type IN ('device', 'engineer')), + + CONSTRAINT unique_ip_per_node + UNIQUE (node_ip, vpn_ip) +); + +CREATE INDEX idx_node_assignments_node +ON node_assignments (node_ip); + +CREATE INDEX idx_node_assignments_tenant +ON node_assignments (tenant_id); + +CREATE INDEX idx_node_assignments_username_node +ON node_assignments (username, node_ip); + + +CREATE TABLE tenant_subnets ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + tenant_id VARCHAR(64) NOT NULL UNIQUE, + subnet CIDR NOT NULL, + node_ip INET NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT unique_subnet UNIQUE (subnet) +); + +CREATE INDEX idx_tenant_subnets_node +ON tenant_subnets (node_ip); + + +CREATE TABLE node_subnets ( + id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + node_ip INET NOT NULL, + subnet CIDR NOT NULL, + is_allocated BOOLEAN DEFAULT FALSE, + tenant_id VARCHAR(64), + + CONSTRAINT unique_subnet_node_subnets UNIQUE (subnet), + + CONSTRAINT chk_allocated CHECK ( + (is_allocated = TRUE AND tenant_id IS NOT NULL) + OR + (is_allocated = FALSE AND tenant_id IS NULL) + ) +); + +CREATE INDEX idx_node_subnets_node +ON node_subnets (node_ip); + +CREATE VIEW v_active_sessions AS +SELECT + ra.username, + ra.nasipaddress AS connected_to_node, + na.node_ip AS assigned_node, + + CASE + WHEN ra.nasipaddress = na.node_ip THEN 'OK' + ELSE 'WRONG NODE' + END AS node_status, + + ra.framedipaddress AS vpn_ip, + ra.acctstarttime AS connected_since, + na.tenant_id + +FROM radacct ra +LEFT JOIN node_assignments na + ON na.username = ra.username + +WHERE ra.acctstoptime IS NULL; \ No newline at end of file diff --git a/kustomization.yaml b/kustomization.yaml new file mode 100644 index 0000000..ff9bbf7 --- /dev/null +++ b/kustomization.yaml @@ -0,0 +1,16 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - cluster.yaml + +configMapGenerator: + - name: postgresql-init + files: + - init.sql=init/init.sql + +##Sync wave -1 → ConfigMap postgresql-init +##Sync wave 0 → Cluster postgresql-prod +generatorOptions: + annotations: + argocd.argoproj.io/sync-wave: "-1" \ No newline at end of file diff --git a/postgresql-prod.yaml b/postgresql-prod.yaml new file mode 100644 index 0000000..a6e0295 --- /dev/null +++ b/postgresql-prod.yaml @@ -0,0 +1,26 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application + +metadata: + name: postgresql-prod + namespace: argocd + +spec: + project: default + + source: + repoURL: ssh://git@gitea-ssh.gitea.svc.cluster.local:22/gitea_admin/postgresql-prod.git + targetRevision: main + path: . + + destination: + server: "https://138.199.131.114:6443" + namespace: postgresql + + syncPolicy: + automated: + prune: true + selfHeal: true + + syncOptions: + - CreateNamespace=true \ No newline at end of file