From 7a7bc34ae3fec63221a031151b3042030d2e311a Mon Sep 17 00:00:00 2001 From: viktorbarzin Date: Sun, 7 Feb 2021 23:45:55 +0000 Subject: [PATCH] initial --- .gitignore | 35 + idrac-power-cycle.sh | 11 + main.tf | 164 ++ migrate_tfstate.txt | 4 + modules/create-vm/main.tf | 153 ++ .../bind/deployment-factory/main.tf | 85 + modules/kubernetes/bind/main.tf | 72 + .../kubernetes/bind/service-factory/main.tf | 28 + modules/kubernetes/bind/variables.tf | 71 + modules/kubernetes/blog/main.tf | 130 ++ modules/kubernetes/dnscrypt/main.tf | 92 + modules/kubernetes/f1-stream/main.tf | 113 + modules/kubernetes/hackmd/main.tf | 168 ++ modules/kubernetes/idrac-power-cycle.sh | 12 + modules/kubernetes/k8s-dashboard/main.tf | 95 + modules/kubernetes/kms/main.tf | 209 ++ modules/kubernetes/kms/variables.tf | 68 + modules/kubernetes/mailserver/main.tf | 65 + modules/kubernetes/mailserver/variables.tf | 118 + modules/kubernetes/main.tf | 133 ++ modules/kubernetes/metallb/main.tf | 21 + .../monitoring/grafana_chart_values.yaml | 2051 +++++++++++++++++ modules/kubernetes/monitoring/main.tf | 148 ++ .../monitoring/prometheus_chart_values.tpl | 176 ++ modules/kubernetes/openid_help_page/main.tf | 112 + modules/kubernetes/pihole/main.tf | 200 ++ modules/kubernetes/privatebin/main.tf | 144 ++ modules/kubernetes/setup_tls_secret/main.tf | 16 + modules/kubernetes/versions.tf | 8 + modules/kubernetes/webhook_handler/main.tf | 146 ++ playbook | 1 + versions.tf | 8 + 32 files changed, 4857 insertions(+) create mode 100644 .gitignore create mode 100644 idrac-power-cycle.sh create mode 100644 main.tf create mode 100644 migrate_tfstate.txt create mode 100644 modules/create-vm/main.tf create mode 100644 modules/kubernetes/bind/deployment-factory/main.tf create mode 100644 modules/kubernetes/bind/main.tf create mode 100644 modules/kubernetes/bind/service-factory/main.tf create mode 100644 modules/kubernetes/bind/variables.tf create mode 100644 modules/kubernetes/blog/main.tf create mode 100644 modules/kubernetes/dnscrypt/main.tf create mode 100644 modules/kubernetes/f1-stream/main.tf create mode 100644 modules/kubernetes/hackmd/main.tf create mode 100644 modules/kubernetes/idrac-power-cycle.sh create mode 100644 modules/kubernetes/k8s-dashboard/main.tf create mode 100644 modules/kubernetes/kms/main.tf create mode 100644 modules/kubernetes/kms/variables.tf create mode 100644 modules/kubernetes/mailserver/main.tf create mode 100644 modules/kubernetes/mailserver/variables.tf create mode 100644 modules/kubernetes/main.tf create mode 100644 modules/kubernetes/metallb/main.tf create mode 100644 modules/kubernetes/monitoring/grafana_chart_values.yaml create mode 100644 modules/kubernetes/monitoring/main.tf create mode 100644 modules/kubernetes/monitoring/prometheus_chart_values.tpl create mode 100644 modules/kubernetes/openid_help_page/main.tf create mode 100644 modules/kubernetes/pihole/main.tf create mode 100644 modules/kubernetes/privatebin/main.tf create mode 100644 modules/kubernetes/setup_tls_secret/main.tf create mode 100644 modules/kubernetes/versions.tf create mode 100644 modules/kubernetes/webhook_handler/main.tf create mode 120000 playbook create mode 100644 versions.tf diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..856495fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/terraform +# Edit at https://www.toptal.com/developers/gitignore?templates=terraform + +### Terraform ### +# Local .terraform directories +**/.terraform/* + +# .tfstate files +*.tfstate +*.tfstate.* + +# Crash log files +crash.log + +# Ignore any .tfvars files that are generated automatically for each Terraform run. Most +# .tfvars files are managed as part of configuration and so should be included in +# version control. +# +# example.tfvars +*.tfvars + +# Ignore override files as they are usually used to override resources locally and so +# are not checked in +override.tf +override.tf.json +*_override.tf +*_override.tf.json + +# Include override files you do wish to add to version control using negated pattern +# !example_override.tf + +# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan +# example: *tfplan* + diff --git a/idrac-power-cycle.sh b/idrac-power-cycle.sh new file mode 100644 index 00000000..6f5a342f --- /dev/null +++ b/idrac-power-cycle.sh @@ -0,0 +1,11 @@ +#!/bin/bash + + +# Get power supply on outside system voltage +curl -s -k -u root:calvin -H"Content-type: application/json" -X GET https://idrac/redfish/v1/Chassis/System.Embedded.1/Power/PowerSupplies/PSU.Slot.2 |jq .LineInputVoltage + +# Power off +curl -s -k -u root:calvin -X POST -d '{"Action": "Reset", "ResetType": "GracefulShutdown"}' -H"Content-type: application/json" https://idrac/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset + +# Power on +curl -s -k -u root:calvin -X POST -d '{"Action": "Reset", "ResetType": "On"}' -H"Content-type: application/json" https://idrac/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset diff --git a/main.tf b/main.tf new file mode 100644 index 00000000..b6e443bf --- /dev/null +++ b/main.tf @@ -0,0 +1,164 @@ +variable "vsphere_password" {} +variable "vsphere_user" {} +variable "vsphere_server" {} +variable "tls_secret_name" {} +variable "tls_crt" {} +variable "tls_key" {} +variable "client_certificate_secret_name" {} +variable "mailserver_accounts" {} +variable "mailserver_aliases" {} +variable "pihole_web_password" {} +variable "webhook_handler_secret" {} +variable "wireguard_wg_0_conf" {} +variable "wireguard_firewall_sh" {} +variable "hackmd_db_password" {} +variable "bind_db_viktorbarzin_me" {} +variable "bind_db_viktorbarzin_lan" {} +variable "bind_named_conf_options" {} +variable "alertmanager_account_password" {} +variable "wireguard_wg_0_key" {} + +variable "ansible_prefix" { + default = "ANSIBLE_VAULT_PASSWORD_FILE=~/.ansible/vault_pass.txt ansible-playbook -i playbook/hosts.yaml playbook/linux.yml -t linux/initial_setup" + description = "Provisioner command" +} +provider "kubernetes" { + config_path = "~/.kube/config" +} + +provider "helm" { + kubernetes { + config_path = "~/.kube/config" + } +} + +# Main module to init infra from +module "pxe_server" { + source = "./modules/create-vm" + vm_name = "pxe-server" + network = "dManagementVMs" + # provisioner_command = "${var.ansible_prefix} -t linux/pxe-server/add-distro" + provisioner_command = "# no provisioner needed #" # Noop until ubuntu autoinstall is setup + + vsphere_password = var.vsphere_password + vsphere_user = var.vsphere_user + vsphere_server = var.vsphere_server + cdrom_path = "ISO/ubuntu-server-20.04.1.iso" + vm_disk_size = 50 + vm_mac_address = "00:50:56:87:4a:2d" +} + +module "k8s_master" { + source = "./modules/create-vm" + vm_name = "k8s-master" + vm_mac_address = "00:50:56:b0:a1:39" + network = "dKubernetes" + provisioner_command = "${var.ansible_prefix} -t linux/k8s/master -e hostname=k8s-master" + + vsphere_password = var.vsphere_password + vsphere_user = var.vsphere_user + vsphere_server = var.vsphere_server + vsphere_datastore = "r730-datastore" + vsphere_resource_pool = "R730" + +} +module "k8s_node1" { + source = "./modules/create-vm" + vm_name = "k8s-node1" + vm_mac_address = "00:50:56:b0:e0:c9" + network = "dKubernetes" + provisioner_command = "${var.ansible_prefix} -t linux/k8s/node -e hostname=k8s-node1 -e k8s_master='wizard@${module.k8s_master.guest_ip}'" + + vsphere_password = var.vsphere_password + vsphere_user = var.vsphere_user + vsphere_server = var.vsphere_server + vsphere_datastore = "r730-datastore" + vsphere_resource_pool = "R730" + +} + +module "k8s_node2" { + source = "./modules/create-vm" + vm_name = "k8s-node2" + vm_mac_address = "00:50:56:b0:a1:36" + network = "dKubernetes" + provisioner_command = "${var.ansible_prefix} -t linux/k8s/node -e hostname=k8s-node2 -e k8s_master='wizard@${module.k8s_master.guest_ip}'" + + vsphere_password = var.vsphere_password + vsphere_user = var.vsphere_user + vsphere_server = var.vsphere_server + vsphere_datastore = "r730-datastore" + vsphere_resource_pool = "R730" +} + +module "k8s_node3" { + source = "./modules/create-vm" + vm_name = "k8s-node3" + vm_mac_address = "00:50:56:b0:a1:37" + network = "dKubernetes" + provisioner_command = "${var.ansible_prefix} -t linux/k8s/node -e hostname=k8s-node3 -e k8s_master='wizard@${module.k8s_master.guest_ip}'" + + vsphere_password = var.vsphere_password + vsphere_user = var.vsphere_user + vsphere_server = var.vsphere_server + vsphere_datastore = "r730-datastore" + vsphere_resource_pool = "R730" +} + +module "k8s_node4" { + source = "./modules/create-vm" + vm_name = "k8s-node4" + vm_mac_address = "00:50:56:b0:a1:38" + network = "dKubernetes" + provisioner_command = "${var.ansible_prefix} -t linux/k8s/node -e hostname=k8s-node4 -e k8s_master='wizard@${module.k8s_master.guest_ip}'" + + vsphere_password = var.vsphere_password + vsphere_user = var.vsphere_user + vsphere_server = var.vsphere_server + vsphere_datastore = "r730-datastore" + vsphere_resource_pool = "R730" +} + +module "k8s_node5" { + source = "./modules/create-vm" + vm_name = "k8s-node5" + vm_mac_address = "00:50:56:b0:a1:40" + network = "dKubernetes" + provisioner_command = "${var.ansible_prefix} -t linux/k8s/node -e hostname=k8s-node5 -e k8s_master='wizard@${module.k8s_master.guest_ip}'" + + vsphere_password = var.vsphere_password + vsphere_user = var.vsphere_user + vsphere_server = var.vsphere_server + vsphere_datastore = "r730-datastore" + vsphere_resource_pool = "R730" +} + +# resource "null_resource" "test" { +# provisioner "local-exec" { +# working_dir = "/home/viktor/" +# command = "ANSIBLE_VAULT_PASSWORD_FILE=~/.ansible/vault_pass.txt ansible-playbook -i playbook/hosts.yaml playbook/linux.yml -t linux/k8s/node -e host='10.0.40.126'" +# } +# } + +module "kubernetes_cluster" { + source = "./modules/kubernetes" + + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key + client_certificate_secret_name = var.client_certificate_secret_name + mailserver_accounts = var.mailserver_accounts + mailserver_aliases = var.mailserver_aliases + pihole_web_password = var.pihole_web_password + webhook_handler_secret = var.webhook_handler_secret + wireguard_wg_0_conf = var.wireguard_wg_0_conf + wireguard_wg_0_key = var.wireguard_wg_0_key + wireguard_firewall_sh = var.wireguard_firewall_sh + hackmd_db_password = var.hackmd_db_password + + bind_db_viktorbarzin_me = var.bind_db_viktorbarzin_me + bind_db_viktorbarzin_lan = var.bind_db_viktorbarzin_lan + bind_named_conf_options = var.bind_named_conf_options + + alertmanager_account_password = var.alertmanager_account_password +} diff --git a/migrate_tfstate.txt b/migrate_tfstate.txt new file mode 100644 index 00000000..4ebc3c7e --- /dev/null +++ b/migrate_tfstate.txt @@ -0,0 +1,4 @@ +# Steps to migrate 1 .tfstate into another + +# Inside the dir to be migrated out from do: +for s in $(tf state list); do tf state mv -state-out=../../terraform.tfstate $s "module.UPPER_WORKSPACE_MODULE_NAME.$s"; done diff --git a/modules/create-vm/main.tf b/modules/create-vm/main.tf new file mode 100644 index 00000000..611648ea --- /dev/null +++ b/modules/create-vm/main.tf @@ -0,0 +1,153 @@ +variable "vsphere_user" { + default = "Administrator@viktorbarzin.lan" +} +variable "vsphere_password" {} +variable "vsphere_server" { + default = "vcenter" +} +variable "vm_name" { + default = "terraform-test" +} +variable "vm_cpus" { + type = number + default = 4 +} + +variable "vm_mem" { + type = number + default = 4096 +} + +variable "vm_guest_id" { + default = "ubuntu64Guest" +} + +variable "vm_disk_size" { + type = number + default = 64 +} + +variable "provisioner_command" { + description = "Additional provisioning commands to run" + # default = "#" + type = string +} + +variable "network" { + description = "Network to attach the vm guest to" +} + +variable "ceph_disk_size" { + type = number + default = 0 +} + +variable "cdrom_path" { + type = string + default = "" +} + +variable "vsphere_datastore" { + type = string + default = "r730-datastore" +} + +variable "vsphere_resource_pool" { + type = string + default = "R730" +} + +variable "vm_mac_address" { + type = string + default = "" +} + +provider "vsphere" { + user = var.vsphere_user + password = var.vsphere_password + vsphere_server = var.vsphere_server + + # If you have a self-signed cert + allow_unverified_ssl = true +} + +data "vsphere_datacenter" "dc" { + name = "Home" +} + +data "vsphere_datastore" "datastore" { + name = var.vsphere_datastore + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_resource_pool" "pool" { + name = var.vsphere_resource_pool + datacenter_id = data.vsphere_datacenter.dc.id +} + +data "vsphere_network" "network" { + name = var.network + datacenter_id = data.vsphere_datacenter.dc.id +} + +resource "vsphere_virtual_machine" "vm" { + name = var.vm_name + resource_pool_id = data.vsphere_resource_pool.pool.id + datastore_id = data.vsphere_datastore.datastore.id + + num_cpus = var.vm_cpus + memory = var.vm_mem + guest_id = var.vm_guest_id + + # If mac address is set create NIC with that MAC + dynamic "network_interface" { + for_each = var.vm_mac_address != "" ? [1] : [] + content { + network_id = data.vsphere_network.network.id + use_static_mac = true + mac_address = var.vm_mac_address + } + } + + # Else create a NIC with random MAC + dynamic "network_interface" { + for_each = var.vm_mac_address == "" ? [1] : [] + content { + network_id = data.vsphere_network.network.id + } + } + + disk { + label = "disk0" + size = var.vm_disk_size + } + + dynamic "disk" { + for_each = var.ceph_disk_size > 0 ? [1] : [] + content { + label = "ceph-disk0" + size = var.ceph_disk_size + unit_number = 1 + } + } + + dynamic "cdrom" { + for_each = var.cdrom_path != "" ? [1] : [] + content { + datastore_id = data.vsphere_datastore.datastore.id + path = var.cdrom_path + } + } + wait_for_guest_net_timeout = 600 + + provisioner "local-exec" { + # for_each = var.provisioner_command != "" ? [1] : [] + # content { + command = "${var.provisioner_command} -e 'host=${vsphere_virtual_machine.vm.default_ip_address}'" + # } + } +} + +output "guest_ip" { + value = vsphere_virtual_machine.vm.default_ip_address +} diff --git a/modules/kubernetes/bind/deployment-factory/main.tf b/modules/kubernetes/bind/deployment-factory/main.tf new file mode 100644 index 00000000..fbdda146 --- /dev/null +++ b/modules/kubernetes/bind/deployment-factory/main.tf @@ -0,0 +1,85 @@ +variable "named_conf_mounts" {} +variable "deployment_name" {} + +resource "kubernetes_deployment" "bind" { + metadata { + name = var.deployment_name + namespace = "bind" + labels = { + "app" = "bind" + "kubernetes.io/cluster-service" : "true" + } + } + spec { + replicas = "3" + selector { + match_labels = { + "app" = var.deployment_name + } + } + template { + metadata { + labels = { + "app" = var.deployment_name + "kubernetes.io/cluster-service" : "true" + } + } + spec { + container { + name = "bind" + image = "resystit/bind9:latest" + image_pull_policy = "IfNotPresent" + port { + container_port = 53 + protocol = "UDP" + } + volume_mount { + mount_path = "/etc/bind/named.conf" + sub_path = "named.conf" + name = "bindconf" + } + + dynamic "volume_mount" { + for_each = [for m in var.named_conf_mounts : + { + name = m.name + mount_path = m.mount_path + sub_path = m.sub_path + }] + content { + name = volume_mount.value.name + mount_path = volume_mount.value.mount_path + sub_path = volume_mount.value.sub_path + } + } + + volume_mount { + mount_path = "/etc/bind/db.viktorbarzin.me" + sub_path = "db.viktorbarzin.me" + name = "bindconf" + } + volume_mount { + mount_path = "/etc/bind/db.viktorbarzin.lan" + sub_path = "db.viktorbarzin.lan" + name = "bindconf" + } + } + container { + name = "bind-exporter" + image = "prometheuscommunity/bind-exporter:latest" + image_pull_policy = "IfNotPresent" + port { + container_port = 9119 + } + } + + volume { + name = "bindconf" + config_map { + name = "bind-configmap" + } + } + } + } + } +} diff --git a/modules/kubernetes/bind/main.tf b/modules/kubernetes/bind/main.tf new file mode 100644 index 00000000..c8b75338 --- /dev/null +++ b/modules/kubernetes/bind/main.tf @@ -0,0 +1,72 @@ +variable "db_viktorbarzin_me" {} +variable "db_viktorbarzin_lan" {} +variable "named_conf_options" {} + +resource "kubernetes_namespace" "bind" { + metadata { + name = "bind" + } +} + +resource "kubernetes_config_map" "bind_configmap" { + metadata { + name = "bind-configmap" + namespace = "bind" + } + + data = { + "db.viktorbarzin.lan" = var.db_viktorbarzin_lan + "db.viktorbarzin.me" = var.db_viktorbarzin_me + "named.conf" = var.named_conf + "named.conf.local" = var.named_conf_local + "named.conf.options" = var.named_conf_options + "public-named.conf.local" = var.public_named_conf_local + "public-named.conf.options" = var.public_named_conf_options + } +} + +module "bind-local-deployment" { + source = "./deployment-factory" + deployment_name = "bind" + named_conf_mounts = [ + { + "mount_path" = "/etc/bind/named.conf.local" + "sub_path" = "named.conf.local" + "name" = "bindconf" + }, + { + mount_path = "/etc/bind/named.conf.options" + sub_path = "named.conf.options" + name = "bindconf" + } + ] +} + +module "bind-local-service" { + source = "./service-factory" + service_name = "bind" + port = 5354 +} + +module "bind-public-deployment" { + source = "./deployment-factory" + deployment_name = "bind-public" + named_conf_mounts = [ + { + "mount_path" = "/etc/bind/named.conf.local" + "sub_path" = "public-named.conf.local" + "name" = "bindconf" + }, + { + mount_path = "/etc/bind/named.conf.options" + sub_path = "public-named.conf.options" + name = "bindconf" + } + ] +} + +module "bind-public-service" { + source = "./service-factory" + service_name = "bind-public" + port = 10053 +} diff --git a/modules/kubernetes/bind/service-factory/main.tf b/modules/kubernetes/bind/service-factory/main.tf new file mode 100644 index 00000000..d64a4a7e --- /dev/null +++ b/modules/kubernetes/bind/service-factory/main.tf @@ -0,0 +1,28 @@ +variable "service_name" {} +variable "port" {} + +resource "kubernetes_service" "bind" { + metadata { + name = var.service_name + namespace = "bind" + annotations = { + "metallb.universe.tf/allow-shared-ip" = "shared" + } + labels = { + "app" = var.service_name + } + } + spec { + type = "LoadBalancer" + external_traffic_policy = "Cluster" + selector = { + "app" = var.service_name + } + port { + name = "dns" + protocol = "UDP" + port = var.port + target_port = "53" + } + } +} diff --git a/modules/kubernetes/bind/variables.tf b/modules/kubernetes/bind/variables.tf new file mode 100644 index 00000000..519048a8 --- /dev/null +++ b/modules/kubernetes/bind/variables.tf @@ -0,0 +1,71 @@ +variable "named_conf" { + default = <How to activate windows +Open the following link and find a key for you version of windows:
+https://goo.gl/BcrPjW +
+
+Open cmd as Administrator and run the following:
+
+slmgr.vbs /ipk key_for_your_windows +
+slmgr.vbs /skms kms.viktorbarzin.me +
+ + slmgr /ato + +
+

