commit 4caa987213687b990b337518532676f5dd118050
Author: viktorbarzin
Date: Sun Feb 7 23:45:55 2021 +0000
initial
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 = <,aaaa:`.
+## Using the `hinfo` option means that some responses will be lies.
+## Unfortunately, the `hinfo` option appears to be required for Android 8+
+
+# blocked_query_response = 'refused'
+
+
+## Load-balancing strategy: 'p2' (default), 'ph', 'first' or 'random'
+
+# lb_strategy = 'p2'
+
+## Set to `true` to constantly try to estimate the latency of all the resolvers
+## and adjust the load-balancing parameters accordingly, or to `false` to disable.
+
+# lb_estimator = true
+
+
+## Log level (0-6, default: 2 - 0 is very verbose, 6 only contains fatal errors)
+
+# log_level = 2
+
+
+## log file for the application
+
+# log_file = 'dnscrypt-proxy.log'
+
+
+## Use the system logger (syslog on Unix, Event Log on Windows)
+
+use_syslog = true
+
+
+## Delay, in minutes, after which certificates are reloaded
+
+cert_refresh_delay = 240
+
+
+## DNSCrypt: Create a new, unique key for every single DNS query
+## This may improve privacy but can also have a significant impact on CPU usage
+## Only enable if you don't have a lot of network load
+
+# dnscrypt_ephemeral_keys = false
+
+
+## DoH: Disable TLS session tickets - increases privacy but also latency
+
+# tls_disable_session_tickets = false
+
+
+## DoH: Use a specific cipher suite instead of the server preference
+## 49199 = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
+## 49195 = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
+## 52392 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
+## 52393 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
+## 4865 = TLS_AES_128_GCM_SHA256
+## 4867 = TLS_CHACHA20_POLY1305_SHA256
+##
+## On non-Intel CPUs such as MIPS routers and ARM systems (Android, Raspberry Pi...),
+## the following suite improves performance.
+## This may also help on Intel CPUs running 32-bit operating systems.
+##
+## Keep tls_cipher_suite empty if you have issues fetching sources or
+## connecting to some DoH servers. Google and Cloudflare are fine with it.
+
+# tls_cipher_suite = [52392, 49199]
+
+
+## Fallback resolvers
+## These are normal, non-encrypted DNS resolvers, that will be only used
+## for one-shot queries when retrieving the initial resolvers list, and
+## only if the system DNS configuration doesn't work.
+## No user application queries will ever be leaked through these resolvers,
+## and they will not be used after IP addresses of resolvers URLs have been found.
+## They will never be used if lists have already been cached, and if stamps
+## don't include host names without IP addresses.
+## They will not be used if the configured system DNS works.
+## Resolvers supporting DNSSEC are recommended.
+##
+## People in China may need to use 114.114.114.114:53 here.
+## Other popular options include 8.8.8.8 and 1.1.1.1.
+##
+## If more than one resolver is specified, they will be tried in sequence.
+
+# fallback_resolvers = ['9.9.9.9:53', '8.8.8.8:53']
+
+
+## Always use the fallback resolver before the system DNS settings.
+
+ignore_system_dns = true
+
+
+## Maximum time (in seconds) to wait for network connectivity before
+## initializing the proxy.
+## Useful if the proxy is automatically started at boot, and network
+## connectivity is not guaranteed to be immediately available.
+## Use 0 to not test for connectivity at all (not recommended),
+## and -1 to wait as much as possible.
+
+netprobe_timeout = 60
+
+## Address and port to try initializing a connection to, just to check
+## if the network is up. It can be any address and any port, even if
+## there is nothing answering these on the other side. Just don't use
+## a local address, as the goal is to check for Internet connectivity.
+## On Windows, a datagram with a single, nul byte will be sent, only
+## when the system starts.
+## On other operating systems, the connection will be initialized
+## but nothing will be sent at all.
+
+netprobe_address = '9.9.9.9:53'
+
+
+## Offline mode - Do not use any remote encrypted servers.
+## The proxy will remain fully functional to respond to queries that
+## plugins can handle directly (forwarding, cloaking, ...)
+
+# offline_mode = false
+
+
+## Additional data to attach to outgoing queries.
+## These strings will be added as TXT records to queries.
+## Do not use, except on servers explicitly asking for extra data
+## to be present.
+## encrypted-dns-server can be configured to use this for access control
+## in the [access_control] section
+
+# query_meta = ["key1:value1", "key2:value2", "token:MySecretToken"]
+
+
+## Automatic log files rotation
+
+# Maximum log files size in MB - Set to 0 for unlimited.
+log_files_max_size = 10
+
+# How long to keep backup files, in days
+log_files_max_age = 7
+
+# Maximum log files backups to keep (or 0 to keep all backups)
+log_files_max_backups = 1
+
+
+
+#########################
+# Filters #
+#########################
+
+## Note: if you are using dnsmasq, disable the `dnssec` option in dnsmasq if you
+## configure dnscrypt-proxy to do any kind of filtering (including the filters
+## below and blacklists).
+## You can still choose resolvers that do DNSSEC validation.
+
+
+## Immediately respond to IPv6-related queries with an empty response
+## This makes things faster when there is no IPv6 connectivity, but can
+## also cause reliability issues with some stub resolvers.
+
+block_ipv6 = false
+
+
+## Immediately respond to A and AAAA queries for host names without a domain name
+
+block_unqualified = true
+
+
+## Immediately respond to queries for local zones instead of leaking them to
+## upstream resolvers (always causing errors or timeouts).
+
+# block_undelegated = true
+
+
+## TTL for synthetic responses sent when a request has been blocked (due to
+## IPv6 or blacklists).
+
+reject_ttl = 600
+
+
+
+##################################################################################
+# Route queries for specific domains to a dedicated set of servers #
+##################################################################################
+
+## See the `example-forwarding-rules.txt` file for an example
+
+# forwarding_rules = 'forwarding-rules.txt'
+
+
+
+###############################
+# Cloaking rules #
+###############################
+
+## Cloaking returns a predefined address for a specific name.
+## In addition to acting as a HOSTS file, it can also return the IP address
+## of a different name. It will also do CNAME flattening.
+##
+## See the `example-cloaking-rules.txt` file for an example
+
+# cloaking_rules = 'cloaking-rules.txt'
+
+## TTL used when serving entries in cloaking-rules.txt
+
+# cloak_ttl = 600
+
+
+###########################
+# DNS cache #
+###########################
+
+## Enable a DNS cache to reduce latency and outgoing traffic
+
+cache = true
+
+
+## Cache size
+
+cache_size = 4096
+
+
+## Minimum TTL for cached entries
+
+cache_min_ttl = 2400
+
+
+## Maximum TTL for cached entries
+
+cache_max_ttl = 86400
+
+
+## Minimum TTL for negatively cached entries
+
+cache_neg_min_ttl = 60
+
+
+## Maximum TTL for negatively cached entries
+
+cache_neg_max_ttl = 600
+
+
+
+##################################
+# Local DoH server #
+##################################
+
+[local_doh]
+
+## dnscrypt-proxy can act as a local DoH server. By doing so, web browsers
+## requiring a direct connection to a DoH server in order to enable some
+## features will enable these, without bypassing your DNS proxy.
+
+## Addresses that the local DoH server should listen to
+
+# listen_addresses = ['127.0.0.1:3000']
+
+
+## Path of the DoH URL. This is not a file, but the part after the hostname
+## in the URL. By convention, `/dns-query` is frequently chosen.
+## For each `listen_address` the complete URL to access the server will be:
+## `https://` (ex: `https://127.0.0.1/dns-query`)
+
+# path = "/dns-query"
+
+
+## Certificate file and key - Note that the certificate has to be trusted.
+## See the documentation (wiki) for more information.
+
+# cert_file = "localhost.pem"
+# cert_key_file = "localhost.pem"
+
+
+
+###############################
+# Query logging #
+###############################
+
+## Log client queries to a file
+
+[query_log]
+
+ ## Path to the query log file (absolute, or relative to the same directory as the config file)
+ ## On non-Windows systems, can be /dev/stdout to log to the standard output (also set log_files_max_size to 0)
+
+ # file = 'query.log'
+ file = '/dev/stdout'
+
+
+ ## Query log format (currently supported: tsv and ltsv)
+
+ format = 'tsv'
+
+
+ ## Do not log these query types, to reduce verbosity. Keep empty to log everything.
+
+ # ignored_qtypes = ['DNSKEY', 'NS']
+
+
+
+############################################
+# Suspicious queries logging #
+############################################
+
+## Log queries for nonexistent zones
+## These queries can reveal the presence of malware, broken/obsolete applications,
+## and devices signaling their presence to 3rd parties.
+
+[nx_log]
+
+ ## Path to the query log file (absolute, or relative to the same directory as the config file)
+
+ # file = 'nx.log'
+
+
+ ## Query log format (currently supported: tsv and ltsv)
+
+ format = 'tsv'
+
+
+
+######################################################
+# Pattern-based blocking (blacklists) #
+######################################################
+
+## Blacklists are made of one pattern per line. Example of valid patterns:
+##
+## example.com
+## =example.com
+## *sex*
+## ads.*
+## ads*.example.*
+## ads*.example[0-9]*.com
+##
+## Example blacklist files can be found at https://download.dnscrypt.info/blacklists/
+## A script to build blacklists from public feeds can be found in the
+## `utils/generate-domains-blacklists` directory of the dnscrypt-proxy source code.
+
+[blacklist]
+
+ ## Path to the file of blocking rules (absolute, or relative to the same directory as the config file)
+
+ # blacklist_file = 'blacklist.txt'
+
+
+ ## Optional path to a file logging blocked queries
+
+ # log_file = 'blocked.log'
+
+
+ ## Optional log format: tsv or ltsv (default: tsv)
+
+ # log_format = 'tsv'
+
+
+
+###########################################################
+# Pattern-based IP blocking (IP blacklists) #
+###########################################################
+
+## IP blacklists are made of one pattern per line. Example of valid patterns:
+##
+## 127.*
+## fe80:abcd:*
+## 192.168.1.4
+
+[ip_blacklist]
+
+ ## Path to the file of blocking rules (absolute, or relative to the same directory as the config file)
+
+ # blacklist_file = 'ip-blacklist.txt'
+
+
+ ## Optional path to a file logging blocked queries
+
+ # log_file = 'ip-blocked.log'
+
+
+ ## Optional log format: tsv or ltsv (default: tsv)
+
+ # log_format = 'tsv'
+
+
+
+######################################################
+# Pattern-based whitelisting (blacklists bypass) #
+######################################################
+
+## Whitelists support the same patterns as blacklists
+## If a name matches a whitelist entry, the corresponding session
+## will bypass names and IP filters.
+##
+## Time-based rules are also supported to make some websites only accessible at specific times of the day.
+
+[whitelist]
+
+ ## Path to the file of whitelisting rules (absolute, or relative to the same directory as the config file)
+
+ # whitelist_file = 'whitelist.txt'
+
+
+ ## Optional path to a file logging whitelisted queries
+
+ # log_file = 'whitelisted.log'
+
+
+ ## Optional log format: tsv or ltsv (default: tsv)
+
+ # log_format = 'tsv'
+
+
+
+##########################################
+# Time access restrictions #
+##########################################
+
+## One or more weekly schedules can be defined here.
+## Patterns in the name-based blocklist can optionally be followed with @schedule_name
+## to apply the pattern 'schedule_name' only when it matches a time range of that schedule.
+##
+## For example, the following rule in a blacklist file:
+## *.youtube.* @time-to-sleep
+## would block access to YouTube during the times defined by the 'time-to-sleep' schedule.
+##
+## {after='21:00', before= '7:00'} matches 0:00-7:00 and 21:00-0:00
+## {after= '9:00', before='18:00'} matches 9:00-18:00
+
+[schedules]
+
+ # [schedules.'time-to-sleep']
+ # mon = [{after='21:00', before='7:00'}]
+ # tue = [{after='21:00', before='7:00'}]
+ # wed = [{after='21:00', before='7:00'}]
+ # thu = [{after='21:00', before='7:00'}]
+ # fri = [{after='23:00', before='7:00'}]
+ # sat = [{after='23:00', before='7:00'}]
+ # sun = [{after='21:00', before='7:00'}]
+
+ # [schedules.'work']
+ # mon = [{after='9:00', before='18:00'}]
+ # tue = [{after='9:00', before='18:00'}]
+ # wed = [{after='9:00', before='18:00'}]
+ # thu = [{after='9:00', before='18:00'}]
+ # fri = [{after='9:00', before='17:00'}]
+
+
+
+#########################
+# Servers #
+#########################
+
+## Remote lists of available servers
+## Multiple sources can be used simultaneously, but every source
+## requires a dedicated cache file.
+##
+## Refer to the documentation for URLs of public sources.
+##
+## A prefix can be prepended to server names in order to
+## avoid collisions if different sources share the same for
+## different servers. In that case, names listed in `server_names`
+## must include the prefixes.
+##
+## If the `urls` property is missing, cache files and valid signatures
+## must already be present. This doesn't prevent these cache files from
+## expiring after `refresh_delay` hours.
+
+[sources]
+
+ ## An example of a remote source from https://github.com/DNSCrypt/dnscrypt-resolvers
+
+ [sources.'public-resolvers']
+ urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/public-resolvers.md', 'https://download.dnscrypt.info/resolvers-list/v2/public-resolvers.md']
+ cache_file = 'public-resolvers.md'
+ minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
+ prefix = ''
+
+ ## Anonymized DNS relays
+
+ [sources.'relays']
+ urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/relays.md', 'https://download.dnscrypt.info/resolvers-list/v2/relays.md']
+ cache_file = 'relays.md'
+ minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
+ refresh_delay = 72
+ prefix = ''
+
+ ## Quad9 over DNSCrypt - https://quad9.net/
+
+ # [sources.quad9-resolvers]
+ # urls = ['https://www.quad9.net/quad9-resolvers.md']
+ # minisign_key = 'RWQBphd2+f6eiAqBsvDZEBXBGHQBJfeG6G+wJPPKxCZMoEQYpmoysKUN'
+ # cache_file = 'quad9-resolvers.md'
+ # prefix = 'quad9-'
+
+ ## Another example source, with resolvers censoring some websites not appropriate for children
+ ## This is a subset of the `public-resolvers` list, so enabling both is useless
+
+ # [sources.'parental-control']
+ # urls = ['https://raw.githubusercontent.com/DNSCrypt/dnscrypt-resolvers/master/v2/parental-control.md', 'https://download.dnscrypt.info/resolvers-list/v2/parental-control.md']
+ # cache_file = 'parental-control.md'
+ # minisign_key = 'RWQf6LRCGA9i53mlYecO4IzT51TGPpvWucNSCh1CBM0QTaLn73Y7GFO3'
+
+
+
+#########################################
+# Servers with known bugs #
+#########################################
+
+[broken_implementations]
+
+# Cisco servers currently cannot handle queries larger than 1472 bytes, and don't
+# truncate reponses larger than questions as expected by the DNSCrypt protocol.
+# This prevents large responses from being received over UDP and over relays.
+#
+# The `dnsdist` server software drops client queries larger than 1500 bytes.
+# They are aware of it and are working on a fix.
+#
+# The list below enables workarounds to make non-relayed usage more reliable
+# until the servers are fixed.
+
+# fragments_blocked = ['cisco', 'cisco-ipv6', 'cisco-familyshield', 'cisco-familyshield-ipv6', 'quad9-dnscrypt-ip4-filter-alt', 'quad9-dnscrypt-ip4-filter-pri', 'quad9-dnscrypt-ip4-nofilter-alt', 'quad9-dnscrypt-ip4-nofilter-pri', 'quad9-dnscrypt-ip6-filter-alt', 'quad9-dnscrypt-ip6-filter-pri', 'quad9-dnscrypt-ip6-nofilter-alt', 'quad9-dnscrypt-ip6-nofilter-pri', 'cleanbrowsing-adult', 'cleanbrowsing-family-ipv6', 'cleanbrowsing-family', 'cleanbrowsing-security']
+
+
+
+
+#################################################################
+# Certificate-based client authentication for DoH #
+#################################################################
+
+# Use a X509 certificate to authenticate yourself when connecting to DoH servers.
+# This is only useful if you are operating your own, private DoH server(s).
+# (for DNSCrypt, see the `query_meta` feature instead)
+
+# [doh_client_x509_auth]
+
+# creds = [
+# { server_name='myserver', client_cert='client.crt', client_key='client.key' }
+# ]
+
+
+
+################################
+# Anonymized DNS #
+################################
+
+[anonymized_dns]
+
+## Routes are indirect ways to reach DNSCrypt servers.
+##
+## A route maps a server name ("server_name") to one or more relays that will be
+## used to connect to that server.
+##
+## A relay can be specified as a DNS Stamp (either a relay stamp, or a
+## DNSCrypt stamp), an IP:port, a hostname:port, or a server name.
+##
+## The following example routes "example-server-1" via `anon-example-1` or `anon-example-2`,
+## and "example-server-2" via the relay whose relay DNS stamp
+## is "sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM".
+##
+## !!! THESE ARE JUST EXAMPLES !!!
+##
+## Review the list of available relays from the "relays.md" file, and, for each
+## server you want to use, define the relays you want connections to go through.
+##
+## Carefully choose relays and servers so that they are run by different entities.
+##
+## "server_name" can also be set to "*" to define a default route, but this is not
+## recommended. If you do so, keep "server_names" short and distinct from relays.
+
+# routes = [
+# { server_name='example-server-1', via=['anon-example-1', 'anon-example-2'] },
+# { server_name='example-server-2', via=['sdns://gRIxMzcuNzQuMjIzLjIzNDo0NDM'] }
+# ]
+
+
+# skip resolvers incompatible with anonymization instead of using them directly
+
+# skip_incompatible = false
+
+
+
+
+## Optional, local, static list of additional servers
+## Mostly useful for testing your own servers.
+
+[static]
+
+ # [static.'myserver']
+ # stamp = 'sdns:AQcAAAAAAAAAAAAQMi5kbnNjcnlwdC1jZXJ0Lg'
+EOT
+}
diff --git a/modules/kubernetes/f1-stream/main.tf b/modules/kubernetes/f1-stream/main.tf
new file mode 100644
index 00000000..e2f79306
--- /dev/null
+++ b/modules/kubernetes/f1-stream/main.tf
@@ -0,0 +1,113 @@
+variable tls_secret_name {}
+variable "tls_crt" {}
+variable "tls_key" {}
+
+resource "kubernetes_namespace" "f1-stream" {
+ metadata {
+ name = "f1-stream"
+ }
+}
+
+resource "kubernetes_deployment" "f1-stream" {
+ metadata {
+ name = "f1-stream"
+ namespace = "f1-stream"
+ labels = {
+ app = "f1-stream"
+ }
+ }
+ spec {
+ replicas = 3
+ selector {
+ match_labels = {
+ app = "f1-stream"
+ }
+ }
+ template {
+ metadata {
+ labels = {
+ app = "f1-stream"
+ }
+ }
+ spec {
+ container {
+ image = "viktorbarzin/f1-stream:latest"
+ name = "f1-stream"
+ resources {
+ limits = {
+ cpu = "0.5"
+ memory = "512Mi"
+ }
+ requests = {
+ cpu = "250m"
+ memory = "512Mi"
+ }
+ }
+ port {
+ container_port = 80
+ }
+ }
+ }
+ }
+ }
+}
+
+
+resource "kubernetes_service" "f1-stream" {
+ metadata {
+ name = "f1-stream"
+ namespace = "f1-stream"
+ labels = {
+ "app" = "f1-stream"
+ }
+ }
+
+ spec {
+ selector = {
+ app = "f1-stream"
+ }
+ port {
+ port = "80"
+ }
+ }
+}
+
+module "tls_secret" {
+ source = "../setup_tls_secret"
+ namespace = "f1-stream"
+ tls_secret_name = var.tls_secret_name
+ tls_crt = var.tls_crt
+ tls_key = var.tls_key
+}
+
+
+resource "kubernetes_ingress" "f1-stream" {
+ metadata {
+ name = "f1-ingress"
+ namespace = "f1-stream"
+ annotations = {
+ "kubernetes.io/ingress.class" = "nginx"
+ "nginx.ingress.kubernetes.io/force-ssl-redirect" : "false"
+ "nginx.ingress.kubernetes.io/ssl-redirect" : "false"
+ }
+ }
+
+ spec {
+ tls {
+ hosts = ["f1.viktorbarzin.me"]
+ secret_name = var.tls_secret_name
+ }
+ rule {
+ host = "f1.viktorbarzin.me"
+ http {
+ path {
+ path = "/"
+ backend {
+ service_name = "f1-stream"
+ service_port = "80"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/kubernetes/hackmd/main.tf b/modules/kubernetes/hackmd/main.tf
new file mode 100644
index 00000000..51a7ba25
--- /dev/null
+++ b/modules/kubernetes/hackmd/main.tf
@@ -0,0 +1,168 @@
+variable "tls_secret_name" {}
+variable "tls_crt" {}
+variable "tls_key" {}
+variable "hackmd_db_password" {}
+
+resource "kubernetes_namespace" "hackmd" {
+ metadata {
+ name = "hackmd"
+ }
+}
+
+module "tls_secret" {
+ source = "../setup_tls_secret"
+ namespace = "hackmd"
+ tls_secret_name = var.tls_secret_name
+ tls_crt = var.tls_crt
+ tls_key = var.tls_key
+}
+
+resource "kubernetes_deployment" "hackmd" {
+ metadata {
+ name = "hackmd"
+ namespace = "hackmd"
+ labels = {
+ app = "hackmd"
+ "kubernetes.io/cluster-service" = "true"
+ }
+ }
+ spec {
+ replicas = 1
+ strategy {
+ type = "Recreate"
+ }
+ selector {
+ match_labels = {
+ app = "hackmd"
+ }
+ }
+ template {
+ metadata {
+ labels = {
+ app = "hackmd"
+ "kubernetes.io/cluster-service" = "true"
+ }
+ }
+ spec {
+ container {
+ image = "postgres:11.6-alpine"
+ name = "postgres"
+ image_pull_policy = "IfNotPresent"
+ env {
+ name = "POSTGRES_USER"
+ value = "codimd"
+ }
+ env {
+ name = "POSTGRES_PASSWORD"
+ value = var.hackmd_db_password
+ }
+ env {
+ name = "POSTGRES_DB"
+ value = "codimd"
+ }
+ resources {
+ limits = {
+ cpu = "1"
+ memory = "1Gi"
+ }
+ requests = {
+ cpu = "1"
+ memory = "1Gi"
+ }
+ }
+ port {
+ container_port = 80
+ }
+ volume_mount {
+ name = "data"
+ mount_path = "/var/lib/postgresql/data"
+ sub_path = "postgres"
+ }
+ }
+
+ container {
+ name = "codumd"
+ image = "nabo.codimd.dev/hackmdio/hackmd:2.0.1"
+ image_pull_policy = "IfNotPresent"
+ env {
+ name = "CMD_DB_URL"
+ value = format("%s%s%s", "postgres://codimd:", var.hackmd_db_password, "@localhost/codimd")
+ }
+ env {
+ name = "CMD_USECDN"
+ value = "false"
+ }
+ volume_mount {
+ name = "data"
+ mount_path = "/home/hackmd/app/public/uploads"
+ sub_path = "hackmd"
+ }
+ port {
+ name = "http"
+ container_port = 3000
+ protocol = "TCP"
+ }
+ }
+ volume {
+ name = "data"
+ iscsi {
+ target_portal = "iscsi.viktorbarzin.lan:3260"
+ fs_type = "ext4"
+ iqn = "iqn.2020-12.lan.viktorbarzin:storage:hackmd"
+ lun = 0
+ read_only = false
+ }
+ }
+ }
+ }
+ }
+}
+
+resource "kubernetes_service" "hackmd" {
+ metadata {
+ name = "hackmd"
+ namespace = "hackmd"
+ labels = {
+ "app" = "hackmd"
+ }
+ }
+
+ spec {
+ selector = {
+ app = "hackmd"
+ }
+ port {
+ port = "80"
+ target_port = "3000"
+ }
+ }
+}
+
+resource "kubernetes_ingress" "hackmd" {
+ metadata {
+ name = "hackmd-ingress"
+ namespace = "hackmd"
+ annotations = {
+ "kubernetes.io/ingress.class" = "nginx"
+ }
+ }
+
+ spec {
+ tls {
+ hosts = ["hackmd.viktorbarzin.me"]
+ secret_name = var.tls_secret_name
+ }
+ rule {
+ host = "hackmd.viktorbarzin.me"
+ http {
+ path {
+ path = "/"
+ backend {
+ service_name = "hackmd"
+ service_port = "80"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/modules/kubernetes/idrac-power-cycle.sh b/modules/kubernetes/idrac-power-cycle.sh
new file mode 100644
index 00000000..41b7f497
--- /dev/null
+++ b/modules/kubernetes/idrac-power-cycle.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+user="user"
+pass="pass"
+# Get power supply on outside system voltage
+curl -s -k -u $user:$pass -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 $user:$pass -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 $user:$pass -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/modules/kubernetes/k8s-dashboard/main.tf b/modules/kubernetes/k8s-dashboard/main.tf
new file mode 100644
index 00000000..67a729a3
--- /dev/null
+++ b/modules/kubernetes/k8s-dashboard/main.tf
@@ -0,0 +1,95 @@
+variable "tls_secret_name" {}
+variable "tls_crt" {}
+variable "tls_key" {}
+variable "client_certificate_secret_name" {}
+
+module "dashboard" {
+ # source = "cookielab/dashboard/kubernetes"
+ # source = "ViktorBarzin/dashboard/kubernetes"
+ # version = "0.13.0"
+ # TODO: update this once merged
+ source = "/opt/terraform-kubernetes-dashboard"
+ # insert the 1 required variable here
+ kubernetes_dashboard_csrf = "kekerino"
+ kubernetes_dashboard_deployment_args = list(
+ "--auto-generate-certificates",
+ "--token-ttl=0"
+ )
+}
+
+module "tls_secret" {
+ source = "../setup_tls_secret"
+ namespace = "kubernetes-dashboard"
+ tls_secret_name = var.tls_secret_name
+ tls_crt = var.tls_crt
+ tls_key = var.tls_key
+}
+
+# # locals {
+# # resources = split("---\n", file("${path.module}/recommended.yaml"))
+# # }
+# # resource "k8s_manifest" "kubernetes-dashboard-manifests" {
+# # count = length(local.resources) - 1
+# # # count = 2
+# # # content = local.resources[1 + count.index]
+# # # content = file("${path.module}/recommended.yaml")
+# # content = local.resources[1]
+# # depends_on = [kubernetes_namespace.kubernetes-dashboard]
+# # }
+# resource "kubectl_manifest" "kubernetes-dashboard-manifests" {
+# yaml_body = file("${path.module}/recommended.yaml")
+# force_new = true
+# depends_on = [kubernetes_namespace.kubernetes-dashboard]
+# }
+
+resource "kubernetes_ingress" "kubernetes-dashboard" {
+ metadata {
+ name = "kubernetes-dashboard"
+ namespace = "kubernetes-dashboard"
+ annotations = {
+ "kubernetes.io/ingress.class" = "nginx"
+ "nginx.ingress.kubernetes.io/backend-protocol" = "HTTPS"
+ "nginx.ingress.kubernetes.io/force-ssl-redirect" = "true"
+ "nginx.ingress.kubernetes.io/auth-tls-verify-client" = "on"
+ "nginx.ingress.kubernetes.io/auth-tls-secret" = var.client_certificate_secret_name
+ }
+ }
+
+ spec {
+ tls {
+ hosts = ["k8s.viktorbarzin.me"]
+ secret_name = var.tls_secret_name
+ }
+ rule {
+ host = "k8s.viktorbarzin.me"
+ http {
+ path {
+ path = "/"
+ backend {
+ service_name = "kubernetes-dashboard"
+ service_port = "443"
+ }
+ }
+ }
+ }
+ }
+ depends_on = [module.dashboard]
+}
+
+# Give cluster-admin permissions to dashboard
+resource "kubernetes_cluster_role_binding" "kubernetes-dashboard" {
+ metadata {
+ name = "admin-user"
+ }
+ role_ref {
+ api_group = "rbac.authorization.k8s.io"
+ kind = "ClusterRole"
+ name = "cluster-admin"
+ }
+ subject {
+ kind = "ServiceAccount"
+ name = "kubernetes-dashboard"
+ namespace = "kubernetes-dashboard"
+ }
+ depends_on = [module.dashboard]
+}
diff --git a/modules/kubernetes/kms/main.tf b/modules/kubernetes/kms/main.tf
new file mode 100644
index 00000000..8b448aa5
--- /dev/null
+++ b/modules/kubernetes/kms/main.tf
@@ -0,0 +1,209 @@
+variable "tls_secret_name" {}
+variable "tls_crt" {}
+variable "tls_key" {}
+
+
+resource "kubernetes_namespace" "kms" {
+ metadata {
+ name = "kms"
+ }
+}
+
+module "tls_secret" {
+ source = "../setup_tls_secret"
+ namespace = "kms"
+ tls_secret_name = var.tls_secret_name
+ tls_crt = var.tls_crt
+ tls_key = var.tls_key
+}
+
+resource "kubernetes_config_map" "kms-web-page" {
+ metadata {
+ name = "kms-web-page-config"
+ namespace = "kms"
+ }
+ data = {
+ "index.html" = var.index_html
+ }
+}
+
+resource "kubernetes_deployment" "kms-web-page" {
+ metadata {
+ name = "kms-web-page"
+ namespace = "kms"
+ labels = {
+ "app" = "kms-web-page"
+ "kubernetes.io/cluster-service" = "true"
+ }
+ }
+ spec {
+ replicas = 3
+ selector {
+ match_labels = {
+ "app" = "kms-web-page"
+ }
+ }
+ template {
+ metadata {
+ labels = {
+ "app" = "kms-web-page"
+ "kubernetes.io/cluster-service" = "true"
+ }
+ }
+ spec {
+ container {
+ image = "nginx"
+ name = "kms-web-page"
+ image_pull_policy = "IfNotPresent"
+ resources {
+ limits = {
+ cpu = "0.5"
+ memory = "512Mi"
+ }
+ requests = {
+ cpu = "0.5"
+ memory = "512Mi"
+ }
+ }
+ port {
+ container_port = 80
+ protocol = "TCP"
+ }
+ volume_mount {
+ name = "config"
+ mount_path = "/usr/share/nginx/html/"
+ }
+ }
+
+ volume {
+ name = "config"
+ config_map {
+ name = "kms-web-page-config"
+ items {
+ key = "index.html"
+ path = "index.html"
+ }
+ }
+ }
+ }
+ }
+ }
+ depends_on = [kubernetes_config_map.kms-web-page]
+}
+
+resource "kubernetes_service" "kms-web-page" {
+ metadata {
+ name = "kms-web-page"
+ namespace = "kms"
+ labels = {
+ "app" = "kms-web-page"
+ }
+ }
+
+ spec {
+ selector = {
+ "app" = "kms-web-page"
+ }
+ port {
+ port = "80"
+ protocol = "TCP"
+ }
+ }
+}
+
+resource "kubernetes_ingress" "kms-web-page" {
+ metadata {
+ name = "kms-web-page"
+ namespace = "kms"
+ annotations = {
+ "kubernetes.io/ingress.class" = "nginx"
+ }
+ }
+
+ spec {
+ tls {
+ hosts = ["kms.viktorbarzin.me"]
+ secret_name = var.tls_secret_name
+ }
+ rule {
+ host = "kms.viktorbarzin.me"
+ http {
+ path {
+ path = "/"
+ backend {
+ service_name = "kms-web-page"
+ service_port = "80"
+ }
+ }
+ }
+ }
+ }
+}
+
+resource "kubernetes_deployment" "windows_kms" {
+ metadata {
+ name = "kms"
+ namespace = "kms"
+ labels = {
+ app = "kms-service"
+ }
+ }
+ spec {
+ replicas = 1
+ selector {
+ match_labels = {
+ app = "kms-service"
+ }
+ }
+ template {
+ metadata {
+ labels = {
+ app = "kms-service"
+ }
+ }
+ spec {
+ container {
+ image = "kebe/vlmcsd:latest"
+ name = "windows-kms"
+ resources {
+ limits = {
+ cpu = "1"
+ memory = "512Mi"
+ }
+ requests = {
+ cpu = "1"
+ memory = "50Mi"
+ }
+ }
+ port {
+ container_port = 1688
+ }
+ }
+ }
+ }
+ }
+}
+
+resource "kubernetes_service" "windows_kms" {
+ metadata {
+ name = "windows-kms"
+ namespace = "kms"
+ labels = {
+ "app" = "windows-kms"
+ }
+ annotations = {
+ "metallb.universe.tf/allow-shared-ip" = "shared"
+ }
+ }
+
+ spec {
+ type = "LoadBalancer"
+ external_traffic_policy = "Cluster"
+ selector = {
+ "app" = "windows-kms"
+ }
+ port {
+ port = "1688"
+ }
+ }
+}
diff --git a/modules/kubernetes/kms/variables.tf b/modules/kubernetes/kms/variables.tf
new file mode 100644
index 00000000..6baa99a9
--- /dev/null
+++ b/modules/kubernetes/kms/variables.tf
@@ -0,0 +1,68 @@
+variable "index_html" {
+
+ 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:
+ #