stem95su: scheduled Drive->site sync CronJob (every 10m)
CronJob stem95su-gdrive-sync (*/10) mounts the content PVC RW and rclone-syncs the read-only Drive folder "claude" (stem claude/files) onto it (rclone/rclone:1.74.3, scope=drive.readonly, empty-source guard + --max-delete 25). ESO ExternalSecret stem95su-rclone <- Vault secret/stem95su. Requires the GCP OAuth app published to Production or the refresh token expires ~weekly. Lands the gdrive-sync stack on master (it had landed on a feature branch by accident on the shared devvm checkout). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
05b50d2b96
commit
6d224861c4
1168 changed files with 120 additions and 358547 deletions
|
|
@ -1,12 +0,0 @@
|
|||
firmly-gerardo-generated@viktorbarzin.me me@viktorbarzin.me
|
||||
closely-keith-generated@viktorbarzin.me vbarzin@gmail.com
|
||||
literally-paolo-generated@viktorbarzin.me viktorbarzin@fb.com
|
||||
hastily-stefanie-generated@viktorbarzin.me elliestamenova@gmail.com
|
||||
vaultwarden@viktorbarzin.me me@viktorbarzin.me
|
||||
|
||||
# plans@ -> spam@: authorizes tripit (SMTP-authed as spam@) to send mail
|
||||
# From: plans@viktorbarzin.me under docker-mailserver SPOOF_PROTECTION (the
|
||||
# smtpd_sender_login_maps union exact-matches this alias to spam@; the @domain
|
||||
# catch-all does NOT, so an explicit entry is required). Also keeps inbound
|
||||
# plans@ routing to spam@ for the tripit mail-ingest poller.
|
||||
plans@viktorbarzin.me spam@viktorbarzin.me
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,303 +0,0 @@
|
|||
variable "roundcube_db_password" {
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
variable "mysql_host" { type = string }
|
||||
|
||||
resource "kubernetes_config_map" "roundcubemail_config" {
|
||||
metadata {
|
||||
name = "roundcubemail.config"
|
||||
namespace = "mailserver"
|
||||
|
||||
labels = {
|
||||
app = "roundcubemail"
|
||||
}
|
||||
annotations = {
|
||||
"reloader.stakater.com/match" = "true"
|
||||
}
|
||||
}
|
||||
|
||||
data = {
|
||||
# Disable TLS peer verification for internal service name connections
|
||||
# The mailserver cert is issued for mail.viktorbarzin.me, not the k8s service name
|
||||
"custom.php" = <<-EOF
|
||||
<?php
|
||||
$config['imap_conn_options'] = [
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
],
|
||||
];
|
||||
$config['smtp_conn_options'] = [
|
||||
'ssl' => [
|
||||
'verify_peer' => false,
|
||||
'verify_peer_name' => false,
|
||||
],
|
||||
];
|
||||
?>
|
||||
EOF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
resource "kubernetes_persistent_volume_claim" "roundcube_html_encrypted" {
|
||||
wait_until_bound = false
|
||||
metadata {
|
||||
name = "roundcubemail-html-encrypted"
|
||||
namespace = kubernetes_namespace.mailserver.metadata[0].name
|
||||
annotations = {
|
||||
"resize.topolvm.io/threshold" = "10%"
|
||||
"resize.topolvm.io/increase" = "100%"
|
||||
"resize.topolvm.io/storage_limit" = "5Gi"
|
||||
}
|
||||
}
|
||||
spec {
|
||||
access_modes = ["ReadWriteOnce"]
|
||||
storage_class_name = "proxmox-lvm-encrypted"
|
||||
resources {
|
||||
requests = {
|
||||
storage = "1Gi"
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
# The autoresizer expands requests.storage up to storage_limit and
|
||||
# PVCs can't shrink. Without this, every TF apply tries to revert
|
||||
# to the spec value, K8s rejects the shrink, and the PVC ends up
|
||||
# in Terminating-but-in-use limbo.
|
||||
ignore_changes = [spec[0].resources[0].requests]
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_persistent_volume_claim" "roundcube_enigma_encrypted" {
|
||||
wait_until_bound = false
|
||||
metadata {
|
||||
name = "roundcubemail-enigma-encrypted"
|
||||
namespace = kubernetes_namespace.mailserver.metadata[0].name
|
||||
annotations = {
|
||||
"resize.topolvm.io/threshold" = "10%"
|
||||
"resize.topolvm.io/increase" = "100%"
|
||||
"resize.topolvm.io/storage_limit" = "5Gi"
|
||||
}
|
||||
}
|
||||
spec {
|
||||
access_modes = ["ReadWriteOnce"]
|
||||
storage_class_name = "proxmox-lvm-encrypted"
|
||||
resources {
|
||||
requests = {
|
||||
storage = "1Gi"
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
# The autoresizer expands requests.storage up to storage_limit and
|
||||
# PVCs can't shrink. Without this, every TF apply tries to revert
|
||||
# to the spec value, K8s rejects the shrink, and the PVC ends up
|
||||
# in Terminating-but-in-use limbo.
|
||||
ignore_changes = [spec[0].resources[0].requests]
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_deployment" "roundcubemail" {
|
||||
metadata {
|
||||
name = "roundcubemail"
|
||||
namespace = "mailserver"
|
||||
labels = {
|
||||
"app" = "roundcubemail"
|
||||
tier = var.tier
|
||||
}
|
||||
annotations = {
|
||||
"reloader.stakater.com/search" = "true"
|
||||
}
|
||||
}
|
||||
spec {
|
||||
replicas = "1"
|
||||
strategy {
|
||||
type = "Recreate"
|
||||
}
|
||||
selector {
|
||||
match_labels = {
|
||||
"app" = "roundcubemail"
|
||||
}
|
||||
}
|
||||
template {
|
||||
metadata {
|
||||
labels = {
|
||||
"app" = "roundcubemail"
|
||||
}
|
||||
}
|
||||
spec {
|
||||
container {
|
||||
name = "roundcube"
|
||||
image = "roundcube/roundcubemail:1.6.13-apache"
|
||||
volume_mount {
|
||||
name = "roundcube-config"
|
||||
mount_path = "/var/roundcube/config/custom.php"
|
||||
sub_path = "custom.php"
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_DEFAULT_HOST"
|
||||
value = "ssl://mailserver" # internal k8s service name
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_DEFAULT_PORT"
|
||||
value = "993"
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_SMTP_SERVER"
|
||||
value = "tls://mailserver" # internal k8s service name
|
||||
}
|
||||
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_SMTP_PORT"
|
||||
value = 587
|
||||
}
|
||||
|
||||
# DB Settings
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_DB_TYPE"
|
||||
value = "mysql"
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_DB_HOST"
|
||||
value = var.mysql_host
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_DB_USER"
|
||||
value = "roundcubemail"
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_DB_PASSWORD"
|
||||
value = var.roundcube_db_password
|
||||
}
|
||||
# Plugins
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_COMPOSER_PLUGINS"
|
||||
value = "mmvi/twofactor_webauthn,texxasrulez/persistent_login,dsoares/rcguard"
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_PLUGINS"
|
||||
value = "attachment_reminder,database_attachments,enigma,twofactor_webauthn,persistent_login,rcguard"
|
||||
}
|
||||
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_SMTP_DEBUG"
|
||||
value = "false"
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_DEBUG_LEVEL"
|
||||
value = "1"
|
||||
}
|
||||
env {
|
||||
name = "ROUNDCUBEMAIL_LOG_DRIVER"
|
||||
# value = "file"
|
||||
value = "syslog"
|
||||
}
|
||||
port {
|
||||
name = "web"
|
||||
container_port = 80
|
||||
protocol = "TCP"
|
||||
}
|
||||
volume_mount {
|
||||
name = "html"
|
||||
mount_path = "/var/www/html"
|
||||
}
|
||||
volume_mount {
|
||||
name = "enigma"
|
||||
mount_path = "/var/roundcube/enigma"
|
||||
}
|
||||
resources {
|
||||
requests = {
|
||||
cpu = "25m"
|
||||
memory = "192Mi"
|
||||
}
|
||||
limits = {
|
||||
memory = "192Mi"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "roundcube-config"
|
||||
config_map {
|
||||
name = kubernetes_config_map.roundcubemail_config.metadata[0].name
|
||||
}
|
||||
}
|
||||
|
||||
volume {
|
||||
name = "html"
|
||||
persistent_volume_claim {
|
||||
claim_name = kubernetes_persistent_volume_claim.roundcube_html_encrypted.metadata[0].name
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "enigma"
|
||||
persistent_volume_claim {
|
||||
claim_name = kubernetes_persistent_volume_claim.roundcube_enigma_encrypted.metadata[0].name
|
||||
}
|
||||
}
|
||||
dns_config {
|
||||
option {
|
||||
name = "ndots"
|
||||
value = "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lifecycle {
|
||||
# KYVERNO_LIFECYCLE_V1: Kyverno admission webhook mutates dns_config with ndots=2
|
||||
ignore_changes = [
|
||||
spec[0].template[0].spec[0].dns_config, # KYVERNO_LIFECYCLE_V1
|
||||
metadata[0].annotations["keel.sh/policy"],
|
||||
metadata[0].annotations["keel.sh/trigger"],
|
||||
metadata[0].annotations["keel.sh/pollSchedule"], # KYVERNO_LIFECYCLE_V2
|
||||
metadata[0].annotations["keel.sh/match-tag"],
|
||||
spec[0].template[0].spec[0].container[0].image, # KEEL_IGNORE_IMAGE — Keel manages tag updates
|
||||
metadata[0].annotations["kubernetes.io/change-cause"],
|
||||
metadata[0].annotations["deployment.kubernetes.io/revision"],
|
||||
spec[0].template[0].metadata[0].annotations["keel.sh/update-time"], # KEEL_LIFECYCLE_V1
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_service" "roundcubemail" {
|
||||
metadata {
|
||||
name = "roundcubemail"
|
||||
namespace = "mailserver"
|
||||
|
||||
labels = {
|
||||
app = "roundcubemail"
|
||||
}
|
||||
}
|
||||
|
||||
spec {
|
||||
selector = {
|
||||
app = "roundcubemail"
|
||||
}
|
||||
|
||||
port {
|
||||
name = "roundcube"
|
||||
protocol = "TCP"
|
||||
port = 80
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "ingress" {
|
||||
source = "../../../../modules/kubernetes/ingress_factory"
|
||||
dns_type = "non-proxied"
|
||||
namespace = "mailserver"
|
||||
name = "mail"
|
||||
service_name = "roundcubemail"
|
||||
tls_secret_name = var.tls_secret_name
|
||||
auth = "required"
|
||||
extra_annotations = {
|
||||
"gethomepage.dev/enabled" = "true"
|
||||
"gethomepage.dev/name" = "Roundcube Mail"
|
||||
"gethomepage.dev/description" = "Webmail client"
|
||||
"gethomepage.dev/icon" = "roundcube.png"
|
||||
"gethomepage.dev/group" = "Other"
|
||||
"gethomepage.dev/pod-selector" = ""
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# this is appended and merged to the main postfix.cf
|
||||
# see defaults - https://github.com/docker-mailserver/docker-mailserver/blob/master/target/postfix/main.cf
|
||||
variable "postfix_cf" {
|
||||
default = <<EOT
|
||||
relayhost = [smtp-relay.brevo.com]:587
|
||||
smtp_sasl_auth_enable = yes
|
||||
smtp_sasl_password_maps = hash:/etc/postfix/sasl/passwd
|
||||
smtp_sasl_security_options = noanonymous
|
||||
smtp_sasl_tls_security_options = noanonymous
|
||||
smtp_tls_security_level = encrypt
|
||||
# TLS cert/key come from docker-mailserver's SSL_TYPE=manual flow, which writes
|
||||
# the authoritative `smtpd_tls_chain_files` into main.cf at boot. Setting the
|
||||
# legacy smtpd_tls_cert_file/smtpd_tls_key_file here too makes postfix warn
|
||||
# ("Both smtpd_tls_chain_files and one or more of the legacy ...") and ignore
|
||||
# them. Dropped to silence the warning — functionally a no-op (chain_files wins).
|
||||
smtpd_use_tls=yes
|
||||
# Require STARTTLS before any AUTH command on the SMTPD listener.
|
||||
# Without this, a misconfigured client that skips STARTTLS would send
|
||||
# PLAIN/LOGIN creds in the clear. docker-mailserver's default does NOT
|
||||
# enforce this at the main.cf level for submission (587).
|
||||
# Note: smtpd_sasl_auth_only (sometimes cited) is NOT a real Postfix
|
||||
# parameter — only smtpd_tls_auth_only is. Addresses code-vnw.
|
||||
smtpd_tls_auth_only = yes
|
||||
header_size_limit = 4096000
|
||||
|
||||
# Debug mail tls
|
||||
smtpd_tls_loglevel = 1
|
||||
#smtpd_tls_ciphers = TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:!aNULL:!SEED:!CAMELLIA:!RSA+AES:!SHA1
|
||||
#tls_medium_cipherlist = ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:!aNULL:!SEED:!CAMELLIA:!RSA+AES:!SHA1
|
||||
|
||||
# Rate limiting (brute-force protection)
|
||||
smtpd_client_connection_rate_limit = 10
|
||||
smtpd_client_message_rate_limit = 30
|
||||
anvil_rate_time_unit = 60s
|
||||
|
||||
# Disable the postscreen decision cache. The default (btree) driver
|
||||
# requires an exclusive file lock for every access, and with postscreen
|
||||
# re-spawning per connection (master.cf: maxproc=1) that produces thousands
|
||||
# of 'unable to get exclusive lock' fatals per day — stalling SMTP
|
||||
# acceptance and starving inbound delivery. lmdb would avoid the lock but
|
||||
# isn't compiled into docker-mailserver 15.0.0's Postfix build
|
||||
# (postconf -m → no lmdb). Proxy:btree is unsafe because postscreen does
|
||||
# its own locking. An empty value disables the cache entirely — legitimate
|
||||
# clients pay the greet/bare-newline re-check on every new TCP session,
|
||||
# which is trivial at our volume (~100 deliveries/day).
|
||||
postscreen_cache_map =
|
||||
EOT
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue