From 35c9d9fd360386139373312d2e6c80f2a1e56887 Mon Sep 17 00:00:00 2001 From: Super User Date: Fri, 30 Oct 2026 17:18:18 -0700 Subject: [PATCH] kubernetes deployment strategy notes --- ansible/README.md | 1 + ansible/ansible.cfg | 2 + ansible/deploy-node.yaml | 130 ++++++++++ ansible/inventory | 7 + argocd/README.md | 1 + argocd/certificate.yaml | 19 ++ argocd/helm-post-install | 24 ++ argocd/ingressroute.yaml | 37 +++ argocd/password | 1 + argocd/token | 1 + calico/README.md | 5 + cert-manager/README.md | 1 + cert-manager/certificate.yaml | 19 ++ cert-manager/cloudflare-secret.yaml | 19 ++ cert-manager/clusterissuer.yaml | 22 ++ cert-manager/install | 11 + democratic-csi/README.md | 1 + democratic-csi/iscsi-values.yaml | 61 +++++ democratic-csi/nfs-values.yaml | 53 ++++ democratic-csi/test-nfs-pvc.yaml | 16 ++ firewall-rules.sh | 31 +++ get_helm.sh | 350 ++++++++++++++++++++++++++ gitea/README.md | 1 + gitea/certificate.yaml | 19 ++ gitea/gitea-deployment.yaml | 70 ++++++ gitea/gitea-pvc.yaml | 33 +++ gitea/gitea-svc.yaml | 28 +++ gitea/ingress.yaml | 24 ++ gitea/ingressroute.yaml | 14 ++ gitea/mariadb-deployment.yaml | 60 +++++ gitea/mariadb-pvc.yaml | 15 ++ gitea/mariadb-svc.yaml | 26 ++ gitea/namespace.yaml | 8 + headlamp/README.md | 3 + headlamp/headlamp-ingressroute.yaml | 16 ++ headlamp/service-account-token.yaml | 27 ++ headlamp/token | 1 + kubeadm/README.md | 1 + kubeadm/kubeadm-config.yaml | 30 +++ metallb/README.md | 1 + metallb/ipaddresspool.yaml | 17 ++ nginx | 1 + rbac/README.md | 3 + rbac/clusterrolebinding.yaml | 12 + rbac/rcuda.crt | 18 ++ rbac/rcuda.csr | 15 ++ rbac/rcuda.key | 28 +++ registration_host_init.sh | 247 ++++++++++++++++++ teleport/README.md | 1 + teleport/certificate.yaml | 19 ++ teleport/ingress.yaml | 22 ++ teleport/ingressroute.yaml | 26 ++ teleport/middlewares.yaml | 10 + teleport/teleport-cluster-values.yaml | 3 + traefik/README.md | 3 + traefik/traefik-values.yaml | 43 ++++ traefik/wildcard.yaml | 20 ++ 57 files changed, 1677 insertions(+) create mode 100644 ansible/README.md create mode 100644 ansible/ansible.cfg create mode 100644 ansible/deploy-node.yaml create mode 100644 ansible/inventory create mode 100644 argocd/README.md create mode 100644 argocd/certificate.yaml create mode 100644 argocd/helm-post-install create mode 100644 argocd/ingressroute.yaml create mode 100644 argocd/password create mode 100644 argocd/token create mode 100644 calico/README.md create mode 100644 cert-manager/README.md create mode 100644 cert-manager/certificate.yaml create mode 100644 cert-manager/cloudflare-secret.yaml create mode 100644 cert-manager/clusterissuer.yaml create mode 100644 cert-manager/install create mode 100644 democratic-csi/README.md create mode 100644 democratic-csi/iscsi-values.yaml create mode 100644 democratic-csi/nfs-values.yaml create mode 100644 democratic-csi/test-nfs-pvc.yaml create mode 100644 firewall-rules.sh create mode 100755 get_helm.sh create mode 100644 gitea/README.md create mode 100644 gitea/certificate.yaml create mode 100644 gitea/gitea-deployment.yaml create mode 100644 gitea/gitea-pvc.yaml create mode 100644 gitea/gitea-svc.yaml create mode 100644 gitea/ingress.yaml create mode 100644 gitea/ingressroute.yaml create mode 100644 gitea/mariadb-deployment.yaml create mode 100644 gitea/mariadb-pvc.yaml create mode 100644 gitea/mariadb-svc.yaml create mode 100644 gitea/namespace.yaml create mode 100644 headlamp/README.md create mode 100644 headlamp/headlamp-ingressroute.yaml create mode 100644 headlamp/service-account-token.yaml create mode 100644 headlamp/token create mode 100644 kubeadm/README.md create mode 100644 kubeadm/kubeadm-config.yaml create mode 100644 metallb/README.md create mode 100644 metallb/ipaddresspool.yaml create mode 160000 nginx create mode 100644 rbac/README.md create mode 100644 rbac/clusterrolebinding.yaml create mode 100644 rbac/rcuda.crt create mode 100644 rbac/rcuda.csr create mode 100644 rbac/rcuda.key create mode 100644 registration_host_init.sh create mode 100644 teleport/README.md create mode 100644 teleport/certificate.yaml create mode 100644 teleport/ingress.yaml create mode 100644 teleport/ingressroute.yaml create mode 100644 teleport/middlewares.yaml create mode 100644 teleport/teleport-cluster-values.yaml create mode 100644 traefik/README.md create mode 100644 traefik/traefik-values.yaml create mode 100644 traefik/wildcard.yaml diff --git a/ansible/README.md b/ansible/README.md new file mode 100644 index 0000000..9bf0c0e --- /dev/null +++ b/ansible/README.md @@ -0,0 +1 @@ +I used AI (Gemini) to take a long collection of commands required to prepare a node to run kubernetes and turned that into an Ansible playbook in order to make the process faster and easier to repeat in the future. diff --git a/ansible/ansible.cfg b/ansible/ansible.cfg new file mode 100644 index 0000000..14c8065 --- /dev/null +++ b/ansible/ansible.cfg @@ -0,0 +1,2 @@ +[defaults] +host_key_checking = False diff --git a/ansible/deploy-node.yaml b/ansible/deploy-node.yaml new file mode 100644 index 0000000..19bdf85 --- /dev/null +++ b/ansible/deploy-node.yaml @@ -0,0 +1,130 @@ +--- +- name: Prepare node for Kubernetes with CRI-O + hosts: all + become: true + tasks: + - name: Disable and stop firewalld + ansible.builtin.systemd: + name: firewalld + state: stopped + enabled: false + + - name: Set SELinux to permissive mode in config + ansible.builtin.lineinfile: + path: /etc/selinux/config + regexp: '^SELINUX=' + line: 'SELINUX=permissive' + + - name: Set SELinux to permissive runtime immediately + ansible.posix.selinux: + policy: targeted + state: permissive + + - name: Configure kernel modules to load on boot + ansible.builtin.copy: + dest: /etc/modules-load.d/k8s.conf + content: | + overlay + br_netfilter + mode: '0644' + + - name: Load kernel modules immediately + community.general.modprobe: + name: "{{ item }}" + state: present + loop: + - overlay + - br_netfilter + + - name: Configure sysctl parameters for Kubernetes networking + ansible.posix.sysctl: + name: "{{ item.key }}" + value: "{{ item.value }}" + sysctl_file: /etc/sysctl.d/k8s.conf + state: present + reload: true + loop: + - { key: 'net.bridge.bridge-nf-call-iptables', value: '1' } + - { key: 'net.bridge.bridge-nf-call-ip6tables', value: '1' } + - { key: 'net.ipv4.ip_forward', value: '1' } + + - name: Add Kubernetes YUM repository + ansible.builtin.yum_repository: + name: kubernetes + description: Kubernetes + baseurl: https://pkgs.k8s.io/core:/stable:/v1.37/rpm/ + enabled: true + gpgcheck: true + gpgkey: https://pkgs.k8s.io/core:/stable:/v1.37/rpm/repodata/repomd.xml.key + + - name: Add CRI-O YUM repository + ansible.builtin.yum_repository: + name: cri-o + description: CRI-O + baseurl: https://download.opensuse.org/repositories/isv:/cri-o:/stable:/v1.37/rpm/ + enabled: true + gpgcheck: true + gpgkey: https://download.opensuse.org/repositories/isv:/cri-o:/stable:/v1.37/rpm/repodata/repomd.xml.key + + - name: Install CRI-O, Podman, and Kubernetes packages + ansible.builtin.dnf: + name: + - cri-o + - cri-tools + - podman + - kubelet + - kubeadm + - kubectl + state: present + + - name: Ensure CRI-O configuration drop-in directory exists + ansible.builtin.file: + path: /etc/crio/crio.conf.d + state: directory + mode: '0755' + + - name: Configure CRI-O cgroup manager + ansible.builtin.copy: + dest: /etc/crio/crio.conf.d/10-cgroup-manager.conf + content: | + [crio.runtime] + conmon_cgroup = "pod" + cgroup_manager = "systemd" + mode: '0644' + notify: Restart crio + + - name: Configure kubelet runtime endpoint + ansible.builtin.copy: + dest: /etc/sysconfig/kubelet + content: | + KUBELET_EXTRA_ARGS="--container-runtime-endpoint=unix:///var/run/crio/crio.sock" + mode: '0644' + notify: Restart kubelet + + - name: Enable and start CRI-O + ansible.builtin.systemd: + name: crio + state: started + enabled: true + daemon_reload: true + + - name: Enable and start kubelet + ansible.builtin.systemd: + name: kubelet + state: started + enabled: true + + - name: Reboot the server + ansible.builtin.reboot: + reboot_timeout: 600 + + handlers: + - name: Restart crio + ansible.builtin.systemd: + name: crio + state: restarted + + - name: Restart kubelet + ansible.builtin.systemd: + name: kubelet + state: restarted diff --git a/ansible/inventory b/ansible/inventory new file mode 100644 index 0000000..97d545f --- /dev/null +++ b/ansible/inventory @@ -0,0 +1,7 @@ +[k8s_nodes] +control-plane.k8s.lab.cudanet.org +compute-node-1.k8s.lab.cudanet.org +compute-node-2.k8s.lab.cudanet.org + +[k8s_nodes:vars] +ansible_user=ansible diff --git a/argocd/README.md b/argocd/README.md new file mode 100644 index 0000000..39d6398 --- /dev/null +++ b/argocd/README.md @@ -0,0 +1 @@ +the initial deployment and configuration of ArgoCD on my cluster was a pretty straightforward helm chart deployment. However, I did run into some issues with how ArgoCD handles SSL that I leveraged AI (Gemini) to help quickly troubleshoot. Beyond that, creating a project, adding a repo, creating an application and deploying nginx was mostly handled referring to my personal notes. diff --git a/argocd/certificate.yaml b/argocd/certificate.yaml new file mode 100644 index 0000000..2d527d7 --- /dev/null +++ b/argocd/certificate.yaml @@ -0,0 +1,19 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + annotations: + name: letsencrypt + namespace: argocd +spec: + commonName: 'argocd.k8s.lab.cudanet.org' + dnsNames: + - 'argocd.k8s.lab.cudanet.org' + duration: 2160h0m0s + issuerRef: + kind: ClusterIssuer + name: letsencrypt + renewBefore: 360h0m0s + secretName: letsencrypt + usages: + - server auth + - client auth diff --git a/argocd/helm-post-install b/argocd/helm-post-install new file mode 100644 index 0000000..a5095e6 --- /dev/null +++ b/argocd/helm-post-install @@ -0,0 +1,24 @@ +NAME: argocd +LAST DEPLOYED: Fri Oct 30 13:32:17 2026 +NAMESPACE: argocd +STATUS: deployed +REVISION: 1 +DESCRIPTION: Install complete +TEST SUITE: None +NOTES: +In order to access the server UI you have the following options: + +1. kubectl port-forward service/argocd-server -n argocd 8080:443 + + and then open the browser on http://localhost:8080 and accept the certificate + +2. enable ingress in the values file `server.ingress.enabled` and either + - Add the annotation for ssl passthrough: https://argo-cd.readthedocs.io/en/stable/operator-manual/ingress/#option-1-ssl-passthrough + - Set the `configs.params."server.insecure"` in the values file and terminate SSL at your ingress: https://argo-cd.readthedocs.io/en/stable/operator-manual/ingress/#option-2-multiple-ingress-objects-and-hosts + + +After reaching the UI the first time you can login with username: admin and the random password generated during the installation. You can find the password by running: + +kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d + +(You should delete the initial secret afterwards as suggested by the Getting Started Guide: https://argo-cd.readthedocs.io/en/stable/getting_started/#4-login-using-the-cli) diff --git a/argocd/ingressroute.yaml b/argocd/ingressroute.yaml new file mode 100644 index 0000000..856a2fe --- /dev/null +++ b/argocd/ingressroute.yaml @@ -0,0 +1,37 @@ +apiVersion: traefik.io/v1alpha1 +kind: ServersTransport +metadata: + name: argocd-server-transport + namespace: argocd +spec: + insecureSkipVerify: true +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: argocd-server + namespace: argocd +spec: + entryPoints: + - websecure + routes: + # 1. gRPC routing for Argo CD CLI and API clients + - match: Host(`argocd.k8s.lab.cudanet.org`) && Headers(`Content-Type`, `application/grpc`) + kind: Rule + priority: 11 + services: + - name: argocd-server + port: 443 + scheme: https + serversTransport: argocd-server-transport + # 2. Standard Web UI traffic + - match: Host(`argocd.k8s.lab.cudanet.org`) + kind: Rule + priority: 10 + services: + - name: argocd-server + port: 443 + scheme: https + serversTransport: argocd-server-transport + tls: + secretName: argocd-tls-cert # Replace with your TLS Secret, or omit if using Traefik default cert / cert-resolver diff --git a/argocd/password b/argocd/password new file mode 100644 index 0000000..94c449b --- /dev/null +++ b/argocd/password @@ -0,0 +1 @@ +Ci4Jz6xJVj9Zg3ER \ No newline at end of file diff --git a/argocd/token b/argocd/token new file mode 100644 index 0000000..64efc5a --- /dev/null +++ b/argocd/token @@ -0,0 +1 @@ +eyJhbGciOiJSUzI1NiIsImtpZCI6InJTeEp1ZTlwbzZnUkduNk5hN2Fhc0hMTHQtRTM1RlZlcGJZX2J4OS1GSFkifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzkzMzk2MzcyLCJpYXQiOjE3OTMzOTI3NzIsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiMDlmZThkZWMtNmZlMy00NzMwLWEzNGYtNTlkNGNiNTE5Yzg2Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJoZWFkbGFtcCIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJoZWFkbGFtcCIsInVpZCI6IjMxZjYyYzRlLWRjNjMtNDQwZi05ZWU4LTBhNDY0YTU2MTNmMyJ9fSwibmJmIjoxNzkzMzkyNzcyLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6aGVhZGxhbXA6aGVhZGxhbXAifQ.t_zHac1qGD2ea3VasvoFRUN1KpN8JR-YenjOcUdTPVIVAY4E2RAveveQKUPVQ_kI-p0NEUfs_cW_p6zZMcOMicib40BS-fVhgCTvtYo2qkmUXRR-sjvNLnk69z-QF_1d-QghCvFm0kSZosyJad_HNqiFwtwhJ5w4fYd_uiAu7zHQuBXLQsLDhvuHkkp4E5zrmZomFx-XQSdAf6YoROuVpz0aEAuAK9dMRc5tmb7u21m11OhemP1DJ43efHu0vV--xLCG738jF_yqonLm7Aj4DzS9-vyNBbaji5Ola57UQLKNeDFdnzEvtvGIenuXMLwpfE9WRfsXYUngFFJKZ0M2sA \ No newline at end of file diff --git a/calico/README.md b/calico/README.md new file mode 100644 index 0000000..cb1345a --- /dev/null +++ b/calico/README.md @@ -0,0 +1,5 @@ +no AI here. Just a copy + paste from my notes + +kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml + +just make sure that the kubeadm-config.yaml specifies the corred pod network subnet 192.168.0.0/16 for Calico. diff --git a/cert-manager/README.md b/cert-manager/README.md new file mode 100644 index 0000000..256f2b7 --- /dev/null +++ b/cert-manager/README.md @@ -0,0 +1 @@ +no AI here - just a quick google search. Once cert-manager was installed via helm, adding a cluster-issuer and requesting certs came from my personal notes having used cert-manager for years. diff --git a/cert-manager/certificate.yaml b/cert-manager/certificate.yaml new file mode 100644 index 0000000..79dfb9f --- /dev/null +++ b/cert-manager/certificate.yaml @@ -0,0 +1,19 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + annotations: + name: letsencrypt + namespace: cert-manager +spec: + commonName: '*.k8s.lab.cudanet.org' + dnsNames: + - '*.k8s.lab.cudanet.org' + duration: 2160h0m0s + issuerRef: + kind: ClusterIssuer + name: letsencrypt + renewBefore: 360h0m0s + secretName: letsencrypt + usages: + - server auth + - client auth diff --git a/cert-manager/cloudflare-secret.yaml b/cert-manager/cloudflare-secret.yaml new file mode 100644 index 0000000..a1b1bf0 --- /dev/null +++ b/cert-manager/cloudflare-secret.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +data: + api-key: OWVjMTdmYzA0MjJlN2VlZmQxNDdmMmU2ZGE5MmQxMjY2OGZiNQ== +kind: Secret +metadata: + annotations: + name: cloudflare-api-key-secret + namespace: cert-manager +type: Opaque +--- +apiVersion: v1 +data: + api-token: NEpmdEMxRm5acGN4U2t2OWFWNk1QWEQ4SEN4eVlIblAyWDhCcTNxZg== +kind: Secret +metadata: + annotations: + name: cloudflare-api-token-secret + namespace: cert-manager +type: Opaque diff --git a/cert-manager/clusterissuer.yaml b/cert-manager/clusterissuer.yaml new file mode 100644 index 0000000..f09f28e --- /dev/null +++ b/cert-manager/clusterissuer.yaml @@ -0,0 +1,22 @@ +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + annotations: + name: letsencrypt +spec: + acme: + email: ryancuda@protonmail.com + privateKeySecretRef: + name: acme-key + server: https://acme-v02.api.letsencrypt.org/directory + solvers: + - dns01: + cloudflare: + apiKeySecretRef: + key: api-key + name: cloudflare-api-key-secret + email: ryancuda@protonmail.com + cnameStrategy: Follow + selector: + dnsZones: + - lab.cudanet.org diff --git a/cert-manager/install b/cert-manager/install new file mode 100644 index 0000000..e57af98 --- /dev/null +++ b/cert-manager/install @@ -0,0 +1,11 @@ +# 1. Add the official Jetstack Helm repository +helm repo add jetstack https://charts.jetstack.io + +# 2. Update your local Helm chart repository cache +helm repo update + +# 3. Install cert-manager and automatically create its Custom Resource Definitions (CRDs) +helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true diff --git a/democratic-csi/README.md b/democratic-csi/README.md new file mode 100644 index 0000000..cded6e1 --- /dev/null +++ b/democratic-csi/README.md @@ -0,0 +1 @@ +no AI used here. I've been using democratic-csi in my lab for years. Just a simple, straightforward helm deployment. diff --git a/democratic-csi/iscsi-values.yaml b/democratic-csi/iscsi-values.yaml new file mode 100644 index 0000000..20da15d --- /dev/null +++ b/democratic-csi/iscsi-values.yaml @@ -0,0 +1,61 @@ +csiDriver: + name: "org.democratic-csi.iscsi" +storageClasses: +- name: iscsi + defaultClass: false + reclaimPolicy: Delete + volumeBindingMode: Immediate + allowVolumeExpansion: true + parameters: + fsType: ext4 + mountOptions: [] + secrets: + provisioner-secret: + controller-publish-secret: + node-stage-secret: + node-publish-secret: + controller-expand-secret: +volumeSnapshotClasses: +- name: iscsi-snapclass + parameters: + detachedSnapshots: "true" + secrets: + snapshotter-secret: +driver: + config: + driver: zfs-generic-iscsi + driver: zfs-generic-iscsi + sshConnection: + host: 10.4.0.100 + port: 22 + username: root + password: "" + zfs: + cli: + paths: + zfs: /usr/sbin/zfs + zpool: /usr/sbin/zpool + sudo: /usr/bin/sudo + chroot: /usr/sbin/chroot + datasetParentName: pool0/vols + detachedSnapshotsDatasetParentName: pool0/snaps + zvolCompression: + zvolDedup: + zvolEnableReservation: false + zvolBlocksize: + iscsi: + shareStrategy: "targetCli" + shareStrategyTargetCli: + #sudoEnabled: true + basename: "iqn.1994-05.com.redhat:d9e2323cca59" + tpg: + attributes: + authentication: 0 + generate_node_acls: 1 + cache_dynamic_acls: 1 + demo_mode_write_protect: 0 + auth: + targetPortal: "10.4.0.100:3260" + interface: "" + namePrefix: + nameSuffix: diff --git a/democratic-csi/nfs-values.yaml b/democratic-csi/nfs-values.yaml new file mode 100644 index 0000000..f6a8226 --- /dev/null +++ b/democratic-csi/nfs-values.yaml @@ -0,0 +1,53 @@ +csiDriver: + name: "org.democratic-csi.nfs" +storageClasses: +- name: nfs + defaultClass: false + reclaimPolicy: Delete + volumeBindingMode: Immediate + allowVolumeExpansion: true + parameters: + fsType: nfs + mountOptions: [] + secrets: + provisioner-secret: + controller-publish-secret: + node-stage-secret: + node-publish-secret: + controller-expand-secret: +volumeSnapshotClasses: +- name: nfs-snapclass + parameters: + detachedSnapshots: "true" + secrets: + snapshotter-secret: +driver: + config: + driver: zfs-generic-nfs + sshConnection: + host: 10.4.0.100 + port: 22 + username: root + password: "" + zfs: + cli: + paths: + zfs: /usr/sbin/zfs + zpool: /usr/sbin/zpool + sudo: /usr/bin/sudo + chroot: /usr/sbin/chroot + datasetParentName: pool0/k8s/nfs/volumes + detachedSnapshotsDatasetParentName: pool0/k8s/nfs/snapshots + datasetEnableQuotas: true + datasetEnableReservation: false + datasetPermissionsMode: "0777" + datasetPermissionsUser: 0 + datasetPermissionsGroup: 0 + nfs: + shareStrategy: "setDatasetProperties" + shareStrategySetDatasetProperties: + properties: + sharenfs: "rw,no_subtree_check,no_root_squash" + #sharenfs: "on" + # share: "" + shareHost: "10.4.0.100" diff --git a/democratic-csi/test-nfs-pvc.yaml b/democratic-csi/test-nfs-pvc.yaml new file mode 100644 index 0000000..9943e4c --- /dev/null +++ b/democratic-csi/test-nfs-pvc.yaml @@ -0,0 +1,16 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: test-nfs + namespace: default + annotations: + volume.beta.kubernetes.io/storage-provisioner: org.democratic-csi.nfs + volume.kubernetes.io/storage-provisioner: org.democratic-csi.nfs +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Gi + storageClassName: nfs + volumeMode: Filesystem diff --git a/firewall-rules.sh b/firewall-rules.sh new file mode 100644 index 0000000..40beac3 --- /dev/null +++ b/firewall-rules.sh @@ -0,0 +1,31 @@ +firewall-cmd --permanent --add-masquerade +firewall-cmd --permanent --add-port=6443/tcp # Kubernetes API Server +firewall-cmd --permanent --add-port=2379-2380/tcp # etcd server client API +firewall-cmd --permanent --add-port=10250/tcp # Kubelet API +firewall-cmd --permanent --add-port=10259/tcp # kube-scheduler +firewall-cmd --permanent --add-port=10257/tcp # kube-controller-manager +firewall-cmd --permanent --add-port=8472/udp # Flannel VXLAN overlay +firewall-cmd --permanent --add-port=51820/udp # Flannel WireGuard (optional/fallback) +firewall-cmd --permanent --zone=trusted --add-source=10.3.0.0/24 # Lab Node Subnet +firewall-cmd --permanent --zone=trusted --add-source=10.244.0.0/16 # Pod CIDR +firewall-cmd --permanent --zone=trusted --add-source=10.96.0.0/12 # Service CIDR +firewall-cmd --permanent --zone=trusted --add-interface=cni0 +firewall-cmd --permanent --zone=trusted --add-interface=flannel.1 +firewall-cmd --reload +firewall-cmd --permanent --add-masquerade +firewall-cmd --permanent --add-port=6443/tcp # API Server +firewall-cmd --permanent --add-port=2379-2380/tcp # etcd +firewall-cmd --permanent --add-port=10250/tcp # Kubelet API +firewall-cmd --permanent --add-port=10259/tcp # kube-scheduler +firewall-cmd --permanent --add-port=10257/tcp # kube-controller-manager +firewall-cmd --permanent --add-port=179/tcp # Calico BGP +firewall-cmd --permanent --add-port=4789/udp # Calico VXLAN (if used instead of IPIP) +firewall-cmd --permanent --add-port=5473/tcp # Typha (for large clusters) +firewall-cmd --permanent --add-protocol=ipip # Calico IP-in-IP encapsulation +firewall-cmd --permanent --zone=trusted --add-source=10.4.0.0/24 # Node Network +firewall-cmd --permanent --zone=trusted --add-source=192.168.0.0/16 # Calico Pod CIDR +firewall-cmd --permanent --zone=trusted --add-source=10.96.0.0/12 # Service CIDR +firewall-cmd --permanent --zone=trusted --add-interface=cali+ +firewall-cmd --permanent --zone=trusted --add-interface=tunl0 +firewall-cmd --permanent --zone=trusted --add-interface=vxlan.calico +firewall-cmd --reload diff --git a/get_helm.sh b/get_helm.sh new file mode 100755 index 0000000..3d232cc --- /dev/null +++ b/get_helm.sh @@ -0,0 +1,350 @@ +#!/usr/bin/env bash + +# Copyright The Helm Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The install script is based off of the MIT-licensed script from glide, +# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get + +: ${BINARY_NAME:="helm"} +: ${USE_SUDO:="true"} +: ${DEBUG:="false"} +: ${VERIFY_CHECKSUM:="true"} +: ${VERIFY_SIGNATURES:="false"} +: ${HELM_INSTALL_DIR:="/usr/local/bin"} +: ${GPG_PUBRING:="pubring.kbx"} + +HAS_CURL="$(type "curl" &> /dev/null && echo true || echo false)" +HAS_WGET="$(type "wget" &> /dev/null && echo true || echo false)" +HAS_OPENSSL="$(type "openssl" &> /dev/null && echo true || echo false)" +HAS_GPG="$(type "gpg" &> /dev/null && echo true || echo false)" +HAS_GIT="$(type "git" &> /dev/null && echo true || echo false)" +HAS_TAR="$(type "tar" &> /dev/null && echo true || echo false)" + +# initArch discovers the architecture for this system. +initArch() { + ARCH=$(uname -m) + case $ARCH in + armv5*) ARCH="armv5";; + armv6*) ARCH="armv6";; + armv7*) ARCH="arm";; + aarch64) ARCH="arm64";; + x86) ARCH="386";; + x86_64) ARCH="amd64";; + i686) ARCH="386";; + i386) ARCH="386";; + esac +} + +# initOS discovers the operating system for this system. +initOS() { + OS=$(echo `uname`|tr '[:upper:]' '[:lower:]') + + case "$OS" in + # Minimalist GNU for Windows + mingw*|cygwin*) OS='windows';; + esac +} + +# runs the given command as root (detects if we are root already) +runAsRoot() { + if [ $EUID -ne 0 -a "$USE_SUDO" = "true" ]; then + sudo "${@}" + else + "${@}" + fi +} + +# verifySupported checks that the os/arch combination is supported for +# binary builds, as well whether or not necessary tools are present. +verifySupported() { + local supported="darwin-amd64\ndarwin-arm64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-loong64\nlinux-ppc64le\nlinux-s390x\nlinux-riscv64\nwindows-amd64\nwindows-arm64" + if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then + echo "No prebuilt binary for ${OS}-${ARCH}." + echo "To build from source, go to https://github.com/helm/helm" + exit 1 + fi + + if [ "${HAS_CURL}" != "true" ] && [ "${HAS_WGET}" != "true" ]; then + echo "Either curl or wget is required" + exit 1 + fi + + if [ "${VERIFY_CHECKSUM}" == "true" ] && [ "${HAS_OPENSSL}" != "true" ]; then + echo "In order to verify checksum, openssl must first be installed." + echo "Please install openssl or set VERIFY_CHECKSUM=false in your environment." + exit 1 + fi + + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + if [ "${HAS_GPG}" != "true" ]; then + echo "In order to verify signatures, gpg must first be installed." + echo "Please install gpg or set VERIFY_SIGNATURES=false in your environment." + exit 1 + fi + if [ "${OS}" != "linux" ]; then + echo "Signature verification is currently only supported on Linux." + echo "Please set VERIFY_SIGNATURES=false or verify the signatures manually." + exit 1 + fi + fi + + if [ "${HAS_GIT}" != "true" ]; then + echo "[WARNING] Could not find git. It is required for plugin installation." + fi + + if [ "${HAS_TAR}" != "true" ]; then + echo "[ERROR] Could not find tar. It is required to extract the helm binary archive." + exit 1 + fi +} + +# checkDesiredVersion checks if the desired version is available. +checkDesiredVersion() { + if [ "x$DESIRED_VERSION" == "x" ]; then + # Get tag from release URL + # Cache behavior is provider-specific, so this mitigation is best effort. + # The current CDN does not revalidate on the request no-cache directive, + # so use a unique query while retaining no-cache for compliant intermediaries. + local latest_release_url="https://get.helm.sh/helm4-latest-version?ts=$(date +%s)" + local latest_release_response="" + if [ "${HAS_CURL}" == "true" ]; then + latest_release_response=$( curl -L --silent --show-error --fail --header "Cache-Control: no-cache" "$latest_release_url" 2>&1 || true ) + elif [ "${HAS_WGET}" == "true" ]; then + latest_release_response=$( wget "$latest_release_url" --header="Cache-Control: no-cache" -q -O - 2>&1 || true ) + fi + TAG=$( echo "$latest_release_response" | grep '^v[0-9]' ) + if [ "x$TAG" == "x" ]; then + printf "Could not retrieve the latest release tag information from %s: %s\n" "${latest_release_url}" "${latest_release_response}" + exit 1 + fi + else + TAG=$DESIRED_VERSION + fi +} + +# checkHelmInstalledVersion checks which version of helm is installed and +# if it needs to be changed. +checkHelmInstalledVersion() { + if [[ -f "${HELM_INSTALL_DIR}/${BINARY_NAME}" ]]; then + local version=$("${HELM_INSTALL_DIR}/${BINARY_NAME}" version --template="{{ .Version }}") + if [[ "$version" == "$TAG" ]]; then + echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" + return 0 + else + echo "Helm ${TAG} is available. Changing from version ${version}." + return 1 + fi + else + return 1 + fi +} + +# downloadFile downloads the latest binary package and also the checksum +# for that binary. +downloadFile() { + HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" + DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST" + CHECKSUM_URL="$DOWNLOAD_URL.sha256" + HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)" + HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST" + HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256" + echo "Downloading $DOWNLOAD_URL" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE" + curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL" + wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL" + fi +} + +# verifyFile verifies the SHA256 checksum of the binary package +# and the GPG signatures for both the package and checksum file +# (depending on settings in environment). +verifyFile() { + if [ "${VERIFY_CHECKSUM}" == "true" ]; then + verifyChecksum + fi + if [ "${VERIFY_SIGNATURES}" == "true" ]; then + verifySignatures + fi +} + +# installFile installs the Helm binary. +installFile() { + HELM_TMP="$HELM_TMP_ROOT/$BINARY_NAME" + mkdir -p "$HELM_TMP" + tar xf "$HELM_TMP_FILE" -C "$HELM_TMP" + HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/helm" + echo "Preparing to install $BINARY_NAME into ${HELM_INSTALL_DIR}" + runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR/$BINARY_NAME" + echo "$BINARY_NAME installed into $HELM_INSTALL_DIR/$BINARY_NAME" +} + +# verifyChecksum verifies the SHA256 checksum of the binary package. +verifyChecksum() { + printf "Verifying checksum... " + local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') + local expected_sum=$(cat ${HELM_SUM_FILE}) + if [ "$sum" != "$expected_sum" ]; then + echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting." + exit 1 + fi + echo "Done." +} + +# verifySignatures obtains the latest KEYS file from GitHub main branch +# as well as the signature .asc files from the specific GitHub release, +# then verifies that the release artifacts were signed by a maintainer's key. +verifySignatures() { + printf "Verifying signatures... " + local keys_filename="KEYS" + local github_keys_url="https://raw.githubusercontent.com/helm/helm/main/${keys_filename}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_keys_url}" -o "${HELM_TMP_ROOT}/${keys_filename}" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/${keys_filename}" "${github_keys_url}" + fi + local gpg_keyring="${HELM_TMP_ROOT}/keyring.gpg" + local gpg_homedir="${HELM_TMP_ROOT}/gnupg" + mkdir -p -m 0700 "${gpg_homedir}" + local gpg_stderr_device="/dev/null" + if [ "${DEBUG}" == "true" ]; then + gpg_stderr_device="/dev/stderr" + fi + gpg --batch --quiet --homedir="${gpg_homedir}" --import "${HELM_TMP_ROOT}/${keys_filename}" 2> "${gpg_stderr_device}" + gpg --batch --no-default-keyring --keyring "${gpg_homedir}/${GPG_PUBRING}" --export > "${gpg_keyring}" + local github_release_url="https://github.com/helm/helm/releases/download/${TAG}" + if [ "${HAS_CURL}" == "true" ]; then + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + curl -SsL "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" -o "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + elif [ "${HAS_WGET}" == "true" ]; then + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" + wget -q -O "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" "${github_release_url}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" + fi + local error_text="If you think this might be a potential security issue," + error_text="${error_text}\nplease see here: https://github.com/helm/community/blob/master/SECURITY.md" + local num_goodlines_sha=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_sha} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz.sha256!" + echo -e "${error_text}" + exit 1 + fi + local num_goodlines_tar=$(gpg --verify --keyring="${gpg_keyring}" --status-fd=1 "${HELM_TMP_ROOT}/helm-${TAG}-${OS}-${ARCH}.tar.gz.asc" 2> "${gpg_stderr_device}" | grep -c -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)') + if [[ ${num_goodlines_tar} -lt 2 ]]; then + echo "Unable to verify the signature of helm-${TAG}-${OS}-${ARCH}.tar.gz!" + echo -e "${error_text}" + exit 1 + fi + echo "Done." +} + +# fail_trap is executed if an error occurs. +fail_trap() { + result=$? + if [ "$result" != "0" ]; then + if [[ -n "$INPUT_ARGUMENTS" ]]; then + echo "Failed to install $BINARY_NAME with the arguments provided: $INPUT_ARGUMENTS" + help + else + echo "Failed to install $BINARY_NAME" + fi + echo -e "\tFor support, go to https://github.com/helm/helm." + fi + cleanup + exit $result +} + +# testVersion tests the installed client to make sure it is working. +testVersion() { + set +e + HELM="$(command -v $BINARY_NAME)" + if [ "$?" = "1" ]; then + echo "$BINARY_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?' + exit 1 + fi + set -e +} + +# help provides possible cli installation arguments +help () { + echo "Accepted cli arguments are:" + echo -e "\t[--help|-h ] ->> prints this help" + echo -e "\t[--version|-v ] . When not defined it fetches the latest release tag from the Helm CDN" + echo -e "\te.g. --version v4.0.0 or -v canary" + echo -e "\t[--no-sudo] ->> install without sudo" +} + +# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977 +cleanup() { + if [[ -d "${HELM_TMP_ROOT:-}" ]]; then + rm -rf "$HELM_TMP_ROOT" + fi +} + +# Execution + +#Stop execution on any error +trap "fail_trap" EXIT +set -e + +# Set debug if desired +if [ "${DEBUG}" == "true" ]; then + set -x +fi + +# Parsing input arguments (if any) +export INPUT_ARGUMENTS="${@}" +set -u +while [[ $# -gt 0 ]]; do + case $1 in + '--version'|-v) + shift + if [[ $# -ne 0 ]]; then + export DESIRED_VERSION="${1}" + if [[ "$1" != "v"* ]]; then + echo "Expected version arg ('${DESIRED_VERSION}') to begin with 'v', fixing..." + export DESIRED_VERSION="v${1}" + fi + else + echo -e "Please provide the desired version. e.g. --version v4.0.0 or -v canary" + exit 0 + fi + ;; + '--no-sudo') + USE_SUDO="false" + ;; + '--help'|-h) + help + exit 0 + ;; + *) exit 1 + ;; + esac + shift +done +set +u + +initArch +initOS +verifySupported +checkDesiredVersion +if ! checkHelmInstalledVersion; then + downloadFile + verifyFile + installFile +fi +testVersion +cleanup diff --git a/gitea/README.md b/gitea/README.md new file mode 100644 index 0000000..2f00190 --- /dev/null +++ b/gitea/README.md @@ -0,0 +1 @@ +no AI used here. Pretty much just a copy+paste of my Gitea deployment on Openshift. Worth noting - I actually started out with a helm chart deployment, but ran into issues with pods not spinning up, so I went back to basics and did it fully manually and worked perfectly. diff --git a/gitea/certificate.yaml b/gitea/certificate.yaml new file mode 100644 index 0000000..a4782b1 --- /dev/null +++ b/gitea/certificate.yaml @@ -0,0 +1,19 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + annotations: + name: letsencrypt + namespace: gitea +spec: + commonName: 'gitea.k8s.lab.cudanet.org' + dnsNames: + - 'gitea.k8s.lab.cudanet.org' + duration: 2160h0m0s + issuerRef: + kind: ClusterIssuer + name: letsencrypt + renewBefore: 360h0m0s + secretName: letsencrypt + usages: + - server auth + - client auth diff --git a/gitea/gitea-deployment.yaml b/gitea/gitea-deployment.yaml new file mode 100644 index 0000000..93b38bc --- /dev/null +++ b/gitea/gitea-deployment.yaml @@ -0,0 +1,70 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + labels: + app: gitea + app.kubernetes.io/component: gitea + app.kubernetes.io/instance: gitea + name: gitea + namespace: gitea +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + deployment: gitea + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + annotations: + creationTimestamp: null + labels: + deployment: gitea + spec: + containers: + - env: + - name: GITEA__database__DB_TYPE + value: mysql + - name: GITEA__database__HOST + value: mariadb + - name: GITEA__database__NAME + value: gitea + - name: GITEA__database__USER + value: gitea + - name: GITEA__database__PASSWD + value: gitea + image: docker.gitea.com/gitea@sha256:3a54ec4b271f0479eb1f40624cbf90f2245306aab8533dd14bb5f49f4c580ccf + imagePullPolicy: IfNotPresent + name: gitea + ports: + - containerPort: 2222 + protocol: TCP + - containerPort: 3000 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /etc/gitea + name: gitea-config + - mountPath: /var/lib/gitea + name: gitea-data + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - name: gitea-config + persistentVolumeClaim: + claimName: gitea-config + - name: gitea-data + persistentVolumeClaim: + claimName: gitea-data diff --git a/gitea/gitea-pvc.yaml b/gitea/gitea-pvc.yaml new file mode 100644 index 0000000..3b6fa14 --- /dev/null +++ b/gitea/gitea-pvc.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + name: gitea-config + namespace: gitea +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + storageClassName: nfs + volumeMode: Filesystem +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + finalizers: + - kubernetes.io/pvc-protection + name: gitea-data + namespace: gitea +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + storageClassName: nfs + volumeMode: Filesystem +--- diff --git a/gitea/gitea-svc.yaml b/gitea/gitea-svc.yaml new file mode 100644 index 0000000..01c7884 --- /dev/null +++ b/gitea/gitea-svc.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: gitea + app.kubernetes.io/component: gitea + app.kubernetes.io/instance: gitea + name: gitea + namespace: gitea +spec: + clusterIP: + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: web + port: 3000 + protocol: TCP + targetPort: 3000 + - name: ssh + port: 2222 + protocol: TCP + targetPort: 2222 + selector: + deployment: gitea + sessionAffinity: None + type: ClusterIP diff --git a/gitea/ingress.yaml b/gitea/ingress.yaml new file mode 100644 index 0000000..617d33a --- /dev/null +++ b/gitea/ingress.yaml @@ -0,0 +1,24 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: gitea-ingress + namespace: gitea + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure +spec: + ingressClassName: traefik + tls: + - hosts: + - gitea.k8s.lab.cudanet.org + secretName: letsencrypt + rules: + - host: gitea.k8s.lab.cudanet.org + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: gitea + port: + number: 3000 diff --git a/gitea/ingressroute.yaml b/gitea/ingressroute.yaml new file mode 100644 index 0000000..70099eb --- /dev/null +++ b/gitea/ingressroute.yaml @@ -0,0 +1,14 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: gitea-web + namespace: gitea +spec: + entryPoints: + - websecure + routes: + - match: Host(`gitea.k8s.lab.cudanet.org`) + kind: Rule + services: + - name: gitea + port: 3000 diff --git a/gitea/mariadb-deployment.yaml b/gitea/mariadb-deployment.yaml new file mode 100644 index 0000000..439ee86 --- /dev/null +++ b/gitea/mariadb-deployment.yaml @@ -0,0 +1,60 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + annotations: + labels: + app: mariadb + app.kubernetes.io/component: mariadb + app.kubernetes.io/instance: mariadb + name: mariadb + namespace: gitea +spec: + progressDeadlineSeconds: 600 + replicas: 1 + revisionHistoryLimit: 10 + selector: + matchLabels: + deployment: mariadb + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + annotations: + labels: + deployment: mariadb + spec: + containers: + - env: + - name: MYSQL_ROOT_PASSWORD + value: gitea + - name: MYSQL_USER + value: gitea + - name: MYSQL_PASSWORD + value: gitea + - name: MYSQL_DATABASE + value: gitea + image: docker.io/library/mariadb@sha256:659a15e38f4ccb2bfa2c7cf824a8a5955fdc5f63cfd01537bc25c9e0ff716605 + imagePullPolicy: IfNotPresent + name: mariadb + ports: + - containerPort: 3306 + protocol: TCP + resources: {} + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /var/lib/mysql + name: mariadb + dnsPolicy: ClusterFirst + restartPolicy: Always + schedulerName: default-scheduler + securityContext: {} + terminationGracePeriodSeconds: 30 + volumes: + - name: mariadb + persistentVolumeClaim: + claimName: mariadb diff --git a/gitea/mariadb-pvc.yaml b/gitea/mariadb-pvc.yaml new file mode 100644 index 0000000..c84f5a7 --- /dev/null +++ b/gitea/mariadb-pvc.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + annotations: + name: mariadb + namespace: gitea +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 100Gi + storageClassName: nfs + volumeMode: Filesystem diff --git a/gitea/mariadb-svc.yaml b/gitea/mariadb-svc.yaml new file mode 100644 index 0000000..2fd5a2a --- /dev/null +++ b/gitea/mariadb-svc.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: v1 +kind: Service +metadata: + annotations: + labels: + app: mariadb + app.kubernetes.io/component: mariadb + app.kubernetes.io/instance: mariadb + name: mariadb + namespace: gitea +spec: + clusterIP: + internalTrafficPolicy: Cluster + ipFamilies: + - IPv4 + ipFamilyPolicy: SingleStack + ports: + - name: 3306-tcp + port: 3306 + protocol: TCP + targetPort: 3306 + selector: + deployment: mariadb + sessionAffinity: None + type: ClusterIP diff --git a/gitea/namespace.yaml b/gitea/namespace.yaml new file mode 100644 index 0000000..f3757af --- /dev/null +++ b/gitea/namespace.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + annotations: + labels: + kubernetes.io/metadata.name: gitea + name: gitea diff --git a/headlamp/README.md b/headlamp/README.md new file mode 100644 index 0000000..41b7f31 --- /dev/null +++ b/headlamp/README.md @@ -0,0 +1,3 @@ +no AI used in the deployment of headlamp, just a straightforward install from the instructions here https://headlamp.dev/docs/latest/installation/in-cluster/ + +However, I did ask AI how to generate a non-expiring token since I didn't remember the exact command to do so. diff --git a/headlamp/headlamp-ingressroute.yaml b/headlamp/headlamp-ingressroute.yaml new file mode 100644 index 0000000..eb624b3 --- /dev/null +++ b/headlamp/headlamp-ingressroute.yaml @@ -0,0 +1,16 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: headlamp + namespace: headlamp +spec: + entryPoints: + - websecure + routes: + - match: Host(`headlamp.k8s.lab.cudanet.org`) + kind: Rule + services: + - name: headlamp + port: 80 + tls: + certResolver: letsencrypt diff --git a/headlamp/service-account-token.yaml b/headlamp/service-account-token.yaml new file mode 100644 index 0000000..6ed89f9 --- /dev/null +++ b/headlamp/service-account-token.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: headlamp-admin + namespace: headlamp +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: headlamp-admin-binding +subjects: + - kind: ServiceAccount + name: headlamp-admin + namespace: headlamp +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: Secret +metadata: + name: headlamp-admin-token + namespace: headlamp + annotations: + kubernetes.io/service-account.name: headlamp-admin +type: kubernetes.io/service-account-token diff --git a/headlamp/token b/headlamp/token new file mode 100644 index 0000000..b3f52df --- /dev/null +++ b/headlamp/token @@ -0,0 +1 @@ +eyJhbGciOiJSUzI1NiIsImtpZCI6InJTeEp1ZTlwbzZnUkduNk5hN2Fhc0hMTHQtRTM1RlZlcGJZX2J4OS1GSFkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJoZWFkbGFtcCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJoZWFkbGFtcC1hZG1pbi10b2tlbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJoZWFkbGFtcC1hZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjgyMmU4ODUxLTE3OTQtNDhkZi1hYjA4LTJiY2FhMDQ0YzgyYSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpoZWFkbGFtcDpoZWFkbGFtcC1hZG1pbiJ9.N7ybibaIl2_bljbsKcR-JiVCpdS15MgwurJqg9QTNchPoVYsncYFbTh4AbASLh97gbE5rYqsdXE7BnoOA70Zn_DZ9KcKI90NzFykDBUPZZaFv1rO5pIRGCdx5gGO5UXjFz3wCYhFTacEGNE9DVFrvBmeNlp8WI7xbHRM2SPG7BDFWwcxWeTXaVFoH6v3P9RRJWXec07JJEq51Un5jjmWV_gFh07pQAkUOwSYNX6ZQG6v2pRDEEAAu0cVmJI3kgvCeQBARXq7XKekGNIt0dQ6MR5B_4IronDDve3AbGLPB7cClKi4oTPD3GJT8cetAHhztIIQp0MnpW-2gNCGnTUmNg \ No newline at end of file diff --git a/kubeadm/README.md b/kubeadm/README.md new file mode 100644 index 0000000..0617cac --- /dev/null +++ b/kubeadm/README.md @@ -0,0 +1 @@ +I mostly referred to my personal notes for the actual process of bootstrapping the cluster with kubeadm. However, I did ask AI to generate an example kubeadm-config.yaml since I didn't seem to have an old copy to reference in my files. diff --git a/kubeadm/kubeadm-config.yaml b/kubeadm/kubeadm-config.yaml new file mode 100644 index 0000000..d246afd --- /dev/null +++ b/kubeadm/kubeadm-config.yaml @@ -0,0 +1,30 @@ +apiVersion: kubeadm.k8s.io/v1beta4 +kind: InitConfiguration +localAPIEndpoint: + advertiseAddress: "10.4.0.10" + bindPort: 6443 +nodeRegistration: + name: "control-plane.k8s.lab.cudanet.org" + criSocket: "unix:///var/run/crio/crio.sock" + imagePullPolicy: "IfNotPresent" + taints: + - effect: "NoSchedule" + key: "node-role.kubernetes.io/control-plane" +--- +apiVersion: kubeadm.k8s.io/v1beta4 +kind: ClusterConfiguration +kubernetesVersion: "v1.37.0" +clusterName: "kubernetes" +controlPlaneEndpoint: "control-plane.k8s.lab.cudanet.org:6443" +apiServer: + certSANs: + - "control-plane.k8s.lab.cudanet.org" + - "10.4.0.10" +networking: + dnsDomain: "cluster.local" + serviceSubnet: "10.96.0.0/12" + podSubnet: "192.168.0.0/16" +--- +apiVersion: kubeletconfiguration.k8s.io/v1beta1 +kind: KubeletConfiguration +cgroupDriver: "systemd" diff --git a/metallb/README.md b/metallb/README.md new file mode 100644 index 0000000..e03bbaf --- /dev/null +++ b/metallb/README.md @@ -0,0 +1 @@ +no AI here, just a quick google "helm install metallb". I've been spoiled with it just being an operator on Openshift for so long, I had to look it up. Creating the ipaddresspool and l2advert was basically just a copy+paste from what I use on OCP. diff --git a/metallb/ipaddresspool.yaml b/metallb/ipaddresspool.yaml new file mode 100644 index 0000000..f4cee6f --- /dev/null +++ b/metallb/ipaddresspool.yaml @@ -0,0 +1,17 @@ +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: default-pool + namespace: metallb-system +spec: + addresses: + - 10.4.0.200-10.4.0.220 +--- +apiVersion: metallb.io/v1beta1 +kind: L2Advertisement +metadata: + name: l2-adv + namespace: metallb-system +spec: + ipAddressPools: + - default-pool diff --git a/nginx b/nginx new file mode 160000 index 0000000..745619c --- /dev/null +++ b/nginx @@ -0,0 +1 @@ +Subproject commit 745619cfc1f3c27930aeffce6a87151fa2f98147 diff --git a/rbac/README.md b/rbac/README.md new file mode 100644 index 0000000..5f44d22 --- /dev/null +++ b/rbac/README.md @@ -0,0 +1,3 @@ +I did rely on AI to kludge my way through this process, because I've never had to do it before. Thus far my experience managing RBAC on kuberneties has been leveraging OIDC with some form of SSO, like LDAP (Active Directory/FreeIPA). + +This was a learning experience for me. diff --git a/rbac/clusterrolebinding.yaml b/rbac/clusterrolebinding.yaml new file mode 100644 index 0000000..8a7b31b --- /dev/null +++ b/rbac/clusterrolebinding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: rcuda-cluster-admin-binding +subjects: +- kind: User + name: rcuda # Must match the CN from the certificate + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io diff --git a/rbac/rcuda.crt b/rbac/rcuda.crt new file mode 100644 index 0000000..04f1427 --- /dev/null +++ b/rbac/rcuda.crt @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC9TCCAd2gAwIBAgIQKfkUgQPfbuziunLbS0Y2BTANBgkqhkiG9w0BAQsFADAV +MRMwEQYDVQQDEwprdWJlcm5ldGVzMB4XDTI2MTAzMDE2MTA1N1oXDTI2MTAzMTE2 +MTA1N1owEDEOMAwGA1UEAxMFcmN1ZGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCTVTfCRu4v4LgHXsFem0KfkoSAxy498g2qCSsgDegDUUYUC7ptXNLG +Y8McyZRlmiCGhfRKygq3zm/3da5w806/ldPZikiUuXIi2brc+2es6ey7tKQUXTtU +/R8NmtyXVIErBwbQeHKXIH65+tJtmWhzvmvbzCIGisaldKnSQDoy3bXPUaawJm0W +wnzsv9YWJ9JIr7RU15N9ngG3RAF+eYgxarJOZHHJK0qmPeSB6Ux9kIGlfSTNndAW +dG0CB81kxV4FnlYQdw+/nBfoluhLAzL+VUzLUeE2ujin3o2Yy6v3Xiqyzg6Sa9y4 +1WcjtP78KuFiQOudiZlUS83bjmAcpBzbAgMBAAGjRjBEMBMGA1UdJQQMMAoGCCsG +AQUFBwMCMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUkd2GsW22p+1bykQ+Pqb9 +JVefciIwDQYJKoZIhvcNAQELBQADggEBAIf+b5tyZYL9sx+eRVTPf0kMXGDDzG30 +eNPhiixChR+kD710OJgNrwfCSTF1/rtelOluVRYK4qs8ktp/ifczBF+AsMT1pW8/ +Ov8FVMT/cJC8P2GAR+6nK4pWYc0PyNpAyjkKq3xLnpc+NIucybPNXiFbXwGeAhFz +ICHI529PVqxyMK+bMEs4wA3ankpkarOd3GkjX2tBhrWbAs8wFF52sRCzBHjTgj37 +sEUrYi7qEkmw4oJTr7LXw6Ajye/9GaFlhScyytr/WeBo3Mp/TcpkoxyIvGMs49wK +5Q80UrCkZSk3zWApuMeqg/pUW7RcsZY9AQMYfe78ziK1wgLjNudH6Y4= +-----END CERTIFICATE----- diff --git a/rbac/rcuda.csr b/rbac/rcuda.csr new file mode 100644 index 0000000..05a4029 --- /dev/null +++ b/rbac/rcuda.csr @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICVTCCAT0CAQAwEDEOMAwGA1UEAwwFcmN1ZGEwggEiMA0GCSqGSIb3DQEBAQUA +A4IBDwAwggEKAoIBAQCTVTfCRu4v4LgHXsFem0KfkoSAxy498g2qCSsgDegDUUYU +C7ptXNLGY8McyZRlmiCGhfRKygq3zm/3da5w806/ldPZikiUuXIi2brc+2es6ey7 +tKQUXTtU/R8NmtyXVIErBwbQeHKXIH65+tJtmWhzvmvbzCIGisaldKnSQDoy3bXP +UaawJm0Wwnzsv9YWJ9JIr7RU15N9ngG3RAF+eYgxarJOZHHJK0qmPeSB6Ux9kIGl +fSTNndAWdG0CB81kxV4FnlYQdw+/nBfoluhLAzL+VUzLUeE2ujin3o2Yy6v3Xiqy +zg6Sa9y41WcjtP78KuFiQOudiZlUS83bjmAcpBzbAgMBAAGgADANBgkqhkiG9w0B +AQsFAAOCAQEAhPbV9ue8FWhX+rlW7qT+4qruzYKFSn5OVZ6qMOAJr4vQwJjQhQbT +MWLxh2nBH1Q56iW1lUVU/FjvyGsHsoPEvNKWxYtBeGeICjllpkcDyCtB0UAHhj0X +efFghXM5kzh6YhgjDiKIajx4m7C0GFZBYvyxo8gYfpp+PBgaWsSztp7dzxQdU74k +sGQ69R/4yQY7uVYgHNCB7shN6jT/XUWGBypOtYejP8cdKf0ZecO1PL8DvsS4jVzL +fVNVEvWf0DlaEJdIJEvFwZNxfqf7UdXARCReZBKs5rYPFBsyjiL4y3v43WzTzZKy +ECLNLgDMicf00nTZ2+ob4q8vJ4kK1cqnXg== +-----END CERTIFICATE REQUEST----- diff --git a/rbac/rcuda.key b/rbac/rcuda.key new file mode 100644 index 0000000..23507fe --- /dev/null +++ b/rbac/rcuda.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCTVTfCRu4v4LgH +XsFem0KfkoSAxy498g2qCSsgDegDUUYUC7ptXNLGY8McyZRlmiCGhfRKygq3zm/3 +da5w806/ldPZikiUuXIi2brc+2es6ey7tKQUXTtU/R8NmtyXVIErBwbQeHKXIH65 ++tJtmWhzvmvbzCIGisaldKnSQDoy3bXPUaawJm0Wwnzsv9YWJ9JIr7RU15N9ngG3 +RAF+eYgxarJOZHHJK0qmPeSB6Ux9kIGlfSTNndAWdG0CB81kxV4FnlYQdw+/nBfo +luhLAzL+VUzLUeE2ujin3o2Yy6v3Xiqyzg6Sa9y41WcjtP78KuFiQOudiZlUS83b +jmAcpBzbAgMBAAECggEAGJQN0j4s/BW7/ZbEIXk7Ow2hRy5zO2hG+UfARv+q5erI +Cw4N0rK6gtgfs4y5Ud3NcUOlhliAQzuQGDMsn7EnQPTIu7DC/PSW4tN5xeW23PTF +RmcTPEZ1YG89n/JmvGQfjEMG0v5U93s3SOVb92axMyXgLnG2d2rtSqrUrbuDOfrb +yxtpE0quBULpKuIdHwxOxntXo4aTAYX1lcS85U4bQXNQ1/7IqSgpHd2eqV0eatHE +LEMhZ0Iicot//VXvWx/zDeAX7nwPSAhtzRlMR2LEh5eYj3lHNd2XxrhYBqhtbDqZ +f2GYuxTkvz5QcGLirYkhOrGC7kZ4Lh9VxGBc6lAtmQKBgQDGLa75VAIBjMcUA7cq +JIf26+IdmMIsbhW/kb4unXkMFLvHMDFJiNks6k9WQymbg2vxuCUeB82CRyahtSDv +kUFgyT+LKRnakiYWnvdEtsC85z5CrpCvW6jQPnKvnecbD/2obxuoco8K6X4IaN/K +EOFmrZbTRSC5oVXcV3ZhhTYzZQKBgQC+UcjLCL4WcvFmwso/CQedSxkrtMvjrMRi +7zcJ+WaQBoTbihtCGmpeOCgNKBx90OdyxjutpREB55w5GUvROjVJOQS34IDr7qT0 +6XRVzP5xEiV7stPRPZq5TfPMlRh/15Iz3sxFL8U4jaLqVtF7b66RoiwhG5E1MrPq +K5qlMwWrPwKBgQCvpbHdnk2g3gtce+aN3Y78lRj2Lnx7MJ7qCdzMKBovUPkVlGs6 +x6XlH09uH1zzTsoPlaygLz5AZ5uf24/zhYSyqew+BRG7HanQhtiV+HxfcX1HUL1A +DzdSSucPLB63Z7bk4IVdCRwrCcQA5CLlT4zXmqeBXVWh8ZLOFl8AFDdz2QKBgQCI +3NVxtKrGCAizZlb8t6ijI7NJK+4Hn2Fm6DIDWTe5UQbPveRWSBrAANBEjdjXXZhj +gEzAVdkCLqW4pglQv/Q74OYUuTzc7bPwb8X6Qb2kYHFWIEaUzSFsKrOtJncE0GhS +JuFsy6qfPbOvo5lnk+vMzPvBH0rwH7MkLhE6xZNJDwKBgBTMnQJxWCKYOGd0RKVv +6MisPvd/nrVvaDaoozCXrWQ+SHa6qZDIHIEQOkpRi5x13A3UgjRfrNWTqinTeKAp +Ue7G+mdfmd+HUVYS5/qJPnnoPsswJ9G9xbGUeo06U5ivs4qj4Rl5OU5nEgwKHpuq +GBb9eonSCVDPpqhJL/50jZ+e +-----END PRIVATE KEY----- diff --git a/registration_host_init.sh b/registration_host_init.sh new file mode 100644 index 0000000..8eb1dfc --- /dev/null +++ b/registration_host_init.sh @@ -0,0 +1,247 @@ +#!/bin/bash +set -e + +echo "# Running [control-plane.k8s.lab.cudanet.org] host initial configuration" + + +exit_and_cancel_build() { + echo 'Host [control-plane.k8s.lab.cudanet.org] initial configuration failed' + + SSL_CA_CERT=$(mktemp) + cat << EOF > $SSL_CA_CERT + -----BEGIN CERTIFICATE----- + MIIG6TCCBNGgAwIBAgIUIacbh/WYmORFpiddBg25g/W4jlQwDQYJKoZIhvcNAQEL + BQAwgYExCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJvbGluYTEQMA4G + A1UEBwwHUmFsZWlnaDEQMA4GA1UECgwHS2F0ZWxsbzEUMBIGA1UECwwLU29tZU9y + Z1VuaXQxHzAdBgNVBAMMFnNhdGwwMS5sYWIuY3VkYW5ldC5vcmcwHhcNMjYwOTEx + MTc1NTA1WhcNMzgwMTE3MTc1NTA1WjCBgTELMAkGA1UEBhMCVVMxFzAVBgNVBAgM + Dk5vcnRoIENhcm9saW5hMRAwDgYDVQQHDAdSYWxlaWdoMRAwDgYDVQQKDAdLYXRl + bGxvMRQwEgYDVQQLDAtTb21lT3JnVW5pdDEfMB0GA1UEAwwWc2F0bDAxLmxhYi5j + dWRhbmV0Lm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAN6uVBDW + Qp941uKRabwV2vtuGftmply2vA4cFbw0k1vAosRPE130ULNkhm8wK7qwu+bqNKi6 + 2okdfIBAm+u7Tw0NYufcMDyEjngqMCG54w3OGbLW4ZQIefIliKw/kdUhxvKvzxNT + DP7kK+uwnunW6l79G8azAtDddhNa0wafkMHpQYeU/Y650oto+c3qjETTIC3M2QVr + N7D5niWAmrLvHyR/2S/W2Oa2929PmENMuBUsEgcoYthagDjse7G2hp8Gll4cCVh4 + LysBzTUFUBC5NRx7eqf0iCycexXcAZunHQfNCwHJNXdUFc61pBeFmTtslxTMdNmV + FZ9SV5++ESHK75RYlhnfHGSiByUtstDXGT1vCUkipmzI4p3zVRPj5whlg0i15hdz + R58q9uftSYOwr5od3UIXJQM2fNlip7rIy85av2le3TkwmjObQqP7nNmacWarwQfs + dTVcslv+CI7vXtdWGqcRMo0db+QQN8tSGOfSTGDqt4lmwk7ESg48UocOAhoOEzIY + pEQnBLtSxEDBsFEu4vwNlq5DanSD3vuxwyDC6T8AdxTQvVclM+x+CsANaVzrLM6W + e7KJOYfe7J/dM28OWOTdLSSJ+L9gYxtdXme9uYLmQptce7KnuwoHozRY/aP0hFN/ + 2hMNEYlPFkUGvvb+hKzziagh+Rky9IP8/dWjAgMBAAGjggFVMIIBUTAMBgNVHRME + BTADAQH/MAsGA1UdDwQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH + AwIwEQYJYIZIAYb4QgEBBAQDAgJEMDUGCWCGSAGG+EIBDQQoFiZLYXRlbGxvIFNT + TCBUb29sIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUepliLqnv9s1n + TKkZeHtZA8eNPdgwgasGA1UdIwSBozCBoKGBh6SBhDCBgTELMAkGA1UEBhMCVVMx + FzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMRAwDgYDVQQHDAdSYWxlaWdoMRAwDgYD + VQQKDAdLYXRlbGxvMRQwEgYDVQQLDAtTb21lT3JnVW5pdDEfMB0GA1UEAwwWc2F0 + bDAxLmxhYi5jdWRhbmV0Lm9yZ4IUIacbh/WYmORFpiddBg25g/W4jlQwDQYJKoZI + hvcNAQELBQADggIBAGeB30OcUUdVO5gTWnNorYHhkhxuWri1ThCQqzBlaUKFQ35I + 120CcZ2rc22Q/kQqLHfB3bvGEe379Bw/6kRKOZVf7cd34yqqORSQxWqkPSQIjKfw + K5kXRn2aCQSh/Ohfoowz0ow5HngTzShiL+KlPuix6Zuz2R+1uZwpDZifUiUVydnx + Ru0XiZBFX+u4zUAPJHYC94t14Omvpo8J1qbfC9Wc9Anlx4I7PXqJ8rygSrxU8him + QhExutff7TC4yA2nPVLvGJV96eAl9nQdiux8EKhvJ4y66/e4IqFNVYtiyCpH2epv + rLRgWCmpBDONHL2LM82HJZXKtU8rwRUCsKnt2pjRR2BTEkCH5GNQasLI76TEorV3 + Z+d8LWBHZgmsbR2BcCv7eSd29NAMoRalCx1emEJm/yizzXm3ADlH9PWR9bwuwMxx + IRcB1EsKXpWlzaJY3TfJ8p1cUqcx3Dm8majSb9nkTkbH5XuVf2hipz1URpYlcDjk + DUcFWbs+2BqLU9Gg4mZJMHc6XDsduv+sjaA5D8w5bYZfgqLA39/LPKPQCQ2vcYIm + CviivD2WpDXdkp2BFNV3lX/H6Vnlc2Mc6utUxMem2Ip7bBISIGayZKHyjOsAU+hh + T/giRQe8jtPFNh+pnEWBo83XMalqn0V5C7m/Jwb28bc7IPtPPlRcfYsnhY5y + -----END CERTIFICATE----- + +EOF + if [ -x /usr/bin/curl ]; then + /usr/bin/curl -o /dev/null -H 'Content-Type: text/plain' --fail --data 'Host initial configuration failed, please see the registration log for more details.' --noproxy \* --cacert $SSL_CA_CERT --silent 'https://satl01.lab.cudanet.org/unattended/failed?token=10fd076d-92f5-4f80-83ad-3cb330ac4069' + elif [ -x /usr/bin/wget ]; then + /usr/bin/wget -q -O /dev/null --method POST --header 'Content-Type: text/plain' --body-data='Host initial configuration failed, please see the registration log for more details.' --no-proxy --ca-certificate $SSL_CA_CERT 'https://satl01.lab.cudanet.org/unattended/failed?token=10fd076d-92f5-4f80-83ad-3cb330ac4069' + else + wget -q -O /dev/null --header 'Content-Type: text/plain' 'https://satl01.lab.cudanet.org/unattended/failed?token=10fd076d-92f5-4f80-83ad-3cb330ac4069' + fi + FINAL_STATUS=$? + + if [ -x "$(command -v subscription-manager)" ] ; then + subscription-manager facts --update + SUB_MAN_STATUS=$? + + if [ $FINAL_STATUS -eq 0 ]; then + FINAL_STATUS=$SUB_MAN_STATUS + fi + fi + + (exit $FINAL_STATUS); + exit 1 +} + +set +e +trap 'exit_and_cancel_build' ERR + + + + +# Select package manager for the OS (sets the $PKG_MANAGER* variables) +if [ -z "$PKG_MANAGER" ]; then + if [ -f /etc/os-release ] ; then + . /etc/os-release + fi + + if [ "${NAME%.*}" = 'FreeBSD' ]; then + PKG_MANAGER='pkg' + PKG_MANAGER_INSTALL="${PKG_MANAGER} install -y" + PKG_MANAGER_REMOVE="${PKG_MANAGER} delete -y" + PKG_MANAGER_UPGRADE="${PKG_MANAGER} install -y" + elif [ -f /etc/fedora-release -o -f /etc/redhat-release -o -f /etc/amazon-linux-release -o -f /etc/system-release ]; then + PKG_MANAGER='dnf' + if [ -f /etc/redhat-release -a "${VERSION_ID%.*}" -le 7 ]; then + PKG_MANAGER='yum' + elif [ -f /etc/system-release ]; then + PKG_MANAGER='yum' + fi + PKG_MANAGER_INSTALL="${PKG_MANAGER} install -y" + PKG_MANAGER_REMOVE="${PKG_MANAGER} remove -y" + PKG_MANAGER_UPGRADE="${PKG_MANAGER} upgrade -y" + elif [ -f /etc/debian_version ]; then + PKG_MANAGER='apt-get' + PKG_MANAGER_INSTALL="${PKG_MANAGER} -o 'Dpkg::Options::=--force-confdef' -o 'Dpkg::Options::=--force-confold' install -y" + PKG_MANAGER_REMOVE="${PKG_MANAGER} -o 'Dpkg::Options::=--force-confdef' -o 'Dpkg::Options::=--force-confold' remove -y" + PKG_MANAGER_UPGRADE="${PKG_MANAGER} -o 'Dpkg::Options::=--force-confdef' -o 'Dpkg::Options::=--force-confold' -o APT::Get::Upgrade-Allow-New='true' upgrade -y" + elif [ -f /etc/arch-release ]; then + PKG_MANAGER='pacman' + PKG_MANAGER_INSTALL="${PKG_MANAGER} --noconfirm -S" + PKG_MANAGER_REMOVE="${PKG_MANAGER} --noconfirm -R" + PKG_MANAGER_UPGRADE="${PKG_MANAGER} --noconfirm -S" + elif [ x$ID = xopensuse-tumbleweed -o x$ID = xsles ]; then + PKG_MANAGER='zypper' + PKG_MANAGER_INSTALL="${PKG_MANAGER} --non-interactive install --auto-agree-with-licenses" + PKG_MANAGER_REMOVE="${PKG_MANAGER} --non-interactive remove" + PKG_MANAGER_UPGRADE="${PKG_MANAGER} --non-interactive update" + fi +fi + + + + +user_exists=false +getent passwd root >/dev/null 2>&1 && user_exists=true + + +if $user_exists; then + + + mkdir -p ~root/.ssh + + cat << EOF >> ~root/.ssh/authorized_keys +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHN+jdbQeYICJHV6iVLv/dj+oqeAapadgAUM8y7ZRRvS foreman-proxy@satl01.lab.cudanet.org +EOF + + chmod 0700 ~root/.ssh + chmod 0600 ~root/.ssh/authorized_keys + chown -R root: ~root/.ssh + + # Restore SELinux context with restorecon, if it's available: + command -v restorecon && restorecon -RvF ~root/.ssh || true + + + +else + echo 'The remote_execution_ssh_user does not exist and remote_execution_create_user is not set to true. remote_execution_ssh_keys snippet will not install keys' +fi + + + + +echo '#' +echo '# Installing Insights client' +echo '#' + +yum install -y insights-client +insights-client --register < /dev/null + + + + +if command -v subscription-manager &>/dev/null; then + echo "Refreshing subscription data" + subscription-manager refresh +fi + + + + + +# Call home to exit build mode + +trap - ERR +SSL_CA_CERT=$(mktemp) +cat << EOF > $SSL_CA_CERT +-----BEGIN CERTIFICATE----- +MIIG6TCCBNGgAwIBAgIUIacbh/WYmORFpiddBg25g/W4jlQwDQYJKoZIhvcNAQEL +BQAwgYExCzAJBgNVBAYTAlVTMRcwFQYDVQQIDA5Ob3J0aCBDYXJvbGluYTEQMA4G +A1UEBwwHUmFsZWlnaDEQMA4GA1UECgwHS2F0ZWxsbzEUMBIGA1UECwwLU29tZU9y +Z1VuaXQxHzAdBgNVBAMMFnNhdGwwMS5sYWIuY3VkYW5ldC5vcmcwHhcNMjYwOTEx +MTc1NTA1WhcNMzgwMTE3MTc1NTA1WjCBgTELMAkGA1UEBhMCVVMxFzAVBgNVBAgM +Dk5vcnRoIENhcm9saW5hMRAwDgYDVQQHDAdSYWxlaWdoMRAwDgYDVQQKDAdLYXRl +bGxvMRQwEgYDVQQLDAtTb21lT3JnVW5pdDEfMB0GA1UEAwwWc2F0bDAxLmxhYi5j +dWRhbmV0Lm9yZzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAN6uVBDW +Qp941uKRabwV2vtuGftmply2vA4cFbw0k1vAosRPE130ULNkhm8wK7qwu+bqNKi6 +2okdfIBAm+u7Tw0NYufcMDyEjngqMCG54w3OGbLW4ZQIefIliKw/kdUhxvKvzxNT +DP7kK+uwnunW6l79G8azAtDddhNa0wafkMHpQYeU/Y650oto+c3qjETTIC3M2QVr +N7D5niWAmrLvHyR/2S/W2Oa2929PmENMuBUsEgcoYthagDjse7G2hp8Gll4cCVh4 +LysBzTUFUBC5NRx7eqf0iCycexXcAZunHQfNCwHJNXdUFc61pBeFmTtslxTMdNmV +FZ9SV5++ESHK75RYlhnfHGSiByUtstDXGT1vCUkipmzI4p3zVRPj5whlg0i15hdz +R58q9uftSYOwr5od3UIXJQM2fNlip7rIy85av2le3TkwmjObQqP7nNmacWarwQfs +dTVcslv+CI7vXtdWGqcRMo0db+QQN8tSGOfSTGDqt4lmwk7ESg48UocOAhoOEzIY +pEQnBLtSxEDBsFEu4vwNlq5DanSD3vuxwyDC6T8AdxTQvVclM+x+CsANaVzrLM6W +e7KJOYfe7J/dM28OWOTdLSSJ+L9gYxtdXme9uYLmQptce7KnuwoHozRY/aP0hFN/ +2hMNEYlPFkUGvvb+hKzziagh+Rky9IP8/dWjAgMBAAGjggFVMIIBUTAMBgNVHRME +BTADAQH/MAsGA1UdDwQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH +AwIwEQYJYIZIAYb4QgEBBAQDAgJEMDUGCWCGSAGG+EIBDQQoFiZLYXRlbGxvIFNT +TCBUb29sIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUepliLqnv9s1n +TKkZeHtZA8eNPdgwgasGA1UdIwSBozCBoKGBh6SBhDCBgTELMAkGA1UEBhMCVVMx +FzAVBgNVBAgMDk5vcnRoIENhcm9saW5hMRAwDgYDVQQHDAdSYWxlaWdoMRAwDgYD +VQQKDAdLYXRlbGxvMRQwEgYDVQQLDAtTb21lT3JnVW5pdDEfMB0GA1UEAwwWc2F0 +bDAxLmxhYi5jdWRhbmV0Lm9yZ4IUIacbh/WYmORFpiddBg25g/W4jlQwDQYJKoZI +hvcNAQELBQADggIBAGeB30OcUUdVO5gTWnNorYHhkhxuWri1ThCQqzBlaUKFQ35I +120CcZ2rc22Q/kQqLHfB3bvGEe379Bw/6kRKOZVf7cd34yqqORSQxWqkPSQIjKfw +K5kXRn2aCQSh/Ohfoowz0ow5HngTzShiL+KlPuix6Zuz2R+1uZwpDZifUiUVydnx +Ru0XiZBFX+u4zUAPJHYC94t14Omvpo8J1qbfC9Wc9Anlx4I7PXqJ8rygSrxU8him +QhExutff7TC4yA2nPVLvGJV96eAl9nQdiux8EKhvJ4y66/e4IqFNVYtiyCpH2epv +rLRgWCmpBDONHL2LM82HJZXKtU8rwRUCsKnt2pjRR2BTEkCH5GNQasLI76TEorV3 +Z+d8LWBHZgmsbR2BcCv7eSd29NAMoRalCx1emEJm/yizzXm3ADlH9PWR9bwuwMxx +IRcB1EsKXpWlzaJY3TfJ8p1cUqcx3Dm8majSb9nkTkbH5XuVf2hipz1URpYlcDjk +DUcFWbs+2BqLU9Gg4mZJMHc6XDsduv+sjaA5D8w5bYZfgqLA39/LPKPQCQ2vcYIm +CviivD2WpDXdkp2BFNV3lX/H6Vnlc2Mc6utUxMem2Ip7bBISIGayZKHyjOsAU+hh +T/giRQe8jtPFNh+pnEWBo83XMalqn0V5C7m/Jwb28bc7IPtPPlRcfYsnhY5y +-----END CERTIFICATE----- + +EOF +if [ -x /usr/bin/curl ]; then + /usr/bin/curl -o /dev/null -H 'Content-Type: text/plain' --fail --noproxy \* --cacert $SSL_CA_CERT --silent 'https://satl01.lab.cudanet.org/unattended/built?token=10fd076d-92f5-4f80-83ad-3cb330ac4069' +elif [ -x /usr/bin/wget ]; then + /usr/bin/wget -q -O /dev/null --method POST --header 'Content-Type: text/plain' --no-proxy --ca-certificate $SSL_CA_CERT 'https://satl01.lab.cudanet.org/unattended/built?token=10fd076d-92f5-4f80-83ad-3cb330ac4069' +else + wget -q -O /dev/null --header 'Content-Type: text/plain' 'https://satl01.lab.cudanet.org/unattended/built?token=10fd076d-92f5-4f80-83ad-3cb330ac4069' +fi +FINAL_STATUS=$? + +if [ -x "$(command -v subscription-manager)" ] ; then + subscription-manager facts --update + SUB_MAN_STATUS=$? + + if [ $FINAL_STATUS -eq 0 ]; then + FINAL_STATUS=$SUB_MAN_STATUS + fi +fi + +(exit $FINAL_STATUS); + + +if [[ $? == 0 ]] ; then + echo "Host [control-plane.k8s.lab.cudanet.org] successfully configured." +else + echo "ERROR: Host [control-plane.k8s.lab.cudanet.org] configured, but failed to report status to Foreman." >&2 + exit 1 +fi + +subscription-manager repos + +exit 0 diff --git a/teleport/README.md b/teleport/README.md new file mode 100644 index 0000000..b09917b --- /dev/null +++ b/teleport/README.md @@ -0,0 +1 @@ +no AI here. literally just followed the instructions https://goteleport.com/docs/installation/self-hosted/helm-deployments/kubernetes-cluster/ diff --git a/teleport/certificate.yaml b/teleport/certificate.yaml new file mode 100644 index 0000000..c5d30e3 --- /dev/null +++ b/teleport/certificate.yaml @@ -0,0 +1,19 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + annotations: + name: letsencrypt + namespace: teleport-cluster +spec: + commonName: 'teleport.k8s.lab.cudanet.org' + dnsNames: + - 'teleport.k8s.lab.cudanet.org' + duration: 2160h0m0s + issuerRef: + kind: ClusterIssuer + name: letsencrypt + renewBefore: 360h0m0s + secretName: letsencrypt + usages: + - server auth + - client auth diff --git a/teleport/ingress.yaml b/teleport/ingress.yaml new file mode 100644 index 0000000..29da1ff --- /dev/null +++ b/teleport/ingress.yaml @@ -0,0 +1,22 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: teleport-ingress + namespace: teleport-cluster + annotations: +spec: + tls: + - hosts: + - teleport.k8s.lab.cudanet.org + secretName: letsencrypt + rules: + - host: teleport.k8s.lab.cudanet.org + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: teleport-cluster + port: + number: 443 diff --git a/teleport/ingressroute.yaml b/teleport/ingressroute.yaml new file mode 100644 index 0000000..2f38134 --- /dev/null +++ b/teleport/ingressroute.yaml @@ -0,0 +1,26 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: ServersTransport +metadata: + name: teleport-backend-transport + namespace: teleport-cluster +spec: + insecureSkipVerify: true +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: teleport-web + namespace: teleport-cluster +spec: + entryPoints: + - websecure + routes: + - match: Host(`teleport.k8s.lab.cudanet.org`) + kind: Rule + services: + - name: teleport-cluster + port: 443 + scheme: https # Crucial if Teleport expects HTTPS internally + tls: + secretName: letsencrypt diff --git a/teleport/middlewares.yaml b/teleport/middlewares.yaml new file mode 100644 index 0000000..b99bb0b --- /dev/null +++ b/teleport/middlewares.yaml @@ -0,0 +1,10 @@ +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: teleport-headers + namespace: teleport-cluster +spec: + headers: + customRequestHeaders: + X-Forwarded-Proto: "https" + X-Forwarded-Ssl: "on" diff --git a/teleport/teleport-cluster-values.yaml b/teleport/teleport-cluster-values.yaml new file mode 100644 index 0000000..af53fdf --- /dev/null +++ b/teleport/teleport-cluster-values.yaml @@ -0,0 +1,3 @@ +clusterName: clusterName +proxyListenerMode: multiplex +acme: false diff --git a/traefik/README.md b/traefik/README.md new file mode 100644 index 0000000..6848829 --- /dev/null +++ b/traefik/README.md @@ -0,0 +1,3 @@ +I did rely on AI to help deploy Traefik since it had been several years since the last time I'd done so. Traefik has a lot of "knobs to fiddle with" when deploying and I wanted the simplest, most straightforward way to handle it. Specifically, I wanted Openshift wildcard route style ingress eg; my-app.k8s.lab.cudanet.org, vs. the other way Traefik is often configured with apps as subpaths, eg; traefik.k8s.lab.cudanet.org/my-app. Not what I wanted. + +Once you meet the requirements for Traefik - ie; need a LoadBalancer provider like MetalLB. Need CSI set up to provision PVCs, Traefik is pretty straightforward. You do also have to create your wildcard DNS entry once your LB is provisioned though. diff --git a/traefik/traefik-values.yaml b/traefik/traefik-values.yaml new file mode 100644 index 0000000..8e3916b --- /dev/null +++ b/traefik/traefik-values.yaml @@ -0,0 +1,43 @@ +# Enable API & Dashboard +api: + dashboard: true + insecure: true + +# Define ports +ports: + web: + port: 8000 + exposedPort: 80 + websecure: + port: 8443 + exposedPort: 443 + +# Enable SafeNaming and allow cross-namespace routing +providers: + kubernetesCRD: + allowCrossNamespace: true + +# If you want to enable the dashboard via IngressRoute +ingressRoute: + dashboard: + enabled: true + +# Traefik static arguments for HTTP->HTTPS redirect & Let's Encrypt DNS-01 (Cloudflare) +additionalArguments: + - "--providers.kubernetescrd.safenaming=true" + - "--entrypoints.web.http.redirections.entrypoint.to=websecure" + - "--entrypoints.web.http.redirections.entrypoint.scheme=https" + - "--certificatesresolvers.letsencrypt.acme.dnschallenge=true" + - "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare" + - "--certificatesresolvers.letsencrypt.acme.email=admin@cudanet.org" + - "--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json" + +env: + - name: CF_DNS_API_TOKEN + value: "" + +# Match existing 128Mi size to satisfy immutability checks +persistence: + enabled: true + path: /data + size: 1Gi diff --git a/traefik/wildcard.yaml b/traefik/wildcard.yaml new file mode 100644 index 0000000..39b5b6b --- /dev/null +++ b/traefik/wildcard.yaml @@ -0,0 +1,20 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: traefik-dashboard-wildcard + namespace: traefik +spec: + entryPoints: + - websecure + routes: + - match: Host(`traefik.k8s.lab.cudanet.org`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`)) + kind: Rule + services: + - name: api@internal + kind: TraefikService + tls: + certResolver: letsencrypt + domains: + - main: "*.k8s.lab.cudanet.org" + sans: + - "k8s.lab.cudanet.org"