Compare commits
10 Commits
f5d65d8ab9
...
84eb44a147
| Author | SHA1 | Date | |
|---|---|---|---|
|
84eb44a147
|
|||
|
5b6f0398f9
|
|||
|
244accede7
|
|||
|
a7d4878365
|
|||
|
454b299e1c
|
|||
|
b2fd9d100a
|
|||
|
65ed6ea664
|
|||
|
4def414c16
|
|||
|
4f5fbcf83a
|
|||
|
35c3c70b08
|
@@ -1,7 +1,7 @@
|
|||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
import { TerraformStack } from "cdktf";
|
import { TerraformStack } from "cdktf";
|
||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
import { NixCache } from "./nixcache";
|
import { NixCache } from "./nix";
|
||||||
|
|
||||||
export class CacheInfrastructure extends TerraformStack {
|
export class CacheInfrastructure extends TerraformStack {
|
||||||
constructor(scope: Construct, id: string) {
|
constructor(scope: Construct, id: string) {
|
||||||
|
|||||||
@@ -6,7 +6,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 { IngressRoute, LonghornPvc } from "../../utils";
|
import { PublicIngressRoute, LonghornPvc } from "../../utils";
|
||||||
|
|
||||||
export class NixCache extends Construct {
|
export class NixCache extends Construct {
|
||||||
constructor(scope: Construct, id: string, provider: KubernetesProvider) {
|
constructor(scope: Construct, id: string, provider: KubernetesProvider) {
|
||||||
@@ -119,14 +119,13 @@ export class NixCache extends Construct {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
new IngressRoute(this, "ingress-route", {
|
new PublicIngressRoute(this, "ingress-route", {
|
||||||
provider,
|
provider,
|
||||||
|
name: "nix-cache",
|
||||||
namespace: "homelab",
|
namespace: "homelab",
|
||||||
host: "nix.dogar.dev",
|
host: "nix.dogar.dev",
|
||||||
serviceName: "nix-cache",
|
serviceName: "nix-cache",
|
||||||
servicePort: 80,
|
servicePort: 80,
|
||||||
entryPoints: ["websecure"],
|
|
||||||
tlsSecretName: "nix-cache-tls",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,8 +39,6 @@ export class CertManager extends Construct {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
// "apiVersion=v1,kind=Secret,namespace=default,name=sample"
|
|
||||||
|
|
||||||
// Self-signed ClusterIssuer for initial CA
|
// Self-signed ClusterIssuer for initial CA
|
||||||
new Manifest(this, "ca-issuer", {
|
new Manifest(this, "ca-issuer", {
|
||||||
provider: kubernetes,
|
provider: kubernetes,
|
||||||
@@ -54,9 +52,7 @@ export class CertManager extends Construct {
|
|||||||
selfSigned: {},
|
selfSigned: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).importFrom(
|
});
|
||||||
`apiVersion=${certManagerApiVersion},kind=ClusterIssuer,name=ca-issuer`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Self-signed CA Certificate
|
// Self-signed CA Certificate
|
||||||
new Manifest(this, "selfsigned-ca", {
|
new Manifest(this, "selfsigned-ca", {
|
||||||
@@ -83,9 +79,7 @@ export class CertManager extends Construct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).importFrom(
|
});
|
||||||
`apiVersion=${certManagerApiVersion},kind=Certificate,name=selfsigned-ca,namespace=${options.namespace}`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// CA-based ClusterIssuer
|
// CA-based ClusterIssuer
|
||||||
new Manifest(this, "cluster-issuer", {
|
new Manifest(this, "cluster-issuer", {
|
||||||
@@ -102,9 +96,7 @@ export class CertManager extends Construct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).importFrom(
|
});
|
||||||
`apiVersion=${certManagerApiVersion},kind=ClusterIssuer,name=cluster-issuer`,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Cloudflare ACME ClusterIssuer
|
// Cloudflare ACME ClusterIssuer
|
||||||
new Manifest(this, "cloudflare-issuer", {
|
new Manifest(this, "cloudflare-issuer", {
|
||||||
@@ -137,8 +129,6 @@ export class CertManager extends Construct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).importFrom(
|
});
|
||||||
`apiVersion=${certManagerApiVersion},kind=ClusterIssuer,name=cloudflare-issuer`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { Release } from "@cdktf/provider-helm/lib/release";
|
|||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
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 { IngressRoute } from "../../utils";
|
|
||||||
|
|
||||||
type LonghornOptions = {
|
type LonghornOptions = {
|
||||||
providers: {
|
providers: {
|
||||||
@@ -53,16 +52,5 @@ 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"],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
---
|
|
||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: action-runner
|
|
||||||
name: action-runner
|
|
||||||
namespace: homelab
|
|
||||||
spec:
|
|
||||||
replicas: 3
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: action-runner
|
|
||||||
strategy: {}
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
creationTimestamp: null
|
|
||||||
labels:
|
|
||||||
app: action-runner
|
|
||||||
spec:
|
|
||||||
nodeSelector:
|
|
||||||
nodepool: worker
|
|
||||||
topologySpreadConstraints:
|
|
||||||
- maxSkew: 1
|
|
||||||
topologyKey: kubernetes.io/hostname
|
|
||||||
whenUnsatisfiable: DoNotSchedule
|
|
||||||
labelSelector:
|
|
||||||
matchLabels:
|
|
||||||
app: action-runner
|
|
||||||
restartPolicy: Always
|
|
||||||
volumes:
|
|
||||||
- name: runner-data
|
|
||||||
persistentVolumeClaim:
|
|
||||||
claimName: action-runner
|
|
||||||
securityContext:
|
|
||||||
fsGroup: 1000
|
|
||||||
containers:
|
|
||||||
- name: runner
|
|
||||||
image: gitea/act_runner:nightly-dind-rootless
|
|
||||||
imagePullPolicy: Always
|
|
||||||
env:
|
|
||||||
- name: DOCKER_HOST
|
|
||||||
value: unix:///run/user/1000/docker.sock
|
|
||||||
- name: GITEA_INSTANCE_URL
|
|
||||||
value: https://git.dogar.dev
|
|
||||||
- name: GITEA_RUNNER_REGISTRATION_TOKEN
|
|
||||||
valueFrom:
|
|
||||||
secretKeyRef:
|
|
||||||
name: runner-secret
|
|
||||||
key: runner-token
|
|
||||||
securityContext:
|
|
||||||
privileged: true
|
|
||||||
volumeMounts:
|
|
||||||
- name: runner-data
|
|
||||||
mountPath: /data
|
|
||||||
---
|
|
||||||
apiVersion: policy/v1
|
|
||||||
kind: PodDisruptionBudget
|
|
||||||
metadata:
|
|
||||||
name: action-runner-pdb
|
|
||||||
namespace: homelab
|
|
||||||
spec:
|
|
||||||
minAvailable: 6
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: action-runner
|
|
||||||
48
k8s-operators/1password/index.ts
Normal file
48
k8s-operators/1password/index.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
type OnePasswordOptions = {
|
||||||
|
provider: HelmProvider;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class OnePassword extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, options: OnePasswordOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const { provider } = options;
|
||||||
|
|
||||||
|
new Release(this, "onepassword-operator", {
|
||||||
|
provider,
|
||||||
|
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",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import * as fs from "fs";
|
|
||||||
import * as path from "path";
|
|
||||||
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
||||||
import { Release } from "@cdktf/provider-helm/lib/release";
|
import { Release } from "@cdktf/provider-helm/lib/release";
|
||||||
import { TerraformStack } from "cdktf";
|
import { TerraformStack } from "cdktf";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
import { BarmanCloudPluginInstall } from "./barman";
|
import { BarmanCloudPluginInstall } from "./barman";
|
||||||
import { Prometheus } from "./prometheus";
|
import { Prometheus } from "./prometheus";
|
||||||
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
import { OnePassword } from "./1password";
|
||||||
|
|
||||||
export class K8SOperators extends TerraformStack {
|
export class K8SOperators extends TerraformStack {
|
||||||
constructor(scope: Construct, id: string) {
|
constructor(scope: Construct, id: string) {
|
||||||
@@ -18,48 +16,16 @@ export class K8SOperators extends TerraformStack {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const kubernetes = new KubernetesProvider(this, "kubernetes", {
|
|
||||||
configPath: "~/.kube/config",
|
|
||||||
});
|
|
||||||
|
|
||||||
new Prometheus(this, "prometheus", {
|
new Prometheus(this, "prometheus", {
|
||||||
providers: {
|
provider: helm,
|
||||||
helm,
|
|
||||||
kubernetes,
|
|
||||||
},
|
|
||||||
namespace: "monitoring",
|
namespace: "monitoring",
|
||||||
name: "prometheus-operator",
|
name: "prometheus-operator",
|
||||||
version: "75.10.0",
|
version: "75.10.0",
|
||||||
});
|
});
|
||||||
|
|
||||||
new Release(this, "onepassword-operator", {
|
new OnePassword(this, "onepassword", {
|
||||||
provider: helm,
|
provider: helm,
|
||||||
name: "onepassword-operator",
|
name: "onepassword",
|
||||||
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", {
|
const cnpg = new Release(this, "cnpg-operator", {
|
||||||
|
|||||||
@@ -2,11 +2,10 @@ import * as fs from "fs";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { Release } from "@cdktf/provider-helm/lib/release";
|
import { Release } from "@cdktf/provider-helm/lib/release";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
import { IngressRoute } from "../../utils";
|
import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
||||||
import { Providers } from "../../types";
|
|
||||||
|
|
||||||
type PrometheusOptions = {
|
type PrometheusOptions = {
|
||||||
providers: Providers;
|
provider: HelmProvider;
|
||||||
name: string;
|
name: string;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
version: string;
|
version: string;
|
||||||
@@ -16,22 +15,8 @@ export class Prometheus extends Construct {
|
|||||||
constructor(scope: Construct, id: string, options: PrometheusOptions) {
|
constructor(scope: Construct, id: string, options: PrometheusOptions) {
|
||||||
super(scope, id);
|
super(scope, id);
|
||||||
|
|
||||||
const { helm, kubernetes } = options.providers;
|
|
||||||
|
|
||||||
new IngressRoute(this, "ingress", {
|
|
||||||
provider: kubernetes,
|
|
||||||
name: "grafana",
|
|
||||||
namespace: options.namespace,
|
|
||||||
entryPoints: ["websecure"],
|
|
||||||
serviceName: "prometheus-operator-grafana",
|
|
||||||
servicePort: 80,
|
|
||||||
tlsSecretName: "grafana-tls",
|
|
||||||
host: "grafana.dogar.dev",
|
|
||||||
});
|
|
||||||
|
|
||||||
new Release(this, id, {
|
new Release(this, id, {
|
||||||
...options,
|
...options,
|
||||||
provider: helm,
|
|
||||||
repository: "https://prometheus-community.github.io/helm-charts",
|
repository: "https://prometheus-community.github.io/helm-charts",
|
||||||
chart: "kube-prometheus-stack",
|
chart: "kube-prometheus-stack",
|
||||||
createNamespace: true,
|
createNamespace: true,
|
||||||
|
|||||||
10
main.ts
10
main.ts
@@ -5,6 +5,7 @@ import { CacheInfrastructure } from "./cache-infrastructure";
|
|||||||
import { UtilityServices } from "./utility-services";
|
import { UtilityServices } from "./utility-services";
|
||||||
import { K8SOperators } from "./k8s-operators";
|
import { K8SOperators } from "./k8s-operators";
|
||||||
import { CoreServices } from "./core-services";
|
import { CoreServices } from "./core-services";
|
||||||
|
import { NetworkSecurity } from "./network-security";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ const env = cleanEnv(process.env, {
|
|||||||
OP_CONNECT_TOKEN: str({ desc: "1Password Connect token." }),
|
OP_CONNECT_TOKEN: str({ desc: "1Password Connect token." }),
|
||||||
ACCESS_KEY: str({ desc: "Access key ID for R2 storage." }),
|
ACCESS_KEY: str({ desc: "Access key ID for R2 storage." }),
|
||||||
SECRET_KEY: str({ desc: "Secret access key for R2 storage." }),
|
SECRET_KEY: str({ desc: "Secret access key for R2 storage." }),
|
||||||
|
VALKEY_PASSWORD: str({ desc: "Password for Valkey database." }),
|
||||||
});
|
});
|
||||||
|
|
||||||
const r2Endpoint = `https://${env.ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
const r2Endpoint = `https://${env.ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
||||||
@@ -23,8 +25,11 @@ const coreServices = new CoreServices(app, "core-services");
|
|||||||
const k8sOperators = new K8SOperators(app, "k8s-operators");
|
const k8sOperators = new K8SOperators(app, "k8s-operators");
|
||||||
k8sOperators.node.addDependency(coreServices);
|
k8sOperators.node.addDependency(coreServices);
|
||||||
|
|
||||||
|
const networkSecurity = new NetworkSecurity(app, "network-security");
|
||||||
|
networkSecurity.node.addDependency(k8sOperators);
|
||||||
|
|
||||||
const utilityServices = new UtilityServices(app, "utility-services");
|
const utilityServices = new UtilityServices(app, "utility-services");
|
||||||
utilityServices.node.addDependency(k8sOperators);
|
utilityServices.node.addDependency(networkSecurity);
|
||||||
|
|
||||||
const caches = new CacheInfrastructure(app, "cache-infrastructure");
|
const caches = new CacheInfrastructure(app, "cache-infrastructure");
|
||||||
caches.node.addDependency(utilityServices);
|
caches.node.addDependency(utilityServices);
|
||||||
@@ -51,8 +56,9 @@ const deploy: (stack: TerraformStack, key: string) => S3Backend = (
|
|||||||
});
|
});
|
||||||
|
|
||||||
deploy(coreServices, "core-services");
|
deploy(coreServices, "core-services");
|
||||||
deploy(utilityServices, "utility-services");
|
|
||||||
deploy(k8sOperators, "k8s-operators");
|
deploy(k8sOperators, "k8s-operators");
|
||||||
|
deploy(networkSecurity, "network-security");
|
||||||
|
deploy(utilityServices, "utility-services");
|
||||||
deploy(caches, "cache-infrastructure");
|
deploy(caches, "cache-infrastructure");
|
||||||
|
|
||||||
app.synth();
|
app.synth();
|
||||||
|
|||||||
83
network-security/index.ts
Normal file
83
network-security/index.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { DataKubernetesNamespaceV1 } from "@cdktf/provider-kubernetes/lib/data-kubernetes-namespace-v1";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
import { DataTerraformRemoteStateS3, TerraformStack } from "cdktf";
|
||||||
|
import { Construct } from "constructs";
|
||||||
|
|
||||||
|
import {
|
||||||
|
RateLimitMiddleware,
|
||||||
|
IpAllowListMiddleware,
|
||||||
|
IpAllowListMiddlewareTCP,
|
||||||
|
} from "./traefik";
|
||||||
|
import { ValkeyCluster } from "./valkey";
|
||||||
|
|
||||||
|
export class NetworkSecurity extends TerraformStack {
|
||||||
|
constructor(scope: Construct, id: string) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const kubernetes = new KubernetesProvider(this, "kubernetes", {
|
||||||
|
configPath: "~/.kube/config",
|
||||||
|
});
|
||||||
|
|
||||||
|
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
|
||||||
|
|
||||||
|
const coreServicesState = new DataTerraformRemoteStateS3(
|
||||||
|
this,
|
||||||
|
"core-services-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 = coreServicesState.getString("namespace-output");
|
||||||
|
const namespaceResource = new DataKubernetesNamespaceV1(
|
||||||
|
this,
|
||||||
|
"homelab-namespace",
|
||||||
|
{
|
||||||
|
provider: kubernetes,
|
||||||
|
metadata: {
|
||||||
|
name: namespaceName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const namespace = namespaceResource.metadata.name;
|
||||||
|
|
||||||
|
new ValkeyCluster(this, "valkey-cluster", {
|
||||||
|
provider: kubernetes,
|
||||||
|
name: "valkey",
|
||||||
|
namespace,
|
||||||
|
});
|
||||||
|
|
||||||
|
new RateLimitMiddleware(this, "rate-limit", {
|
||||||
|
provider: kubernetes,
|
||||||
|
namespace,
|
||||||
|
name: "rate-limit",
|
||||||
|
});
|
||||||
|
|
||||||
|
new IpAllowListMiddleware(this, "internal-ip-allow-list", {
|
||||||
|
provider: kubernetes,
|
||||||
|
namespace,
|
||||||
|
name: "ip-allow-list",
|
||||||
|
sourceRanges: ["192.168.18.0/24", "10.43.0.0/16"],
|
||||||
|
});
|
||||||
|
|
||||||
|
new IpAllowListMiddlewareTCP(this, "tcp-internal-ip-allow-list", {
|
||||||
|
provider: kubernetes,
|
||||||
|
namespace,
|
||||||
|
name: "tcp-ip-allow-list",
|
||||||
|
sourceRanges: ["192.168.18.0/24", "10.42.0.0/16"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
2
network-security/traefik/index.ts
Normal file
2
network-security/traefik/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { RateLimitMiddleware } from "./rateLimit";
|
||||||
|
export { IpAllowListMiddleware, IpAllowListMiddlewareTCP } from "./ipAllowList";
|
||||||
64
network-security/traefik/ipAllowList.ts
Normal file
64
network-security/traefik/ipAllowList.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
|
||||||
|
type IpAllowListMiddlewareOptions = {
|
||||||
|
provider: KubernetesProvider;
|
||||||
|
namespace: string;
|
||||||
|
name: string;
|
||||||
|
sourceRanges: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class IpAllowListMiddleware extends Construct {
|
||||||
|
constructor(
|
||||||
|
scope: Construct,
|
||||||
|
id: string,
|
||||||
|
opts: IpAllowListMiddlewareOptions,
|
||||||
|
) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
new Manifest(this, opts.name, {
|
||||||
|
provider: opts.provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "traefik.io/v1alpha1",
|
||||||
|
kind: "Middleware",
|
||||||
|
metadata: {
|
||||||
|
name: opts.name,
|
||||||
|
namespace: opts.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
ipAllowList: {
|
||||||
|
sourceRange: opts.sourceRanges,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class IpAllowListMiddlewareTCP extends Construct {
|
||||||
|
constructor(
|
||||||
|
scope: Construct,
|
||||||
|
id: string,
|
||||||
|
opts: IpAllowListMiddlewareOptions,
|
||||||
|
) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
new Manifest(this, opts.name, {
|
||||||
|
provider: opts.provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "traefik.io/v1alpha1",
|
||||||
|
kind: "MiddlewareTCP",
|
||||||
|
metadata: {
|
||||||
|
name: opts.name,
|
||||||
|
namespace: opts.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
ipAllowList: {
|
||||||
|
sourceRange: opts.sourceRanges,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
51
network-security/traefik/rateLimit.ts
Normal file
51
network-security/traefik/rateLimit.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { Manifest } from "@cdktf/provider-kubernetes/lib/manifest";
|
||||||
|
import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
||||||
|
|
||||||
|
type RateLimitMiddlewareOptions = {
|
||||||
|
provider: KubernetesProvider;
|
||||||
|
namespace: string;
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
average?: number; // default 10
|
||||||
|
burst?: number; // default 50
|
||||||
|
period?: string; // default "1s"
|
||||||
|
};
|
||||||
|
|
||||||
|
export class RateLimitMiddleware extends Construct {
|
||||||
|
public readonly ref: string;
|
||||||
|
|
||||||
|
constructor(scope: Construct, id: string, opts: RateLimitMiddlewareOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const average = opts.average ?? 10;
|
||||||
|
const burst = opts.burst ?? 50;
|
||||||
|
const period = opts.period ?? "1s";
|
||||||
|
|
||||||
|
this.ref = `${opts.namespace}/${opts.name}`;
|
||||||
|
|
||||||
|
new Manifest(this, opts.name, {
|
||||||
|
provider: opts.provider,
|
||||||
|
manifest: {
|
||||||
|
apiVersion: "traefik.io/v1alpha1",
|
||||||
|
kind: "Middleware",
|
||||||
|
metadata: {
|
||||||
|
name: opts.name,
|
||||||
|
namespace: opts.namespace,
|
||||||
|
},
|
||||||
|
spec: {
|
||||||
|
rateLimit: {
|
||||||
|
average,
|
||||||
|
burst,
|
||||||
|
period,
|
||||||
|
redis: {
|
||||||
|
endpoints: [`valkey.${opts.namespace}.svc.cluster.local:6379`],
|
||||||
|
secret: "valkey",
|
||||||
|
db: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,14 +18,14 @@ 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", {
|
new OnePasswordSecret(this, "secret", {
|
||||||
provider,
|
provider,
|
||||||
name: "valkey",
|
name: "valkey",
|
||||||
namespace,
|
namespace,
|
||||||
itemPath: "vaults/Lab/items/valkey",
|
itemPath: "vaults/Lab/items/valkey",
|
||||||
});
|
});
|
||||||
|
|
||||||
new DeploymentV1(this, "valkey-deployment", {
|
new DeploymentV1(this, "deployment", {
|
||||||
provider,
|
provider,
|
||||||
metadata: {
|
metadata: {
|
||||||
name,
|
name,
|
||||||
@@ -107,13 +107,9 @@ export class ValkeyCluster extends Construct {
|
|||||||
name,
|
name,
|
||||||
namespace,
|
namespace,
|
||||||
labels,
|
labels,
|
||||||
annotations: {
|
|
||||||
"external-dns.alpha.kubernetes.io/hostname": "valkey.dogar.dev",
|
|
||||||
"metallb.io/ip-allocated-from-pool": "pool",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
spec: {
|
spec: {
|
||||||
type: "LoadBalancer",
|
type: "ClusterIP",
|
||||||
selector: labels,
|
selector: labels,
|
||||||
port: [
|
port: [
|
||||||
{
|
{
|
||||||
@@ -2,7 +2,7 @@ import * as fs from "fs";
|
|||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { Release } from "@cdktf/provider-helm/lib/release";
|
import { Release } from "@cdktf/provider-helm/lib/release";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
import { IngressRoute, OnePasswordSecret } from "../../utils";
|
import { PublicIngressRoute, OnePasswordSecret } from "../../utils";
|
||||||
import { Providers } from "../../types";
|
import { Providers } from "../../types";
|
||||||
|
|
||||||
type AuthentikServerOptions = {
|
type AuthentikServerOptions = {
|
||||||
@@ -44,14 +44,13 @@ export class AuthentikServer extends Construct {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
new IngressRoute(this, "ingress", {
|
new PublicIngressRoute(this, "ingress", {
|
||||||
provider: kubernetes,
|
provider: kubernetes,
|
||||||
name: options.name,
|
name: options.name,
|
||||||
namespace: options.namespace,
|
namespace: options.namespace,
|
||||||
host: "auth.dogar.dev",
|
host: "auth.dogar.dev",
|
||||||
serviceName: `authentik-server`,
|
serviceName: `authentik-server`,
|
||||||
servicePort: 80,
|
servicePort: 80,
|
||||||
tlsSecretName: "authentik-tls",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import * as path from "path";
|
|||||||
import { Release } from "@cdktf/provider-helm/lib/release";
|
import { Release } from "@cdktf/provider-helm/lib/release";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
|
|
||||||
import { OnePasswordSecret, IngressRoute, IngressRouteTcp } from "../../utils";
|
import {
|
||||||
|
OnePasswordSecret,
|
||||||
|
PublicIngressRoute,
|
||||||
|
IngressRouteTcp,
|
||||||
|
} from "../../utils";
|
||||||
import type { Providers } from "../../types";
|
import type { Providers } from "../../types";
|
||||||
|
|
||||||
type GiteaServerOptions = {
|
type GiteaServerOptions = {
|
||||||
@@ -69,20 +73,20 @@ export class GiteaServer extends Construct {
|
|||||||
new IngressRouteTcp(this, "ssh-ingress", {
|
new IngressRouteTcp(this, "ssh-ingress", {
|
||||||
provider: kubernetes,
|
provider: kubernetes,
|
||||||
namespace: options.namespace,
|
namespace: options.namespace,
|
||||||
|
name: options.name,
|
||||||
|
match: "HostSNI(`*`)",
|
||||||
entryPoint: "ssh",
|
entryPoint: "ssh",
|
||||||
serviceName: `${options.name}-ssh`,
|
serviceName: `${options.name}-ssh`,
|
||||||
servicePort: 22,
|
servicePort: 22,
|
||||||
});
|
});
|
||||||
|
|
||||||
new IngressRoute(this, "http-ingress", {
|
new PublicIngressRoute(this, "http-ingress", {
|
||||||
provider: kubernetes,
|
provider: kubernetes,
|
||||||
namespace: options.namespace,
|
namespace: options.namespace,
|
||||||
name: options.name,
|
name: options.name,
|
||||||
entryPoints: ["websecure"],
|
|
||||||
host: "git.dogar.dev",
|
host: "git.dogar.dev",
|
||||||
serviceName: `${options.name}-http`,
|
serviceName: `${options.name}-http`,
|
||||||
servicePort: 3000,
|
servicePort: 3000,
|
||||||
tlsSecretName: `${options.name}-tls`,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { HelmProvider } from "@cdktf/provider-helm/lib/provider";
|
|||||||
import { DataTerraformRemoteStateS3, TerraformStack } from "cdktf";
|
import { DataTerraformRemoteStateS3, TerraformStack } from "cdktf";
|
||||||
import { Construct } from "constructs";
|
import { Construct } from "constructs";
|
||||||
|
|
||||||
import { ValkeyCluster } from "./valkey";
|
|
||||||
import { GiteaRunner, GiteaServer } from "./gitea";
|
import { GiteaRunner, GiteaServer } from "./gitea";
|
||||||
import { AuthentikServer } from "./authentik";
|
import { AuthentikServer } from "./authentik";
|
||||||
import { PostgresCluster } from "./postgres";
|
import { PostgresCluster } from "./postgres";
|
||||||
@@ -26,24 +25,28 @@ export class UtilityServices extends TerraformStack {
|
|||||||
|
|
||||||
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
|
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
|
||||||
|
|
||||||
const homelabState = new DataTerraformRemoteStateS3(this, "homelab-state", {
|
const coreServicesState = new DataTerraformRemoteStateS3(
|
||||||
usePathStyle: true,
|
this,
|
||||||
skipRegionValidation: true,
|
"core-services-state",
|
||||||
skipCredentialsValidation: true,
|
{
|
||||||
skipRequestingAccountId: true,
|
usePathStyle: true,
|
||||||
skipS3Checksum: true,
|
skipRegionValidation: true,
|
||||||
encrypt: true,
|
skipCredentialsValidation: true,
|
||||||
bucket: "terraform-state",
|
skipRequestingAccountId: true,
|
||||||
key: "core-services/terraform.tfstate",
|
skipS3Checksum: true,
|
||||||
endpoints: {
|
encrypt: true,
|
||||||
s3: `https://${r2Endpoint}`,
|
bucket: "terraform-state",
|
||||||
|
key: "core-services/terraform.tfstate",
|
||||||
|
endpoints: {
|
||||||
|
s3: `https://${r2Endpoint}`,
|
||||||
|
},
|
||||||
|
region: "auto",
|
||||||
|
accessKey: process.env.ACCESS_KEY,
|
||||||
|
secretKey: process.env.SECRET_KEY,
|
||||||
},
|
},
|
||||||
region: "auto",
|
);
|
||||||
accessKey: process.env.ACCESS_KEY,
|
|
||||||
secretKey: process.env.SECRET_KEY,
|
|
||||||
});
|
|
||||||
|
|
||||||
const namespaceName = homelabState.getString("namespace-output");
|
const namespaceName = coreServicesState.getString("namespace-output");
|
||||||
const namespaceResource = new DataKubernetesNamespaceV1(
|
const namespaceResource = new DataKubernetesNamespaceV1(
|
||||||
this,
|
this,
|
||||||
"homelab-namespace",
|
"homelab-namespace",
|
||||||
@@ -70,12 +73,6 @@ export class UtilityServices extends TerraformStack {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const valkeyCluster = new ValkeyCluster(this, "valkey-cluster", {
|
|
||||||
namespace,
|
|
||||||
provider: kubernetes,
|
|
||||||
name: "valkey",
|
|
||||||
});
|
|
||||||
|
|
||||||
const postgres = new PostgresCluster(this, "postgres-cluster", {
|
const postgres = new PostgresCluster(this, "postgres-cluster", {
|
||||||
certManagerApiVersion: "cert-manager.io/v1",
|
certManagerApiVersion: "cert-manager.io/v1",
|
||||||
name: "postgres-cluster",
|
name: "postgres-cluster",
|
||||||
@@ -96,7 +93,6 @@ export class UtilityServices extends TerraformStack {
|
|||||||
namespace,
|
namespace,
|
||||||
});
|
});
|
||||||
|
|
||||||
authentik.node.addDependency(valkeyCluster);
|
|
||||||
authentik.node.addDependency(postgres);
|
authentik.node.addDependency(postgres);
|
||||||
|
|
||||||
const gitea = new GiteaServer(this, "gitea-server", {
|
const gitea = new GiteaServer(this, "gitea-server", {
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
export { CloudflareCertificate } from "./cert-manager";
|
export { CloudflareCertificate } from "./cert-manager";
|
||||||
export { OnePasswordSecret } from "./1password-secret";
|
export { OnePasswordSecret } from "./1password-secret";
|
||||||
export { IngressRoute, IngressRouteTcp } from "./traefik";
|
export {
|
||||||
|
PublicIngressRoute,
|
||||||
|
InternalIngressRoute,
|
||||||
|
IngressRouteTcp,
|
||||||
|
} from "./traefik";
|
||||||
export { LonghornPvc } from "./longhorn";
|
export { LonghornPvc } from "./longhorn";
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export { IngressRoute } from "./ingress";
|
export { PublicIngressRoute, InternalIngressRoute } from "./ingress";
|
||||||
export { IngressRouteTcp } from "./ingress-tcp";
|
export { IngressRouteTcp } from "./ingressTCP";
|
||||||
|
|||||||
2
utils/traefik/ingress/index.ts
Normal file
2
utils/traefik/ingress/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { PublicIngressRoute } from "./publicIngress";
|
||||||
|
export { InternalIngressRoute } from "./internalIngress";
|
||||||
@@ -2,11 +2,12 @@ import { Construct } from "constructs";
|
|||||||
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 { CloudflareCertificate } from "../cert-manager";
|
import { CloudflareCertificate } from "../../cert-manager";
|
||||||
|
|
||||||
type IngressRouteOptions = {
|
export type IngressRouteOptions = {
|
||||||
provider: KubernetesProvider;
|
provider: KubernetesProvider;
|
||||||
namespace: string;
|
namespace: string;
|
||||||
|
name: string;
|
||||||
|
|
||||||
/** Hostname for this route (e.g. npm.dogar.dev) */
|
/** Hostname for this route (e.g. npm.dogar.dev) */
|
||||||
host: string;
|
host: string;
|
||||||
@@ -26,9 +27,6 @@ type IngressRouteOptions = {
|
|||||||
|
|
||||||
/** Extra middlewares (traefik format: namespace/name) */
|
/** Extra middlewares (traefik format: namespace/name) */
|
||||||
middlewares?: string[];
|
middlewares?: string[];
|
||||||
|
|
||||||
/** Name override (otherwise auto) */
|
|
||||||
name?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export class IngressRoute extends Construct {
|
export class IngressRoute extends Construct {
|
||||||
@@ -37,7 +35,7 @@ export class IngressRoute extends Construct {
|
|||||||
constructor(scope: Construct, id: string, opts: IngressRouteOptions) {
|
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;
|
||||||
const path = opts.path ?? "/";
|
const path = opts.path ?? "/";
|
||||||
const entryPoints = opts.entryPoints ?? ["websecure"];
|
const entryPoints = opts.entryPoints ?? ["websecure"];
|
||||||
|
|
||||||
63
utils/traefik/ingress/internalIngress.ts
Normal file
63
utils/traefik/ingress/internalIngress.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { IngressRoute, IngressRouteOptions } from "./ingress";
|
||||||
|
import { DataTerraformRemoteStateS3 } from "cdktf";
|
||||||
|
import { DataKubernetesNamespaceV1 } from "@cdktf/provider-kubernetes/lib/data-kubernetes-namespace-v1";
|
||||||
|
|
||||||
|
type InternalIngressRouteOptions = Omit<
|
||||||
|
IngressRouteOptions,
|
||||||
|
"entryPoints" | "tlsSecretName" | "middlewares"
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class InternalIngressRoute extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, opts: InternalIngressRouteOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
|
||||||
|
|
||||||
|
const coreServicesState = new DataTerraformRemoteStateS3(
|
||||||
|
this,
|
||||||
|
"core-services-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 = coreServicesState.getString("namespace-output");
|
||||||
|
const namespaceResource = new DataKubernetesNamespaceV1(
|
||||||
|
this,
|
||||||
|
"core-services-namespace",
|
||||||
|
{
|
||||||
|
provider: opts.provider,
|
||||||
|
metadata: {
|
||||||
|
name: namespaceName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const namespace = namespaceResource.metadata.name;
|
||||||
|
|
||||||
|
new IngressRoute(this, opts.name, {
|
||||||
|
provider: opts.provider,
|
||||||
|
namespace: opts.namespace,
|
||||||
|
host: opts.host,
|
||||||
|
path: opts.path ?? "/",
|
||||||
|
serviceName: opts.serviceName,
|
||||||
|
servicePort: opts.servicePort,
|
||||||
|
entryPoints: ["websecure"],
|
||||||
|
tlsSecretName: `${opts.name}-tls`,
|
||||||
|
middlewares: [`${namespace}/ip-allow-list`],
|
||||||
|
name: opts.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
63
utils/traefik/ingress/publicIngress.ts
Normal file
63
utils/traefik/ingress/publicIngress.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { Construct } from "constructs";
|
||||||
|
import { IngressRoute, IngressRouteOptions } from "./ingress";
|
||||||
|
import { DataTerraformRemoteStateS3 } from "cdktf";
|
||||||
|
import { DataKubernetesNamespaceV1 } from "@cdktf/provider-kubernetes/lib/data-kubernetes-namespace-v1";
|
||||||
|
|
||||||
|
type PublicIngressRouteOptions = Omit<
|
||||||
|
IngressRouteOptions,
|
||||||
|
"entryPoints" | "tlsSecretName" | "middlewares"
|
||||||
|
>;
|
||||||
|
|
||||||
|
export class PublicIngressRoute extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, opts: PublicIngressRouteOptions) {
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const r2Endpoint = `${process.env.ACCOUNT_ID!}.r2.cloudflarestorage.com`;
|
||||||
|
|
||||||
|
const coreServicesState = new DataTerraformRemoteStateS3(
|
||||||
|
this,
|
||||||
|
"core-services-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 = coreServicesState.getString("namespace-output");
|
||||||
|
const namespaceResource = new DataKubernetesNamespaceV1(
|
||||||
|
this,
|
||||||
|
"core-services-namespace",
|
||||||
|
{
|
||||||
|
provider: opts.provider,
|
||||||
|
metadata: {
|
||||||
|
name: namespaceName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const namespace = namespaceResource.metadata.name;
|
||||||
|
|
||||||
|
new IngressRoute(this, opts.name, {
|
||||||
|
provider: opts.provider,
|
||||||
|
namespace: opts.namespace,
|
||||||
|
host: opts.host,
|
||||||
|
path: opts.path ?? "/",
|
||||||
|
serviceName: opts.serviceName,
|
||||||
|
servicePort: opts.servicePort,
|
||||||
|
entryPoints: ["websecure"],
|
||||||
|
tlsSecretName: `${opts.name}-tls`,
|
||||||
|
middlewares: [`${namespace}/rate-limit`],
|
||||||
|
name: opts.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
1
utils/traefik/ingressTCP/index.ts
Normal file
1
utils/traefik/ingressTCP/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { IngressRouteTcp } from "./ingress-tcp";
|
||||||
@@ -4,6 +4,13 @@ import { KubernetesProvider } from "@cdktf/provider-kubernetes/lib/provider";
|
|||||||
|
|
||||||
type IngressRouteTcpOptions = {
|
type IngressRouteTcpOptions = {
|
||||||
provider: KubernetesProvider;
|
provider: KubernetesProvider;
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match rule.
|
||||||
|
* Default is `HostSNI(\`*\`)` which is correct for most TCP services.
|
||||||
|
*/
|
||||||
|
match: string;
|
||||||
|
|
||||||
/** Namespace where the IngressRouteTCP will be created */
|
/** Namespace where the IngressRouteTCP will be created */
|
||||||
namespace: string;
|
namespace: string;
|
||||||
@@ -16,15 +23,6 @@ type IngressRouteTcpOptions = {
|
|||||||
|
|
||||||
/** Backend service port */
|
/** Backend service port */
|
||||||
servicePort: number;
|
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 {
|
export class IngressRouteTcp extends Construct {
|
||||||
@@ -33,14 +31,7 @@ export class IngressRouteTcp extends Construct {
|
|||||||
constructor(scope: Construct, id: string, opts: IngressRouteTcpOptions) {
|
constructor(scope: Construct, id: string, opts: IngressRouteTcpOptions) {
|
||||||
super(scope, id);
|
super(scope, id);
|
||||||
|
|
||||||
const name =
|
const { name, match } = opts;
|
||||||
opts.name ??
|
|
||||||
`tcp-${opts.entryPoint}-${opts.serviceName}`.replace(
|
|
||||||
/[^a-zA-Z0-9-]/g,
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
|
|
||||||
const matchRule = opts.match ?? "HostSNI(`*`)";
|
|
||||||
|
|
||||||
this.manifest = new Manifest(this, name, {
|
this.manifest = new Manifest(this, name, {
|
||||||
provider: opts.provider,
|
provider: opts.provider,
|
||||||
@@ -55,7 +46,7 @@ export class IngressRouteTcp extends Construct {
|
|||||||
entryPoints: [opts.entryPoint],
|
entryPoints: [opts.entryPoint],
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
match: matchRule,
|
match,
|
||||||
services: [
|
services: [
|
||||||
{
|
{
|
||||||
name: opts.serviceName,
|
name: opts.serviceName,
|
||||||
Reference in New Issue
Block a user