diff --git a/terraform/k8sapps/apps.tf b/terraform/k8sapps/apps.tf index 0dc2eb3..4594e13 100644 --- a/terraform/k8sapps/apps.tf +++ b/terraform/k8sapps/apps.tf @@ -35,3 +35,47 @@ module "m3uproxy" { wireguard_private_key = var.wireguard_private_key wireguard_public_key = var.wireguard_public_key } + +module "mail" { + source = "../modules/mail" + + persistent_folder = local.persistent_folder + + primary_domain = local.primary_domain + email_accounts = local.email_accounts + + relay_smtp_host = local.scaleway_smtp_host + relay_smtp_port = 465 + relay_smtp_username = var.scaleway_project_id + relay_smtp_password = scaleway_iam_api_key.mail_app_api.secret_key + + fetch_emails_schedule = local.fetch_emails_schedule + + mail_crypt_private_key = var.mail_crypt_private_key + mail_crypt_public_key = var.mail_crypt_public_key +} + +module "gitea" { + source = "../modules/gitea" + + persistent_folder = local.persistent_folder + database_folder = local.database_folder + + backup_folder = local.gitea_backup_path + backup_retention_days = local.local_backup_retention_days + backup_cron_schedule = local.gitea_sql_backup_cron_schedule + + db_password = var.gitea_mysql_password + db_root_password = var.mysql_root_password + + tag = local.gitea_version + fqdn = local.gitea_fqdn + app_name = "A13Labs git repository" + + relay_smtp_host = local.scaleway_smtp_host + relay_smtp_port = local.scaleway_smtp_port + relay_smtp_username = var.scaleway_project_id + relay_smtp_password = scaleway_iam_api_key.mail_app_api.secret_key + + from_email = "gitea@${local.primary_domain}" +} diff --git a/terraform/k8sapps/backups.tf b/terraform/k8sapps/backups.tf new file mode 100644 index 0000000..c8f4103 --- /dev/null +++ b/terraform/k8sapps/backups.tf @@ -0,0 +1,58 @@ +module "mail_backup" { + + source = "../modules/scw_backup_job" + + namespace = module.mail.namespace + app_name = module.mail.app_name + + scaleway_organization_id = var.scaleway_organization_id + scaleway_project_id = var.scaleway_organization_id + access_key = scaleway_iam_api_key.backup_app_api.access_key + secret_key = scaleway_iam_api_key.backup_app_api.secret_key + bucket_name = scaleway_object_bucket.backup.name + + retention_days = local.local_backup_retention_days + cron_schedule = local.mail_files_backup_cron_schedule + + source_folder = module.mail.path + target_folder = local.mail_backup_path +} + +module "gitea_backup" { + source = "../modules/scw_backup_job" + + namespace = module.gitea.namespace + app_name = module.gitea.app_name + + scaleway_organization_id = var.scaleway_organization_id + scaleway_project_id = var.scaleway_organization_id + access_key = scaleway_iam_api_key.backup_app_api.access_key + secret_key = scaleway_iam_api_key.backup_app_api.secret_key + bucket_name = scaleway_object_bucket.backup.name + + retention_days = local.local_backup_retention_days + cron_schedule = local.gitea_files_backup_cron_schedule + + source_folder = module.gitea.path + target_folder = local.gitea_backup_path +} + +module "nextcloud_backup" { + + source = "../modules/scw_backup_job" + + namespace = kubernetes_namespace.nextcloud.metadata[0].name + app_name = "nextcloud" + + scaleway_organization_id = var.scaleway_organization_id + scaleway_project_id = var.scaleway_organization_id + access_key = scaleway_iam_api_key.backup_app_api.access_key + secret_key = scaleway_iam_api_key.backup_app_api.secret_key + bucket_name = scaleway_object_bucket.backup.name + + retention_days = local.local_backup_retention_days + cron_schedule = local.nextcloud_files_backup_cron_schedule + + source_folder = local.nextcloud_path + target_folder = local.nextcloud_backup_path +} diff --git a/terraform/k8sapps/config.tf b/terraform/k8sapps/config.tf index b4ce05e..7793009 100644 --- a/terraform/k8sapps/config.tf +++ b/terraform/k8sapps/config.tf @@ -14,10 +14,8 @@ locals { } } - gitea_version = "1.23.4" - gitea_fqdn = "code.${local.primary_domain}" - gitea_sql_backup_cron_schedule = "20 2 * * *" - gitea_files_backup_cron_schedule = "30 2 * * *" + gitea_version = "1.23.4" + gitea_fqdn = "code.${local.primary_domain}" nextcloud_version = "30.0.0-apache" nextcloud_fqdn = "cloud.${local.primary_domain}" @@ -51,13 +49,17 @@ locals { rustdesk_fqdn = "rustdesk.${local.primary_domain}" # backup - local_backup_retention_days = 2 - cloud_backup_retention_days = 3 + local_backup_retention_days = 2 + cloud_backup_retention_days = 3 + mail_backup_path = "${local.persistent_folder}/backup.mail" + gitea_backup_path = "${local.persistent_folder}/backup.gitea" + gitea_sql_backup_cron_schedule = "20 2 * * *" + gitea_files_backup_cron_schedule = "30 2 * * *" + mail_files_backup_cron_schedule = "0 3 * * *" # mail settings - getmail_version = "v0.0.1-6.19.07" - fetch_emails_schedule = "*/2 * * * *" - mail_files_backup_cron_schedule = "0 3 * * *" + getmail_version = "v0.0.1-6.19.07" + fetch_emails_schedule = "*/2 * * * *" # email settings scaleway_smtp_host = "smtp.tem.scw.cloud" diff --git a/terraform/k8sapps/nextcloud.tf b/terraform/k8sapps/nextcloud.tf index f374a1b..85b72ed 100644 --- a/terraform/k8sapps/nextcloud.tf +++ b/terraform/k8sapps/nextcloud.tf @@ -169,29 +169,6 @@ module "nextcloud_database" { backup_folder = local.nextcloud_backup_path } -# -# Setup rsync backup job to scaleway bucket -# -module "nextcloud_backup" { - - source = "../modules/scw_backup_job" - - namespace = kubernetes_namespace.nextcloud.metadata[0].name - app_name = "nextcloud" - - scaleway_organization_id = var.scaleway_organization_id - scaleway_project_id = var.scaleway_organization_id - access_key = scaleway_iam_api_key.backup_app_api.access_key - secret_key = scaleway_iam_api_key.backup_app_api.secret_key - bucket_name = scaleway_object_bucket.backup.name - - retention_days = local.local_backup_retention_days - cron_schedule = local.nextcloud_files_backup_cron_schedule - - source_folder = local.nextcloud_path - target_folder = local.nextcloud_backup_path -} - # # Nextcloud service, ingress, deployment and file backup job # diff --git a/terraform/modules/gitea/config.tf b/terraform/modules/gitea/config.tf new file mode 100644 index 0000000..ec522f6 --- /dev/null +++ b/terraform/modules/gitea/config.tf @@ -0,0 +1,43 @@ + +locals { + gitea_env_vars = { + APP_NAME = var.app_name + HTTP_PORT = "3000" + RUN_MODE = "prod" + LFS_START_SERVER = "true" + SSH_DOMAIN = var.fqdn + DISABLE_REGISTRATION = true + INSTALL_LOCK = "true" + DB_TYPE = "mysql" + DB_NAME = "gitea" + DB_HOST = module.gitea_database.host + SSH_LISTEN_PORT = 22 + GITEA__mailer__ENABLED = true + GITEA__mailer__FROM = var.from_email + GITEA__mailer__SMTP_ADDR = var.relay_smtp_host + GITEA__mailer__SMTP_PORT = var.relay_smtp_port + GITEA__mailer__PROTOCOL = "smtps" + } + + gitea_secrets = { + DB_USER = { + key = "username" + name = module.gitea_database.app_crendentials_name + } + DB_PASSWD = { + key = "password" + name = module.gitea_database.app_crendentials_name + } + GITEA__mailer__USER = { + key = "username" + name = kubernetes_secret.gitea_mail_credentials.metadata[0].name + } + GITEA__mailer__PASSWD = { + key = "password" + name = kubernetes_secret.gitea_mail_credentials.metadata[0].name + } + } + + gitea_path = "${var.persistent_folder}/gitea" + gitea_db_path = "${var.database_folder}/mysql.gitea" +} diff --git a/terraform/k8sapps/gitea.tf b/terraform/modules/gitea/main.tf similarity index 67% rename from terraform/k8sapps/gitea.tf rename to terraform/modules/gitea/main.tf index 13912c1..57d9f32 100644 --- a/terraform/k8sapps/gitea.tf +++ b/terraform/modules/gitea/main.tf @@ -1,50 +1,3 @@ -locals { - gitea_env_vars = { - APP_NAME = "A13Labs git repository" - HTTP_PORT = "3000" - RUN_MODE = "prod" - LFS_START_SERVER = "true" - SSH_DOMAIN = local.gitea_fqdn - DISABLE_REGISTRATION = true - INSTALL_LOCK = "true" - DB_TYPE = "mysql" - DB_NAME = "gitea" - DB_HOST = module.gitea_database.host - SSH_LISTEN_PORT = 22 - GITEA__mailer__ENABLED = true - GITEA__mailer__FROM = "gitea@alexpires.me" - GITEA__mailer__SMTP_ADDR = local.scaleway_smtp_host - GITEA__mailer__SMTP_PORT = local.scaleway_smtp_port - GITEA__mailer__PROTOCOL = "smtps" - } - - gitea_secrets = { - DB_USER = { - key = "username" - name = module.gitea_database.app_crendentials_name - } - DB_PASSWD = { - key = "password" - name = module.gitea_database.app_crendentials_name - } - GITEA__mailer__USER = { - key = "username" - name = kubernetes_secret.gitea_mail_credentials.metadata[0].name - } - GITEA__mailer__PASSWD = { - key = "password" - name = kubernetes_secret.gitea_mail_credentials.metadata[0].name - } - } - - gitea_path = "${local.persistent_folder}/gitea" - gitea_db_path = "${local.database_folder}/mysql.gitea" - gitea_backup_path = "${local.persistent_folder}/backup.gitea" -} - -# -# Namespace -# resource "kubernetes_namespace" "gitea" { metadata { annotations = { @@ -59,65 +12,35 @@ resource "kubernetes_namespace" "gitea" { } } -# -# Mail credentials -# resource "kubernetes_secret" "gitea_mail_credentials" { metadata { name = "gitea-mail-credentials" namespace = kubernetes_namespace.gitea.metadata[0].name } data = { - username = var.scaleway_project_id - password = scaleway_iam_api_key.mail_app_api.secret_key + username = var.relay_smtp_username + password = var.relay_smtp_password } type = "Opaque" } -# -# Setup MariaDB database -# module "gitea_database" { - source = "../modules/k8s_mariadb" + source = "../k8s_mariadb" namespace = kubernetes_namespace.gitea.metadata[0].name app_name = "gitea" - app_password = var.gitea_mysql_password - root_password = var.mysql_root_password + app_password = var.db_password + root_password = var.db_root_password - retention_days = local.local_backup_retention_days - cron_schedule = local.gitea_sql_backup_cron_schedule + retention_days = var.backup_retention_days + cron_schedule = var.backup_cron_schedule database_folder = local.gitea_db_path - backup_folder = local.gitea_backup_path + backup_folder = var.backup_folder } -# -# Setup rsync backup job to scaleway bucket -# -module "gitea_backup" { - source = "../modules/scw_backup_job" - namespace = kubernetes_namespace.gitea.metadata[0].name - app_name = "gitea" - - scaleway_organization_id = var.scaleway_organization_id - scaleway_project_id = var.scaleway_organization_id - access_key = scaleway_iam_api_key.backup_app_api.access_key - secret_key = scaleway_iam_api_key.backup_app_api.secret_key - bucket_name = scaleway_object_bucket.backup.name - - retention_days = local.local_backup_retention_days - cron_schedule = local.gitea_files_backup_cron_schedule - - source_folder = local.gitea_path - target_folder = local.gitea_backup_path -} - -# -# Gitea service, ingress, deployment and file backup job -# resource "kubernetes_service_account" "gitea_service_account" { metadata { @@ -162,11 +85,11 @@ resource "kubernetes_ingress_v1" "gitea_ingress" { } spec { tls { - hosts = [local.gitea_fqdn] + hosts = [var.fqdn] secret_name = "gitea-tls" } rule { - host = local.gitea_fqdn + host = var.fqdn http { path { backend { @@ -210,7 +133,7 @@ resource "kubernetes_deployment" "gitea" { service_account_name = kubernetes_service_account.gitea_service_account.metadata[0].name container { name = "gitea" - image = "gitea/gitea:${local.gitea_version}" + image = "gitea/gitea:${var.tag}" image_pull_policy = "Always" resources { diff --git a/terraform/modules/gitea/outputs.tf b/terraform/modules/gitea/outputs.tf new file mode 100644 index 0000000..b4b096c --- /dev/null +++ b/terraform/modules/gitea/outputs.tf @@ -0,0 +1,15 @@ +output "path" { + value = local.gitea_path +} + +output "db_path" { + value = local.gitea_db_path +} + +output "namespace" { + value = kubernetes_namespace.gitea.metadata[0].name +} + +output "app_name" { + value = "gitea" +} diff --git a/terraform/modules/gitea/provider.tf b/terraform/modules/gitea/provider.tf new file mode 100644 index 0000000..8b9fe2f --- /dev/null +++ b/terraform/modules/gitea/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_version = "~>1.8" + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "~>2.18" + } + scaleway = { + source = "scaleway/scaleway" + version = "~>2.13" + } + } +} diff --git a/terraform/modules/gitea/variables.tf b/terraform/modules/gitea/variables.tf new file mode 100644 index 0000000..f894477 --- /dev/null +++ b/terraform/modules/gitea/variables.tf @@ -0,0 +1,79 @@ +variable "persistent_folder" { + description = "The path to the persistent folder" + type = string +} + +variable "database_folder" { + description = "The path to the database folder" + type = string +} + +variable "backup_folder" { + description = "The path to the backup folder" + type = string +} + +variable "backup_retention_days" { + description = "The number of days to retain backups" + type = number +} + +variable "backup_cron_schedule" { + description = "The cron schedule for backups" + type = string +} + +variable "db_password" { + description = "The password for the Gitea MySQL database" + type = string + sensitive = true +} + +variable "db_root_password" { + description = "The root password for the MySQL database" + type = string + sensitive = true +} + +variable "tag" { + description = "The version of the websites to install" + type = string + default = "1.23.4" +} + +variable "fqdn" { + description = "The fully qualified domain name for the Rustdesk server" + type = string +} + +variable "app_name" { + description = "The name of the application" + type = string +} + +variable "relay_smtp_host" { + description = "The SMTP relay host" + type = string +} + +variable "relay_smtp_port" { + description = "The SMTP relay port" + type = number +} + +variable "relay_smtp_username" { + description = "The SMTP relay username" + type = string + sensitive = true +} + +variable "relay_smtp_password" { + description = "The SMTP relay password" + type = string + sensitive = true +} + +variable "from_email" { + description = "The email address to use as the sender" + type = string +} diff --git a/terraform/modules/mail/config.tf b/terraform/modules/mail/config.tf new file mode 100644 index 0000000..22a8bfd --- /dev/null +++ b/terraform/modules/mail/config.tf @@ -0,0 +1,56 @@ +locals { + persistent_folder = var.persistent_folder + mail_path = "${local.persistent_folder}/mail" + mailboxes_path = "${local.mail_path}/mailboxes" + getmail_path = "${local.mail_path}/getmail" + postfix_path = "${local.mail_path}/postfix" + mail_backup_path = "${local.persistent_folder}/backup.mail" + + getmail_jobs = flatten([ + for account in var.email_accounts : [ + for mailbox in account.mailboxes : { + name = "${account.recipient}-${mailbox}" + recipient = "${account.recipient}+${mailbox}" + domain = account.domain + server = account.receive.server + port = account.receive.port + username = account.username + password = account.password + mailbox = mailbox + delete_after = coalesce(account.delete_after, 30) + } + ] + ]) + + postfix_env_vars = { + # Email configuration + SMTP_HOST = var.relay_smtp_host + SMTP_PORT = var.relay_smtp_port + MY_PRIMARY_DOMAIN = var.primary_domain + MY_DESTINATION = join(",", [for account in var.email_accounts : account.domain]) + MY_NETWORKS = "127.0.0.0/8" + MAILNAME = "smtp.${var.primary_domain}" + DOVECOT_AUTH_ADDRESS = "dovecot-auth.mail.svc.cluster.local" + DOVECOT_AUTH_PORT = 31000 + DOVECOT_LMTP_ADDRESS = "dovecot-lmtp.mail.svc.cluster.local" + DOVECOT_LMTP_PORT = 31024 + LOG_TO_STDOUT = 0 + } + + password_hashes = { + for account in var.email_accounts : account.username => data.external.password_hasher[account.username].result.hex_encoded_bcrypt_hash + } + + postfix_secrets = { + SMTP_USER = { + key = "username" + name = kubernetes_secret.relay_mail_credentials.metadata[0].name + } + + SMTP_PASSWORD = { + key = "password" + name = kubernetes_secret.relay_mail_credentials.metadata[0].name + } + } +} + diff --git a/terraform/modules/mail/lib/bcrypt_hash.py b/terraform/modules/mail/lib/bcrypt_hash.py new file mode 100644 index 0000000..b8bbd07 --- /dev/null +++ b/terraform/modules/mail/lib/bcrypt_hash.py @@ -0,0 +1,27 @@ +import sys +import json +import bcrypt +import binascii + +# Read input JSON from stdin +input_data = json.load(sys.stdin) +password = input_data.get("password") +cost = int(input_data.get("cost", "10")) # Default cost 10 + +if not password: + print(json.dumps({"error": "Password is required"}), file=sys.stderr) + sys.exit(1) + +# Generate bcrypt hash +hashed_bytes = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt(rounds=cost)) +hashed_string = hashed_bytes.decode('utf-8') + +# Hex encode for Glauth +hex_encoded_hash = binascii.hexlify(hashed_string.encode('utf-8')).decode('utf-8') + +# Output JSON to stdout +output_data = { + "bcrypt_hash": hashed_string, + "hex_encoded_bcrypt_hash": hex_encoded_hash +} +print(json.dumps(output_data)) \ No newline at end of file diff --git a/terraform/k8sapps/mail.tf b/terraform/modules/mail/main.tf similarity index 77% rename from terraform/k8sapps/mail.tf rename to terraform/modules/mail/main.tf index 8cfe2f7..b9c7beb 100644 --- a/terraform/k8sapps/mail.tf +++ b/terraform/modules/mail/main.tf @@ -1,61 +1,6 @@ -locals { - mail_path = "${local.persistent_folder}/mail" - mailboxes_path = "${local.mail_path}/mailboxes" - getmail_path = "${local.mail_path}/getmail" - postfix_path = "${local.mail_path}/postfix" - mail_backup_path = "${local.persistent_folder}/backup.mail" - - getmail_jobs = flatten([ - for account in local.email_accounts : [ - for mailbox in account.mailboxes : { - name = "${account.recipient}-${mailbox}" - recipient = "${account.recipient}+${mailbox}" - domain = account.domain - server = account.receive.server - port = account.receive.port - username = account.username - password = account.password - mailbox = mailbox - delete_after = try(account.delete_after, 30) - } - ] - ]) - - postfix_env_vars = { - # Email configuration - SMTP_HOST = local.scaleway_smtp_host - SMTP_PORT = 465 - MY_PRIMARY_DOMAIN = local.primary_domain - MY_DESTINATION = join(",", [for account in local.email_accounts : account.domain]) - MY_NETWORKS = "127.0.0.0/8" - MAILNAME = "smtp.${local.primary_domain}" - DOVECOT_AUTH_ADDRESS = "dovecot-auth.mail.svc.cluster.local" - DOVECOT_AUTH_PORT = 31000 - DOVECOT_LMTP_ADDRESS = "dovecot-lmtp.mail.svc.cluster.local" - DOVECOT_LMTP_PORT = 31024 - LOG_TO_STDOUT = 0 - } - - password_hashes = { - for account in local.email_accounts : account.username => data.external.password_hasher[account.username].result.hex_encoded_bcrypt_hash - } - - postfix_secrets = { - SMTP_USER = { - key = "username" - name = kubernetes_secret.relay_mail_credentials.metadata[0].name - } - - SMTP_PASSWORD = { - key = "password" - name = kubernetes_secret.relay_mail_credentials.metadata[0].name - } - } -} - data "external" "password_hasher" { - for_each = { for account in local.email_accounts : account.username => account } - program = ["python3", "${path.module}/../../lib/bcrypt_hash.py"] + for_each = { for account in var.email_accounts : account.username => account } + program = ["python3", "${path.module}//lib/bcrypt_hash.py"] query = { password = each.value.password @@ -81,7 +26,7 @@ resource "kubernetes_secret" "dovecot_users" { namespace = kubernetes_namespace.mail.metadata[0].name } data = { - "users" = join("\n", [for account in local.email_accounts : "${account.username}:{BLF-CRYPT.HEX}${local.password_hashes[account.username]}:1000:1000::/var/mail/${account.username}::"]) + "users" = join("\n", [for account in var.email_accounts : "${account.username}:{BLF-CRYPT.HEX}${local.password_hashes[account.username]}:1000:1000::/var/mail/${account.username}::"]) } } @@ -91,8 +36,8 @@ resource "kubernetes_secret" "relay_mail_credentials" { namespace = kubernetes_namespace.mail.metadata[0].name } data = { - username = var.scaleway_project_id - password = scaleway_iam_api_key.mail_app_api.secret_key + username = var.relay_smtp_username + password = var.relay_smtp_password } type = "Opaque" } @@ -138,7 +83,7 @@ resource "kubernetes_secret" "bcc_map" { namespace = kubernetes_namespace.mail.metadata[0].name } data = { - "bcc_map" = join("\n", [for account in local.email_accounts : "/([^@]+)@${replace(account.domain, ".", "\\.")}/ $${1}+Sent@${account.domain}"]) + "bcc_map" = join("\n", [for account in var.email_accounts : "/([^@]+)@${replace(account.domain, ".", "\\.")}/ $${1}+Sent@${account.domain}"]) } } @@ -201,32 +146,12 @@ resource "kubernetes_manifest" "mail_certificate" { name = "letsencrypt-prod" kind = "ClusterIssuer" } - commonName = "imap.${local.primary_domain}" - dnsNames = flatten([for account in local.email_accounts : ["imap.${account.domain}", "smtp.${account.domain}"]]) + commonName = "imap.${var.primary_domain}" + dnsNames = flatten([for account in var.email_accounts : ["imap.${account.domain}", "smtp.${account.domain}"]]) } } } -module "mail_backup" { - - source = "../modules/scw_backup_job" - - namespace = kubernetes_namespace.mail.metadata[0].name - app_name = "mail" - - scaleway_organization_id = var.scaleway_organization_id - scaleway_project_id = var.scaleway_organization_id - access_key = scaleway_iam_api_key.backup_app_api.access_key - secret_key = scaleway_iam_api_key.backup_app_api.secret_key - bucket_name = scaleway_object_bucket.backup.name - - retention_days = local.local_backup_retention_days - cron_schedule = local.mail_files_backup_cron_schedule - - source_folder = local.mail_path - target_folder = local.mail_backup_path -} - resource "kubernetes_cron_job_v1" "fetch_emails" { metadata { namespace = kubernetes_namespace.mail.metadata[0].name @@ -234,7 +159,7 @@ resource "kubernetes_cron_job_v1" "fetch_emails" { } spec { - schedule = local.fetch_emails_schedule + schedule = var.fetch_emails_schedule job_template { metadata { @@ -256,7 +181,7 @@ resource "kubernetes_cron_job_v1" "fetch_emails" { spec { container { name = "getmail" - image = "a13labs/getmail6:${local.getmail_version}" + image = "a13labs/getmail6:${var.getmail_tag}" args = concat(["--getmaildir", "/var/getmail"], flatten([for job in local.getmail_jobs : ["-r", "/getmail/getmailrc_${job.name}"]])) volume_mount { @@ -327,7 +252,7 @@ resource "kubernetes_deployment" "dovecot" { service_account_name = kubernetes_service_account.mail_app.metadata[0].name container { name = "dovecot" - image = "dovecot/dovecot:latest" + image = "dovecot/dovecot:${var.dovecot_tag}" port { container_port = 31993 host_port = 993 @@ -448,7 +373,7 @@ resource "kubernetes_deployment" "postfix" { service_account_name = kubernetes_service_account.mail_app.metadata[0].name container { name = "postfix" - image = "tozd/postfix:alpine-316" + image = "tozd/postfix:${var.postfix_tag}" port { container_port = 465 host_port = 465 diff --git a/terraform/modules/mail/outputs.tf b/terraform/modules/mail/outputs.tf new file mode 100644 index 0000000..fd286f0 --- /dev/null +++ b/terraform/modules/mail/outputs.tf @@ -0,0 +1,11 @@ +output "path" { + value = local.mail_path +} + +output "namespace" { + value = kubernetes_namespace.mail.metadata[0].name +} + +output "app_name" { + value = "mail" +} diff --git a/terraform/modules/mail/provider.tf b/terraform/modules/mail/provider.tf new file mode 100644 index 0000000..8b9fe2f --- /dev/null +++ b/terraform/modules/mail/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_version = "~>1.8" + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "~>2.18" + } + scaleway = { + source = "scaleway/scaleway" + version = "~>2.13" + } + } +} diff --git a/terraform/k8sapps/resources/mail/dovecot.conf b/terraform/modules/mail/resources/mail/dovecot.conf similarity index 100% rename from terraform/k8sapps/resources/mail/dovecot.conf rename to terraform/modules/mail/resources/mail/dovecot.conf diff --git a/terraform/k8sapps/resources/mail/getmailrc.tpl b/terraform/modules/mail/resources/mail/getmailrc.tpl similarity index 100% rename from terraform/k8sapps/resources/mail/getmailrc.tpl rename to terraform/modules/mail/resources/mail/getmailrc.tpl diff --git a/terraform/k8sapps/resources/mail/run.config b/terraform/modules/mail/resources/mail/run.config similarity index 100% rename from terraform/k8sapps/resources/mail/run.config rename to terraform/modules/mail/resources/mail/run.config diff --git a/terraform/k8sapps/resources/mail/sieve b/terraform/modules/mail/resources/mail/sieve similarity index 100% rename from terraform/k8sapps/resources/mail/sieve rename to terraform/modules/mail/resources/mail/sieve diff --git a/terraform/modules/mail/variables.tf b/terraform/modules/mail/variables.tf new file mode 100644 index 0000000..884ce2e --- /dev/null +++ b/terraform/modules/mail/variables.tf @@ -0,0 +1,82 @@ +variable "persistent_folder" { + description = "The path to the persistent folder" + type = string +} + +variable "primary_domain" { + description = "The primary domain for the websites" + type = string +} + +variable "email_accounts" { + description = "List of email accounts to be created" + type = list(object({ + recipient = string + domain = string + username = string + password = string + delete_after = optional(number) + mailboxes = list(string) + receive = object({ + server = string + port = number + }) + })) +} + +variable "relay_smtp_host" { + description = "The SMTP relay host" + type = string +} + +variable "relay_smtp_port" { + description = "The SMTP relay port" + type = number +} + +variable "relay_smtp_username" { + description = "The SMTP relay username" + type = string + sensitive = true +} + +variable "relay_smtp_password" { + description = "The SMTP relay password" + type = string + sensitive = true +} + +variable "fetch_emails_schedule" { + description = "The cron schedule for fetching emails" + type = string +} + +variable "getmail_tag" { + description = "The version of getmail to install" + type = string + default = "v0.0.1-6.19.07" +} + +variable "postfix_tag" { + description = "The version of postfix to install" + type = string + default = "alpine-316" +} + +variable "dovecot_tag" { + description = "The version of dovecot to install" + type = string + default = "latest" +} + +variable "mail_crypt_private_key" { + description = "The private key for mail encryption" + type = string + sensitive = true +} + +variable "mail_crypt_public_key" { + description = "The public key for mail encryption" + type = string + sensitive = true +} diff --git a/terraform/modules/seafile/config.tf b/terraform/modules/seafile/config.tf new file mode 100644 index 0000000..2daf8be --- /dev/null +++ b/terraform/modules/seafile/config.tf @@ -0,0 +1,7 @@ +locals { + persistent_folder = var.persistent_folder + websites_path = "${local.persistent_folder}/web" + primary_domain = var.primary_domain + websites = var.websites +} + diff --git a/terraform/modules/seafile/main.tf b/terraform/modules/seafile/main.tf new file mode 100644 index 0000000..7d02c6b --- /dev/null +++ b/terraform/modules/seafile/main.tf @@ -0,0 +1,128 @@ +# +# Namespace for Seafile +# +resource "kubernetes_namespace" "seafile" { + metadata { + annotations = { + name = "seafile" + } + + labels = { + name = "seafile" + } + + name = "seafile" + } +} + +# +# Persistent Volume for Seafile data +# +resource "kubernetes_persistent_volume" "seafile_data" { + metadata { + name = "seafile-data" + labels = { + type = "local" # Example label, adjust as needed + } + } + spec { + capacity = { + storage = "10Gi" + } + access_modes = ["ReadWriteOnce"] + persistent_volume_source { + host_path { + path = "/opt/seafile-data" # Consider making this configurable via variables + } + } + persistent_volume_reclaim_policy = "Retain" # Or Delete, depending on desired behavior + storage_class_name = "manual" # Or your specific storage class + volume_mode = "Filesystem" + } +} + +# +# Persistent Volume Claim for Seafile data +# +resource "kubernetes_persistent_volume_claim" "seafile_data" { + metadata { + name = "seafile-data" + namespace = kubernetes_namespace.seafile.metadata[0].name + } + spec { + access_modes = ["ReadWriteOnce"] + resources { + requests = { + storage = "10Gi" + } + } + storage_class_name = "manual" # Must match the PV's storage class + volume_name = kubernetes_persistent_volume.seafile_data.metadata[0].name + } + wait_until_bound = false # Set to true if you need to ensure binding before proceeding +} + +# +# ConfigMap for Seafile environment variables +# +resource "kubernetes_config_map" "seafile_env" { + metadata { + name = "seafile-env" + namespace = kubernetes_namespace.seafile.metadata[0].name + } + data = { + # for Seafile server + TIME_ZONE = "UTC" # Consider making this configurable + SEAFILE_LOG_TO_STDOUT = "false" + SITE_ROOT = "/" + ENABLE_SEADOC = "false" + SEADOC_SERVER_URL = "https://seafile.example.com/sdoc-server" # Replace with actual URL or variable + SEAFILE_SERVER_HOSTNAME = "seafile.example.com" # Replace with actual hostname or variable + SEAFILE_SERVER_PROTOCOL = "http" # Replace with actual protocol or variable + + # for database - Consider using secrets for credentials + SEAFILE_MYSQL_DB_HOST = "" # Replace with actual host or variable/secret + SEAFILE_MYSQL_DB_PORT = "3306" + SEAFILE_MYSQL_DB_USER = "seafile" # Replace with actual user or variable/secret + SEAFILE_MYSQL_DB_CCNET_DB_NAME = "ccnet_db" + SEAFILE_MYSQL_DB_SEAFILE_DB_NAME = "seafile_db" + SEAFILE_MYSQL_DB_SEAHUB_DB_NAME = "seahub_db" + + # Init + ## for Seafile admin - Consider using secrets + INIT_SEAFILE_ADMIN_EMAIL = "" # Replace with actual email or variable/secret + + ## For S3 storage backend (only valid in INIT_S3_STORAGE_BACKEND_CONFIG = true) - Consider using secrets + INIT_S3_STORAGE_BACKEND_CONFIG = "false" + INIT_S3_COMMIT_BUCKET = "" + INIT_S3_FS_BUCKET = "" + INIT_S3_BLOCK_BUCKET = "" + INIT_S3_KEY_ID = "" + INIT_S3_USE_V4_SIGNATURE = "true" + INIT_S3_AWS_REGION = "us-east-1" + INIT_S3_HOST = "s3.us-east-1.amazonaws.com" + INIT_S3_USE_HTTPS = "true" + } +} + +# +# Service for Seafile +# +resource "kubernetes_service" "seafile" { + metadata { + name = "seafile" + namespace = kubernetes_namespace.seafile.metadata[0].name + } + spec { + selector = { + app = "seafile" # Ensure your Seafile deployment/pod has this label + } + port { + protocol = "TCP" + port = 80 + target_port = 80 + node_port = 30000 # NodePort might not be ideal for production, consider Ingress or LoadBalancer service type without explicit nodePort + } + type = "LoadBalancer" # Or ClusterIP/NodePort depending on your needs + } +} diff --git a/terraform/modules/seafile/provider.tf b/terraform/modules/seafile/provider.tf new file mode 100644 index 0000000..8b9fe2f --- /dev/null +++ b/terraform/modules/seafile/provider.tf @@ -0,0 +1,13 @@ +terraform { + required_version = "~>1.8" + required_providers { + kubernetes = { + source = "hashicorp/kubernetes" + version = "~>2.18" + } + scaleway = { + source = "scaleway/scaleway" + version = "~>2.13" + } + } +} diff --git a/terraform/modules/seafile/resources/nginx/conf.d/default.conf b/terraform/modules/seafile/resources/nginx/conf.d/default.conf new file mode 100644 index 0000000..d352c7b --- /dev/null +++ b/terraform/modules/seafile/resources/nginx/conf.d/default.conf @@ -0,0 +1,35 @@ +server { + listen 8080; + server_name localhost; + + gzip on; + gzip_comp_level 9; + gzip_types + text/html + text/plain + text/css + text/js + text/xml + text/javascript + application/javascript + application/json + application/xml + application/rss+xml + image/svg+xml; + + root /tmp/site; + + location / { + index index.html index.htm; + } + + location ~* ^/([^/]+) { + index index.html index.htm; + error_page 404 = @error; + } + + error_page 404 /404.html; + location @error { + try_files /$1/404.html /404.html =404; + } +} diff --git a/terraform/modules/seafile/resources/nginx/nginx.conf b/terraform/modules/seafile/resources/nginx/nginx.conf new file mode 100644 index 0000000..12c02fa --- /dev/null +++ b/terraform/modules/seafile/resources/nginx/nginx.conf @@ -0,0 +1,34 @@ +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /tmp/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + proxy_temp_path /tmp/proxy_temp; + client_body_temp_path /tmp/client_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + + keepalive_timeout 65; + + + include /etc/nginx/conf.d/*.conf; +} \ No newline at end of file diff --git a/terraform/modules/seafile/variables.tf b/terraform/modules/seafile/variables.tf new file mode 100644 index 0000000..86adb67 --- /dev/null +++ b/terraform/modules/seafile/variables.tf @@ -0,0 +1,23 @@ +variable "persistent_folder" { + description = "The path to the persistent folder" + type = string +} + +variable "primary_domain" { + description = "The primary domain for the websites" + type = string +} + +variable "tag" { + description = "The version of the websites to install" + type = string + default = "stable-alpine" +} + +variable "websites" { + description = "A list of websites to be deployed" + type = map(object({ + domain_name = string + fqdn = list(string) + })) +}