Compare commits

...

4 Commits

15 changed files with 274 additions and 761 deletions

View File

@@ -1,85 +0,0 @@
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",
},
});
}
}

View File

@@ -1,90 +0,0 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflare-domains-config
namespace: homelab
data:
DOMAINS: "auth.dogar.dev"
PROXIED: "true"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflare-domains-config-non-proxied
namespace: homelab
data:
DOMAINS: "dogar.dev,git.dogar.dev,nix.dogar.dev,pip.dogar.dev,npm.dogar.dev"
PROXIED: "false"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflare-ddns
namespace: homelab
spec:
replicas: 1
selector:
matchLabels:
app: cloudflare-ddns
template:
metadata:
labels:
app: cloudflare-ddns
spec:
nodeSelector:
nodepool: worker
containers:
- name: cloudflare-ddns
image: favonia/cloudflare-ddns:latest
env:
- name: CLOUDFLARE_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-token
key: token
- name: DOMAINS
valueFrom:
configMapKeyRef:
name: cloudflare-domains-config
key: DOMAINS
- name: UPDATE_TIMEOUT
value: "30s"
- name: IP6_PROVIDER
value: "none"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflare-ddns-non-proxied
namespace: homelab
spec:
replicas: 1
selector:
matchLabels:
app: cloudflare-ddns
template:
metadata:
labels:
app: cloudflare-ddns
spec:
nodeSelector:
nodepool: worker
containers:
- name: cloudflare-ddns-non-proxied
image: favonia/cloudflare-ddns:latest
env:
- name: CLOUDFLARE_API_TOKEN
valueFrom:
secretKeyRef:
name: cloudflare-token
key: token
- name: DOMAINS
valueFrom:
configMapKeyRef:
name: cloudflare-domains-config-non-proxied
key: DOMAINS
- name: UPDATE_TIMEOUT
value: "30s"
- name: IP6_PROVIDER
value: "none"

View File

@@ -1,12 +1,11 @@
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 { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
export { CloudflareCertificate } from "./certificate";
type CertManagerOptions = {
providers: {
kubernetes: KubernetesProvider;
@@ -34,12 +33,14 @@ export class CertManager extends Construct {
chart: "cert-manager",
createNamespace: true,
values: [
fs.readFileSync("helm/values/cert-manager.values.yaml", {
fs.readFileSync(path.join(__dirname, "values.yaml"), {
encoding: "utf8",
}),
],
});
// "apiVersion=v1,kind=Secret,namespace=default,name=sample"
// Self-signed ClusterIssuer for initial CA
new Manifest(this, "ca-issuer", {
provider: kubernetes,
@@ -53,7 +54,9 @@ export class CertManager extends Construct {
selfSigned: {},
},
},
});
}).importFrom(
`apiVersion=${certManagerApiVersion},kind=ClusterIssuer,name=ca-issuer`,
);
// Self-signed CA Certificate
new Manifest(this, "selfsigned-ca", {
@@ -80,7 +83,9 @@ export class CertManager extends Construct {
},
},
},
});
}).importFrom(
`apiVersion=${certManagerApiVersion},kind=Certificate,name=selfsigned-ca,namespace=${options.namespace}`,
);
// CA-based ClusterIssuer
new Manifest(this, "cluster-issuer", {
@@ -97,7 +102,9 @@ export class CertManager extends Construct {
},
},
},
});
}).importFrom(
`apiVersion=${certManagerApiVersion},kind=ClusterIssuer,name=cluster-issuer`,
);
// Cloudflare ACME ClusterIssuer
new Manifest(this, "cloudflare-issuer", {
@@ -130,6 +137,8 @@ export class CertManager extends Construct {
},
},
},
});
}).importFrom(
`apiVersion=${certManagerApiVersion},kind=ClusterIssuer,name=cloudflare-issuer`,
);
}
}

View File

@@ -1,6 +1,6 @@
crds:
enabled: true
prometheus:
enabled: false
enabled: true
webhook:
timeoutSeconds: 4

69
core-services/index.ts Normal file
View File

@@ -0,0 +1,69 @@
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
import { NamespaceV1 } from "@cdktf/provider-kubernetes/lib/namespace-v1";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { TerraformOutput, TerraformStack } from "cdktf";
import { Construct } from "constructs";
import { Longhorn } from "./longhorn";
import { MetalLB } from "./metallb";
import { Traefik } from "./traefik";
import { CertManager } from "./cert-manager";
export class CoreServices 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 namespace = "homelab";
new NamespaceV1(this, "namespace", {
provider: kubernetes,
metadata: {
name: namespace,
},
}).importFrom("homelab");
new TerraformOutput(this, "namespace-output", {
value: namespace,
});
new Longhorn(this, "longhorn", {
name: "longhorn",
providers: {
kubernetes,
helm,
},
});
new MetalLB(this, "metallb", {
provider: helm,
name: "metallb",
namespace: "metallb-system",
});
new Traefik(this, "traefik", {
provider: helm,
namespace,
name: "traefik",
});
new CertManager(this, "cert-manager", {
certManagerApiVersion: "cert-manager.io/v1",
name: "cert-manager",
namespace,
version: "1.18.2",
providers: {
kubernetes,
helm,
},
});
}
}

View File

@@ -1,9 +1,11 @@
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 { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { IngressRoute } from "../../utils";
type LonghornOptions = {
providers: {
@@ -28,7 +30,7 @@ export class Longhorn extends Construct {
chart: "longhorn",
createNamespace: true,
values: [
fs.readFileSync("helm/values/longhorn.values.yaml", {
fs.readFileSync(path.join(__dirname, "values.yaml"), {
encoding: "utf8",
}),
],
@@ -51,5 +53,16 @@ export class Longhorn extends Construct {
},
},
});
new IngressRoute(this, "ingress", {
provider: kubernetes,
name: "longhorn",
namespace,
serviceName: "longhorn-frontend",
servicePort: 80,
host: "longhorn.dogar.dev",
tlsSecretName: "longhorn-tls",
entryPoints: ["websecure"],
});
}
}

View File

@@ -16,12 +16,4 @@ metrics:
serviceMonitor:
enabled: true
ingress:
enabled: true
ingressClassName: traefik
host: longhorn.dogar.dev
tls: true
tlsSecretName: longhorn-tls
annotations:
cert-manager.io/cluster-issuer: cloudflare-issuer
cert-manager.io/acme-challenge-type: dns01
cert-manager.io/private-key-size: "4096"
enabled: false

View File

@@ -1,15 +0,0 @@
grafana:
enabled: true
ingress:
enabled: true
ingressClassName: traefik
annotations:
cert-manager.io/cluster-issuer: cloudflare-issuer
cert-manager.io/acme-challenge-type: dns01
cert-manager.io/private-key-size: "4096"
hosts:
- grafana.dogar.dev
tls:
- secretName: grafana-tls
hosts:
- grafana.dogar.dev

120
main.ts
View File

@@ -1,88 +1,24 @@
import * as dotenv from "dotenv";
import { cleanEnv, str } from "envalid";
import { Construct } from "constructs";
import { App, TerraformStack, LocalBackend, TerraformOutput } from "cdktf";
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { NamespaceV1 } from "@cdktf/provider-kubernetes/lib/namespace-v1";
import { Longhorn } from "./longhorn";
import { CertManager } from "./cert-manager";
import { Traefik } from "./traefik";
import { MetalLB } from "./metallb";
import { App, S3Backend, TerraformStack } from "cdktf";
import { CacheInfrastructure } from "./cache-infrastructure";
import { UtilityServices } from "./utility-services";
import { K8SOperators } from "./k8s-operators";
import { CoreServices } from "./core-services";
dotenv.config();
cleanEnv(process.env, {
const env = cleanEnv(process.env, {
ACCOUNT_ID: str({ desc: "Cloudflare account id." }),
OP_CONNECT_TOKEN: str({ desc: "1Password Connect token." }),
ACCESS_KEY: str({ desc: "Access key ID for R2 storage." }),
SECRET_KEY: str({ desc: "Secret access key for R2 storage." }),
});
class CoreServices 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 namespace = "homelab";
new NamespaceV1(this, "namespace", {
provider: kubernetes,
metadata: {
name: namespace,
},
});
new TerraformOutput(this, "namespace-output", {
value: namespace,
});
new Longhorn(this, "longhorn", {
name: "longhorn",
providers: {
kubernetes,
helm,
},
});
new MetalLB(this, "metallb", {
provider: helm,
name: "metallb",
namespace: "metallb-system",
});
new Traefik(this, "traefik", {
provider: helm,
namespace,
name: "traefik",
});
new CertManager(this, "cert-manager", {
certManagerApiVersion: "cert-manager.io/v1",
name: "cert-manager",
namespace,
version: "1.18.2",
providers: {
kubernetes,
helm,
},
});
}
}
const r2Endpoint = `https://${env.ACCOUNT_ID}.r2.cloudflarestorage.com`;
const app = new App();
const coreServices = new CoreServices(app, "homelab");
const coreServices = new CoreServices(app, "core-services");
const k8sOperators = new K8SOperators(app, "k8s-operators");
k8sOperators.node.addDependency(coreServices);
@@ -93,24 +29,30 @@ utilityServices.node.addDependency(k8sOperators);
const caches = new CacheInfrastructure(app, "cache-infrastructure");
caches.node.addDependency(utilityServices);
new LocalBackend(coreServices, {
path: "terraform.tfstate",
workspaceDir: ".",
});
const deploy: (stack: TerraformStack, key: string) => S3Backend = (
stack,
key,
) =>
new S3Backend(stack, {
bucket: "terraform-state",
key: `${key}/terraform.tfstate`,
region: "auto",
endpoints: {
s3: r2Endpoint,
},
accessKey: env.ACCESS_KEY,
secretKey: env.SECRET_KEY,
encrypt: true,
usePathStyle: true,
skipRegionValidation: true,
skipCredentialsValidation: true,
skipRequestingAccountId: true,
skipS3Checksum: true,
});
new LocalBackend(caches, {
path: "terraform.tfstate",
workspaceDir: "./cachestf",
});
new LocalBackend(utilityServices, {
path: "terraform.tfstate",
workspaceDir: "./utilityservicestf",
});
new LocalBackend(k8sOperators, {
path: "terraform.tfstate",
workspaceDir: "./k8soperatorstf",
});
deploy(coreServices, "core-services");
deploy(utilityServices, "utility-services");
deploy(k8sOperators, "k8s-operators");
deploy(caches, "cache-infrastructure");
app.synth();

View File

@@ -1,452 +0,0 @@
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { Construct } from "constructs";
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: "cloudflare-token",
key: "access_key_id",
},
secretAccessKey: {
name: "cloudflare-token",
key: "secret_access_key",
},
region: {
name: "cloudflare-token",
key: "AWS_REGION",
},
},
wal: {
compression: "gzip",
},
data: {
compression: "gzip",
},
};
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,
annotations: {
"external-dns.alpha.kubernetes.io/hostname":
"postgres.dogar.dev",
},
},
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,
},
},
},
});
}
}

