feat: organize all services into separate stacks by dependency
This commit is contained in:
@@ -1,48 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import { Construct } from "constructs";
|
|
||||||
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
|
||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
|
||||||
|
|
||||||
type OnePasswordSecret = {
|
|
||||||
id?: string;
|
|
||||||
namespace?: string;
|
|
||||||
name: string;
|
|
||||||
itemPath: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type OnePasswordOptions = {
|
|
||||||
provider: KubernetesProvider;
|
|
||||||
namespace: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class OnePassword extends Construct {
|
|
||||||
constructor(scope: Construct, id: string, options: OnePasswordOptions) {
|
|
||||||
super(scope, id);
|
|
||||||
|
|
||||||
const secrets: OnePasswordSecret[] = JSON.parse(
|
|
||||||
fs.readFileSync("1password/secrets.json", {
|
|
||||||
encoding: "utf8",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
secrets.forEach((secret) => {
|
|
||||||
new Manifest(this, secret.id ?? secret.name, {
|
|
||||||
provider: options.provider,
|
|
||||||
manifest: {
|
|
||||||
apiVersion: "onepassword.com/v1",
|
|
||||||
kind: "OnePasswordItem",
|
|
||||||
metadata: {
|
|
||||||
name: secret.name,
|
|
||||||
namespace: secret.namespace ?? options.namespace,
|
|
||||||
annotations: {
|
|
||||||
"operator.1password.io/auto-restart": "true",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
spec: {
|
|
||||||
itemPath: secret.itemPath,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"name": "gitea-admin",
|
|
||||||
"itemPath": "vaults/Lab/items/gitea-admin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "pihole-admin",
|
|
||||||
"itemPath": "vaults/Lab/items/pihole"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "postgres-password",
|
|
||||||
"itemPath": "vaults/Lab/items/Postgres"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "runner-secret",
|
|
||||||
"itemPath": "vaults/Lab/items/Gitea"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "cloudflare-token",
|
|
||||||
"itemPath": "vaults/Lab/items/cloudflare"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "cloudflare-token-longhorn",
|
|
||||||
"name": "cloudflare-token",
|
|
||||||
"itemPath": "vaults/Lab/items/cloudflare",
|
|
||||||
"namespace": "longhorn-system"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "valkey",
|
|
||||||
"itemPath": "vaults/Lab/items/valkey"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gitea-oauth",
|
|
||||||
"itemPath": "vaults/Lab/items/gitea-oauth"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "gitea-elasticsearch",
|
|
||||||
"itemPath": "vaults/Lab/items/gitea-elasticsearch"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "smtp-token",
|
|
||||||
"itemPath": "vaults/Lab/items/smtp-token"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "longhorn-encryption",
|
|
||||||
"itemPath": "vaults/Lab/items/longhorn-encryption"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "authentik-secret-key",
|
|
||||||
"itemPath": "vaults/Lab/items/authentik-secret-key"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "curseforge",
|
|
||||||
"itemPath": "vaults/Lab/items/curseforge"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "devpi-secret",
|
|
||||||
"itemPath": "vaults/Lab/items/devpi-secret"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
|
||||||
import { Release } from "@cdktf/provider-helm/lib/release";
|
|
||||||
import { Construct } from "constructs";
|
|
||||||
|
|
||||||
type AuthentikServerOptions = {
|
|
||||||
provider: HelmProvider;
|
|
||||||
name: string;
|
|
||||||
namespace: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class AuthentikServer extends Construct {
|
|
||||||
constructor(scope: Construct, id: string, options: AuthentikServerOptions) {
|
|
||||||
super(scope, id);
|
|
||||||
|
|
||||||
new Release(this, id, {
|
|
||||||
...options,
|
|
||||||
repository: "https://charts.goauthentik.io",
|
|
||||||
chart: "authentik",
|
|
||||||
createNamespace: true,
|
|
||||||
values: [
|
|
||||||
fs.readFileSync("helm/values/authentik.values.yaml", {
|
|
||||||
encoding: "utf8",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1085
barman.yaml
Normal file
1085
barman.yaml
Normal file
File diff suppressed because it is too large
Load Diff
17
cache-infrastructure/index.ts
Normal file
17
cache-infrastructure/index.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { TerraformStack } from "cdktf";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
import { NixCache } from "./nixcache";
|
||||||
|
|
||||||
|
export class CacheInfrastructure extends TerraformStack {
|
||||||
|
constructor(scope: Construct, id: string) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const kubernetes = new KubernetesProvider(this, "kubernetes", {
|
||||||
|
configPath: "~/.kube/config",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add cache-related infrastructure components here
|
||||||
|
new NixCache(this, "nix-cache", kubernetes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,18 @@
|
|||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
import { TerraformStack } from "cdktf";
|
|
||||||
import { PersistentVolumeClaimV1 } from "@cdktf/provider-kubernetes/lib/persistent-volume-claim-v1";
|
import { PersistentVolumeClaimV1 } from "@cdktf/provider-kubernetes/lib/persistent-volume-claim-v1";
|
||||||
import { ConfigMapV1 } from "@cdktf/provider-kubernetes/lib/config-map-v1";
|
import { ConfigMapV1 } from "@cdktf/provider-kubernetes/lib/config-map-v1";
|
||||||
|
|
||||||
import { DeploymentV1 } from "@cdktf/provider-kubernetes/lib/deployment-v1";
|
import { DeploymentV1 } from "@cdktf/provider-kubernetes/lib/deployment-v1";
|
||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
import { TraefikIngressRoute } from "../traefik/ingress-route";
|
import { IngressRoute } from "../../utils";
|
||||||
import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1";
|
import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1";
|
||||||
|
|
||||||
export class NixCache extends TerraformStack {
|
export class NixCache extends Construct {
|
||||||
constructor(scope: Construct, id: string) {
|
constructor(scope: Construct, id: string, kubernetes: KubernetesProvider) {
|
||||||
super(scope, id);
|
super(scope, id);
|
||||||
|
|
||||||
const kubernetes = new KubernetesProvider(this, "kubernetes", {
|
|
||||||
configPath: "~/.kube/config",
|
|
||||||
});
|
|
||||||
|
|
||||||
const pvc = new PersistentVolumeClaimV1(this, "pvc", {
|
const pvc = new PersistentVolumeClaimV1(this, "pvc", {
|
||||||
provider: kubernetes,
|
provider: kubernetes,
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -134,7 +129,7 @@ export class NixCache extends TerraformStack {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
new TraefikIngressRoute(this, "ingress-route", {
|
new IngressRoute(this, "ingress-route", {
|
||||||
provider: kubernetes,
|
provider: kubernetes,
|
||||||
namespace: "homelab",
|
namespace: "homelab",
|
||||||
host: "nix.dogar.dev",
|
host: "nix.dogar.dev",
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
|
||||||
import { Release } from "@cdktf/provider-helm/lib/release";
|
|
||||||
import { Construct } from "constructs";
|
|
||||||
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
|
||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
|
||||||
|
|
||||||
type GiteaServerOptions = {
|
|
||||||
providers: {
|
|
||||||
helm: HelmProvider;
|
|
||||||
kubernetes: KubernetesProvider;
|
|
||||||
};
|
|
||||||
name: string;
|
|
||||||
namespace: string;
|
|
||||||
r2Endpoint: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class GiteaServer extends Construct {
|
|
||||||
constructor(scope: Construct, id: string, options: GiteaServerOptions) {
|
|
||||||
super(scope, id);
|
|
||||||
|
|
||||||
const { kubernetes, helm } = options.providers;
|
|
||||||
|
|
||||||
new Release(this, id, {
|
|
||||||
...options,
|
|
||||||
provider: helm,
|
|
||||||
repository: "https://dl.gitea.com/charts",
|
|
||||||
chart: "gitea",
|
|
||||||
createNamespace: true,
|
|
||||||
set: [
|
|
||||||
{
|
|
||||||
name: "gitea.config.storage.MINIO_ENDPOINT",
|
|
||||||
value: options.r2Endpoint,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
values: [
|
|
||||||
fs.readFileSync("helm/values/gitea.values.yaml", {
|
|
||||||
encoding: "utf8",
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
new Manifest(this, `${id}-ssh-ingress`, {
|
|
||||||
provider: kubernetes,
|
|
||||||
manifest: {
|
|
||||||
apiVersion: "traefik.io/v1alpha1",
|
|
||||||
kind: "IngressRouteTCP",
|
|
||||||
metadata: {
|
|
||||||
name: "gitea-ssh-ingress",
|
|
||||||
namespace: options.namespace,
|
|
||||||
},
|
|
||||||
spec: {
|
|
||||||
entryPoints: ["ssh"],
|
|
||||||
routes: [
|
|
||||||
{
|
|
||||||
match: "HostSNI(`*`)",
|
|
||||||
services: [
|
|
||||||
{
|
|
||||||
name: `${options.name}-ssh`,
|
|
||||||
port: 22,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
42
k8s-operators/barman.ts
Normal file
42
k8s-operators/barman.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { NullProvider } from "@cdktf/provider-null/lib/provider";
|
||||||
|
import { Resource } from "@cdktf/provider-null/lib/resource";
|
||||||
|
|
||||||
|
export interface BarmanCloudPluginInstallOptions {
|
||||||
|
/** URL to the CloudNativePG barman-cloud plugin manifest */
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BarmanCloudPluginInstall extends Construct {
|
||||||
|
constructor(
|
||||||
|
scope: Construct,
|
||||||
|
id: string,
|
||||||
|
opts: BarmanCloudPluginInstallOptions,
|
||||||
|
) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const { url } = opts;
|
||||||
|
|
||||||
|
const applyCmd = ["kubectl", "apply", "-f", url].join(" ");
|
||||||
|
const deleteCmd = ["kubectl", "delete", "-f", url].join(" ");
|
||||||
|
|
||||||
|
new Resource(this, "barman-install", {
|
||||||
|
provider: new NullProvider(this, "barman"),
|
||||||
|
provisioners: [
|
||||||
|
{
|
||||||
|
type: "local-exec",
|
||||||
|
when: "create",
|
||||||
|
command: applyCmd,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "local-exec",
|
||||||
|
when: "destroy",
|
||||||
|
command: deleteCmd,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
triggers: {
|
||||||
|
url,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
72
k8s-operators/index.ts
Normal file
72
k8s-operators/index.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
||||||
|
import { Release } from "@cdktf/provider-helm/lib/release";
|
||||||
|
import { TerraformStack } from "cdktf";
|
||||||
|
import { Construct } from "constructs";
|
||||||
|
import { BarmanCloudPluginInstall } from "./barman";
|
||||||
|
import { Prometheus } from "./prometheus";
|
||||||
|
|
||||||
|
export class K8SOperators extends TerraformStack {
|
||||||
|
constructor(scope: Construct, id: string) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const helm = new HelmProvider(this, "helm", {
|
||||||
|
kubernetes: {
|
||||||
|
configPath: "~/.kube/config",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new Prometheus(this, "prometheus", {
|
||||||
|
provider: helm,
|
||||||
|
namespace: "monitoring",
|
||||||
|
name: "prometheus-operator",
|
||||||
|
version: "75.10.0",
|
||||||
|
});
|
||||||
|
|
||||||
|
new Release(this, "onepassword-operator", {
|
||||||
|
provider: helm,
|
||||||
|
name: "onepassword-operator",
|
||||||
|
chart: "connect",
|
||||||
|
repository: "https://1password.github.io/connect-helm-charts/",
|
||||||
|
namespace: "1password",
|
||||||
|
createNamespace: true,
|
||||||
|
set: [
|
||||||
|
{
|
||||||
|
name: "operator.create",
|
||||||
|
value: "true",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
setSensitive: [
|
||||||
|
{
|
||||||
|
name: "operator.token.value",
|
||||||
|
value: process.env.OP_CONNECT_TOKEN!,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "connect.credentials_base64",
|
||||||
|
value: btoa(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.join(__dirname, "1password-credentials.json"),
|
||||||
|
"utf-8",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const cnpg = new Release(this, "cnpg-operator", {
|
||||||
|
provider: helm,
|
||||||
|
repository: "https://cloudnative-pg.github.io/charts",
|
||||||
|
chart: "cloudnative-pg",
|
||||||
|
name: "postgres-system",
|
||||||
|
namespace: "cnpg-system",
|
||||||
|
createNamespace: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const barman = new BarmanCloudPluginInstall(this, "barman-cloud-plugin", {
|
||||||
|
url: "https://github.com/cloudnative-pg/plugin-barman-cloud/releases/download/v0.9.0/manifest.yaml",
|
||||||
|
});
|
||||||
|
|
||||||
|
barman.node.addDependency(cnpg);
|
||||||
|
}
|
||||||
|
}
|
||||||
113
main.ts
113
main.ts
@@ -1,35 +1,27 @@
|
|||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
import { cleanEnv, str } from "envalid";
|
import { cleanEnv, str } from "envalid";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
import { App, TerraformStack, LocalBackend, PgBackend } from "cdktf";
|
import { App, TerraformStack, LocalBackend, TerraformOutput } from "cdktf";
|
||||||
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
import { NamespaceV1 } from "@cdktf/provider-kubernetes/lib/namespace-v1";
|
import { NamespaceV1 } from "@cdktf/provider-kubernetes/lib/namespace-v1";
|
||||||
|
|
||||||
import { GiteaServer } from "./gitea";
|
|
||||||
import { OnePassword } from "./1password";
|
|
||||||
import { PostgresCluster } from "./postgres";
|
|
||||||
import { Longhorn } from "./longhorn";
|
import { Longhorn } from "./longhorn";
|
||||||
import { AuthentikServer } from "./authentik";
|
|
||||||
import { ValkeyCluster } from "./valkey";
|
|
||||||
import { CertManager } from "./cert-manager";
|
import { CertManager } from "./cert-manager";
|
||||||
import { Traefik } from "./traefik";
|
import { Traefik } from "./traefik";
|
||||||
import { Prometheus } from "./prometheus";
|
|
||||||
import { MetalLB } from "./metallb";
|
import { MetalLB } from "./metallb";
|
||||||
import { NixCache } from "./nixcache";
|
import { CacheInfrastructure } from "./cache-infrastructure";
|
||||||
|
import { UtilityServices } from "./utility-services";
|
||||||
|
import { K8SOperators } from "./k8s-operators";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const env = cleanEnv(process.env, {
|
cleanEnv(process.env, {
|
||||||
ACCOUNT_ID: str({ desc: "Cloudflare account id." }),
|
ACCOUNT_ID: str({ desc: "Cloudflare account id." }),
|
||||||
PG_CONN_STR: str({
|
OP_CONNECT_TOKEN: str({ desc: "1Password Connect token." }),
|
||||||
desc: "PostgreSQL connection string for Terraform state backend.",
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const r2Endpoint = `https://${env.ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
class CoreServices extends TerraformStack {
|
||||||
|
|
||||||
class Homelab extends TerraformStack {
|
|
||||||
constructor(scope: Construct, id: string) {
|
constructor(scope: Construct, id: string) {
|
||||||
super(scope, id);
|
super(scope, id);
|
||||||
|
|
||||||
@@ -52,6 +44,10 @@ class Homelab extends TerraformStack {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
new TerraformOutput(this, "namespace-output", {
|
||||||
|
value: namespace,
|
||||||
|
});
|
||||||
|
|
||||||
new Longhorn(this, "longhorn", {
|
new Longhorn(this, "longhorn", {
|
||||||
name: "longhorn",
|
name: "longhorn",
|
||||||
providers: {
|
providers: {
|
||||||
@@ -66,21 +62,14 @@ class Homelab extends TerraformStack {
|
|||||||
namespace: "metallb-system",
|
namespace: "metallb-system",
|
||||||
});
|
});
|
||||||
|
|
||||||
new OnePassword(this, "one-password", {
|
|
||||||
provider: kubernetes,
|
|
||||||
namespace,
|
|
||||||
});
|
|
||||||
|
|
||||||
new Traefik(this, "traefik", {
|
new Traefik(this, "traefik", {
|
||||||
provider: helm,
|
provider: helm,
|
||||||
namespace,
|
namespace,
|
||||||
name: "traefik",
|
name: "traefik",
|
||||||
});
|
});
|
||||||
|
|
||||||
const certManagerApiVersion = "cert-manager.io/v1";
|
|
||||||
|
|
||||||
new CertManager(this, "cert-manager", {
|
new CertManager(this, "cert-manager", {
|
||||||
certManagerApiVersion,
|
certManagerApiVersion: "cert-manager.io/v1",
|
||||||
name: "cert-manager",
|
name: "cert-manager",
|
||||||
namespace,
|
namespace,
|
||||||
version: "1.18.2",
|
version: "1.18.2",
|
||||||
@@ -89,71 +78,39 @@ class Homelab extends TerraformStack {
|
|||||||
helm,
|
helm,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
new Prometheus(this, "prometheus", {
|
|
||||||
provider: helm,
|
|
||||||
namespace,
|
|
||||||
name: "prometheus-operator",
|
|
||||||
version: "75.10.0",
|
|
||||||
});
|
|
||||||
|
|
||||||
const pg = new PostgresCluster(this, "postgres-cluster", {
|
|
||||||
certManagerApiVersion,
|
|
||||||
name: "postgres-cluster",
|
|
||||||
namespace,
|
|
||||||
providers: {
|
|
||||||
kubernetes,
|
|
||||||
helm,
|
|
||||||
},
|
|
||||||
users: ["shahab", "budget-tracker", "authentik", "gitea"],
|
|
||||||
primaryUser: "shahab",
|
|
||||||
initSecretName: "postgres-password",
|
|
||||||
backupR2EndpointURL: r2Endpoint,
|
|
||||||
});
|
|
||||||
|
|
||||||
const valkey = new ValkeyCluster(this, "valkey-cluster", {
|
|
||||||
provider: kubernetes,
|
|
||||||
namespace,
|
|
||||||
name: "valkey",
|
|
||||||
});
|
|
||||||
|
|
||||||
const authentik = new AuthentikServer(this, "authentik-server", {
|
|
||||||
provider: helm,
|
|
||||||
name: "authentik",
|
|
||||||
namespace,
|
|
||||||
});
|
|
||||||
|
|
||||||
authentik.node.addDependency(pg);
|
|
||||||
authentik.node.addDependency(valkey);
|
|
||||||
|
|
||||||
const gitea = new GiteaServer(this, "gitea-server", {
|
|
||||||
name: "gitea",
|
|
||||||
namespace,
|
|
||||||
providers: {
|
|
||||||
helm,
|
|
||||||
kubernetes,
|
|
||||||
},
|
|
||||||
r2Endpoint: `${env.ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
|
||||||
});
|
|
||||||
|
|
||||||
gitea.node.addDependency(authentik);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = new App();
|
const app = new App();
|
||||||
const homelab = new Homelab(app, "homelab");
|
const coreServices = new CoreServices(app, "homelab");
|
||||||
|
|
||||||
const nixCache = new NixCache(app, "nix-cache");
|
const k8sOperators = new K8SOperators(app, "k8s-operators");
|
||||||
nixCache.node.addDependency(homelab);
|
k8sOperators.node.addDependency(coreServices);
|
||||||
|
|
||||||
new LocalBackend(homelab, {
|
const utilityServices = new UtilityServices(app, "utility-services");
|
||||||
|
utilityServices.node.addDependency(k8sOperators);
|
||||||
|
|
||||||
|
const caches = new CacheInfrastructure(app, "cache-infrastructure");
|
||||||
|
caches.node.addDependency(utilityServices);
|
||||||
|
|
||||||
|
new LocalBackend(coreServices, {
|
||||||
path: "terraform.tfstate",
|
path: "terraform.tfstate",
|
||||||
workspaceDir: ".",
|
workspaceDir: ".",
|
||||||
});
|
});
|
||||||
|
|
||||||
new PgBackend(nixCache, {
|
new LocalBackend(caches, {
|
||||||
schemaName: "nix_cache",
|
path: "terraform.tfstate",
|
||||||
connStr: env.PG_CONN_STR,
|
workspaceDir: "./cachestf",
|
||||||
|
});
|
||||||
|
|
||||||
|
new LocalBackend(utilityServices, {
|
||||||
|
path: "terraform.tfstate",
|
||||||
|
workspaceDir: "./utilityservicestf",
|
||||||
|
});
|
||||||
|
|
||||||
|
new LocalBackend(k8sOperators, {
|
||||||
|
path: "terraform.tfstate",
|
||||||
|
workspaceDir: "./k8soperatorstf",
|
||||||
});
|
});
|
||||||
|
|
||||||
app.synth();
|
app.synth();
|
||||||
|
|||||||
124
package-lock.json
generated
124
package-lock.json
generated
@@ -9,46 +9,60 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cdktf/provider-helm": "10.5.0",
|
"@cdktf/provider-helm": "12.1.1",
|
||||||
"@cdktf/provider-kubernetes": "11.12.1",
|
"@cdktf/provider-kubernetes": "12.1.0",
|
||||||
"cdktf": "^0.20.12",
|
"@cdktf/provider-null": "^11.0.0",
|
||||||
"constructs": "^10.4.2",
|
"cdktf": "^0.21.0",
|
||||||
"dotenv": "^16.5.0",
|
"constructs": "^10.4.3",
|
||||||
"envalid": "^8.0.0"
|
"dotenv": "^17.2.3",
|
||||||
|
"envalid": "^8.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.3",
|
"@types/node": "^24.10.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "24"
|
"node": "24"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cdktf/provider-helm": {
|
"node_modules/@cdktf/provider-helm": {
|
||||||
"version": "10.5.0",
|
"version": "12.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@cdktf/provider-helm/-/provider-helm-10.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/@cdktf/provider-helm/-/provider-helm-12.1.1.tgz",
|
||||||
"integrity": "sha512-u3Q6VNIayaSFfEKZh+JG3PDrwcl9igHLWUdi6cK1G385tw4UyUpZ8osUnGhOErxbZtlcp4yeZ1c5+1OMP4epLA==",
|
"integrity": "sha512-bi1Smig+b38eKs0yP/JJhbwTKHclp91fNLkcEDS7nI+6AQ4+uqN24CxHGUc6hpwNmatNnLH90gR0+iq/p6KEuw==",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18.12.0"
|
"node": ">= 20.9.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cdktf": "^0.20.0",
|
"cdktf": "^0.21.0",
|
||||||
"constructs": "^10.3.0"
|
"constructs": "^10.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cdktf/provider-kubernetes": {
|
"node_modules/@cdktf/provider-kubernetes": {
|
||||||
"version": "11.12.1",
|
"version": "12.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@cdktf/provider-kubernetes/-/provider-kubernetes-11.12.1.tgz",
|
"resolved": "https://registry.npmjs.org/@cdktf/provider-kubernetes/-/provider-kubernetes-12.1.0.tgz",
|
||||||
"integrity": "sha512-8LgaY0VULF/2f8iXqojGujP7DKSzl1didqbxMb7uMX0oE3EVDdtmJNIAY2D6oXjW95b9+NVQmhg4iN/jiF7zpA==",
|
"integrity": "sha512-GVFbQIPaMeGbzbGyvTWwBUgdc9kKOGWRQNmzvD5A1bFtDTAVk77kRfdfooVuj869TDHF77WXIn6LGp8uuHoJrQ==",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18.12.0"
|
"node": ">= 20.9.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cdktf": "^0.20.0",
|
"cdktf": "^0.21.0",
|
||||||
"constructs": "^10.3.0"
|
"constructs": "^10.4.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@cdktf/provider-null": {
|
||||||
|
"version": "11.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@cdktf/provider-null/-/provider-null-11.0.0.tgz",
|
||||||
|
"integrity": "sha512-OX/ADMXtPWBV/ZTBxCiMGUX0EMI+ooxXZWrZAskJAKIO2Ny1tpBXLE13NpfU9fG+6GkR4e1hLNsOMdO99DuhCA==",
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 20.9.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"cdktf": "^0.21.0",
|
||||||
|
"constructs": "^10.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cspotcode/source-map-support": {
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
@@ -93,9 +107,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tsconfig/node10": {
|
"node_modules/@tsconfig/node10": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.12",
|
||||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
|
||||||
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
|
"integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
@@ -121,9 +135,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "24.9.1",
|
"version": "24.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
|
||||||
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
|
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
"peer": true,
|
||||||
@@ -165,9 +179,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cdktf": {
|
"node_modules/cdktf": {
|
||||||
"version": "0.20.12",
|
"version": "0.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/cdktf/-/cdktf-0.20.12.tgz",
|
"resolved": "https://registry.npmjs.org/cdktf/-/cdktf-0.21.0.tgz",
|
||||||
"integrity": "sha512-ZBg2gA3Uw0WvGFlgrY1uxo6QHWn+ZdHiDkZQyOsTBl68k62UlaV8K7RR51d0E/amQG/CjtKOJr5XPFFAcOq0VA==",
|
"integrity": "sha512-bdTOOyrFSXw0p5d7/3dye7ZWYzrUatyMjWEAAwTGoqghjygRj6Q55y1QZnSB021NRDzYZ3BhFGsOkpmIjQMzNQ==",
|
||||||
"bundleDependencies": [
|
"bundleDependencies": [
|
||||||
"archiver",
|
"archiver",
|
||||||
"json-stable-stringify",
|
"json-stable-stringify",
|
||||||
@@ -177,11 +191,11 @@
|
|||||||
"peer": true,
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"archiver": "7.0.1",
|
"archiver": "7.0.1",
|
||||||
"json-stable-stringify": "1.2.1",
|
"json-stable-stringify": "1.3.0",
|
||||||
"semver": "7.7.1"
|
"semver": "7.7.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"constructs": "^10.3.0"
|
"constructs": "^10.4.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cdktf/node_modules/@isaacs/cliui": {
|
"node_modules/cdktf/node_modules/@isaacs/cliui": {
|
||||||
@@ -766,12 +780,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cdktf/node_modules/json-stable-stringify": {
|
"node_modules/cdktf/node_modules/json-stable-stringify": {
|
||||||
"version": "1.2.1",
|
"version": "1.3.0",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.8",
|
"call-bind": "^1.0.8",
|
||||||
"call-bound": "^1.0.3",
|
"call-bound": "^1.0.4",
|
||||||
"isarray": "^2.0.5",
|
"isarray": "^2.0.5",
|
||||||
"jsonify": "^0.0.1",
|
"jsonify": "^0.0.1",
|
||||||
"object-keys": "^1.1.1"
|
"object-keys": "^1.1.1"
|
||||||
@@ -852,6 +866,17 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cdktf/node_modules/minimatch": {
|
||||||
|
"version": "5.1.6",
|
||||||
|
"inBundle": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"brace-expansion": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cdktf/node_modules/minipass": {
|
"node_modules/cdktf/node_modules/minipass": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
@@ -940,17 +965,6 @@
|
|||||||
"minimatch": "^5.1.0"
|
"minimatch": "^5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cdktf/node_modules/readdir-glob/node_modules/minimatch": {
|
|
||||||
"version": "5.1.6",
|
|
||||||
"inBundle": true,
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cdktf/node_modules/safe-buffer": {
|
"node_modules/cdktf/node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"funding": [
|
"funding": [
|
||||||
@@ -971,7 +985,7 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cdktf/node_modules/semver": {
|
"node_modules/cdktf/node_modules/semver": {
|
||||||
"version": "7.7.1",
|
"version": "7.7.2",
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -1249,9 +1263,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/constructs": {
|
"node_modules/constructs": {
|
||||||
"version": "10.4.2",
|
"version": "10.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/constructs/-/constructs-10.4.3.tgz",
|
||||||
"integrity": "sha512-wsNxBlAott2qg8Zv87q3eYZYgheb9lchtBfjHzzLHtXbttwSrHPs1NNQbBrmbb1YZvYg2+Vh0Dor76w4mFxJkA==",
|
"integrity": "sha512-3+ZB67qWGM1vEstNpj6pGaLNN1qz4gxC1CBhEUhZDZk0PqzQWY65IzC1Doq17MGPa9xa2wJ1G/DJ3swU8kWAHQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
@@ -1273,9 +1287,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.6.1",
|
"version": "17.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
||||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
"integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
@@ -1285,9 +1299,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/envalid": {
|
"node_modules/envalid": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/envalid/-/envalid-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/envalid/-/envalid-8.1.1.tgz",
|
||||||
"integrity": "sha512-OT6+qVhKVyCidaGoXflb2iK1tC8pd0OV2Q+v9n33wNhUJ+lus+rJobUj4vJaQBPxPZ0vYrPGuxdrenyCAIJcow==",
|
"integrity": "sha512-vOUfHxAFFvkBjbVQbBfgnCO9d3GcNfMMTtVfgqSU2rQGMFEVqWy9GBuoSfHnwGu7EqR0/GeukQcL3KjFBaga9w==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "2.8.1"
|
"tslib": "2.8.1"
|
||||||
|
|||||||
17
package.json
17
package.json
@@ -23,16 +23,17 @@
|
|||||||
"upgrade:next": "npm i cdktf@next cdktf-cli@next"
|
"upgrade:next": "npm i cdktf@next cdktf-cli@next"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cdktf/provider-helm": "10.5.0",
|
"@cdktf/provider-helm": "12.1.1",
|
||||||
"@cdktf/provider-kubernetes": "11.12.1",
|
"@cdktf/provider-kubernetes": "12.1.0",
|
||||||
"cdktf": "^0.20.12",
|
"@cdktf/provider-null": "^11.0.0",
|
||||||
"constructs": "^10.4.2",
|
"cdktf": "^0.21.0",
|
||||||
"dotenv": "^16.5.0",
|
"constructs": "^10.4.3",
|
||||||
"envalid": "^8.0.0"
|
"dotenv": "^17.2.3",
|
||||||
|
"envalid": "^8.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^24.0.3",
|
"@types/node": "^24.10.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
|
||||||
import { Release } from "@cdktf/provider-helm/lib/release";
|
|
||||||
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
|
|
||||||
type PostgresClusterOptions = {
|
type PostgresClusterOptions = {
|
||||||
providers: {
|
provider: KubernetesProvider;
|
||||||
kubernetes: KubernetesProvider;
|
|
||||||
helm: HelmProvider;
|
|
||||||
};
|
|
||||||
name: string;
|
name: string;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
users: string[];
|
users: string[];
|
||||||
@@ -22,16 +17,7 @@ export class PostgresCluster extends Construct {
|
|||||||
constructor(scope: Construct, id: string, options: PostgresClusterOptions) {
|
constructor(scope: Construct, id: string, options: PostgresClusterOptions) {
|
||||||
super(scope, id);
|
super(scope, id);
|
||||||
|
|
||||||
const { kubernetes, helm } = options.providers;
|
const { provider } = options;
|
||||||
|
|
||||||
new Release(this, "cnpg-operator", {
|
|
||||||
provider: helm,
|
|
||||||
repository: "https://cloudnative-pg.github.io/charts",
|
|
||||||
chart: "cloudnative-pg",
|
|
||||||
name: "postgres-system",
|
|
||||||
namespace: "cnpg-system",
|
|
||||||
createNamespace: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const destinationPath = "s3://postgres-backups/";
|
const destinationPath = "s3://postgres-backups/";
|
||||||
const endpointURL = options.backupR2EndpointURL;
|
const endpointURL = options.backupR2EndpointURL;
|
||||||
@@ -64,7 +50,7 @@ export class PostgresCluster extends Construct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
new Manifest(this, "r2-backup-store", {
|
new Manifest(this, "r2-backup-store", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: "barmancloud.cnpg.io/v1",
|
apiVersion: "barmancloud.cnpg.io/v1",
|
||||||
kind: "ObjectStore",
|
kind: "ObjectStore",
|
||||||
@@ -95,7 +81,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Self-signed issuer for creating CA certificates
|
// Self-signed issuer for creating CA certificates
|
||||||
new Manifest(this, "selfsigned-issuer", {
|
new Manifest(this, "selfsigned-issuer", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Issuer",
|
kind: "Issuer",
|
||||||
@@ -111,7 +97,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Server CA certificate
|
// Server CA certificate
|
||||||
new Manifest(this, "server-ca-cert", {
|
new Manifest(this, "server-ca-cert", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Certificate",
|
kind: "Certificate",
|
||||||
@@ -140,7 +126,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Issuer using the server CA
|
// Issuer using the server CA
|
||||||
new Manifest(this, "server-ca-issuer", {
|
new Manifest(this, "server-ca-issuer", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Issuer",
|
kind: "Issuer",
|
||||||
@@ -158,7 +144,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Server certificate
|
// Server certificate
|
||||||
new Manifest(this, "server-cert", {
|
new Manifest(this, "server-cert", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Certificate",
|
kind: "Certificate",
|
||||||
@@ -187,7 +173,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Client CA certificate
|
// Client CA certificate
|
||||||
new Manifest(this, "client-ca", {
|
new Manifest(this, "client-ca", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Certificate",
|
kind: "Certificate",
|
||||||
@@ -216,7 +202,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Issuer using the client CA
|
// Issuer using the client CA
|
||||||
new Manifest(this, "client-ca-issuer", {
|
new Manifest(this, "client-ca-issuer", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Issuer",
|
kind: "Issuer",
|
||||||
@@ -234,7 +220,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Secret for client certificate
|
// Secret for client certificate
|
||||||
new Manifest(this, `${certNames.client}-secret`, {
|
new Manifest(this, `${certNames.client}-secret`, {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
kind: "Secret",
|
kind: "Secret",
|
||||||
@@ -250,7 +236,7 @@ export class PostgresCluster extends Construct {
|
|||||||
|
|
||||||
// Client certificate for streaming replica
|
// Client certificate for streaming replica
|
||||||
new Manifest(this, "streaming-replica-cert", {
|
new Manifest(this, "streaming-replica-cert", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Certificate",
|
kind: "Certificate",
|
||||||
@@ -277,7 +263,7 @@ export class PostgresCluster extends Construct {
|
|||||||
options.users.forEach(
|
options.users.forEach(
|
||||||
(user) =>
|
(user) =>
|
||||||
new Manifest(this, `${user}-client-cert`, {
|
new Manifest(this, `${user}-client-cert`, {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: certManagerApiVersion,
|
apiVersion: certManagerApiVersion,
|
||||||
kind: "Certificate",
|
kind: "Certificate",
|
||||||
@@ -302,7 +288,7 @@ export class PostgresCluster extends Construct {
|
|||||||
);
|
);
|
||||||
|
|
||||||
new Manifest(this, "postgres-cluster", {
|
new Manifest(this, "postgres-cluster", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
fieldManager: { forceConflicts: true },
|
fieldManager: { forceConflicts: true },
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: "postgresql.cnpg.io/v1",
|
apiVersion: "postgresql.cnpg.io/v1",
|
||||||
@@ -435,7 +421,7 @@ export class PostgresCluster extends Construct {
|
|||||||
});
|
});
|
||||||
|
|
||||||
new Manifest(this, "postgres-backup-job", {
|
new Manifest(this, "postgres-backup-job", {
|
||||||
provider: kubernetes,
|
provider,
|
||||||
manifest: {
|
manifest: {
|
||||||
apiVersion: "postgresql.cnpg.io/v1",
|
apiVersion: "postgresql.cnpg.io/v1",
|
||||||
kind: "ScheduledBackup",
|
kind: "ScheduledBackup",
|
||||||
|
|||||||
7
types/index.ts
Normal file
7
types/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
||||||
|
|
||||||
|
export type Providers = {
|
||||||
|
kubernetes: KubernetesProvider;
|
||||||
|
helm: HelmProvider;
|
||||||
|
};
|
||||||
47
utility-services/authentik/index.ts
Normal file
47
utility-services/authentik/index.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import { Release } from "@cdktf/provider-helm/lib/release";
|
||||||
|
import { Construct } from "constructs";
|
||||||
|
import { OnePasswordSecret } from "../../utils";
|
||||||
|
import { Providers } from "../../types";
|
||||||
|
|
||||||
|
type AuthentikServerOptions = {
|
||||||
|
providers: Providers;
|
||||||
|
name: string;
|
||||||
|
namespace: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class AuthentikServer extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, options: AuthentikServerOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const { kubernetes, helm } = options.providers;
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "secret-key", {
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "authentik-secret-key",
|
||||||
|
namespace: options.namespace,
|
||||||
|
itemPath: "vaults/Lab/items/authentik-secret-key",
|
||||||
|
});
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "smtp", {
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "authentik-smtp-token",
|
||||||
|
namespace: options.namespace,
|
||||||
|
itemPath: "vaults/Lab/items/smtp-token",
|
||||||
|
});
|
||||||
|
|
||||||
|
new Release(this, id, {
|
||||||
|
...options,
|
||||||
|
provider: helm,
|
||||||
|
repository: "https://charts.goauthentik.io",
|
||||||
|
chart: "authentik",
|
||||||
|
createNamespace: true,
|
||||||
|
values: [
|
||||||
|
fs.readFileSync(path.join(__dirname, "values.yaml"), {
|
||||||
|
encoding: "utf8",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}).importFrom("homelab/authentik");
|
||||||
|
}
|
||||||
|
}
|
||||||
110
utility-services/authentik/values.yaml
Normal file
110
utility-services/authentik/values.yaml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
global:
|
||||||
|
addPrometheusAnnotations: true
|
||||||
|
securityContext:
|
||||||
|
runAsUser: 1000
|
||||||
|
fsGroup: 1000
|
||||||
|
podLabels:
|
||||||
|
app: authentik
|
||||||
|
nodeSelector:
|
||||||
|
nodepool: worker
|
||||||
|
topologySpreadConstraints:
|
||||||
|
- maxSkew: 1
|
||||||
|
topologyKey: kubernetes.io/hostname
|
||||||
|
whenUnsatisfiable: DoNotSchedule
|
||||||
|
labelSelector:
|
||||||
|
matchLabels:
|
||||||
|
app: authentik
|
||||||
|
env:
|
||||||
|
- name: AUTHENTIK_SECRET_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: authentik-secret-key
|
||||||
|
key: password
|
||||||
|
- name: AUTHENTIK_EMAIL__USERNAME
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: authentik-smtp-token
|
||||||
|
key: authentik-username
|
||||||
|
- name: AUTHENTIK_EMAIL__PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: authentik-smtp-token
|
||||||
|
key: authentik-password
|
||||||
|
- name: AUTHENTIK_EMAIL__FROM
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: authentik-smtp-token
|
||||||
|
key: authentik-username
|
||||||
|
- name: AUTHENTIK_EMAIL__USE_TLS
|
||||||
|
value: "true"
|
||||||
|
- name: AUTHENTIK_POSTGRESQL__SSLMODE
|
||||||
|
value: verify-full
|
||||||
|
- name: AUTHENTIK_POSTGRESQL__SSLROOTCERT
|
||||||
|
value: "/opt/authentik/certs/ca.crt"
|
||||||
|
- name: AUTHENTIK_POSTGRESQL__SSLCERT
|
||||||
|
value: "/opt/authentik/certs/tls.crt"
|
||||||
|
- name: AUTHENTIK_POSTGRESQL__SSLKEY
|
||||||
|
value: "/opt/authentik/certs/tls.key"
|
||||||
|
- name: AUTHENTIK_REDIS__PASSWORD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: valkey
|
||||||
|
key: password
|
||||||
|
volumes:
|
||||||
|
- name: ssl-bundle
|
||||||
|
projected:
|
||||||
|
sources:
|
||||||
|
- secret:
|
||||||
|
name: authentik-client-cert
|
||||||
|
items:
|
||||||
|
- key: tls.crt
|
||||||
|
path: tls.crt
|
||||||
|
- key: tls.key
|
||||||
|
path: tls.key
|
||||||
|
mode: 0600
|
||||||
|
- secret:
|
||||||
|
name: postgres-server-cert
|
||||||
|
items:
|
||||||
|
- key: ca.crt
|
||||||
|
path: ca.crt
|
||||||
|
volumeMounts:
|
||||||
|
- name: ssl-bundle
|
||||||
|
mountPath: /opt/authentik/certs
|
||||||
|
readOnly: true
|
||||||
|
|
||||||
|
authentik:
|
||||||
|
error_reporting:
|
||||||
|
enabled: false
|
||||||
|
email:
|
||||||
|
host: "smtp.protonmail.ch"
|
||||||
|
port: 587
|
||||||
|
postgresql:
|
||||||
|
host: postgres-cluster-rw
|
||||||
|
user: authentik
|
||||||
|
name: authentik
|
||||||
|
redis:
|
||||||
|
host: valkey
|
||||||
|
|
||||||
|
server:
|
||||||
|
replicas: 3
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: cloudflare-issuer
|
||||||
|
cert-manager.io/acme-challenge-type: dns01
|
||||||
|
cert-manager.io/private-key-size: "4096"
|
||||||
|
ingressClassName: traefik
|
||||||
|
hosts:
|
||||||
|
- auth.dogar.dev
|
||||||
|
tls:
|
||||||
|
- secretName: authentik-tls
|
||||||
|
hosts:
|
||||||
|
- auth.dogar.dev
|
||||||
|
|
||||||
|
worker:
|
||||||
|
replicas: 3
|
||||||
|
|
||||||
|
postgresql:
|
||||||
|
enabled: false
|
||||||
|
redis:
|
||||||
|
enabled: false
|
||||||
1
utility-services/gitea/index.ts
Normal file
1
utility-services/gitea/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { GiteaServer } from "./server";
|
||||||
82
utility-services/gitea/server.ts
Normal file
82
utility-services/gitea/server.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
||||||
|
import { Release } from "@cdktf/provider-helm/lib/release";
|
||||||
|
import { Construct } from "constructs";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
|
||||||
|
import { OnePasswordSecret } from "../../utils";
|
||||||
|
import { IngressRouteTcp } from "../../utils/traefik";
|
||||||
|
|
||||||
|
type GiteaServerOptions = {
|
||||||
|
providers: {
|
||||||
|
helm: HelmProvider;
|
||||||
|
kubernetes: KubernetesProvider;
|
||||||
|
};
|
||||||
|
name: string;
|
||||||
|
namespace: string;
|
||||||
|
r2Endpoint: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class GiteaServer extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, options: GiteaServerOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const { kubernetes, helm } = options.providers;
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "admin", {
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "gitea-admin",
|
||||||
|
namespace: options.namespace,
|
||||||
|
itemPath: "vaults/Lab/items/gitea-admin",
|
||||||
|
});
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "oauth", {
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "gitea-oauth",
|
||||||
|
namespace: options.namespace,
|
||||||
|
itemPath: "vaults/Lab/items/gitea-oauth",
|
||||||
|
});
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "smtp", {
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "gitea-smtp-token",
|
||||||
|
namespace: options.namespace,
|
||||||
|
itemPath: "vaults/Lab/items/smtp-token",
|
||||||
|
});
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "r2", {
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "gitea-cloudflare-token",
|
||||||
|
namespace: options.namespace,
|
||||||
|
itemPath: "vaults/Lab/items/cloudflare",
|
||||||
|
});
|
||||||
|
|
||||||
|
new Release(this, id, {
|
||||||
|
...options,
|
||||||
|
provider: helm,
|
||||||
|
repository: "https://dl.gitea.com/charts",
|
||||||
|
chart: "gitea",
|
||||||
|
createNamespace: true,
|
||||||
|
set: [
|
||||||
|
{
|
||||||
|
name: "gitea.config.storage.MINIO_ENDPOINT",
|
||||||
|
value: options.r2Endpoint,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
values: [
|
||||||
|
fs.readFileSync(path.join(__dirname, "values.yaml"), {
|
||||||
|
encoding: "utf8",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
new IngressRouteTcp(this, "ssh-ingress", {
|
||||||
|
provider: kubernetes,
|
||||||
|
namespace: options.namespace,
|
||||||
|
entryPoint: "ssh",
|
||||||
|
serviceName: `${options.name}-ssh`,
|
||||||
|
servicePort: 22,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
161
utility-services/gitea/values.yaml
Normal file
161
utility-services/gitea/values.yaml
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
global:
|
||||||
|
storageClass: longhorn
|
||||||
|
image:
|
||||||
|
rootless: false
|
||||||
|
service:
|
||||||
|
http:
|
||||||
|
annotations:
|
||||||
|
metallb.universe.tf/allow-shared-ip: gitea
|
||||||
|
ssh:
|
||||||
|
annotations:
|
||||||
|
metallb.universe.tf/allow-shared-ip: gitea
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
annotations:
|
||||||
|
cert-manager.io/cluster-issuer: cloudflare-issuer
|
||||||
|
cert-manager.io/acme-challenge-type: dns01
|
||||||
|
cert-manager.io/private-key-size: 4096
|
||||||
|
className: traefik
|
||||||
|
hosts:
|
||||||
|
- host: git.dogar.dev
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
tls:
|
||||||
|
- secretName: gitea-tls
|
||||||
|
hosts:
|
||||||
|
- git.dogar.dev
|
||||||
|
gitea:
|
||||||
|
podAnnotations:
|
||||||
|
prometheus.io/scrape: "true"
|
||||||
|
prometheus.io/port: "6060"
|
||||||
|
admin:
|
||||||
|
existingSecret: gitea-admin
|
||||||
|
metrics:
|
||||||
|
enabled: true
|
||||||
|
serviceMonitor:
|
||||||
|
enabled: true
|
||||||
|
config:
|
||||||
|
server:
|
||||||
|
ENABLE_PPROF: true
|
||||||
|
ENABLE_GZIP: true
|
||||||
|
LFS_START_SERVER: true
|
||||||
|
SSH_DOMAIN: git.dogar.dev
|
||||||
|
database:
|
||||||
|
DB_TYPE: postgres
|
||||||
|
HOST: postgres-cluster-rw
|
||||||
|
NAME: gitea
|
||||||
|
USER: gitea
|
||||||
|
SSL_MODE: verify-full
|
||||||
|
metrics:
|
||||||
|
ENABLED: true
|
||||||
|
cache:
|
||||||
|
ADAPTER: memory
|
||||||
|
session:
|
||||||
|
PROVIDER: db
|
||||||
|
PROVIDER_CONFIG: ""
|
||||||
|
queue:
|
||||||
|
TYPE: channel
|
||||||
|
storage:
|
||||||
|
STORAGE_TYPE: minio
|
||||||
|
MINIO_USE_SSL: true
|
||||||
|
MINIO_BUCKET_LOOKUP_STYLE: path
|
||||||
|
MINIO_LOCATION: auto
|
||||||
|
service:
|
||||||
|
DISABLE_REGISTRATION: true
|
||||||
|
oauth2_client:
|
||||||
|
ENABLE_AUTO_REGISTRATION: true
|
||||||
|
mailer:
|
||||||
|
ENABLED: true
|
||||||
|
PROTOCOL: smtp+starttls
|
||||||
|
SMTP_ADDR: smtp.protonmail.ch
|
||||||
|
SMTP_PORT: 587
|
||||||
|
FROM: git@dogar.dev
|
||||||
|
picture:
|
||||||
|
GRAVATAR_SOURCE: gravatar
|
||||||
|
oauth:
|
||||||
|
- name: "authentik"
|
||||||
|
provider: "openidConnect"
|
||||||
|
existingSecret: gitea-oauth
|
||||||
|
autoDiscoverUrl: "https://auth.dogar.dev/application/o/gitea/.well-known/openid-configuration"
|
||||||
|
iconUrl: "https://goauthentik.io/img/icon.png"
|
||||||
|
scopes: "email profile"
|
||||||
|
additionalConfigFromEnvs:
|
||||||
|
- name: GITEA__MAILER__PASSWD
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-smtp-token
|
||||||
|
key: gitea-password
|
||||||
|
- name: GITEA__PACKAGES__CHUNKED_UPLOAD_PATH
|
||||||
|
value: "/tmp/gitea-uploads"
|
||||||
|
- name: GITEA__PACKAGES__CHUNKED_UPLOAD_CONCURRENCY
|
||||||
|
value: "4"
|
||||||
|
- name: GITEA__STORAGE__MINIO_ACCESS_KEY_ID
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-cloudflare-token
|
||||||
|
key: access_key_id
|
||||||
|
- name: GITEA__STORAGE__MINIO_SECRET_ACCESS_KEY
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-cloudflare-token
|
||||||
|
key: secret_access_key
|
||||||
|
persistence:
|
||||||
|
labels:
|
||||||
|
recurring-job.longhorn.io/source: "enabled"
|
||||||
|
recurring-job.longhorn.io/daily-backup: "enabled"
|
||||||
|
enabled: true
|
||||||
|
size: 50Gi
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteMany
|
||||||
|
deployment:
|
||||||
|
env:
|
||||||
|
- name: PGSSLMODE
|
||||||
|
value: verify-full
|
||||||
|
- name: PGSSLROOTCERT
|
||||||
|
value: /opt/gitea/.postgresql/root.crt
|
||||||
|
- name: PGSSLCERT
|
||||||
|
value: /opt/gitea/.postgresql/postgresql.crt
|
||||||
|
- name: PGSSLKEY
|
||||||
|
value: /opt/gitea/.postgresql/postgresql.key
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 6
|
||||||
|
memory: 6Gi
|
||||||
|
extraVolumes:
|
||||||
|
- name: ssl-bundle
|
||||||
|
projected:
|
||||||
|
sources:
|
||||||
|
- secret:
|
||||||
|
name: gitea-client-cert
|
||||||
|
items:
|
||||||
|
- key: tls.crt
|
||||||
|
path: postgresql.crt
|
||||||
|
- key: tls.key
|
||||||
|
path: postgresql.key
|
||||||
|
mode: 0600
|
||||||
|
- secret:
|
||||||
|
name: postgres-server-cert
|
||||||
|
items:
|
||||||
|
- key: ca.crt
|
||||||
|
path: root.crt
|
||||||
|
- name: gitea-temp
|
||||||
|
emptyDir: {}
|
||||||
|
extraInitVolumeMounts:
|
||||||
|
- name: ssl-bundle
|
||||||
|
mountPath: /opt/gitea/.postgresql
|
||||||
|
readOnly: true
|
||||||
|
extraContainerVolumeMounts:
|
||||||
|
- name: ssl-bundle
|
||||||
|
mountPath: /opt/gitea/.postgresql
|
||||||
|
readOnly: true
|
||||||
|
readOnly: true
|
||||||
|
- name: gitea-temp
|
||||||
|
mountPath: /tmp/gitea-uploads
|
||||||
|
postgresql-ha:
|
||||||
|
enabled: false
|
||||||
|
valkey-cluster:
|
||||||
|
enabled: false
|
||||||
94
utility-services/index.ts
Normal file
94
utility-services/index.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import * as path from "path";
|
||||||
|
import { DataKubernetesNamespaceV1 } from "@cdktf/provider-kubernetes/lib/data-kubernetes-namespace-v1";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
||||||
|
import { DataTerraformRemoteStateLocal, TerraformStack } from "cdktf";
|
||||||
|
import { Construct } from "constructs";
|
||||||
|
|
||||||
|
import { ValkeyCluster } from "./valkey";
|
||||||
|
import { GiteaServer } from "./gitea";
|
||||||
|
import { AuthentikServer } from "./authentik";
|
||||||
|
import { PostgresCluster } from "./postgres";
|
||||||
|
|
||||||
|
export class UtilityServices extends TerraformStack {
|
||||||
|
constructor(scope: Construct, id: string) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const kubernetes = new KubernetesProvider(this, "kubernetes", {
|
||||||
|
configPath: "~/.kube/config",
|
||||||
|
});
|
||||||
|
|
||||||
|
const helm = new HelmProvider(this, "helm", {
|
||||||
|
kubernetes: {
|
||||||
|
configPath: "~/.kube/config",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const homelabState = new DataTerraformRemoteStateLocal(
|
||||||
|
this,
|
||||||
|
"homelab-state",
|
||||||
|
{
|
||||||
|
path: path.join(
|
||||||
|
__dirname,
|
||||||
|
"../cdktf.out/stacks/homelab/terraform.tfstate",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const namespaceName = homelabState.getString("namespace-output");
|
||||||
|
const namespaceResource = new DataKubernetesNamespaceV1(
|
||||||
|
this,
|
||||||
|
"homelab-namespace",
|
||||||
|
{
|
||||||
|
provider: kubernetes,
|
||||||
|
metadata: {
|
||||||
|
name: namespaceName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const namespace = namespaceResource.metadata.name;
|
||||||
|
|
||||||
|
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
|
||||||
|
|
||||||
|
const valkeyCluster = new ValkeyCluster(this, "valkey-cluster", {
|
||||||
|
namespace,
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "valkey",
|
||||||
|
});
|
||||||
|
|
||||||
|
const postgres = new PostgresCluster(this, "postgres-cluster", {
|
||||||
|
certManagerApiVersion: "cert-manager.io/v1",
|
||||||
|
name: "postgres-cluster",
|
||||||
|
namespace,
|
||||||
|
provider: kubernetes,
|
||||||
|
users: ["shahab", "budget-tracker", "authentik", "gitea"],
|
||||||
|
primaryUser: "shahab",
|
||||||
|
initSecretName: "postgres-password",
|
||||||
|
backupR2EndpointURL: `https://${r2Endpoint}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const authentik = new AuthentikServer(this, "authentik-server", {
|
||||||
|
providers: {
|
||||||
|
helm,
|
||||||
|
kubernetes,
|
||||||
|
},
|
||||||
|
name: "authentik",
|
||||||
|
namespace,
|
||||||
|
});
|
||||||
|
|
||||||
|
authentik.node.addDependency(valkeyCluster);
|
||||||
|
authentik.node.addDependency(postgres);
|
||||||
|
|
||||||
|
const gitea = new GiteaServer(this, "gitea-server", {
|
||||||
|
providers: {
|
||||||
|
helm,
|
||||||
|
kubernetes,
|
||||||
|
},
|
||||||
|
name: "gitea",
|
||||||
|
namespace,
|
||||||
|
r2Endpoint: r2Endpoint,
|
||||||
|
});
|
||||||
|
|
||||||
|
gitea.node.addDependency(authentik);
|
||||||
|
}
|
||||||
|
}
|
||||||
456
utility-services/postgres/index.ts
Normal file
456
utility-services/postgres/index.ts
Normal file
@@ -0,0 +1,456 @@
|
|||||||
|
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
import { Construct } from "constructs";
|
||||||
|
import { OnePasswordSecret } from "../../utils";
|
||||||
|
|
||||||
|
type PostgresClusterOptions = {
|
||||||
|
provider: KubernetesProvider;
|
||||||
|
name: string;
|
||||||
|
namespace: string;
|
||||||
|
users: string[];
|
||||||
|
primaryUser: string;
|
||||||
|
initSecretName: string;
|
||||||
|
certManagerApiVersion: string;
|
||||||
|
backupR2EndpointURL: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class PostgresCluster extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, options: PostgresClusterOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const { provider } = options;
|
||||||
|
|
||||||
|
const destinationPath = "s3://postgres-backups/";
|
||||||
|
const endpointURL = options.backupR2EndpointURL;
|
||||||
|
const barmanStoreName = "r2-postgres-backup-store";
|
||||||
|
const backupServerName = `${options.name}-backup`;
|
||||||
|
|
||||||
|
const barmanConfiguration = {
|
||||||
|
destinationPath,
|
||||||
|
endpointURL,
|
||||||
|
s3Credentials: {
|
||||||
|
accessKeyId: {
|
||||||
|
name: "barman-cloudflare-token",
|
||||||
|
key: "access_key_id",
|
||||||
|
},
|
||||||
|
secretAccessKey: {
|
||||||
|
name: "barman-cloudflare-token",
|
||||||
|
key: "secret_access_key",
|
||||||
|
},
|
||||||
|
region: {
|
||||||
|
name: "barman-cloudflare-token",
|
||||||
|
key: "AWS_REGION",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wal: {
|
||||||
|
compression: "gzip",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
compression: "gzip",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "barman-cloudflare-token", {
|
||||||
|
provider: options.provider,
|
||||||
|
name: "barman-cloudflare-token",
|
||||||
|
namespace: options.namespace,
|
||||||
|
itemPath: "vaults/Lab/items/cloudflare",
|
||||||
|
});
|
||||||
|
|
||||||
|
new Manifest(this, "r2-backup-store", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "barmancloud.cnpg.io/v1",
|
||||||
|
kind: "ObjectStore",
|
||||||
|
metadata: {
|
||||||
|
namespace: options.namespace,
|
||||||
|
name: barmanStoreName,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
retentionPolicy: "15d",
|
||||||
|
configuration: {
|
||||||
|
...barmanConfiguration,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { certManagerApiVersion } = options;
|
||||||
|
|
||||||
|
const certNames = {
|
||||||
|
server: "postgres-server-cert",
|
||||||
|
client: "postgres-client-cert",
|
||||||
|
};
|
||||||
|
|
||||||
|
const caNames = {
|
||||||
|
server: "postgres-server-ca",
|
||||||
|
client: "postgres-client-ca",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Self-signed issuer for creating CA certificates
|
||||||
|
new Manifest(this, "selfsigned-issuer", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Issuer",
|
||||||
|
metadata: {
|
||||||
|
name: "selfsigned-issuer",
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
selfSigned: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Server CA certificate
|
||||||
|
new Manifest(this, "server-ca-cert", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Certificate",
|
||||||
|
metadata: {
|
||||||
|
name: "server-ca",
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
isCA: true,
|
||||||
|
commonName: caNames.server,
|
||||||
|
secretName: caNames.server,
|
||||||
|
privateKey: {
|
||||||
|
algorithm: "ECDSA",
|
||||||
|
size: 384,
|
||||||
|
},
|
||||||
|
duration: "52560h", // 6 years
|
||||||
|
renewBefore: "8760h", // 1 year before expiration
|
||||||
|
issuerRef: {
|
||||||
|
name: "selfsigned-issuer",
|
||||||
|
kind: "Issuer",
|
||||||
|
group: "cert-manager.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Issuer using the server CA
|
||||||
|
new Manifest(this, "server-ca-issuer", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Issuer",
|
||||||
|
metadata: {
|
||||||
|
name: `${caNames.server}-issuer`,
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
ca: {
|
||||||
|
secretName: caNames.server,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Server certificate
|
||||||
|
new Manifest(this, "server-cert", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Certificate",
|
||||||
|
metadata: {
|
||||||
|
name: certNames.server,
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
secretName: certNames.server,
|
||||||
|
usages: ["server auth"],
|
||||||
|
dnsNames: [
|
||||||
|
"postgres-cluster-rw",
|
||||||
|
"postgres-cluster-rw.homelab.svc.cluster.local",
|
||||||
|
"postgres.dogar.dev",
|
||||||
|
],
|
||||||
|
duration: "4380h", // 6 months
|
||||||
|
renewBefore: "720h", // 30 days before expiration
|
||||||
|
issuerRef: {
|
||||||
|
name: `${caNames.server}-issuer`,
|
||||||
|
kind: "Issuer",
|
||||||
|
group: "cert-manager.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Client CA certificate
|
||||||
|
new Manifest(this, "client-ca", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Certificate",
|
||||||
|
metadata: {
|
||||||
|
name: "client-ca",
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
isCA: true,
|
||||||
|
commonName: caNames.client,
|
||||||
|
secretName: caNames.client,
|
||||||
|
privateKey: {
|
||||||
|
algorithm: "ECDSA",
|
||||||
|
size: 256,
|
||||||
|
},
|
||||||
|
duration: "52560h", // 6 years
|
||||||
|
renewBefore: "8760h", // 1 year before expiration
|
||||||
|
issuerRef: {
|
||||||
|
name: "selfsigned-issuer",
|
||||||
|
kind: "Issuer",
|
||||||
|
group: "cert-manager.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Issuer using the client CA
|
||||||
|
new Manifest(this, "client-ca-issuer", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Issuer",
|
||||||
|
metadata: {
|
||||||
|
name: `${caNames.client}-issuer`,
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
ca: {
|
||||||
|
secretName: caNames.client,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Secret for client certificate
|
||||||
|
new Manifest(this, `${certNames.client}-secret`, {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "v1",
|
||||||
|
kind: "Secret",
|
||||||
|
metadata: {
|
||||||
|
name: certNames.client,
|
||||||
|
namespace: options.namespace,
|
||||||
|
labels: {
|
||||||
|
"cnpg.io/reload": "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Client certificate for streaming replica
|
||||||
|
new Manifest(this, "streaming-replica-cert", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Certificate",
|
||||||
|
metadata: {
|
||||||
|
name: certNames.client,
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
secretName: certNames.client,
|
||||||
|
usages: ["client auth"],
|
||||||
|
commonName: "streaming_replica",
|
||||||
|
duration: "4380h", // 6 months
|
||||||
|
renewBefore: "720h", // 30 days before expiration
|
||||||
|
issuerRef: {
|
||||||
|
name: "postgres-client-ca-issuer",
|
||||||
|
kind: "Issuer",
|
||||||
|
group: "cert-manager.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Client certificates for users
|
||||||
|
options.users.forEach(
|
||||||
|
(user) =>
|
||||||
|
new Manifest(this, `${user}-client-cert`, {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: certManagerApiVersion,
|
||||||
|
kind: "Certificate",
|
||||||
|
metadata: {
|
||||||
|
name: `${user}-client-cert`,
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
secretName: `${user}-client-cert`,
|
||||||
|
usages: ["client auth"],
|
||||||
|
commonName: user,
|
||||||
|
duration: "4380h", // 6 months
|
||||||
|
renewBefore: "720h", // 30 days before expiration
|
||||||
|
issuerRef: {
|
||||||
|
name: "postgres-client-ca-issuer",
|
||||||
|
kind: "Issuer",
|
||||||
|
group: "cert-manager.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
new Manifest(this, "postgres-cluster", {
|
||||||
|
provider,
|
||||||
|
fieldManager: { forceConflicts: true },
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "postgresql.cnpg.io/v1",
|
||||||
|
kind: "Cluster",
|
||||||
|
metadata: {
|
||||||
|
name: options.name,
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
instances: 3,
|
||||||
|
minSyncReplicas: 1,
|
||||||
|
maxSyncReplicas: 2,
|
||||||
|
primaryUpdateStrategy: "unsupervised",
|
||||||
|
certificates: {
|
||||||
|
serverCASecret: certNames.server,
|
||||||
|
serverTLSSecret: certNames.server,
|
||||||
|
clientCASecret: certNames.client,
|
||||||
|
replicationTLSSecret: certNames.client,
|
||||||
|
},
|
||||||
|
postgresql: {
|
||||||
|
parameters: {
|
||||||
|
archive_mode: "on",
|
||||||
|
archive_timeout: "60min",
|
||||||
|
checkpoint_timeout: "10min",
|
||||||
|
checkpoint_completion_target: "0.7",
|
||||||
|
dynamic_shared_memory_type: "posix",
|
||||||
|
full_page_writes: "on",
|
||||||
|
log_destination: "csvlog",
|
||||||
|
log_directory: "/controller/log",
|
||||||
|
log_filename: "postgres",
|
||||||
|
log_rotation_age: "0",
|
||||||
|
log_rotation_size: "0",
|
||||||
|
log_truncate_on_rotation: "false",
|
||||||
|
logging_collector: "on",
|
||||||
|
max_parallel_workers: "32",
|
||||||
|
max_replication_slots: "32",
|
||||||
|
max_worker_processes: "32",
|
||||||
|
max_slot_wal_keep_size: "256MB",
|
||||||
|
max_wal_size: "512MB",
|
||||||
|
min_wal_size: "128MB",
|
||||||
|
shared_memory_type: "mmap",
|
||||||
|
shared_preload_libraries: "",
|
||||||
|
ssl_max_protocol_version: "TLSv1.3",
|
||||||
|
ssl_min_protocol_version: "TLSv1.3",
|
||||||
|
wal_compression: "on",
|
||||||
|
wal_keep_size: "128MB",
|
||||||
|
wal_level: "replica",
|
||||||
|
wal_log_hints: "on",
|
||||||
|
wal_receiver_timeout: "5s",
|
||||||
|
wal_sender_timeout: "5s",
|
||||||
|
},
|
||||||
|
pg_hba: [
|
||||||
|
`hostssl all ${options.primaryUser} all cert`,
|
||||||
|
"hostssl sameuser all all cert",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
name: "barman-cloud.cloudnative-pg.io",
|
||||||
|
isWALArchiver: true,
|
||||||
|
parameters: {
|
||||||
|
barmanObjectName: barmanStoreName,
|
||||||
|
serverName: backupServerName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
bootstrap: {
|
||||||
|
recovery: {
|
||||||
|
source: "clusterBackup",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
externalClusters: [
|
||||||
|
{
|
||||||
|
name: "clusterBackup",
|
||||||
|
plugin: {
|
||||||
|
name: "barman-cloud.cloudnative-pg.io",
|
||||||
|
parameters: {
|
||||||
|
barmanObjectName: barmanStoreName,
|
||||||
|
serverName: backupServerName,
|
||||||
|
skipWalArchiveCheck: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
managed: {
|
||||||
|
services: {
|
||||||
|
disabledDefaultServices: ["ro", "r"],
|
||||||
|
additional: [
|
||||||
|
{
|
||||||
|
selectorType: "rw",
|
||||||
|
serviceTemplate: {
|
||||||
|
metadata: {
|
||||||
|
name: "postgres-cluster",
|
||||||
|
superuser: true,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
type: "LoadBalancer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
name: options.primaryUser,
|
||||||
|
inRoles: ["postgres"],
|
||||||
|
inherit: true,
|
||||||
|
disablePassword: true,
|
||||||
|
createdb: true,
|
||||||
|
createrole: true,
|
||||||
|
login: true,
|
||||||
|
ensure: "present",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
storage: {
|
||||||
|
size: "10Gi",
|
||||||
|
storageClass: "longhorn",
|
||||||
|
},
|
||||||
|
walStorage: {
|
||||||
|
size: "2Gi",
|
||||||
|
storageClass: "longhorn",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new Manifest(this, "postgres-backup-job", {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "postgresql.cnpg.io/v1",
|
||||||
|
kind: "ScheduledBackup",
|
||||||
|
metadata: {
|
||||||
|
name: "postgres-cluster",
|
||||||
|
namespace: options.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
immediate: true,
|
||||||
|
// weekly midnight on Sunday
|
||||||
|
schedule: "* 0 0 * * 0",
|
||||||
|
backupOwnerReference: "self",
|
||||||
|
method: "plugin",
|
||||||
|
pluginConfiguration: {
|
||||||
|
name: "barman-cloud.cloudnative-pg.io",
|
||||||
|
parameters: {
|
||||||
|
barmanObjectName: barmanStoreName,
|
||||||
|
serverName: backupServerName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cluster: {
|
||||||
|
name: options.name,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { DeploymentV1 } from "@cdktf/provider-kubernetes/lib/deployment-v1";
|
|||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1";
|
import { ServiceV1 } from "@cdktf/provider-kubernetes/lib/service-v1";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
|
import { OnePasswordSecret } from "../../utils";
|
||||||
|
|
||||||
type ValkeyClusterOptions = {
|
type ValkeyClusterOptions = {
|
||||||
provider: KubernetesProvider;
|
provider: KubernetesProvider;
|
||||||
@@ -17,6 +18,13 @@ export class ValkeyCluster extends Construct {
|
|||||||
const labels = { app: "valkey" };
|
const labels = { app: "valkey" };
|
||||||
const { provider, name, namespace } = options;
|
const { provider, name, namespace } = options;
|
||||||
|
|
||||||
|
new OnePasswordSecret(this, "valkey-secret", {
|
||||||
|
provider,
|
||||||
|
name: "valkey",
|
||||||
|
namespace,
|
||||||
|
itemPath: "vaults/Lab/items/valkey",
|
||||||
|
});
|
||||||
|
|
||||||
new DeploymentV1(this, "valkey-deployment", {
|
new DeploymentV1(this, "valkey-deployment", {
|
||||||
provider,
|
provider,
|
||||||
metadata: {
|
metadata: {
|
||||||
36
utils/1password-secret/index.ts
Normal file
36
utils/1password-secret/index.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
|
||||||
|
type SecretOptions = {
|
||||||
|
provider: KubernetesProvider;
|
||||||
|
namespace: string;
|
||||||
|
name: string;
|
||||||
|
itemPath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class OnePasswordSecret extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, options: SecretOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const { itemPath, name, namespace, provider } = options;
|
||||||
|
|
||||||
|
new Manifest(this, name, {
|
||||||
|
provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "onepassword.com/v1",
|
||||||
|
kind: "OnePasswordItem",
|
||||||
|
metadata: {
|
||||||
|
name,
|
||||||
|
namespace,
|
||||||
|
annotations: {
|
||||||
|
"operator.1password.io/auto-restart": "true",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
itemPath,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
85
utils/cert-manager/index.ts
Normal file
85
utils/cert-manager/index.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
|
||||||
|
export interface CertificateOptions {
|
||||||
|
provider: KubernetesProvider;
|
||||||
|
|
||||||
|
/** Namespace to create the Certificate in */
|
||||||
|
namespace: string;
|
||||||
|
|
||||||
|
/** Required name of the certificate (and CRD name) */
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/** Secret name for storing the issued TLS cert */
|
||||||
|
secretName: string;
|
||||||
|
|
||||||
|
/** One or more DNS names the certificate should cover */
|
||||||
|
dnsNames: string[];
|
||||||
|
|
||||||
|
/** Reference to the cert-manager issuer */
|
||||||
|
issuerRef: {
|
||||||
|
name: string;
|
||||||
|
kind?: string; // ClusterIssuer or Issuer
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Optional duration (default: cert-manager default) */
|
||||||
|
duration?: string;
|
||||||
|
|
||||||
|
/** Optional renewBefore (default: cert-manager default) */
|
||||||
|
renewBefore?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Certificate extends Construct {
|
||||||
|
public readonly manifest: Manifest;
|
||||||
|
|
||||||
|
constructor(scope: Construct, id: string, opts: CertificateOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const manifest: any = {
|
||||||
|
apiVersion: "cert-manager.io/v1",
|
||||||
|
kind: "Certificate",
|
||||||
|
metadata: {
|
||||||
|
name: opts.name,
|
||||||
|
namespace: opts.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
secretName: opts.secretName,
|
||||||
|
dnsNames: opts.dnsNames,
|
||||||
|
issuerRef: {
|
||||||
|
name: opts.issuerRef.name,
|
||||||
|
kind: opts.issuerRef.kind ?? "ClusterIssuer",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (opts.duration) {
|
||||||
|
manifest.spec.duration = opts.duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.renewBefore) {
|
||||||
|
manifest.spec.renewBefore = opts.renewBefore;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.manifest = new Manifest(this, id, {
|
||||||
|
provider: opts.provider,
|
||||||
|
manifest,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CloudflareCertificate extends Certificate {
|
||||||
|
constructor(
|
||||||
|
scope: Construct,
|
||||||
|
id: string,
|
||||||
|
opts: Omit<CertificateOptions, "issuerRef">,
|
||||||
|
) {
|
||||||
|
super(scope, id, {
|
||||||
|
...opts,
|
||||||
|
issuerRef: {
|
||||||
|
name: "cloudflare-issuer",
|
||||||
|
kind: "ClusterIssuer",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
3
utils/index.ts
Normal file
3
utils/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { CloudflareCertificate } from "./cert-manager";
|
||||||
|
export { OnePasswordSecret } from "./1password-secret";
|
||||||
|
export { IngressRoute } from "./traefik";
|
||||||
2
utils/traefik/index.ts
Normal file
2
utils/traefik/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { IngressRoute } from "./ingress";
|
||||||
|
export { IngressRouteTcp } from "./ingress-tcp";
|
||||||
71
utils/traefik/ingress-tcp.ts
Normal file
71
utils/traefik/ingress-tcp.ts
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
|
||||||
|
export interface IngressRouteTcpOptions {
|
||||||
|
provider: KubernetesProvider;
|
||||||
|
|
||||||
|
/** Namespace where the IngressRouteTCP will be created */
|
||||||
|
namespace: string;
|
||||||
|
|
||||||
|
/** EntryPoint name (e.g., "ssh", "mc25565", "postgres", etc.) */
|
||||||
|
entryPoint: string;
|
||||||
|
|
||||||
|
/** Backend service name */
|
||||||
|
serviceName: string;
|
||||||
|
|
||||||
|
/** Backend service port */
|
||||||
|
servicePort: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match rule.
|
||||||
|
* Default is `HostSNI(\`*\`)` which is correct for most TCP services.
|
||||||
|
*/
|
||||||
|
match?: string;
|
||||||
|
|
||||||
|
/** Name override (CR name) */
|
||||||
|
name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IngressRouteTcp extends Construct {
|
||||||
|
public readonly manifest: Manifest;
|
||||||
|
|
||||||
|
constructor(scope: Construct, id: string, opts: IngressRouteTcpOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const name =
|
||||||
|
opts.name ??
|
||||||
|
`tcp-${opts.entryPoint}-${opts.serviceName}`.replace(
|
||||||
|
/[^a-zA-Z0-9-]/g,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
|
||||||
|
const matchRule = opts.match ?? "HostSNI(`*`)";
|
||||||
|
|
||||||
|
this.manifest = new Manifest(this, name, {
|
||||||
|
provider: opts.provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "traefik.io/v1alpha1",
|
||||||
|
kind: "IngressRouteTCP",
|
||||||
|
metadata: {
|
||||||
|
name,
|
||||||
|
namespace: opts.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
entryPoints: [opts.entryPoint],
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
match: matchRule,
|
||||||
|
services: [
|
||||||
|
{
|
||||||
|
name: opts.serviceName,
|
||||||
|
port: opts.servicePort,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
|||||||
|
|
||||||
import { CloudflareCertificate } from "../cert-manager";
|
import { CloudflareCertificate } from "../cert-manager";
|
||||||
|
|
||||||
export interface TraefikIngressRouteOptions {
|
export interface IngressRouteOptions {
|
||||||
provider: KubernetesProvider;
|
provider: KubernetesProvider;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
|
|
||||||
@@ -31,10 +31,10 @@ export interface TraefikIngressRouteOptions {
|
|||||||
name?: string;
|
name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TraefikIngressRoute extends Construct {
|
export class IngressRoute extends Construct {
|
||||||
public readonly manifest: Manifest;
|
public readonly manifest: Manifest;
|
||||||
|
|
||||||
constructor(scope: Construct, id: string, opts: TraefikIngressRouteOptions) {
|
constructor(scope: Construct, id: string, opts: IngressRouteOptions) {
|
||||||
super(scope, id);
|
super(scope, id);
|
||||||
|
|
||||||
const name = opts.name ?? `route-${opts.host.replace(/\./g, "-")}`;
|
const name = opts.name ?? `route-${opts.host.replace(/\./g, "-")}`;
|
||||||
Reference in New Issue
Block a user