diff --git a/apps/portfolio-edge-namespace.yaml b/apps/portfolio-edge-namespace.yaml new file mode 100644 index 0000000..eaffeea --- /dev/null +++ b/apps/portfolio-edge-namespace.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: portfolio-edge + annotations: + argocd.argoproj.io/sync-wave: "-2" + labels: + app.kubernetes.io/name: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform + pod-security.kubernetes.io/enforce: restricted + pod-security.kubernetes.io/enforce-version: v1.36 + pod-security.kubernetes.io/audit: restricted + pod-security.kubernetes.io/audit-version: v1.36 + pod-security.kubernetes.io/warn: restricted + pod-security.kubernetes.io/warn-version: v1.36 diff --git a/apps/portfolio-edge-project.yaml b/apps/portfolio-edge-project.yaml new file mode 100644 index 0000000..66be939 --- /dev/null +++ b/apps/portfolio-edge-project.yaml @@ -0,0 +1,39 @@ +apiVersion: argoproj.io/v1alpha1 +kind: AppProject +metadata: + name: portfolio-edge + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "-1" +spec: + description: Restricted GitOps project for the public portfolio Cloudflare Tunnel + + sourceRepos: + - http://git.home.lan/jay/k8s-gitops.git + + destinations: + - namespace: portfolio-edge + server: https://kubernetes.default.svc + + namespaceResourceWhitelist: + - group: "" + kind: LimitRange + - group: "" + kind: ResourceQuota + - group: "" + kind: ServiceAccount + - group: apps + kind: Deployment + - group: bitnami.com + kind: SealedSecret + - group: networking.k8s.io + kind: NetworkPolicy + - group: policy + kind: PodDisruptionBudget + + orphanedResources: + warn: true + ignore: + - group: "" + kind: Secret + name: portfolio-edge-tunnel-token diff --git a/apps/portfolio-edge.yaml b/apps/portfolio-edge.yaml new file mode 100644 index 0000000..6d41290 --- /dev/null +++ b/apps/portfolio-edge.yaml @@ -0,0 +1,34 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: portfolio-edge + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "0" + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: portfolio-edge + + source: + repoURL: http://git.home.lan/jay/k8s-gitops.git + targetRevision: main + path: apps/portfolio-edge + + destination: + server: https://kubernetes.default.svc + namespace: portfolio-edge + + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - ApplyOutOfSyncOnly=true + - PruneLast=true + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 1m diff --git a/apps/portfolio-edge/deployment.yaml b/apps/portfolio-edge/deployment.yaml new file mode 100644 index 0000000..62ef3d0 --- /dev/null +++ b/apps/portfolio-edge/deployment.yaml @@ -0,0 +1,125 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: portfolio-edge + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/component: edge + app.kubernetes.io/part-of: portfolio-platform +spec: + replicas: 2 + revisionHistoryLimit: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + selector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + template: + metadata: + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/component: edge + app.kubernetes.io/part-of: portfolio-platform + spec: + serviceAccountName: portfolio-edge + automountServiceAccountToken: false + terminationGracePeriodSeconds: 30 + securityContext: + runAsNonRoot: true + runAsUser: 65532 + runAsGroup: 65532 + seccompProfile: + type: RuntimeDefault + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + topologyKey: kubernetes.io/hostname + labelSelector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + containers: + - name: cloudflared + image: cloudflare/cloudflared:2026.7.2 + imagePullPolicy: IfNotPresent + command: + - cloudflared + - tunnel + - --no-autoupdate + - --loglevel + - info + - --metrics + - 0.0.0.0:2000 + - run + env: + - name: TUNNEL_TOKEN + valueFrom: + secretKeyRef: + name: portfolio-edge-tunnel-token + key: token + ports: + - name: metrics + containerPort: 2000 + protocol: TCP + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + volumeMounts: + - name: tmp + mountPath: /tmp + resources: + requests: + cpu: 25m + memory: 64Mi + ephemeral-storage: 16Mi + limits: + cpu: 250m + memory: 256Mi + ephemeral-storage: 128Mi + startupProbe: + httpGet: + path: /ready + port: metrics + periodSeconds: 3 + timeoutSeconds: 2 + failureThreshold: 40 + readinessProbe: + httpGet: + path: /ready + port: metrics + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + livenessProbe: + httpGet: + path: /ready + port: metrics + initialDelaySeconds: 20 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + volumes: + - name: tmp + emptyDir: + medium: Memory + sizeLimit: 16Mi diff --git a/apps/portfolio-edge/kustomization.yaml b/apps/portfolio-edge/kustomization.yaml new file mode 100644 index 0000000..2df34c9 --- /dev/null +++ b/apps/portfolio-edge/kustomization.yaml @@ -0,0 +1,13 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: portfolio-edge + +resources: + - serviceaccount.yaml + - sealedsecret.yaml + - resourcequota.yaml + - limitrange.yaml + - deployment.yaml + - networkpolicy.yaml + - poddisruptionbudget.yaml diff --git a/apps/portfolio-edge/limitrange.yaml b/apps/portfolio-edge/limitrange.yaml new file mode 100644 index 0000000..3649aaf --- /dev/null +++ b/apps/portfolio-edge/limitrange.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: LimitRange +metadata: + name: portfolio-edge-limits + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform +spec: + limits: + - type: Container + defaultRequest: + cpu: 25m + memory: 64Mi + ephemeral-storage: 16Mi + default: + cpu: 250m + memory: 256Mi + ephemeral-storage: 128Mi + max: + cpu: 500m + memory: 512Mi + ephemeral-storage: 256Mi diff --git a/apps/portfolio-edge/networkpolicy.yaml b/apps/portfolio-edge/networkpolicy.yaml new file mode 100644 index 0000000..2babe16 --- /dev/null +++ b/apps/portfolio-edge/networkpolicy.yaml @@ -0,0 +1,108 @@ +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny-all + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-dns-egress + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: kube-system + podSelector: + matchLabels: + k8s-app: kube-dns + ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-cloudflare-tunnel-egress + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + policyTypes: + - Egress + egress: + - to: + - ipBlock: + cidr: 0.0.0.0/0 + except: + - 10.0.0.0/8 + - 100.64.0.0/10 + - 127.0.0.0/8 + - 169.254.0.0/16 + - 172.16.0.0/12 + - 192.168.0.0/16 + ports: + - protocol: UDP + port: 7844 + - protocol: TCP + port: 7844 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-portfolio-origin-egress + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: portfolio + podSelector: + matchLabels: + app.kubernetes.io/name: portfolio + app.kubernetes.io/instance: portfolio + ports: + - protocol: TCP + port: 80 + - protocol: TCP + port: 8080 diff --git a/apps/portfolio-edge/poddisruptionbudget.yaml b/apps/portfolio-edge/poddisruptionbudget.yaml new file mode 100644 index 0000000..a10e3f3 --- /dev/null +++ b/apps/portfolio-edge/poddisruptionbudget.yaml @@ -0,0 +1,15 @@ +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: portfolio-edge + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform +spec: + minAvailable: 1 + selector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge diff --git a/apps/portfolio-edge/resourcequota.yaml b/apps/portfolio-edge/resourcequota.yaml new file mode 100644 index 0000000..83d3f18 --- /dev/null +++ b/apps/portfolio-edge/resourcequota.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: ResourceQuota +metadata: + name: portfolio-edge-quota + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/part-of: portfolio-platform +spec: + hard: + requests.cpu: "250m" + requests.memory: 512Mi + limits.cpu: "1" + limits.memory: 1Gi + requests.ephemeral-storage: 256Mi + limits.ephemeral-storage: 1Gi + pods: "4" + secrets: "5" + count/deployments.apps: "2" + count/networkpolicies.networking.k8s.io: "10" + count/poddisruptionbudgets.policy: "2" diff --git a/apps/portfolio-edge/sealedsecret.yaml b/apps/portfolio-edge/sealedsecret.yaml new file mode 100644 index 0000000..b240b16 --- /dev/null +++ b/apps/portfolio-edge/sealedsecret.yaml @@ -0,0 +1,13 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + name: portfolio-edge-tunnel-token + namespace: portfolio-edge +spec: + encryptedData: + token: AgAxFQBo2LTfbjzYFIBy8W/6KpnDy0IKQa5CY5LNZcSVv6+O7P/TCYbR9oUQBV5celabmDpZXVmoAHOrrXZeEiLcHjVpwur9hTbFUkE6jSyOL1X46SA7MHwZg3Y7jR+RXE9MWVGVJhy2QxZ3gExsOAAEi+9NZvy7pwFKocLIVpkux1gfXDTjMRTy9OwclD96kTPAcoxq7y99VccTVQo6/6Au1Ry+wEyvmMjiZhbYx1W5f3g1/j7JLvpLXe0OBcqHTIdbcRh6EQQQYnulzhuDfK1rOmpZ+PBit2amD1NN3+okGXm01EvKtu08cTTczn1Xp/jwkwlr0RYKNeOM0pnhhZbWrdSVRYIASYDc3B67ZT2SyHn0TfKDJS2RJ8KtPcXpEu5rGMa7l3sppMwXAemUI/mATe1TtZDHtfMKqBYHFRHNKynVdGs6FXGzQ2qisSzStx4nMg0M2MpQ2Qp9HIMJQ8n0X41Xn5VrGUQ+nf+rMi/fEXeD04r8502Mt7I9E5k0apxh3b9WzVNUa2mohkhhMV4AiJtaTy/Yesothb5vZ13WnyWIRqgh8aRDjvzhdDLDBKaZCpnbytlooSjg7JdQmcHoZNcL3Eb6HBjGj9v4JEnLpSPaplOz3jyLcGfXyTykoiPxWsWXrjVNyA5JkOS2dvU6vQQ65JRJW0NDoqUkDSLXJBQIRpWt8eWHfEyXfAWVErZM2uR74QoB/ABWdKspGR+S0pfhrhpe4c2ycJQ8MZByMegXMmV1JxwfUGi6P0gnvHpHw1V9L8ZYRQpA6cokCdY5cLwmqNjrhZ9K28PwrlaBOdHexPy39B9FmswaE5+L4zLSqMcp+2cimdoKsAlAKzpNi/e/SFFkqfGI5Ekpw6SRAd/doXoymxE4fShATFApRaNsfyPpo09/hfyjJwM9BApRvbjM6IF7dGnShkMfnjYCET4ghQuqhw5NRFM+RKREd6+1z9cnI65JEsybzhXL+6b5qzSj1hGuRMEjLMbF1Zl6Whjta0krl5YatrT4AB18bHW0xpSMVtW6YCT/y4xHYXhZa/uCOSepcp7sQ9TlafRWPBsvuN9nlCI5AiMoaZGkFiQ25CParB+RNtMIj5/TnGJSI9DmhK3Nhrapymxg1h+tAZXyF3sbtX1F3YbZ+k0IJRWlFC+pE5lBTKC3Y2C5AcGTGI9UNPI6HT5FWepGT5k4Zw== + template: + metadata: + name: portfolio-edge-tunnel-token + namespace: portfolio-edge diff --git a/apps/portfolio-edge/serviceaccount.yaml b/apps/portfolio-edge/serviceaccount.yaml new file mode 100644 index 0000000..ea609d2 --- /dev/null +++ b/apps/portfolio-edge/serviceaccount.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: portfolio-edge + namespace: portfolio-edge + labels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + app.kubernetes.io/component: edge + app.kubernetes.io/part-of: portfolio-platform +automountServiceAccountToken: false diff --git a/apps/portfolio/networkpolicy.yaml b/apps/portfolio/networkpolicy.yaml index 37cdb29..d7fbbd8 100644 --- a/apps/portfolio/networkpolicy.yaml +++ b/apps/portfolio/networkpolicy.yaml @@ -39,3 +39,31 @@ spec: ports: - protocol: TCP port: 8080 +--- +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-portfolio-edge + namespace: portfolio + labels: + app.kubernetes.io/name: portfolio + app.kubernetes.io/part-of: portfolio-platform +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: portfolio + app.kubernetes.io/instance: portfolio + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: portfolio-edge + podSelector: + matchLabels: + app.kubernetes.io/name: cloudflared + app.kubernetes.io/instance: portfolio-edge + ports: + - protocol: TCP + port: 8080