View File

@@ -0,0 +1,110 @@
import { Construct } from "constructs";
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
import { OnePasswordSecret } from "../../utils";
import { ConfigMapV1 } from "@cdktf/provider-kubernetes/lib/config-map-v1";
import { DeploymentV1 } from "@cdktf/provider-kubernetes/lib/deployment-v1";
type DynamicDNSOptions = {
provider: KubernetesProvider;
name: string;
namespace: string;
records: string[];
};
export class DynamicDNS extends Construct {
constructor(scope: Construct, id: string, options: DynamicDNSOptions) {
super(scope, id);
const { provider, name, namespace, records } = options;
new OnePasswordSecret(this, "cloudflare-token", {
provider,
name: "ddns-cloudflare-token",
namespace: options.namespace,
itemPath: "vaults/Lab/items/cloudflare",
});
new ConfigMapV1(this, "ddns-configmap", {
provider,
metadata: {
name,
namespace,
},
data: {
DOMAINS: records.join(","),
PROXIED: "false",
},
});
new DeploymentV1(this, "ddns-deployment", {
provider,
metadata: {
name,
namespace,
},
spec: {
selector: {
matchLabels: {
app: name,
},
},
template: {
metadata: {
labels: {
app: name,
},
},
spec: {
nodeSelector: {
nodepool: "worker",
},
container: [
{
name: "ddns-updater",
image: "favonia/cloudflare-ddns:latest",
env: [
{
name: "CLOUDFLARE_API_TOKEN",
valueFrom: {
secretKeyRef: {
name: "ddns-cloudflare-token",
key: "token",
},
},
},
{
name: "DOMAINS",
valueFrom: {
configMapKeyRef: {
name,
key: "DOMAINS",
},
},
},
{
name: "PROXIED",
valueFrom: {
configMapKeyRef: {
name,
key: "PROXIED",
},
},
},
{
name: "UPDATE_TIMEOUT",
value: "30s",
},
{
name: "IP6_PROVIDER",
value: "none",
},
],
},
],
},
},
},
});
}
}

View File

@@ -1,14 +1,14 @@
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 { DataTerraformRemoteStateS3, TerraformStack } from "cdktf";
import { Construct } from "constructs";
import { ValkeyCluster } from "./valkey";
import { GiteaServer } from "./gitea";
import { AuthentikServer } from "./authentik";
import { PostgresCluster } from "./postgres";
import { DynamicDNS } from "./dynamic-dns";
export class UtilityServices extends TerraformStack {
constructor(scope: Construct, id: string) {
@@ -24,16 +24,24 @@ export class UtilityServices extends TerraformStack {
},
});
const homelabState = new DataTerraformRemoteStateLocal(
this,
"homelab-state",
{
path: path.join(
__dirname,
"../cdktf.out/stacks/homelab/terraform.tfstate",
),
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
const homelabState = new DataTerraformRemoteStateS3(this, "homelab-state", {
usePathStyle: true,
skipRegionValidation: true,
skipCredentialsValidation: true,
skipRequestingAccountId: true,
skipS3Checksum: true,
encrypt: true,
bucket: "terraform-state",
key: "core-services/terraform.tfstate",
endpoints: {
s3: `https://${r2Endpoint}`,
},
);
region: "auto",
accessKey: process.env.ACCESS_KEY,
secretKey: process.env.SECRET_KEY,
});
const namespaceName = homelabState.getString("namespace-output");
const namespaceResource = new DataKubernetesNamespaceV1(
@@ -48,7 +56,19 @@ export class UtilityServices extends TerraformStack {
);
const namespace = namespaceResource.metadata.name;
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
new DynamicDNS(this, "dynamic-dns", {
provider: kubernetes,
namespace,
name: "cloudflare-ddns",
records: [
"dogar.dev",
"auth.dogar.dev",
"git.dogar.dev",
"nix.dogar.dev",
"pip.dogar.dev",
"npm.dogar.dev",
],
});
const valkeyCluster = new ValkeyCluster(this, "valkey-cluster", {
namespace,