+

If you have an evaluation windows, you need to change it to retail one. This is how:

+
+From an elevated command prompt, determine the current edition name with the command
+DISM /online /Get-CurrentEdition. +
Make note of the edition ID, an abbreviated form of the edition name. Then run +
+DISM /online /Set-Edition: /ProductKey:XXXXX-XXXXX-XXXXX-XXXXX-XXXXX /AcceptEula +
providing the edition ID and a retail product key. The server will restart +

+
+ + +

How to activate Microsoft Office

+
+ + CD \Program Files\Microsoft Office\Office16 OR CD \Program Files (x86)\Microsoft Office\Office16 + +
+ + cscript ospp.vbs /sethst:kms.viktorbarzin.me + +
+ + cscript ospp.vbs /inpkey:xxxxx-xxxxx-xxxxx-xxxxx-xxxxx + +
+where 'xxxx' is a key for your office. Some examples for office 2016 - https://www.techdee.com/microsoft-office-2016-product-key/ +
+ + cscript ospp.vbs /act + + +
+
+If you messed up activation settings reset them using +
+slmgr /upk + +
+slmgr /cpky +
+and +
+slmgr /rearm + +

Buy me a beer :P

+EOT +} diff --git a/modules/kubernetes/mailserver/main.tf b/modules/kubernetes/mailserver/main.tf new file mode 100644 index 00000000..a0bbdae6 --- /dev/null +++ b/modules/kubernetes/mailserver/main.tf @@ -0,0 +1,65 @@ +variable "mailserver_accounts" {} +variable postfix_account_aliases {} + +resource "kubernetes_namespace" "mailserver" { + metadata { + name = "mailserver" + } +} + +resource "kubernetes_config_map" "mailserver_env_config" { + metadata { + name = "mailserver.env.config" + namespace = "mailserver" + labels = { + app = "mailserver" + } + } + + data = { + DMS_DEBUG = "0" + ENABLE_CLAMAV = "0" + ENABLE_FAIL2BAN = "1" + ENABLE_FETCHMAIL = "0" + ENABLE_POSTGREY = "0" + ENABLE_SPAMASSASSIN = "0" + ENABLE_SRS = "1" + FETCHMAIL_POLL = "120" + ONE_DIR = "1" + OVERRIDE_HOSTNAME = "mail.viktorbarzin.me" + TLS_LEVEL = "intermediate" + } +} + +locals { + postfix_accounts_cf = join("\n", [for user, pass in var.mailserver_accounts : "${user}|${bcrypt(pass, 6)}"]) + # postfix_accounts_cf = join("\n", [for user, pass in var.mailserver_accounts : format("%s%s%s", user, "|{SHA512-CRYPT}$6$$", sha512(pass))]) # Does not work :/ +} + +resource "kubernetes_config_map" "mailserver_config" { + metadata { + name = "mailserver.config" + namespace = "mailserver" + + labels = { + app = "mailserver" + } + } + + data = { + # Actual mail settings + "postfix-accounts.cf" = local.postfix_accounts_cf + "postfix-main.cf" = var.postfix_cf + "postfix-virtual.cf" = var.postfix_account_aliases + + KeyTable = "mail._domainkey.viktorbarzin.me viktorbarzin.me:mail:/etc/opendkim/keys/viktorbarzin.me-mail.key\n" + SigningTable = "*@viktorbarzin.me mail._domainkey.viktorbarzin.me\n" + TrustedHosts = "127.0.0.1\nlocalhost\n" + } + # Password hashes are different each time and avoid changing secret constantly. + # Either 1.Create consistent hashes or 2.Find a way to ignore_changes on per password + lifecycle { + ignore_changes = [data["postfix-accounts.cf"]] + } + +} diff --git a/modules/kubernetes/mailserver/variables.tf b/modules/kubernetes/mailserver/variables.tf new file mode 100644 index 00000000..f20b862b --- /dev/null +++ b/modules/kubernetes/mailserver/variables.tf @@ -0,0 +1,118 @@ +variable "postfix_cf" { + default = < bind -> dnscrypt +} + +module privatebin { + source = "./privatebin" + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key +} + +module webhook_handler { + source = "./webhook_handler" + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key + webhook_secret = var.webhook_handler_secret +} + +module wireguard { + source = "./wireguard" + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key + wg_0_conf = var.wireguard_wg_0_conf + wg_0_key = var.wireguard_wg_0_key + firewall_sh = var.wireguard_firewall_sh +} diff --git a/modules/kubernetes/metallb/main.tf b/modules/kubernetes/metallb/main.tf new file mode 100644 index 00000000..10b9079f --- /dev/null +++ b/modules/kubernetes/metallb/main.tf @@ -0,0 +1,21 @@ +# Creates namespace and everythin needed +module "metallb" { + source = "colinwilson/metallb/kubernetes" + version = "0.1.5" +} + +resource "kubernetes_config_map" "config" { + metadata { + name = "config" + namespace = "metallb-system" + } + data = { + config = < name of the datasource. Required +- name: Prometheus + # datasource type. Required + type: prometheus + # access mode. proxy or direct (Server or Browser in the UI). Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + url: http://prometheus-server + # database password, if used + password: + # database user, if used + user: + # database name, if used + database: + # enable/disable basic auth + basicAuth: + # basic auth username + basicAuthUser: + # basic auth password + basicAuthPassword: + # enable/disable with credentials headers + withCredentials: + # mark as default datasource. Max one per org + isDefault: + # fields that will be converted to json and stored in json_data + #jsonData: + # graphiteVersion: \"1.1\" + # tlsAuth: true + # tlsAuthWithCACert: true + # json object of data that will be encrypted. + #secureJsonData: + # tlsCACert: \"...\" + # tlsClientCert: \"...\" + # tlsClientKey: \"...\" + version: 1 + # allow users to edit datasources from the UI. + editable: false + EOT + } + + type = "Opaque" +} + +resource "kubernetes_persistent_volume" "prometheus_grafana_pv" { + metadata { + name = "grafana-iscsi-pv" + } + spec { + capacity = { + "storage" = "2Gi" + } + access_modes = ["ReadWriteOnce"] + persistent_volume_source { + iscsi { + target_portal = "iscsi.viktorbarzin.lan:3260" + iqn = "iqn.2020-12.lan.viktorbarzin:storage:monitoring:grafana" + lun = 0 + fs_type = "ext4" + } + } + } +} +resource "kubernetes_persistent_volume_claim" "prometheus_grafana_pvc" { + metadata { + name = "grafana-iscsi-pvc" + namespace = "monitoring" + } + spec { + access_modes = ["ReadWriteOnce"] + resources { + requests = { + "storage" = "2Gi" + } + } + } +} + +resource "helm_release" "grafana" { + namespace = "monitoring" + create_namespace = true + name = "grafana" + + repository = "https://grafana.github.io/helm-charts" + chart = "grafana" + + values = [file("${path.module}/grafana_chart_values.yaml")] +} diff --git a/modules/kubernetes/monitoring/prometheus_chart_values.tpl b/modules/kubernetes/monitoring/prometheus_chart_values.tpl new file mode 100644 index 00000000..7c32778c --- /dev/null +++ b/modules/kubernetes/monitoring/prometheus_chart_values.tpl @@ -0,0 +1,176 @@ +# Helm values +# all values - https://github.com/prometheus-community/helm-charts/blob/main/charts/prometheus/values.yaml +alertmanager: + persistentVolume: + # enabled: false + existingClaim: alertmanager-iscsi-pvc + # storageClass: rook-cephfs + strategy: + type: Recreate + ingress: + enabled: "true" + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + # Enable client certificate authentication + nginx.ingress.kubernetes.io/auth-tls-verify-client: "on" + # Create the secret containing the trusted ca certificates + nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret" + tls: + - secretName: "tls-secret" + hosts: + - "alertmanager.viktorbarzin.me" + hosts: + - "alertmanager.viktorbarzin.me" +alertmanagerFiles: + alertmanager.yml: + global: + smtp_from: "alertmanager@viktorbarzin.me" + # smtp_smarthost: "smtp.viktorbarzin.me:587" + smtp_smarthost: "mailserver.mailserver.svc.cluster.local:587" + smtp_auth_username: "alertmanager@viktorbarzin.me" + smtp_auth_password: "${alertmanager_mail_pass}" + smtp_require_tls: true + templates: + - "/etc/alertmanager/template/*.tmpl" + route: + group_by: ["alertname"] + group_wait: 3s + group_interval: 5s + repeat_interval: 1h + receiver: SMTP_STARTTLS + receivers: + - name: 'SMTP_STARTTLS' + email_configs: + - to: "me@viktorbarzin.me" + send_resolved: true + tls_config: + insecure_skip_verify: true + +server: + # Enable me to delete metrics + # extraFlags: + # - "web.enable-admin-api" + persistentVolume: + # enabled: false + existingClaim: prometheus-iscsi-pvc + # storageClass: rook-cephfs + retention: "12w" # ~100GB storage + strategy: + type: Recreate + ingress: + enabled: "true" + annotations: + kubernetes.io/ingress.class: nginx + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + # Enable client certificate authentication + nginx.ingress.kubernetes.io/auth-tls-verify-client: "on" + # Create the secret containing the trusted ca certificates + nginx.ingress.kubernetes.io/auth-tls-secret: "default/ca-secret" + tls: + - secretName: "tls-secret" + hosts: + - "prometheus.viktorbarzin.me" + hosts: + - "prometheus.viktorbarzin.me" + alertmanagers: + - static_configs: + - targets: + - "prometheus-alertmanager.monitoring.svc.cluster.local" + # - "alertmanager.viktorbarzin.me" + tls_config: + insecure_skip_verify: true + +serverFiles: + # prometheus.yml: + # alertingaaa: + # alertmanagers: + # - static_configs: + # targets: "alertmanager.viktorbarzin.lan" + alerting_rules.yml: + groups: + - name: NodeDown + rules: + - alert: NodeDown + expr: up{job="kubernetes-nodes"} == 0 + for: 1m + labels: + severity: page + annotations: + summary: Node down. + - name: NodeHighCPUUsage + rules: + - alert: NodeHighCPUUsage + expr: node_load1 > 2 + # for: 10m + for: 1m # DEBUG + labels: + severity: page + annotations: + summary: High CPU usage on node. + # - name: PodStuckNotReady + # rules: + # - alert: PodStuckNotReady + # expr: kube_pod_status_ready{condition="true"} == 0 + # for: 5m + # labels: + # severity: page + # annotations: + # summary: Pod stuck not ready. + - name: ReadyPodsInDeploymentLessThanSpec + rules: + - alert: ReadyPodsInDeploymentLessThanSpec + expr: kube_deployment_status_replicas_available - on(namespace, deployment) kube_deployment_spec_replicas < 0 + for: 10m + labels: + severity: page + annotations: + summary: Number of ready pods in deployment is less than what is defined in spec. + - name: PowerOutage + rules: + - alert: PowerOutage + expr: r730_idrac_powerSupplyCurrentInputVoltage < 200 + labels: + severity: page + annotations: + summary: Power voltage on a power supply is critically low indicating power outage. + +extraScrapeConfigs: | + - job_name: 'snmp-idrac' + static_configs: + - targets: + - "idrac.viktorbarzin.lan:161" + metrics_path: '/snmp' + params: + module: [dell_idrac] + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - source_labels: [__param_target] + target_label: instance + - target_label: __address__ + replacement: 'prometheus-snmp-exporter.monitoring.svc.cluster.local:9116' + metric_relabel_configs: + - source_labels: [ __name__ ] + target_label: '__name__' + action: replace + regex: '(.*)' + replacement: 'r730_idrac_${1}' + - job_name: 'openwrt' + static_configs: + - targets: + - "home.viktorbarzin.lan:9100" + metrics_path: '/metrics' + relabel_configs: + - source_labels: [__address__] + target_label: __param_target + - source_labels: [__param_target] + target_label: instance + - target_label: __address__ + replacement: 'home.viktorbarzin.lan:9100' + metric_relabel_configs: + - source_labels: [ __name__ ] + target_label: '__name__' + action: replace + regex: '(.*)' + replacement: 'openwrt_${1}' diff --git a/modules/kubernetes/openid_help_page/main.tf b/modules/kubernetes/openid_help_page/main.tf new file mode 100644 index 00000000..0d9c16eb --- /dev/null +++ b/modules/kubernetes/openid_help_page/main.tf @@ -0,0 +1,112 @@ +variable "tls_secret_name" {} +variable "tls_crt" {} +variable "tls_key" {} + +resource "kubernetes_namespace" "openid_help_page" { + metadata { + name = "openid-help-page" + } +} + +module "tls_secret" { + source = "../setup_tls_secret" + namespace = "openid-help-page" + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key +} + +resource "kubernetes_deployment" "openid_help_page" { + metadata { + name = "openid-help-page" + namespace = "openid-help-page" + labels = { + app = "openid-help-page" + } + } + spec { + replicas = 3 + selector { + match_labels = { + app = "openid-help-page" + } + } + template { + metadata { + labels = { + app = "openid-help-page" + } + } + spec { + container { + image = "viktorbarzin/openid-create-account-help-webpage:latest" + name = "openid-help-page" + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "250m" + memory = "50Mi" + } + } + port { + container_port = 80 + } + } + } + } + } +} + +resource "kubernetes_service" "openid_help_page" { + metadata { + name = "openid-help-page" + namespace = "openid-help-page" + } + + spec { + port { + name = "service-port" + protocol = "TCP" + port = 80 + target_port = "80" + } + + selector = { + app = "openid-help-page" + } + type = "ClusterIP" + session_affinity = "None" + } +} + +resource "kubernetes_ingress" "openid_help_page" { + metadata { + name = "openid-help-page" + namespace = "openid-help-page" + annotations = { + "kubernetes.io/ingress.class" = "nginx" + } + } + + spec { + tls { + hosts = ["kubectl.viktorbarzin.me"] + secret_name = var.tls_secret_name + } + rule { + host = "kubectl.viktorbarzin.me" + http { + path { + path = "/" + backend { + service_name = "openid-help-page" + service_port = "80" + } + } + } + } + } +} diff --git a/modules/kubernetes/pihole/main.tf b/modules/kubernetes/pihole/main.tf new file mode 100644 index 00000000..71e810df --- /dev/null +++ b/modules/kubernetes/pihole/main.tf @@ -0,0 +1,200 @@ +variable "tls_secret_name" {} +variable "tls_crt" {} +variable "tls_key" {} +variable "web_password" {} + +resource "kubernetes_namespace" "pihole" { + metadata { + name = "pihole" + } +} + +module "tls_secret" { + source = "../setup_tls_secret" + namespace = "pihole" + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key +} + + +resource "kubernetes_config_map" "external_conf" { + metadata { + name = "external-conf" + namespace = "pihole" + + labels = { + app = "pihole" + } + } + data = { + "external.conf" = "$HTTP[\"host\"] == \"pihole.viktorbarzin.me\" {\n server.document-root = \"/var/www/html/admin/\"\n}\n" + } +} + +resource "kubernetes_deployment" "pihole" { + metadata { + name = "pihole" + namespace = "pihole" + labels = { + app = "pihole" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "pihole" + } + } + template { + metadata { + labels = { + app = "pihole" + } + } + spec { + container { + image = "pihole/pihole:latest" + name = "pihole" + resources { + limits = { + cpu = "1" + memory = "1Gi" + } + requests = { + cpu = "1" + memory = "1Gi" + } + } + port { + container_port = 80 + } + env { + name = "DNS1" + value = "10.0.20.200#5354" # bind + } + env { + name = "VIRTUAL_HOST" + value = "pihole.viktorbarzin.me" + } + env { + name = "WEBPASSWORD" + value = var.web_password + } + env { + name = "TZ" + value = "Europe/Sofia" + } + volume_mount { + name = "external-conf" + mount_path = "/tmp/external.conf" + sub_path = "external.conf" + } + volume_mount { + name = "pihole-local-etc-volume" + mount_path = "/etc/pihole" + } + volume_mount { + name = "pihole-local-dnsmasq-volume" + mount_path = "/etc/dnsmasq.d" + } + } + volume { + name = "external-conf" + config_map { + name = "external-conf" + } + } + volume { + name = "pihole-local-etc-volume" + empty_dir {} # no hard dependencies on truenas which needs dns + } + volume { + name = "pihole-local-dnsmasq-volume" + empty_dir {} # no hard dependencies on truenas which needs dns + } + } + } + } +} + +resource "kubernetes_service" "pihole-dns" { + metadata { + name = "pihole-dns" + namespace = "pihole" + labels = { + "app" = "pihole" + } + annotations = { + "metallb.universe.tf/allow-shared-ip" : "shared" + } + } + + spec { + type = "LoadBalancer" + external_traffic_policy = "Cluster" + selector = { + app = "pihole" + } + port { + name = "dns-udp" + port = "53" + protocol = "UDP" + } + } +} + +resource "kubernetes_service" "pihole-web" { + metadata { + name = "pihole-web" + namespace = "pihole" + labels = { + "app" = "pihole" + } + annotations = { + "metallb.universe.tf/allow-shared-ip" : "shared" + } + } + + spec { + selector = { + app = "pihole" + } + port { + name = "dns-web" + port = "80" + } + } +} + +resource "kubernetes_ingress" "pihole" { + metadata { + name = "pihole-ingress" + namespace = "pihole" + annotations = { + "kubernetes.io/ingress.class" = "nginx" + "nginx.ingress.kubernetes.io/auth-tls-verify-client" = "on" + "nginx.ingress.kubernetes.io/auth-tls-secret" = "default/ca-secret" + } + } + + spec { + tls { + hosts = ["pihole.viktorbarzin.me"] + secret_name = var.tls_secret_name + } + rule { + host = "pihole.viktorbarzin.me" + http { + path { + path = "/" + backend { + service_name = "pihole-web" + service_port = "80" + } + } + } + } + } +} diff --git a/modules/kubernetes/privatebin/main.tf b/modules/kubernetes/privatebin/main.tf new file mode 100644 index 00000000..5386acec --- /dev/null +++ b/modules/kubernetes/privatebin/main.tf @@ -0,0 +1,144 @@ +variable "tls_secret_name" {} +variable "tls_crt" {} +variable "tls_key" {} + +resource "kubernetes_namespace" "privatebin" { + metadata { + name = "privatebin" + } +} + +module "tls_secret" { + source = "../setup_tls_secret" + namespace = "privatebin" + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key +} + +resource "kubernetes_deployment" "privatebin" { + metadata { + name = "privatebin" + namespace = "privatebin" + labels = { + app = "privatebin" + "kubernetes.io/cluster-service" = "true" + } + } + spec { + replicas = 1 + strategy { + type = "Recreate" + } + selector { + match_labels = { + app = "privatebin" + } + } + template { + metadata { + labels = { + app = "privatebin" + "kubernetes.io/cluster-service" = "true" + } + } + spec { + container { + image = "privatebin/nginx-fpm-alpine" + name = "privatebin" + image_pull_policy = "IfNotPresent" + resources { + limits = { + cpu = "1" + memory = "1Gi" + } + requests = { + cpu = "1" + memory = "1Gi" + } + } + port { + container_port = 8080 + } + volume_mount { + name = "data" + mount_path = "/srv/data" + sub_path = "data" + } + } + + volume { + name = "data" + iscsi { + target_portal = "iscsi.viktorbarzin.lan:3260" + fs_type = "ext4" + iqn = "iqn.2020-12.lan.viktorbarzin:storage:privatebin" + lun = 0 + read_only = false + } + } + } + } + } +} + +resource "kubernetes_service" "privatebin" { + metadata { + name = "privatebin" + namespace = "privatebin" + labels = { + "app" = "privatebin" + } + } + + spec { + selector = { + app = "privatebin" + } + port { + port = "80" + target_port = "8080" + } + } +} + +resource "kubernetes_ingress" "privatebin" { + metadata { + name = "privatebin-ingress" + namespace = "privatebin" + annotations = { + "kubernetes.io/ingress.class" = "nginx" + } + } + + spec { + tls { + hosts = ["privatebin.viktorbarzin.me", "pb.viktorbarzin.me"] + secret_name = var.tls_secret_name + } + rule { + host = "privatebin.viktorbarzin.me" + http { + path { + path = "/" + backend { + service_name = "privatebin" + service_port = "80" + } + } + } + } + rule { + host = "pb.viktorbarzin.me" + http { + path { + path = "/" + backend { + service_name = "privatebin" + service_port = "80" + } + } + } + } + } +} diff --git a/modules/kubernetes/setup_tls_secret/main.tf b/modules/kubernetes/setup_tls_secret/main.tf new file mode 100644 index 00000000..3ad16d9e --- /dev/null +++ b/modules/kubernetes/setup_tls_secret/main.tf @@ -0,0 +1,16 @@ +variable namespace {} +variable tls_secret_name {} +variable tls_crt {} +variable tls_key {} + +resource "kubernetes_secret" "tls_secret" { + metadata { + name = var.tls_secret_name + namespace = var.namespace + } + data = { + "tls.crt" = var.tls_crt + "tls.key" = var.tls_key + } + type = "kubernetes.io/tls" +} diff --git a/modules/kubernetes/versions.tf b/modules/kubernetes/versions.tf new file mode 100644 index 00000000..5d7c4592 --- /dev/null +++ b/modules/kubernetes/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + } + } + required_version = ">= 0.13" +} diff --git a/modules/kubernetes/webhook_handler/main.tf b/modules/kubernetes/webhook_handler/main.tf new file mode 100644 index 00000000..90e4594b --- /dev/null +++ b/modules/kubernetes/webhook_handler/main.tf @@ -0,0 +1,146 @@ + +variable "tls_secret_name" {} +variable "tls_crt" {} +variable "tls_key" {} +variable "webhook_secret" {} + +resource "kubernetes_namespace" "webhook-handler" { + metadata { + name = "webhook-handler" + } +} + +module "tls_secret" { + source = "../setup_tls_secret" + namespace = "webhook-handler" + tls_secret_name = var.tls_secret_name + tls_crt = var.tls_crt + tls_key = var.tls_key +} + +resource "kubernetes_cluster_role" "deployment_updater" { + metadata { + name = "deployment-updater" + } + + rule { + verbs = ["create", "update", "get", "patch", "list"] + api_groups = ["extensions", "apps", ""] + resources = ["deployments", "namespaces", "pods", "services"] + } +} + +resource "kubernetes_cluster_role_binding" "update_deployment_binding" { + metadata { + name = "update-deployment-binding" + } + + subject { + kind = "ServiceAccount" + name = "default" + namespace = "webhook-handler" + } + + role_ref { + api_group = "rbac.authorization.k8s.io" + kind = "ClusterRole" + name = "deployment-updater" + } +} + +resource "kubernetes_deployment" "webhook_handler" { + metadata { + name = "webhook-handler" + namespace = "webhook-handler" + labels = { + app = "webhook-handler" + } + } + spec { + replicas = 1 + selector { + match_labels = { + app = "webhook-handler" + } + } + template { + metadata { + labels = { + app = "webhook-handler" + } + } + spec { + container { + image = "viktorbarzin/webhook-handler:latest" + name = "webhook-handler" + resources { + limits = { + cpu = "0.5" + memory = "512Mi" + } + requests = { + cpu = "250m" + memory = "50Mi" + } + } + port { + container_port = 80 + } + env { + name = "WEBHOOKSECRET" + value = var.webhook_secret + } + } + } + } + } +} + +resource "kubernetes_service" "webhook_handler" { + metadata { + name = "webhook-handler" + namespace = "webhook-handler" + labels = { + "app" = "webhook-handler" + } + } + + spec { + selector = { + app = "webhook-handler" + } + port { + port = "80" + target_port = "3000" + } + } +} + +resource "kubernetes_ingress" "webhook_handler" { + metadata { + name = "webhook-handler-ingress" + namespace = "webhook-handler" + annotations = { + "kubernetes.io/ingress.class" = "nginx" + } + } + + spec { + tls { + hosts = ["webhook.viktorbarzin.me"] + secret_name = var.tls_secret_name + } + rule { + host = "webhook.viktorbarzin.me" + http { + path { + path = "/" + backend { + service_name = "webhook-handler" + service_port = "80" + } + } + } + } + } +} diff --git a/playbook b/playbook new file mode 120000 index 00000000..9f2f234b --- /dev/null +++ b/playbook @@ -0,0 +1 @@ +../playbook \ No newline at end of file diff --git a/versions.tf b/versions.tf new file mode 100644 index 00000000..5d7c4592 --- /dev/null +++ b/versions.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + } + } + required_version = ">= 0.13" +}