🏡 index : github.com/captn3m0/nebula.git

author Nemo <commits@captnemo.in> 2021-10-15 18:24:13.0 +05:30:00
committer GitHub <noreply@github.com> 2021-10-15 12:54:13.0 +00:00:00
commit
137eb3469f699eee2633801f123add1b0a7a7971 [patch]
tree
7a95b030240f2f4bd7a5e938e04f278fab26693a
parent
8d7875d174cff81e7e654f671ed22e4a3b2d3a3e
download
137eb3469f699eee2633801f123add1b0a7a7971.tar.gz

Terraform Upgrade to 1.x (#3)

Co-authored-by: Hashfyre <joy.bhattacherjee@gmail.com>

Diff

 .gitignore                     |   1 +
 .terraform-version             |   2 +-
 HACKING.md                     |  18 ++++++++++++++++++
 data.tf                        |   8 ++++++++
 echoserver.tf                  |   5 +++--
 elibsrv.tf                     |  16 +++++-----------
 firefox-sync.tf                |  24 ++++++++++--------------
 jupyter.tf                     |   1 -
 kaarana.tf                     |  21 +++++++++++----------
 kayak.tf                       |   1 -
 klaxon.tf                      |  22 ++++++++--------------
 kube-test.tf                   |   1 -
 main.tf                        |  97 ++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
 miniflux.tf                    |  17 +++++++----------
 nextcloud.tf                   |  48 ++++++++++++++++--------------------------------
 providers.tf                   |  42 +++++++++++++++++++++++++++---------------
 pulse.tf                       |   1 -
 rss-bridge.tf                  |  22 +++++++++-------------
 secrets.tf                     |  93 ++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
 server.tf                      |   1 -
 state.tf                       |   1 +
 variables.tf                   |  14 ++++----------
 wiki.tf                        |  42 +++++++++++++++++++-----------------------
 znc.tf                         |  30 +++++++++++++++---------------
 cloudflare/main.tf             | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 cloudflare/providers.tf        |   7 +++++++
 cloudflare/variables.tf        |   5 +++--
 db/network.tf                  |   1 +
 db/outputs.tf                  |   3 ++-
 db/postgres.tf                 |  17 +++++++++--------
 db/providers.tf                |  19 +++++++++++++++++++
 db/variables.tf                |   6 ++++--
 db/volumes.tf                  |   1 +
 digitalocean/droplets.tf       |   5 +++--
 digitalocean/firewall.tf       |  55 ++++++++++++++++++++++++++-----------------------------
 digitalocean/networking.tf     |   5 +++--
 digitalocean/providers.tf      |  19 +++++++++++++++++++
 docker/data.tf                 |   1 +
 docker/got.tf                  |   7 -------
 docker/images.tf               |   8 ++++----
 docker/locals.tf               |  13 +++++--------
 docker/lychee.tf               |   1 -
 docker/main.tf                 |   1 -
 docker/network.tf              |   1 +
 docker/outputs.tf              |   7 ++++---
 docker/providers.tf            |  19 +++++++++++++++++++
 docker/traefik.tf              |  55 +++++++++++++++++++++++++++++++------------------------
 docker/ubooquity.tf            |  59 ++++++++++++++++++++++++++++++++++++-----------------------
 docker/variables.tf            |  14 +++++++-------
 docker/volumes.tf              |   1 -
 gitea/data.tf                  |  21 +++++++++++----------
 gitea/main.tf                  |  67 ++++++++++++++++++++++++++++++++++++-------------------------------
 gitea/mysql.tf                 |   1 -
 gitea/network.tf               |   1 +
 gitea/providers.tf             |  19 +++++++++++++++++++
 gitea/redis.tf                 |   9 +++++----
 gitea/variables.tf             |  34 ++++++++++++++++++++++++----------
 gitea/volume.tf                |   1 +
 kaarana/database.tf            |   8 +++-----
 kaarana/images.tf              |   7 ++++---
 kaarana/traefik.tf             |  20 ++++++++++----------
 kaarana/vars.tf                |   8 ++++++--
 kaarana/wordpress.tf           |  21 ++++-----------------
 media/airsonic.tf              |  15 ---------------
 media/data.tf                  |   1 +
 media/emby.tf                  |  45 +++++++++++++++++++++++++++++----------------
 media/jackett.tf               |  19 +++++++++++--------
 media/lidarr.tf                |  29 ++++++++++++++++++++---------
 media/navidrome.tf             |  13 +++++++------
 media/network.tf               |   1 +
 media/outputs.tf               |   5 +++--
 media/providers.tf             |  19 +++++++++++++++++++
 media/radarr.tf                |   8 +++++---
 media/requestrr.tf             |  10 ++++++----
 media/sonarr.tf                |   7 ++++---
 media/transmission.tf          |  40 +++++++++++++++++++++++++++-------------
 media/variables.tf             |  22 ++++++++++++++--------
 monitoring/act.tf              |   7 +++----
 monitoring/cadvisor.tf         |  14 ++++----------
 monitoring/data.tf             |   1 +
 monitoring/grafana.tf          |  20 +++++++-------------
 monitoring/images.tf           |   9 +++++----
 monitoring/network.tf          |   1 +
 monitoring/nodeexporter.tf     |  11 +++++++----
 monitoring/prometheus.tf       |  22 ++++++----------------
 monitoring/providers.tf        |  19 +++++++++++++++++++
 monitoring/speedtest.tf        |  13 +++----------
 monitoring/variables.tf        |  16 +++++++++-------
 opml/main.tf                   |  18 ++++++++++--------
 opml/network.tf                |   1 +
 opml/providers.tf              |  19 +++++++++++++++++++
 opml/redis.tf                  |  14 ++++++++------
 opml/variables.tf              |  17 ++++++++++++-----
 radicale/config                |   4 ++++
 radicale/main.tf               |  45 ++++++++++++---------------------------------
 radicale/providers.tf          |  19 +++++++++++++++++++
 radicale/variables.tf          |   3 ++-
 timemachine/main.tf            |  28 ++++++++++++++--------------
 timemachine/providers.tf       |  19 +++++++++++++++++++
 timemachine/variables.tf       |  18 +++++++++++++-----
 docker/conf/traefik.toml       |  27 ---------------------------
 gitea/conf/conf.ini.tpl        |   6 +++---
 media/conf/transmission.json   |   6 +++---
 modules/container/image.tf     |   9 +++++++++
 modules/container/locals.tf    |  45 ++++++++++++++++++++++++++++-----------------
 modules/container/main.tf      | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 modules/container/providers.tf |   9 +++++++++
 modules/container/vars.tf      |  64 +++++++++++++++++++++++++++++++++++++++++-----------------------
 modules/image/main.tf          |   9 +++++----
 modules/postgres/main.tf       |   9 +++++----
 modules/postgres/providers.tf  |  19 +++++++++++++++++++
 modules/postgres/vars.tf       |   5 +++--
 112 files changed, 1206 insertions(+), 988 deletions(-)

diff --git a/.gitignore b/.gitignore
index 704997f..2036315 100644
--- a/.gitignore
+++ a/.gitignore
@@ -1,8 +1,9 @@
*.tfvars
.terraform.tfstate.lock.info
.terraform
*.tfstate
*.tfstate.backup
*.terraform.lock.hcl
*.out
*.backup
secrets
diff --git a/.terraform-version b/.terraform-version
index 44ab23e..66c4c22 100644
--- a/.terraform-version
+++ a/.terraform-version
@@ -1,1 +1,1 @@
0.11.13
1.0.9
diff --git a/HACKING.md b/HACKING.md
new file mode 100644
index 0000000..69a00e4 100644
--- /dev/null
+++ a/HACKING.md
@@ -1,0 +1,18 @@
# Hacking on the thing

Generate certs as per:

https://gist.github.com/captn3m0/2c2e723b2dcd5cdaad733aad12be59a2

Copy ca.pem, server-cert.pem, server-key.pem to /etc/docker/certs.

Make sure server-key.pem is 0400 in permissions.

Run `systemctl edit docker`

````

/etc/systemd/system/docker.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/bin/dockerd --tlsverify --tlscacert=/etc/docker/certs/ca.pem --tlscert=/etc/docker/certs/server-cert.pem --tlskey=/etc/docker/certs/server-key.pem -H=0.0.0.0:2376 -H unix:///var/run/docker.sock
````

diff --git a/data.tf b/data.tf
index e45042d..952f3ae 100644
--- a/data.tf
+++ a/data.tf
@@ -1,3 +1,11 @@
data "docker_network" "bridge" {

  name = "bridge"
}

data "cloudflare_zones" "bb8" {

  filter {

    name        = "bb8"
    lookup_type = "exact"
    match       = "bb8.fun"
  }
}
diff --git a/echoserver.tf b/echoserver.tf
index 23675cd..19bf136 100644
--- a/echoserver.tf
+++ a/echoserver.tf
@@ -1,11 +1,12 @@
module "echo-server" {

  source = "modules/container"
  source = "./modules/container"
  name   = "echo-server"
  image  = "jmalloc/echo-server:latest"

  web {

  web = {

    expose = "true"
    port   = 8080
    host   = "debug.${var.root-domain},debug.in.${var.root-domain}"
  }
}

diff --git a/elibsrv.tf b/elibsrv.tf
index 3daa65e..ea771a5 100644
--- a/elibsrv.tf
+++ a/elibsrv.tf
@@ -1,14 +1,14 @@
module "elibsrv" {

  name   = "elibsrv"
  name   = "./elibsrv"
  source = "./modules/container"
  image  = "captn3m0/elibsrv"

  resource {

  resource = {

    memory      = 512
    memory_swap = 512
  }

  web {

  web = {

    expose = true
    host   = "ebooks.${var.root-domain}"
    auth   = true
@@ -40,12 +40,6 @@
    "elibsrv_thumbheight=320",
    "elibsrv_title=Scarif Media Archives",
  ]
  networks_advanced = [

    {
      name = "traefik"
    },
    {
      name = "bridge"
    },
  ]
  networks = ["bridge"]
}

diff --git a/firefox-sync.tf b/firefox-sync.tf
index 5eb18e2..f79b6a9 100644
--- a/firefox-sync.tf
+++ a/firefox-sync.tf
@@ -1,24 +1,26 @@
module "firefox-sync" {

  name   = "firefox-sync"
  source = "./modules/container"
  image  = "mozilla/syncserver:latest"

  // Default is port 80
  web {

  web = {

    expose = true
    port   = "5000"
    host   = "firesync.${var.root-domain}"
  }

  resource {

  resource = {

    memory      = "400"
    memory_swap = "400"
  }

  volumes = [{

    host_path      = "/mnt/xwing/data/firefox-sync"
    container_path = "/data"
  }]
  volumes = [

    {
      host_path      = "/mnt/xwing/data/firefox-sync"
      container_path = "/data"
    },
  ]

  env = [

    "SYNCSERVER_PUBLIC_URL=https://firesync.${var.root-domain}",
@@ -29,12 +31,6 @@
    "PORT=5000",
  ]

  networks_advanced = [

    {
      name = "traefik"
    },
    {
      name = "bridge"
    },
  ]
  networks = ["bridge"]
}

diff --git a/jupyter.tf b/jupyter.tf
index 43cc05a..3deceed 100644
--- a/jupyter.tf
+++ a/jupyter.tf
@@ -10,4 +10,3 @@
#     },
#   ]
# }

diff --git a/kaarana.tf b/kaarana.tf
index e0fe45d..86f1615 100644
--- a/kaarana.tf
+++ a/kaarana.tf
@@ -1,16 +1,16 @@
# kaarana related stuff

module "kaarana" {

  source = "./kaarana"

  root_db_password = "${data.pass_password.kaarana-root-db-password.password}"
  db_password      = "${data.pass_password.kaarana-db-password.password}"

  providers = {

    docker = "docker.sydney"
  }
}
# module "kaarana" {
#   source = "./kaarana"

#   root_db_password = data.pass_password.kaarana-root-db-password.password
#   db_password      = data.pass_password.kaarana-db-password.password

#   providers = {
#     docker = docker.sydney
#   }
# }

data "pass_password" "kaarana-root-db-password" {

  path = "KAARANA_DB_ROOT_PASSWORD"
}
@@ -18,3 +18,4 @@
data "pass_password" "kaarana-db-password" {

  path = "KAARANA_DB_PASSWORD"
}

diff --git a/kayak.tf b/kayak.tf
index 3b131b8..53e14da 100644
--- a/kayak.tf
+++ a/kayak.tf
@@ -39,4 +39,3 @@
#   type   = "A"
#   ttl    = 120
# }

diff --git a/klaxon.tf b/klaxon.tf
index d34deeb..47401ae 100644
--- a/klaxon.tf
+++ a/klaxon.tf
@@ -1,20 +1,20 @@
module "klaxon-db" {

  source   = "modules/postgres"
  source   = "./modules/postgres"
  name     = "klaxon"
  password = "${data.pass_password.klaxon-db-password.password}"
  password = data.pass_password.klaxon-db-password.password
}

module "klaxon" {

  name   = "klaxon"
  source = "modules/container"
  source = "./modules/container"

  web {

  web = {

    expose = true
    port   = "3000"
    host   = "klaxon.${var.root-domain}"
  }

  resource {

  resource = {

    memory      = 1024
    memory_swap = 1024
  }
@@ -29,18 +29,12 @@
    "KLAXON_FORCE_SSL=false",
    "KLAXON_COMPILE_ASSETS=true",
    "ADMIN_EMAILS=klaxon@captnemo.in",
    "MAILER_FROM_ADDRESS=klaxon@sendgrid.captnemo.in"
    "MAILER_FROM_ADDRESS=klaxon@sendgrid.captnemo.in",
  ]
  restart = "always"

  image = "themarshallproject/klaxon"

  networks_advanced = [

    {
      name = "traefik"
      }, {
      name = "postgres"
      }, {
      name = "external"
  }]
  networks = ["postgres", "external"]
}

diff --git a/kube-test.tf b/kube-test.tf
index 89bb3a9..66bfa62 100644
--- a/kube-test.tf
+++ a/kube-test.tf
@@ -17,4 +17,3 @@
#     }
#   }
# }

diff --git a/main.tf b/main.tf
index 29ea359..5a45435 100644
--- a/main.tf
+++ a/main.tf
@@ -1,96 +1,97 @@
module "cloudflare" {

  source = "cloudflare"
  domain = "bb8.fun"
  ips    = "${var.ips}"
  source  = "./cloudflare"
  domain  = "bb8.fun"
  zone_id = lookup(data.cloudflare_zones.bb8.zones[0], "id")
  ips     = var.ips

  droplet_ip = "${module.digitalocean.droplet_ipv4}"
  droplet_ip = module.digitalocean.droplet_ipv4
}

module "docker" {

  source              = "docker"
  web_username        = "${data.pass_password.web_username.password}"
  web_password        = "${data.pass_password.web_password.password}"
  cloudflare_key      = "${data.pass_password.cloudflare_key.password}"
  source              = "./docker"
  web_username        = data.pass_password.web_username.password
  web_password        = data.pass_password.web_password.password
  cloudflare_key      = data.pass_password.cloudflare_key.password
  cloudflare_email    = "bb8@captnemo.in"
  wiki_session_secret = "${data.pass_password.wiki_session_secret.password}"
  ips                 = "${var.ips}"
  wiki_session_secret = data.pass_password.wiki_session_secret.password
  ips                 = var.ips
  domain              = "bb8.fun"
}

module "db" {

  source                 = "db"
  postgres-root-password = "${data.pass_password.postgres-root-password.password}"
  ips                    = "${var.ips}"
  source                 = "./db"
  postgres-root-password = data.pass_password.postgres-root-password.password
  ips                    = var.ips
}

module "timemachine" {

  source     = "timemachine"
  ips        = "${var.ips}"
  source     = "./timemachine"
  ips        = var.ips
  username-1 = "vikalp"
  username-2 = "rishav"
  password-1 = "${data.pass_password.timemachine-password-1.password}"
  password-2 = "${data.pass_password.timemachine-password-2.password}"
  password-1 = data.pass_password.timemachine-password-1.password
  password-2 = data.pass_password.timemachine-password-2.password
}

module "gitea" {

  source            = "gitea"
  source            = "./gitea"
  domain            = "git.captnemo.in"
  traefik-labels    = "${var.traefik-common-labels}"
  ips               = "${var.ips}"
  secret-key        = "${data.pass_password.gitea-secret-key.password}"
  internal-token    = "${data.pass_password.gitea-internal-token.password}"
  smtp-password     = "${data.pass_password.gitea-smtp-password.password}"
  lfs-jwt-secret    = "${data.pass_password.gitea-lfs-jwt-secret.password}"
  oauth2-jwt-secret = "${data.pass_password.gitea-oauth2-jwt-secret.password}"
  traefik-labels    = var.traefik-common-labels
  ips               = var.ips
  secret-key        = data.pass_password.gitea-secret-key.password
  internal-token    = data.pass_password.gitea-internal-token.password
  smtp-password     = data.pass_password.gitea-smtp-password.password
  lfs-jwt-secret    = data.pass_password.gitea-lfs-jwt-secret.password
  oauth2-jwt-secret = data.pass_password.gitea-oauth2-jwt-secret.password

  //passed, but not used
  mysql-password = ""

  traefik-network-id = "${module.docker.traefik-network-id}"
  traefik-network-id = module.docker.traefik-network-id
}

module "opml" {

  source             = "opml"
  source             = "./opml"
  domain             = "opml.bb8.fun"
  client-id          = "${data.pass_password.opml-github-client-id.password}"
  client-secret      = "${data.pass_password.opml-github-client-secret.password}"
  traefik-network-id = "${module.docker.traefik-network-id}"
  client-id          = data.pass_password.opml-github-client-id.password
  client-secret      = data.pass_password.opml-github-client-secret.password
  traefik-network-id = module.docker.traefik-network-id
}

module "radicale" {

  source = "radicale"
  source = "./radicale"
  domain = "radicale.bb8.fun"
}

module "media" {

  source             = "media"
  source             = "./media"
  domain             = "bb8.fun"
  traefik-labels     = "${var.traefik-common-labels}"
  ips                = "${var.ips}"
  traefik-network-id = "${module.docker.traefik-network-id}"
  lastfm_api_key     = "${data.pass_password.navidrome-lastfm-api-key.password}"
  lastfm_secret      = "${data.pass_password.navidrome-lastfm-secret.password}"
  spotify_id         = "${data.pass_password.navidrome-spotify-id.password}"
  spotify_secret     = "${data.pass_password.navidrome-spotify-secret.password}"
  traefik-labels     = var.traefik-common-labels
  ips                = var.ips
  traefik-network-id = module.docker.traefik-network-id
  lastfm_api_key     = data.pass_password.navidrome-lastfm-api-key.password
  lastfm_secret      = data.pass_password.navidrome-lastfm-secret.password
  spotify_id         = data.pass_password.navidrome-spotify-id.password
  spotify_secret     = data.pass_password.navidrome-spotify-secret.password
}

module "monitoring" {

  source                     = "monitoring"
  gf-security-admin-password = "${data.pass_password.gf-security-admin-password.password}"
  source                     = "./monitoring"
  gf-security-admin-password = data.pass_password.gf-security-admin-password.password
  domain                     = "bb8.fun"
  transmission               = "${module.media.names-transmission}"
  traefik-labels             = "${var.traefik-common-labels}"
  ips                        = "${var.ips}"
  links-traefik              = "${module.docker.names-traefik}"
  traefik-network-id         = "${module.docker.traefik-network-id}"
  transmission               = module.media.names-transmission
  traefik-labels             = var.traefik-common-labels
  ips                        = var.ips
  links-traefik              = module.docker.names-traefik
  traefik-network-id         = module.docker.traefik-network-id
}

module "digitalocean" {

  source = "digitalocean"
  source = "./digitalocean"
}

// Used to force access to ISP related resources
# module "tinyproxy" {
#   source = "tinyproxy"
#   source = "./tinyproxy"
#   ips    = "${var.ips}"
# }
diff --git a/miniflux.tf b/miniflux.tf
index b72ae8b..a4f74f1 100644
--- a/miniflux.tf
+++ a/miniflux.tf
@@ -1,19 +1,15 @@
module "miniflux-container" {

  name   = "miniflux"
  source = "modules/container"
  image  = "miniflux/miniflux:2.0.28"
  source = "./modules/container"
  image  = "miniflux/miniflux:2.0.33"

  web {

  web = {

    expose = true
    port   = 8080
    host   = "rss.captnemo.in"
  }

  networks = "${list(

    data.docker_network.bridge.id,
    module.docker.traefik-network-id,
    module.db.postgres-network-id
  )}"
  networks = ["bridge", "postgres"]

  env = [

    "DATABASE_URL=postgres://miniflux:${data.pass_password.miniflux-db-password.password}@postgres/miniflux?sslmode=disable",
@@ -22,7 +18,8 @@
}

module "miniflux-db" {

  source   = "modules/postgres"
  source   = "./modules/postgres"
  name     = "miniflux"
  password = "${data.pass_password.miniflux-db-password.password}"
  password = data.pass_password.miniflux-db-password.password
}

diff --git a/nextcloud.tf b/nextcloud.tf
index 3ff3a20..7ad9f87 100644
--- a/nextcloud.tf
+++ a/nextcloud.tf
@@ -1,18 +1,20 @@
module "nextcloud-db" {

  source   = "modules/postgres"
  source   = "./modules/postgres"
  name     = "nextcloud"
  password = "${data.pass_password.nextcloud-db-password.password}"
  password = data.pass_password.nextcloud-db-password.password
}

module "nextcloud-container" {

  source = "modules/container"
  source = "./modules/container"
  name   = "nextcloud"
  image  = "nextcloud:stable-apache"

  volumes = [{

    container_path = "/var/www/html"
    host_path      = "/mnt/xwing/data/nextcloud"
  }]
  volumes = [

    {
      container_path = "/var/www/html"
      host_path      = "/mnt/xwing/data/nextcloud"
    },
  ]

  env = [

    "POSTGRES_DB=nextcloud",
@@ -24,28 +26,18 @@
    "REDIS_HOST=nextcloud-redis",
  ]

  resource {

  resource = {

    memory      = 1024
    memory_swap = 1024
  }

  web {

  web = {

    expose = true
    port   = 80
    host   = "c.${var.root-domain}"
  }

  networks_advanced = [

    {
      name = "traefik"
    },
    {
      name = "nextcloud"
    },
    {
      name = "postgres"
    },
  ]
  networks = ["nextcloud", "postgres"]
}

resource "docker_network" "nextcloud" {

@@ -55,23 +47,15 @@

module "nextcloud-redis" {

  name       = "nextcloud-redis"
  source     = "modules/container"
  source     = "./modules/container"
  image      = "redis:alpine"
  keep_image = true

  networks_advanced = [

    {
      name = "nextcloud"
    },
  ]

  # ThisSucks
  web {

    expose = "false"
  }
  networks = ["nextcloud"]

  resource {

  resource = {

    memory      = 256
    memory_swap = 256
  }
}

diff --git a/providers.tf b/providers.tf
index 5b660f3..160466a 100644
--- a/providers.tf
+++ a/providers.tf
@@ -1,41 +1,53 @@
provider "docker" {

  host      = "tcp://docker.vpn.bb8.fun:2376"
  cert_path = "./secrets/tatooine"
  version   = "~> 2.2.0"


}

provider "docker" {

  host      = "tcp://docker.dovpn.bb8.fun:2376"
  cert_path = "./secrets/sydney"
  version   = "~> 2.2.0"
  alias     = "sydney"
}

provider "kubernetes" {

  # version = "1.3.0-custom"
  host = "https://k8s.bb8.fun:6443"

  config_path = "${path.root}/k8s/auth/kubeconfig"
}

provider "cloudflare" {

  email = "bb8@captnemo.in"
  token = "${data.pass_password.cloudflare_key.password}"
  email   = "bb8@captnemo.in"
  api_key = data.pass_password.cloudflare_key.password
}

provider "postgresql" {

  host     = "postgres.vpn.bb8.fun"
  port     = 5432
  username = "postgres"
  password = "${data.pass_password.postgres-root-password.password}"
  password = data.pass_password.postgres-root-password.password
  sslmode  = "disable"
}

provider "digitalocean" {

  token = "${data.pass_password.digitalocean-token.password}"
  token = data.pass_password.digitalocean-token.password
}

provider "pass" {

  store_dir     = "/home/nemo/.password-store/Nebula/"
  refresh_store = false
}


terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/pulse.tf b/pulse.tf
index 98c17bf..e022acc 100644
--- a/pulse.tf
+++ a/pulse.tf
@@ -21,4 +21,3 @@
#     memory = 2048
#   }
# }

diff --git a/rss-bridge.tf b/rss-bridge.tf
index 226b114..5bb7b93 100644
--- a/rss-bridge.tf
+++ a/rss-bridge.tf
@@ -1,30 +1,26 @@
module "rss-bridge" {

  name   = "rss-bridge"
  source = "modules/container"
  source = "./modules/container"

  image = "captn3m0/rss-bridge:develop"

  resource {

  resource = {

    memory      = 256
    memory_swap = 256
  }

  web {

  web = {

    expose = "true"
    host   = "rss-bridge.${var.root-domain}"
  }

  networks_advanced = [

    {
      name = "external"
    },
  networks = ["external"]

  volumes = [

    {
      name = "traefik"
      container_path = "/app/whitelist.txt"
      host_path      = "/mnt/xwing/config/rss-bridge/whitelist.txt"
    },
  ]

  volumes = [{

    container_path = "/app/whitelist.txt"
    host_path      = "/mnt/xwing/config/rss-bridge/whitelist.txt"
  }]
}

diff --git a/secrets.tf b/secrets.tf
index c45b699..7f48b3d 100644
--- a/secrets.tf
+++ a/secrets.tf
@@ -1,169 +1,174 @@
data "pass_password" "airsonic-smtp-password" {

  path = "AIRSONIC_SMTP_PASSWORD"
  path = "Nebula/AIRSONIC_SMTP_PASSWORD"
}

data "pass_password" "digitalocean-token" {

  path = "DO_TOKEN"
  path = "Nebula/DO_TOKEN"
}

data "pass_password" "gitea-internal-token" {

  path = "GITEA_INTERNAL_TOKEN"
  path = "Nebula/GITEA_INTERNAL_TOKEN"
}

data "pass_password" "gitea-lfs-jwt-secret" {

  path = "GITEA_LFS_JWT_SECRET"
  path = "Nebula/GITEA_LFS_JWT_SECRET"
}

data "pass_password" "gitea-secret-key" {

  path = "GITEA_SECRET_KEY"
  path = "Nebula/GITEA_SECRET_KEY"
}

data "pass_password" "gitea-oauth2-jwt-secret" {

  path = "GITEA_OAUTH2_JWT_SECRET"
  path = "Nebula/GITEA_OAUTH2_JWT_SECRET"
}

data "pass_password" "gf-security-admin-password" {

  path = "GRAFANA_ADMIN_PASSWORD"
  path = "Nebula/GRAFANA_ADMIN_PASSWORD"
}

data "pass_password" "gitea-smtp-password" {

  path = "GITEA_SMTP_PASSWORD"
  path = "Nebula/GITEA_SMTP_PASSWORD"
}

data "pass_password" "miniflux-db-password" {

  path = "MINIFLUX_DB_PASSWORD"
  path = "Nebula/MINIFLUX_DB_PASSWORD"
}

data "pass_password" "cloudflare_key" {

  path = "CLOUDFLARE_KEY"
  path = "Nebula/CLOUDFLARE_KEY"
}

// /me gives up on upper casing here and scripts it instead

data "pass_password" "monica-app-key" {

  path = "monica-app-key"
  path = "Nebula/monica-app-key"
}

data "pass_password" "monica-db-password" {

  path = "monica-db-password"
  path = "Nebula/monica-db-password"
}

data "pass_password" "monica-hash-salt" {

  path = "monica-hash-salt"
  path = "Nebula/monica-hash-salt"
}

data "pass_password" "monica-smtp-password" {

  path = "monica-smtp-password"
  path = "Nebula/monica-smtp-password"
}

data "pass_password" "nextcloud-db-password" {

  path = "nextcloud-db-password"
  path = "Nebula/nextcloud-db-password"
}

data "pass_password" "opml-github-client-id" {

  path = "opml-github-client-id"
  path = "Nebula/opml-github-client-id"
}

data "pass_password" "opml-github-client-secret" {

  path = "opml-github-client-secret"
  path = "Nebula/opml-github-client-secret"
}

data "pass_password" "outline_secret_key" {

  path = "outline-secret-key"
  path = "Nebula/outline-secret-key"
}

data "pass_password" "outline_slack_app_id" {

  path = "outline-slack-app-id"
  path = "Nebula/outline-slack-app-id"
}

data "pass_password" "outline_slack_key" {

  path = "outline-slack-key"
  path = "Nebula/outline-slack-key"
}

data "pass_password" "outline_slack_secret" {

  path = "outline-slack-secret"
  path = "Nebula/outline-slack-secret"
}

data "pass_password" "outline_slack_verification_token" {

  path = "outline-slack-verification-token"
  path = "Nebula/outline-slack-verification-token"
}

data "pass_password" "outline_smtp_password" {

  path = "outline-smtp-password"
  path = "Nebula/outline-smtp-password"
}

data "pass_password" "pihole_password" {

  path = "pihole-password"
  path = "Nebula/pihole_password"
}

data "pass_password" "syncserver_secret" {

  path = "SYNCSERVER_SECRET"
  path = "Nebula/SYNCSERVER_SECRET"
}

data "pass_password" "timemachine-password-1" {

  path = "timemachine-password-1"
  path = "Nebula/timemachine-password-1"
}

data "pass_password" "timemachine-password-2" {

  path = "timemachine-password-2"
  path = "Nebula/timemachine-password-2"
}

data "pass_password" "postgres-root-password" {

  path = "postgres-root-password"
  path = "Nebula/postgres-root-password"
}

data "pass_password" "znc_pass" {

  path = "znc-pass"
  path = "Nebula/znc-pass"
}

data "pass_password" "znc_user" {

  path = "znc-user"
  path = "Nebula/znc-user"
}

data "pass_password" "wiki_session_secret" {

  path = "wiki_session_secret"
  path = "Nebula/wiki_session_secret"
}

data "pass_password" "web_username" {

  path = "web_username"
  path = "Nebula/web_username"
}

data "pass_password" "web_password" {

  path = "web_password"
  path = "Nebula/web_password"
}

data "pass_password" "stringer-db-password" {

  path = "stringer-db-password"
  path = "Nebula/stringer-db-password"
}

data "pass_password" "stringer-secret-token" {

  path = "stringer-secret-token"
  path = "Nebula/stringer-secret-token"
}

data "pass_password" "wiki-db-password" {

  path = "wiki-db-password"
  path = "Nebula/wiki-db-password"
}

data "pass_password" "klaxon-db-password" {

  path = "klaxon-db-password"
  path = "Nebula/klaxon-db-password"
}

data "pass_password" "klaxon-secret-key" {

  path = "klaxon-secret-key"
  path = "Nebula/klaxon-secret-key"
}

data "pass_password" "klaxon-sendgrid-password" {

  path = "klaxon-sendgrid-password"
  path = "Nebula/klaxon-sendgrid-password"
}


data "pass_password" "navidrome-lastfm-api-key" {

  path = "navidrome-lastfm-api-key"
  path = "Nebula/navidrome-lastfm-api-key"
}

data "pass_password" "navidrome-lastfm-secret" {

  path = "navidrome-lastfm-secret"
  path = "Nebula/navidrome-lastfm-secret"
}

data "pass_password" "navidrome-spotify-id" {

  path = "navidrome-spotify-id"
  path = "Nebula/navidrome-spotify-id"
}

data "pass_password" "navidrome-spotify-secret" {

  path = "navidrome-spotify-secret"
  path = "Nebula/navidrome-spotify-secret"
}

diff --git a/server.tf b/server.tf
deleted file mode 100644
index 8b13789..0000000 100644
--- a/server.tf
+++ /dev/null
@@ -1,1 +1,0 @@

diff --git a/state.tf b/state.tf
index 91d84e1..13d526c 100644
--- a/state.tf
+++ a/state.tf
@@ -6,3 +6,4 @@
    profile = "nebula"
  }
}

diff --git a/variables.tf b/variables.tf
index 475bb36..e628539 100644
--- a/variables.tf
+++ a/variables.tf
@@ -1,5 +1,5 @@
variable "ips" {

  type = "map"
  type = map(string)

  default = {

    eth0    = "192.168.1.111"
@@ -11,24 +11,17 @@
}

variable "traefik-common-labels" {

  type = "map"
  type = map(string)

  default = {

    "traefik.enable" = "true"


    // HSTS
    "traefik.frontend.headers.SSLTemporaryRedirect" = "true"

    "traefik.frontend.headers.STSSeconds"           = "2592000"

    "traefik.frontend.headers.STSIncludeSubdomains" = "false"


    // X-Powered-By, Server headers
    "traefik.frontend.headers.customResponseHeaders" = "X-Powered-By:Allomancy||X-Server:Blackbox||X-Clacks-Overhead:GNU Terry Pratchett"


    // X-Frame-Options
    "traefik.frontend.headers.customFrameOptionsValue" = "ALLOW-FROM https://bb8.fun/"

    "traefik.frontend.headers.contentTypeNosniff"      = "true"

    "traefik.frontend.headers.browserXSSFilter"        = "true"


    "traefik.frontend.headers.browserXSSFilter"      = "true"

    // Use the Traefik network
    "traefik.docker.network" = "traefik"

  }
@@ -38,3 +31,4 @@
  description = "root domain for most applications"
  default     = "bb8.fun"
}

diff --git a/wiki.tf b/wiki.tf
index 8e743bc..824c017 100644
--- a/wiki.tf
+++ a/wiki.tf
@@ -1,50 +1,44 @@
data "template_file" "wiki-config" {

  template = "${file("docker/conf/wiki.tpl")}"
  vars {

    DB_PASSWORD = "${data.pass_password.wiki-db-password.password}"
  template = file("docker/conf/wiki.tpl")
  vars = {

    DB_PASSWORD = data.pass_password.wiki-db-password.password
  }
}

resource "local_file" "wiki-config" {

  content  = "${data.template_file.wiki-config.rendered}"
  content  = data.template_file.wiki-config.rendered
  filename = "docker/conf/wiki.yml"
}

module "wiki-container" {

  name   = "wiki2"
  source = "modules/container"
  source = "./modules/container"
  image  = "requarks/wiki:2"

  resource {

  resource = {

    memory      = 1024
    memory_swap = 1024
  }

  web {

  web = {

    expose = true
    port   = 3000
    host   = "wiki.bb8.fun"
  }

  networks_advanced = [

    {
      name = "traefik"
      }, {
      name = "postgres"
      }, {
      name = "external"
  }]
  networks = ["postgres", "external"]

  uploads = [

    {
      content = "${file("docker/conf/wiki.yml")}"
      content = file("docker/conf/wiki.yml")
      file    = "/wiki/config.yml"
    }
    },
  ]

  volumes = [{

    host_path      = "/mnt/xwing/data/wiki/data"
    container_path = "/data"
  volumes = [

    {
      host_path      = "/mnt/xwing/data/wiki/data"
      container_path = "/data"
    },
    {
      host_path      = "/mnt/xwing/data/wiki/databackup"
@@ -53,11 +47,13 @@
    {
      host_path      = "/mnt/xwing/data/wiki/repo"
      container_path = "/old/repo"
  }]
    },
  ]
}

module "wiki-db" {

  source   = "modules/postgres"
  source   = "./modules/postgres"
  name     = "wikijs"
  password = "${data.pass_password.wiki-db-password.password}"
  password = data.pass_password.wiki-db-password.password
}

diff --git a/znc.tf b/znc.tf
index 6375d01..1749a6b 100644
--- a/znc.tf
+++ a/znc.tf
@@ -1,21 +1,21 @@
module "znc" {

  source = "modules/container"
  source = "./modules/container"
  image  = "znc:latest"
  name   = "znc"

  web {

    expose = "false"
    host   = ""
  }
  volumes = [

    {
      container_path = "/znc-data"
      host_path      = "/mnt/xwing/config/znc"
    },
  ]

  volumes = [{

    container_path = "/znc-data"
    host_path      = "/mnt/xwing/config/znc"
  }]

  ports = [{

    internal = 6697
    external = 6697
    ip       = "${var.ips["tun0"]}"
  }]
  ports = [

    {
      internal = "6697"
      external = "6697"
      ip       = var.ips["tun0"]
    },
  ]
}

diff --git a/cloudflare/main.tf b/cloudflare/main.tf
index dbaec8e..bd6b637 100644
--- a/cloudflare/main.tf
+++ a/cloudflare/main.tf
@@ -1,21 +1,21 @@
/**
 *   in.bb8.fun
 * *.in.bb8.fun
 */

resource "cloudflare_record" "home" {

  domain = "${var.domain}"
  name   = "in"
  value  = "${var.ips["eth0"]}"
  type   = "A"
  zone_id = var.zone_id
  name    = "in"
  value   = var.ips["eth0"]
  type    = "A"
}

resource "cloudflare_record" "home-wildcard" {

  domain = "${var.domain}"
  name   = "*.in"
  value  = "${cloudflare_record.home.hostname}"
  type   = "CNAME"
  ttl    = 3600
  zone_id = var.zone_id
  name    = "*.in"
  value   = cloudflare_record.home.hostname
  type    = "CNAME"
  ttl     = 3600
}

/**
@@ -23,42 +23,42 @@
 * *.bb8.fun  -> bb8.fun
 */
resource "cloudflare_record" "internet" {

  domain = "${var.domain}"
  name   = "@"
  value  = "${var.droplet_ip}"
  type   = "A"
  zone_id = var.zone_id
  name    = "@"
  value   = var.droplet_ip
  type    = "A"
}

resource "cloudflare_record" "internet-wildcard" {

  domain = "${var.domain}"
  name   = "*.${var.domain}"
  value  = "${cloudflare_record.internet.hostname}"
  type   = "CNAME"
  ttl    = 3600
  zone_id = var.zone_id
  name    = var.domain
  value   = cloudflare_record.internet.hostname
  type    = "CNAME"
  ttl     = 3600
}

resource "cloudflare_record" "dns" {

  domain = "${var.domain}"
  name   = "dns"
  value  = "${var.ips["static"]}"
  type   = "A"
  zone_id = var.zone_id
  name    = "dns"
  value   = var.ips["static"]
  type    = "A"
}

resource "cloudflare_record" "doh" {

  domain = "${var.domain}"
  name   = "doh"
  value  = "${var.ips["static"]}"
  type   = "A"
  zone_id = var.zone_id
  name    = "doh"
  value   = var.ips["static"]
  type    = "A"
}

// This ensures that _acme-challenge is not a CNAME
// alongside the above wildcard CNAME entry.
resource "cloudflare_record" "acme-no-cname-1" {

  domain = "${var.domain}"
  name   = "_acme-challenge.${var.domain}"
  type   = "A"
  value  = "127.0.0.1"
  ttl    = "300"
  zone_id = var.zone_id
  name    = "_acme-challenge.${var.domain}"
  type    = "A"
  value   = "127.0.0.1"
  ttl     = "300"
}

/**
@@ -66,18 +66,18 @@
 * *.vpn.bb8.fun
 */
resource "cloudflare_record" "vpn" {

  domain = "${var.domain}"
  name   = "vpn"
  value  = "${var.ips["tun0"]}"
  type   = "A"
  zone_id = var.zone_id
  name    = "vpn"
  value   = var.ips["tun0"]
  type    = "A"
}

resource "cloudflare_record" "vpn_wildcard" {

  domain = "${var.domain}"
  name   = "*.vpn.${var.domain}"
  value  = "${cloudflare_record.vpn.hostname}"
  type   = "CNAME"
  ttl    = 3600
  zone_id = var.zone_id
  name    = "*.vpn.${var.domain}"
  value   = cloudflare_record.vpn.hostname
  type    = "CNAME"
  ttl     = 3600
}

/**
@@ -85,25 +85,25 @@
 * *.vpn.bb8.fun
 */
resource "cloudflare_record" "dovpn" {

  domain = "${var.domain}"
  name   = "dovpn"
  value  = "${var.ips["dovpn"]}"
  type   = "A"
  zone_id = var.zone_id
  name    = "dovpn"
  value   = var.ips["dovpn"]
  type    = "A"
}

resource "cloudflare_record" "dovpn_wildcard" {

  domain = "${var.domain}"
  name   = "*.dovpn.${var.domain}"
  value  = "${cloudflare_record.dovpn.hostname}"
  type   = "CNAME"
  ttl    = 3600
  zone_id = var.zone_id
  name    = "*.dovpn.${var.domain}"
  value   = cloudflare_record.dovpn.hostname
  type    = "CNAME"
  ttl     = 3600
}

resource "cloudflare_record" "etcd" {

  domain = "${var.domain}"
  name   = "etcd"
  value  = "${var.ips["dovpn"]}"
  type   = "A"
  zone_id = var.zone_id
  name    = "etcd"
  value   = var.ips["dovpn"]
  type    = "A"
}

########################
@@ -111,21 +111,21 @@
########################

resource "cloudflare_record" "mailgun-spf" {

  domain = "${var.domain}"
  name   = "l"
  value  = "v=spf1 include:mailgun.org ~all"
  type   = "TXT"
  zone_id = var.zone_id
  name    = "l"
  value   = "v=spf1 include:mailgun.org ~all"
  type    = "TXT"
}

resource "cloudflare_record" "mailgun-dkim" {

  domain = "${var.domain}"
  name   = "k1._domainkey.l"
  value  = "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnbP+IQkuPkgmUhpqCKzIdDSZ0HazaMp+cdBH++LBed8oY8/jmV8BhxMp5JwyePzRTxneT8ASsRtcp7CQ3z4nMC7aFX0kH6Bnu2v+u2JWudxs8x0I02OrPbSaQ5QVQdbAaCUCEfCQ06LJsn8aqPNrRIOWEMnxln+ebFJ0wKGscFQIDAQAB"
  type   = "TXT"
  zone_id = var.zone_id
  name    = "k1._domainkey.l"
  value   = "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnbP+IQkuPkgmUhpqCKzIdDSZ0HazaMp+cdBH++LBed8oY8/jmV8BhxMp5JwyePzRTxneT8ASsRtcp7CQ3z4nMC7aFX0kH6Bnu2v+u2JWudxs8x0I02OrPbSaQ5QVQdbAaCUCEfCQ06LJsn8aqPNrRIOWEMnxln+ebFJ0wKGscFQIDAQAB"
  type    = "TXT"
}

resource "cloudflare_record" "mailgun-mxa" {

  domain   = "${var.domain}"
  zone_id  = var.zone_id
  name     = "l"
  value    = "mxa.mailgun.org"
  type     = "MX"
@@ -133,7 +133,7 @@
}

resource "cloudflare_record" "mailgun-mxb" {

  domain   = "${var.domain}"
  zone_id  = var.zone_id
  name     = "l"
  value    = "mxb.mailgun.org"
  type     = "MX"
@@ -141,9 +141,9 @@
}

resource "cloudflare_record" "k8s" {

  domain = "${var.domain}"
  name   = "k8s"
  value  = "10.8.0.1"
  type   = "A"
  ttl    = 3600
  zone_id = var.zone_id
  name    = "k8s"
  value   = "10.8.0.1"
  type    = "A"
  ttl     = 3600
}
diff --git a/cloudflare/providers.tf b/cloudflare/providers.tf
new file mode 100644
index 0000000..6d40668 100644
--- /dev/null
+++ a/cloudflare/providers.tf
@@ -1,0 +1,7 @@
terraform {

  required_providers {

    cloudflare = {

      source = "cloudflare/cloudflare"
    }
  }
}
diff --git a/cloudflare/variables.tf b/cloudflare/variables.tf
index 6c1cb5e..c99a7d9 100644
--- a/cloudflare/variables.tf
+++ a/cloudflare/variables.tf
@@ -1,9 +1,10 @@
variable "domain" {

  type = "string"
  type = string
}

variable "ips" {

  type = "map"
  type = map
}

variable "droplet_ip" {}
variable "zone_id" {}
diff --git a/db/network.tf b/db/network.tf
index 72659f8..6593e41 100644
--- a/db/network.tf
+++ a/db/network.tf
@@ -8,3 +8,4 @@
    gateway = "172.20.0.9"
  }
}

diff --git a/db/outputs.tf b/db/outputs.tf
index 0db41e2..400bd17 100644
--- a/db/outputs.tf
+++ a/db/outputs.tf
@@ -1,3 +1,4 @@
output "postgres-network-id" {

  value = "${docker_network.postgres.name}"
  value = docker_network.postgres.name
}

diff --git a/db/postgres.tf b/db/postgres.tf
index f56cb20..95ec82b 100644
--- a/db/postgres.tf
+++ a/db/postgres.tf
@@ -1,18 +1,18 @@
resource "docker_container" "postgres" {

  name  = "postgres"
  image = "${docker_image.postgres.latest}"
  image = docker_image.postgres.latest

  volumes {

    volume_name    = "${docker_volume.postgres_volume.name}"
    volume_name    = docker_volume.postgres_volume.name
    container_path = "/var/lib/postgresql/data"
    host_path      = "${docker_volume.postgres_volume.mountpoint}"
    host_path      = docker_volume.postgres_volume.mountpoint
  }

  // This is so that other host-only services can share this
  ports {

    internal = 5432
    external = 5432
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
  }

  // This is a not-so-great idea
@@ -20,7 +20,7 @@
  ports {

    internal = 5432
    external = 5432
    ip       = "${var.ips["tun0"]}"
    ip       = var.ips["tun0"]
  }

  memory                = 256
@@ -32,12 +32,12 @@
    "POSTGRES_PASSWORD=${var.postgres-root-password}",
  ]

  networks = ["${docker_network.postgres.id}", "${data.docker_network.bridge.id}"]
  networks = [docker_network.postgres.id, data.docker_network.bridge.id]
}

resource "docker_image" "postgres" {

  name          = "${data.docker_registry_image.postgres.name}"
  pull_triggers = ["${data.docker_registry_image.postgres.sha256_digest}"]
  name          = data.docker_registry_image.postgres.name
  pull_triggers = [data.docker_registry_image.postgres.sha256_digest]
}

data "docker_registry_image" "postgres" {

@@ -47,3 +47,4 @@
data "docker_network" "bridge" {

  name = "bridge"
}

diff --git a/db/providers.tf b/db/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/db/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/db/variables.tf b/db/variables.tf
index e57033a..d6424da 100644
--- a/db/variables.tf
+++ a/db/variables.tf
@@ -1,10 +1,12 @@
variable "postgres-version" {

  description = "postgres version to use for fetching the docker image"
  default     = "10-alpine"
}

variable "ips" {

  type = "map"
  type = map(string)
}

variable "postgres-root-password" {}
variable "postgres-root-password" {

}

diff --git a/db/volumes.tf b/db/volumes.tf
index 7c6a85c..b27958c 100644
--- a/db/volumes.tf
+++ a/db/volumes.tf
@@ -1,3 +1,4 @@
resource "docker_volume" "postgres_volume" {

  name = "postgres_volume"
}

diff --git a/digitalocean/droplets.tf b/digitalocean/droplets.tf
index 6c79fd8..5aafef1 100644
--- a/digitalocean/droplets.tf
+++ a/digitalocean/droplets.tf
@@ -1,5 +1,5 @@
resource "digitalocean_droplet" "sydney" {

  image              = ""
  image              = "??"
  name               = "sydney.captnemo.in"
  region             = "blr1"
  size               = "s-1vcpu-2gb"
@@ -18,5 +18,6 @@
}

output "droplet_ipv4" {

  value = "${digitalocean_droplet.sydney.ipv4_address}"
  value = digitalocean_droplet.sydney.ipv4_address
}

diff --git a/digitalocean/firewall.tf b/digitalocean/firewall.tf
index 919e8a9..ce4045b 100644
--- a/digitalocean/firewall.tf
+++ a/digitalocean/firewall.tf
@@ -1,38 +1,35 @@
resource "digitalocean_firewall" "web" {

  name = "web-inbound"

  inbound_rule = [

    {
      protocol         = "tcp"
      port_range       = "80"
      source_addresses = ["0.0.0.0/0", "::/0"]
    },
    {
      protocol         = "tcp"
      port_range       = "443"
      source_addresses = ["0.0.0.0/0", "::/0"]
    },
  ]
  inbound_rule {

    protocol         = "tcp"
    port_range       = "80"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }
  inbound_rule {

    protocol         = "tcp"
    port_range       = "443"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }
}

resource "digitalocean_firewall" "ssh" {

  name = "ssh-inbound"

  inbound_rule = [

    {
      protocol         = "tcp"
      port_range       = "22"
      source_addresses = ["0.0.0.0/0", "::/0"]
    },
    {
      protocol         = "tcp"
      port_range       = "222"
      source_addresses = ["0.0.0.0/0", "::/0"]
    },
    {
      protocol         = "tcp"
      port_range       = "24"
      source_addresses = ["0.0.0.0/0", "::/0"]
    },
  ]
  inbound_rule {

    protocol         = "tcp"
    port_range       = "22"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }
  inbound_rule {

    protocol         = "tcp"
    port_range       = "222"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }
  inbound_rule {

    protocol         = "tcp"
    port_range       = "24"
    source_addresses = ["0.0.0.0/0", "::/0"]
  }
}

diff --git a/digitalocean/networking.tf b/digitalocean/networking.tf
index fcefd33..94b1672 100644
--- a/digitalocean/networking.tf
+++ a/digitalocean/networking.tf
@@ -1,4 +1,5 @@
resource "digitalocean_floating_ip" "sydney" {

  droplet_id = "${digitalocean_droplet.sydney.id}"
  region     = "${digitalocean_droplet.sydney.region}"
  droplet_id = digitalocean_droplet.sydney.id
  region     = digitalocean_droplet.sydney.region
}

diff --git a/digitalocean/providers.tf b/digitalocean/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/digitalocean/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/docker/data.tf b/docker/data.tf
index 7305e7b..d10d4a0 100644
--- a/docker/data.tf
+++ a/docker/data.tf
@@ -9,3 +9,4 @@
data "docker_registry_image" "lychee" {

  name = "linuxserver/lychee:latest"
}

diff --git a/docker/got.tf b/docker/got.tf
index aa9a553..f9b0328 100644
--- a/docker/got.tf
+++ a/docker/got.tf
@@ -6,22 +6,15 @@
#   name          = "${data.docker_registry_image.gotviz.name}"
#   pull_triggers = ["${data.docker_registry_image.gotviz.sha256_digest}"]
# }


# resource "docker_container" "gotviz" {
#   name  = "gotviz"
#   image = "${docker_image.gotviz.latest}"


#   labels = "${merge(
#     local.traefik_common_labels, map(
#       "traefik.port", 8080,
#       "traefik.frontend.rule","Host:got-relationships.${var.domain}"
#   ))}"


#   restart               = "unless-stopped"
#   destroy_grace_seconds = 60
#   must_run              = true
# }

diff --git a/docker/images.tf b/docker/images.tf
index 583dc7b..73a2096 100644
--- a/docker/images.tf
+++ a/docker/images.tf
@@ -1,11 +1,11 @@
resource "docker_image" "traefik17" {

  name          = "${data.docker_registry_image.traefik.name}"
  pull_triggers = ["${data.docker_registry_image.traefik.sha256_digest}"]
  name          = data.docker_registry_image.traefik.name
  pull_triggers = [data.docker_registry_image.traefik.sha256_digest]
}

resource "docker_image" "ubooquity" {

  name          = "${data.docker_registry_image.ubooquity.name}"
  pull_triggers = ["${data.docker_registry_image.ubooquity.sha256_digest}"]
  name          = data.docker_registry_image.ubooquity.name
  pull_triggers = [data.docker_registry_image.ubooquity.sha256_digest]
}

# resource "docker_image" "lychee" {
diff --git a/docker/locals.tf b/docker/locals.tf
index fcdafc3..70ba788 100644
--- a/docker/locals.tf
+++ a/docker/locals.tf
@@ -1,20 +1,17 @@
locals {

  traefik_common_labels {

  traefik_common_labels = {

    "traefik.enable" = "true"


    // HSTS
    "traefik.frontend.headers.SSLTemporaryRedirect" = "true"

    "traefik.frontend.headers.STSSeconds"           = "2592000"

    "traefik.frontend.headers.STSIncludeSubdomains" = "false"


    // X-Powered-By, Server headers
    "traefik.frontend.headers.customResponseHeaders" = "${var.xpoweredby}"


    "traefik.frontend.headers.customResponseHeaders" = var.xpoweredby

    // X-Frame-Options
    "traefik.frontend.headers.customFrameOptionsValue" = "${var.xfo_allow}"

    "traefik.frontend.headers.customFrameOptionsValue" = var.xfo_allow

    "traefik.frontend.headers.contentTypeNosniff"      = "true"

    "traefik.frontend.headers.browserXSSFilter"        = "true"


    "traefik.docker.network" = "traefik"

    "traefik.docker.network"                           = "traefik"

  }
}

diff --git a/docker/lychee.tf b/docker/lychee.tf
index a2f0a9c..dedc70a 100644
--- a/docker/lychee.tf
+++ a/docker/lychee.tf
@@ -28,4 +28,3 @@
#   ]
#   # links = ["${var.links-mariadb}"]
# }

diff --git a/docker/main.tf b/docker/main.tf
deleted file mode 100644
index 8b13789..0000000 100644
--- a/docker/main.tf
+++ /dev/null
@@ -1,1 +1,0 @@

diff --git a/docker/network.tf b/docker/network.tf
index c2b6e0a..908d003 100644
--- a/docker/network.tf
+++ a/docker/network.tf
@@ -1,5 +1,6 @@
resource "docker_network" "traefik" {

  name     = "traefik"
  driver   = "bridge"
  internal = true
}

diff --git a/docker/outputs.tf b/docker/outputs.tf
index 99b17d9..ca08f80 100644
--- a/docker/outputs.tf
+++ a/docker/outputs.tf
@@ -1,15 +1,16 @@
# output "lychee-ip" {
#   value = "${docker_container.lychee.ip_address}"
# }

output "names-traefik" {

  value = "${docker_container.traefik.name}"
  value = docker_container.traefik.name
}

output "traefik-network-id" {

  value = "${docker_network.traefik.id}"
  value = docker_network.traefik.id
}

output "auth-header" {

  value = "${var.basic_auth}"
  value = var.basic_auth
}

diff --git a/docker/providers.tf b/docker/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/docker/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/docker/traefik.tf b/docker/traefik.tf
index 4671ea7..a178342 100644
--- a/docker/traefik.tf
+++ a/docker/traefik.tf
@@ -1,70 +1,78 @@
resource "docker_container" "traefik" {

  name  = "traefik"
  image = "${docker_image.traefik17.latest}"
  image = docker_image.traefik17.latest

  # Admin Backend
  ports {

    internal = 1111
    external = 1111
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
  }

  ports {

    internal = 1111
    external = 1111
    ip       = "${var.ips["tun0"]}"
    ip       = var.ips["tun0"]
  }

  # Local Web Server
  ports {

    internal = 80
    external = 80
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
  }

  # Local Web Server (HTTPS)
  ports {

    internal = 443
    external = 443
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
  }

  # Proxied via sydney.captnemo.in
  ports {

    internal = 443
    external = 443
    ip       = "${var.ips["tun0"]}"
    ip       = var.ips["tun0"]
  }

  ports {

    internal = 80
    external = 80
    ip       = "${var.ips["tun0"]}"
    ip       = var.ips["tun0"]
  }

  upload {

    content = "${file("${path.module}/conf/traefik.toml")}"
    content = file("${path.module}/conf/traefik.toml")
    file    = "/etc/traefik/traefik.toml"
  }

  upload {

    content = "${file("/home/nemo/projects/personal/certs/git.captnemo.in/fullchain.pem")}"
    file    = "/etc/traefik/git.captnemo.in.crt"
    content = file(

      "/home/nemo/projects/personal/certs/git.captnemo.in/fullchain.pem",
    )
    file = "/etc/traefik/git.captnemo.in.crt"
  }

  upload {

    content = "${file("/home/nemo/projects/personal/certs/git.captnemo.in/privkey.pem")}"
    file    = "/etc/traefik/git.captnemo.in.key"
    content = file(

      "/home/nemo/projects/personal/certs/git.captnemo.in/privkey.pem",
    )
    file = "/etc/traefik/git.captnemo.in.key"
  }

  upload {

    content = "${file("/home/nemo/projects/personal/certs/rss.captnemo.in/fullchain.pem")}"
    file    = "/etc/traefik/rss.captnemo.in.crt"
    content = file(

      "/home/nemo/projects/personal/certs/rss.captnemo.in/fullchain.pem",
    )
    file = "/etc/traefik/rss.captnemo.in.crt"
  }

  upload {

    content = "${file("/home/nemo/projects/personal/certs/rss.captnemo.in/privkey.pem")}"
    file    = "/etc/traefik/rss.captnemo.in.key"
    content = file(

      "/home/nemo/projects/personal/certs/rss.captnemo.in/privkey.pem",
    )
    file = "/etc/traefik/rss.captnemo.in.key"
  }

  volumes {

@@ -85,17 +93,16 @@

  // `bridge` is auto-connected for now
  // https://github.com/terraform-providers/terraform-provider-docker/issues/10
  networks = [

    "${docker_network.traefik.id}",
    "${data.docker_network.bridge.id}",
  ]
  networks_advanced {

    name = "traefik"
  }

  networks_advanced {

    name = "bridge"
  }

  env = [

    "CLOUDFLARE_EMAIL=${var.cloudflare_email}",
    "CLOUDFLARE_API_KEY=${var.cloudflare_key}",
  ]
}

data "docker_network" "bridge" {

  name = "bridge"
}
diff --git a/docker/ubooquity.tf b/docker/ubooquity.tf
index 1486d81..d0bc92d 100644
--- a/docker/ubooquity.tf
+++ a/docker/ubooquity.tf
@@ -1,6 +1,13 @@
locals {

  l = merge(local.traefik_common_labels, {

    "traefik.port"          = 3000

    "traefik.frontend.rule" = "Host:${var.domain}"

  })
}

resource "docker_container" "ubooquity" {

  name  = "ubooquity"
  image = "${docker_image.ubooquity.latest}"
  image = docker_image.ubooquity.latest

  restart               = "unless-stopped"
  destroy_grace_seconds = 30
@@ -25,32 +32,37 @@
    host_path      = "/mnt/xwing/media/EBooks/Comics"
    container_path = "/comics"
  }

  labels {

    "traefik.enable" = "true"


    "traefik.admin.port"          = 2203

    "traefik.admin.frontend.rule" = "Host:library.${var.domain}"


    # I do not trust the Ubooquity authentication
    # it does some shady JS encryption
    "traefik.admin.frontend.auth.basic" = "${var.basic_auth}"


    "traefik.read.port"          = 2202

    "traefik.read.frontend.rule" = "Host:read.${var.domain},comics.${var.domain},books.${var.domain}"


    "traefik.read.frontend.headers.SSLTemporaryRedirect"  = "true"

    "traefik.read.frontend.headers.STSSeconds"            = "2592000"

    "traefik.read.frontend.headers.STSIncludeSubdomains"  = "false"

    "traefik.read.frontend.headers.contentTypeNosniff"    = "true"

    "traefik.read.frontend.headers.browserXSSFilter"      = "true"

    "traefik.read.frontend.headers.customResponseHeaders" = "${var.xpoweredby}"

    "traefik.frontend.headers.customFrameOptionsValue"    = "${var.xfo_allow}"

    "traefik.docker.network"                              = "traefik"

    label = "traefik.enable"
    value = "true"
  }
  labels {

    label = "traefik.admin.port"
    value = 2203
  }
  labels {

    label = "traefik.admin.frontend.rule"
    value = "Host:library.${var.domain}"
  }
  labels {

    label = "traefik.admin.frontend.auth.basic"
    value = var.basic_auth
  }
  labels {

    label = "traefik.read.port"
    value = 2202
  }
  labels {

    label = "traefik.read.frontend.rule"
    value = "Host:read.${var.domain},comics.${var.domain},books.${var.domain}"
  }
  labels {

    label = "traefik.docker.network"
    value = "traefik"
  }

  upload {

    content = "${file("${path.module}/conf/ubooquity.json")}"
    content = file("${path.module}/conf/ubooquity.json")
    file    = "/config/preferences.json"
  }

@@ -61,3 +73,4 @@
    "MAXMEM=800",
  ]
}

diff --git a/docker/variables.tf b/docker/variables.tf
index bb8a347..9e73360 100644
--- a/docker/variables.tf
+++ a/docker/variables.tf
@@ -1,18 +1,18 @@
variable "web_username" {

  type = "string"
  type = string
}

variable "web_password" {

  type = "string"
  type = string
}

variable "cloudflare_key" {

  type        = "string"
  type        = string
  description = "cloudflare API Key"
}

variable "cloudflare_email" {

  type        = "string"
  type        = string
  description = "cloudflare email address"
}

@@ -39,15 +39,15 @@
}

variable "wiki_session_secret" {

  type = "string"
  type = string
}

variable "domain" {

  type = "string"
  type = string
}

variable "ips" {

  type = "map"
  type = map(string)
}

# variable "links-mariadb" {}
diff --git a/docker/volumes.tf b/docker/volumes.tf
deleted file mode 100644
index 8b13789..0000000 100644
--- a/docker/volumes.tf
+++ /dev/null
@@ -1,1 +1,0 @@

diff --git a/gitea/data.tf b/gitea/data.tf
index 8ca4074..61be64b 100644
--- a/gitea/data.tf
+++ a/gitea/data.tf
@@ -1,6 +1,6 @@
# https://github.com/go-gitea/gitea/releases
data "docker_registry_image" "gitea" {

  name = "gitea/gitea:1.14"
  name = "gitea/gitea:1.15"
}

data "docker_registry_image" "redis" {

@@ -8,14 +8,15 @@
}

data "template_file" "gitea-config-file" {

  template = "${file("${path.module}/conf/conf.ini.tpl")}"

  vars {

    secret_key        = "${var.secret-key}"
    internal_token    = "${var.internal-token}"
    smtp_password     = "${var.smtp-password}"
    lfs-jwt-secret    = "${var.lfs-jwt-secret}"
    mysql-password    = "${var.mysql-password}"
    oauth2-jwt-secret = "${var.oauth2-jwt-secret}"
  template = file("${path.module}/conf/conf.ini.tpl")

  vars = {

    secret_key        = var.secret-key
    internal_token    = var.internal-token
    smtp_password     = var.smtp-password
    lfs-jwt-secret    = var.lfs-jwt-secret
    mysql-password    = var.mysql-password
    oauth2-jwt-secret = var.oauth2-jwt-secret
  }
}

diff --git a/gitea/main.tf b/gitea/main.tf
index bc42154..267df3f 100644
--- a/gitea/main.tf
+++ a/gitea/main.tf
@@ -1,71 +1,74 @@
locals {

  l = merge(var.traefik-labels, {

    "traefik.port"          = 3000

    "traefik.frontend.rule" = "Host:${var.domain}"

  })
}

resource "docker_container" "gitea" {

  name  = "gitea"
  image = "${docker_image.gitea.latest}"
  image = docker_image.gitea.latest

  labels = "${merge(

    var.traefik-labels, map(
      "traefik.port", 3000,
      "traefik.frontend.rule", "Host:${var.domain}"
  ))}"
  dynamic "labels" {

    for_each = local.l
    content {

      label = labels.key
      value = labels.value
    }
  }

  volumes {

    volume_name    = "${docker_volume.gitea_volume.name}"
    volume_name    = docker_volume.gitea_volume.name
    container_path = "/data"
    host_path      = "${docker_volume.gitea_volume.mountpoint}"
    host_path      = docker_volume.gitea_volume.mountpoint
  }

  # Logos
  # TODO: Add svg

  upload {

    content = "${file("${path.module}/conf/public/img/gitea-lg.png")}"
    file    = "/data/gitea/public/img/gitea-lg.png"
    content_base64 = filebase64("${path.module}/conf/public/img/gitea-lg.png")
    file           = "/data/gitea/public/img/gitea-lg.png"
  }
  upload {

    content = "${file("${path.module}/conf/public/img/gitea-sm.png")}"
    file    = "/data/gitea/public/img/gitea-sm.png"
    content_base64 = filebase64("${path.module}/conf/public/img/gitea-sm.png")
    file           = "/data/gitea/public/img/gitea-sm.png"
  }
  upload {

    content    = "${file("${path.module}/conf/public/img/gitea-sm.png")}"
    file       = "/data/gitea/public/img/favicon.png"
    executable = false
    content_base64 = filebase64("${path.module}/conf/public/img/gitea-sm.png")
    file           = "/data/gitea/public/img/favicon.png"
    executable     = false
  }
  upload {

    content = "${file("${path.module}/../docker/conf/humans.txt")}"
    content = file("${path.module}/../docker/conf/humans.txt")
    file    = "/data/gitea/public/humans.txt"
  }
  upload {

    content = "${file("${path.module}/conf/public/robots.txt")}"
    content = file("${path.module}/conf/public/robots.txt")
    file    = "/data/gitea/public/robots.txt"
  }

  # Extra Links in header
  upload {

    content = "${file("${path.module}/conf/extra_links.tmpl")}"
    content = file("${path.module}/conf/extra_links.tmpl")
    file    = "/data/gitea/templates/custom/extra_links.tmpl"
  }

  # This is the main configuration file
  upload {

    content = "${data.template_file.gitea-config-file.rendered}"
    content = data.template_file.gitea-config-file.rendered
    file    = "/data/gitea/conf/app.ini"
  }

  memory                = 800
  restart               = "always"
  destroy_grace_seconds = 10
  must_run              = true
  networks              = ["${docker_network.gitea.id}", "${var.traefik-network-id}"]

  # This doesn't work.
  # See https://github.com/terraform-providers/terraform-provider-docker/issues/48
  # lifecycle {
  #   ignore_changes = [
  #     "upload.2151376053.content",
  #     "upload.2151376053.executable",
  #     "upload.2151376053.file",
  #   ]
  # }
  networks              = ["gitea", "traefik"]
}

resource "docker_image" "gitea" {

  name          = "${data.docker_registry_image.gitea.name}"
  pull_triggers = ["${data.docker_registry_image.gitea.sha256_digest}"]
  name          = data.docker_registry_image.gitea.name
  pull_triggers = [data.docker_registry_image.gitea.sha256_digest]
}

diff --git a/gitea/mysql.tf b/gitea/mysql.tf
deleted file mode 100644
index 8b13789..0000000 100644
--- a/gitea/mysql.tf
+++ /dev/null
@@ -1,1 +1,0 @@

diff --git a/gitea/network.tf b/gitea/network.tf
index 9613ebe..142547d 100644
--- a/gitea/network.tf
+++ a/gitea/network.tf
@@ -1,4 +1,5 @@
resource "docker_network" "gitea" {

  name   = "gitea"
  driver = "bridge"
}

diff --git a/gitea/providers.tf b/gitea/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/gitea/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/gitea/redis.tf b/gitea/redis.tf
index 6d3b06c..1d2751d 100644
--- a/gitea/redis.tf
+++ a/gitea/redis.tf
@@ -1,6 +1,6 @@
resource "docker_container" "redis" {

  name  = "gitea-redis"
  image = "${docker_image.redis.latest}"
  image = docker_image.redis.latest

  volumes {

    host_path      = "/mnt/xwing/cache/gitea"
@@ -12,11 +12,12 @@
  destroy_grace_seconds = 10
  must_run              = true

  networks = ["${docker_network.gitea.id}"]
  networks = [docker_network.gitea.id]
}

resource "docker_image" "redis" {

  name          = "${data.docker_registry_image.redis.name}"
  pull_triggers = ["${data.docker_registry_image.redis.sha256_digest}"]
  name          = data.docker_registry_image.redis.name
  pull_triggers = [data.docker_registry_image.redis.sha256_digest]
  keep_locally  = true
}

diff --git a/gitea/variables.tf b/gitea/variables.tf
index 5457f6f..b52b8f9 100644
--- a/gitea/variables.tf
+++ a/gitea/variables.tf
@@ -1,18 +1,32 @@
variable "traefik-labels" {

  type = "map"
  type = map(string)
}

variable "domain" {}
variable "domain" {

}

variable "ips" {

  type = "map"
  type = map(string)
}

variable "secret-key" {

}

variable "internal-token" {

}

variable "smtp-password" {

}

variable "secret-key" {}
variable "internal-token" {}
variable "smtp-password" {}
variable "lfs-jwt-secret" {}
variable "oauth2-jwt-secret" {}
variable "mysql-password" {}
variable "lfs-jwt-secret" {

}

variable "oauth2-jwt-secret" {

}

variable "mysql-password" {

}

variable "traefik-network-id" {

}

variable "traefik-network-id" {}
diff --git a/gitea/volume.tf b/gitea/volume.tf
index 9cdc53e..7b83622 100644
--- a/gitea/volume.tf
+++ a/gitea/volume.tf
@@ -1,3 +1,4 @@
resource "docker_volume" "gitea_volume" {

  name = "gitea_volume"
}

diff --git a/kaarana/database.tf b/kaarana/database.tf
index 17756db..d29ef83 100644
--- a/kaarana/database.tf
+++ a/kaarana/database.tf
@@ -18,7 +18,7 @@
// Run a small mySQL container in this subnet

resource "docker_container" "mysql" {

  image    = "${docker_image.db.latest}"
  image    = docker_image.db.latest
  name     = "kaarana-mariadb"
  restart  = "always"
  must_run = true
@@ -35,8 +35,6 @@
    container_path = "/var/lib/mysql"
  }

  networks_advanced {

    name    = "kaarana-db"
    aliases = ["${local.db_hostname}"]
  }
  networks = ["kaarana-db"]
}

diff --git a/kaarana/images.tf b/kaarana/images.tf
index f1e3e77..fd0df71 100644
--- a/kaarana/images.tf
+++ a/kaarana/images.tf
@@ -1,10 +1,10 @@
data "docker_registry_image" "wp" {

  name = "wordpress:latest"
}

resource "docker_image" "wp" {

  name          = "wordpress"
  pull_triggers = ["${data.docker_registry_image.wp.sha256_digest}"]
  pull_triggers = [data.docker_registry_image.wp.sha256_digest]
}

data "docker_registry_image" "db" {

@@ -13,7 +13,7 @@

resource "docker_image" "db" {

  name          = "mariadb"
  pull_triggers = ["${data.docker_registry_image.db.sha256_digest}"]
  pull_triggers = [data.docker_registry_image.db.sha256_digest]
}

data "docker_registry_image" "traefik" {

@@ -22,5 +22,6 @@

resource "docker_image" "traefik" {

  name          = "traefik"
  pull_triggers = ["${data.docker_registry_image.db.sha256_digest}"]
  pull_triggers = [data.docker_registry_image.db.sha256_digest]
}

diff --git a/kaarana/traefik.tf b/kaarana/traefik.tf
index adf5ab0..2788b94 100644
--- a/kaarana/traefik.tf
+++ a/kaarana/traefik.tf
@@ -12,7 +12,7 @@

resource "docker_container" "traefik" {

  name  = "traefik"
  image = "${docker_image.traefik.latest}"
  image = docker_image.traefik.latest

  # Do not offer HTTP2
  # https://community.containo.us/t/traefikv2-http-2-0/1199
@@ -21,7 +21,7 @@
  ]

  upload {

    content = "${file("${path.module}/traefik.toml")}"
    content = file("${path.module}/traefik.toml")
    file    = "/etc/traefik/traefik.toml"
  }

@@ -53,12 +53,12 @@
  destroy_grace_seconds = 10
  must_run              = true

  networks_advanced = [

    {
      name = "bridge"
    },
    {
      name = "traefik"
    },
  ]
  networks_advanced {

    name = "bridge"
  }

  networks_advanced {

    name = "traefik"
  }
}

diff --git a/kaarana/vars.tf b/kaarana/vars.tf
index a826515..f3ec232 100644
--- a/kaarana/vars.tf
+++ a/kaarana/vars.tf
@@ -1,8 +1,12 @@
variable "root_db_password" {}
variable "db_password" {}
variable "root_db_password" {

}

variable "db_password" {

}

locals {

  username    = "wordpress"
  database    = "wordpress"
  db_hostname = "kaarana.db"
}

diff --git a/kaarana/wordpress.tf b/kaarana/wordpress.tf
index d5483f3..e6b2843 100644
--- a/kaarana/wordpress.tf
+++ a/kaarana/wordpress.tf
@@ -1,18 +1,16 @@
resource "docker_container" "wp" {

  image = "${docker_image.wp.latest}"
  image = docker_image.wp.latest
  name  = "kaarana-wordpress"

  restart  = "always"
  must_run = true

  labels {

  labels = {

    "traefik.enable"                   = "true"

    "traefik.tcp.routers.kaarana.rule" = "HostSNI(`kaarana.captnemo.in`)"

    "traefik.tcp.routers.kaarana.tls"  = "true"


    # "traefik.tcp.routers.kaarana.tls.options"                 = "foo"
    "traefik.tcp.services.wordpress.loadbalancer.server.port" = "80"


    # "traefik.tcp.routers.kaarana.entrypoints"                 = "web-secure"
    "traefik.tcp.routers.kaarana.tls.certResolver"    = "default"

    "traefik.tcp.routers.kaarana.tls.domains[0].main" = "kaarana.captnemo.in"

@@ -37,17 +35,6 @@
    ip       = "10.8.0.1"
  }

  networks_advanced = [

    {
      name = "kaarana-db"
    },
    {
      // TODO: Once configuration/plugins have stabilized
      // remove internet access from wordpress
      name = "bridge"
    },
    {
      name = "traefik"
    },
  ]
  networks = ["bridge", "kaarana-db"]
}

diff --git a/media/airsonic.tf b/media/airsonic.tf
index 90a36be..b0911c5 100644
--- a/media/airsonic.tf
+++ a/media/airsonic.tf
@@ -1,44 +1,35 @@
# module "airsonic" {
#   source = "../modules/container"
#   image  = "linuxserver/airsonic:latest"
#   name   = "airsonic"

#   resource {
#     memory      = "1024"
#     memory_swap = "1024"
#   }

#   web {
#     port   = 4040
#     host   = "airsonic.bb8.fun"
#     expose = true
#   }

#   networks = "${list(docker_network.media.id, data.docker_network.bridge.id)}"

#   env = [
#     "PUID=1004",
#     "PGID=1003",
#     "TZ=Asia/Kolkata",
#     "JAVA_OPTS=-Xmx512m -Dserver.use-forward-headers=true -Dserver.context-path=/",
#   ]

#   devices = [{
#     host_path      = "/dev/snd"
#     container_path = "/dev/snd"
#   }]

#   # files = [
#   #   "/usr/lib/jvm/java-1.8-openjdk/jre/lib/airsonic.properties",
#   #   "/usr/lib/jvm/java-1.8-openjdk/jre/lib/sound.properties",
#   # ]


#   # contents = [
#   #   "${data.template_file.airsonic-properties-file.rendered}",
#   #   "${file("${path.module}/conf/airsonic.sound.properties")}",
#   # ]

#   volumes = [
#     {
#       host_path      = "/mnt/xwing/config/airsonic2"
@@ -62,16 +53,10 @@
#     },
#   ]
# }

# data "template_file" "airsonic-properties-file" {
#   template = "${file("${path.module}/conf/airsonic.properties.tpl")}"


#   vars {
#     smtp-password = "${var.airsonic-smtp-password}"


#     # db-password   = "${var.airsonic-db-password}"
#   }
# }

diff --git a/media/data.tf b/media/data.tf
index e45042d..247e9cc 100644
--- a/media/data.tf
+++ a/media/data.tf
@@ -1,3 +1,4 @@
data "docker_network" "bridge" {

  name = "bridge"
}

diff --git a/media/emby.tf b/media/emby.tf
index a27ceb4..9c673cd 100644
--- a/media/emby.tf
+++ a/media/emby.tf
@@ -1,6 +1,15 @@

locals {

  emby_labels = merge(var.traefik-labels, {

    "traefik.frontend.rule"           = "Host:emby.in.${var.domain},emby.${var.domain}"

    "traefik.frontend.passHostHeader" = "true"

    "traefik.port"                    = 8096

  })
}

resource "docker_container" "emby" {

  name  = "emby"
  image = "${docker_image.emby.latest}"
  image = docker_image.emby.latest

  volumes {

    host_path      = "/mnt/xwing/config/emby"
@@ -12,36 +21,40 @@
    container_path = "/media"
  }

  labels = "${merge(

    var.traefik-labels,
    map(
      "traefik.frontend.rule", "Host:emby.in.${var.domain},emby.${var.domain}",
      "traefik.frontend.passHostHeader", "true",
      "traefik.port", 8096,
    ))}"
  dynamic "labels" {

    for_each = local.emby_labels
    content {

      label = labels.key
      value = labels.value
    }
  }

  networks = ["${docker_network.media.id}", "${var.traefik-network-id}"]
  networks = [docker_network.media.id, var.traefik-network-id]

  memory                = 2048
  restart               = "unless-stopped"
  destroy_grace_seconds = 10
  must_run              = true

  devices {

    host_path      = "/dev/dri"
    container_path = "/dev/dri"
  }

  # Running as lounge:tatooine
  env = [

    "APP_USER=lounge",
    "APP_UID=1004",
    "APP_GID=1003",
    "APP_CONFIG=/mnt/xwing/config",
    "TZ=Asia/Kolkata",
    "UID=1004",
    "GID=1003",
    "GIDLIST=1003"
  ]
}

resource "docker_image" "emby" {

  name          = "${data.docker_registry_image.emby.name}"
  pull_triggers = ["${data.docker_registry_image.emby.sha256_digest}"]
  name          = data.docker_registry_image.emby.name
  pull_triggers = [data.docker_registry_image.emby.sha256_digest]
}

data "docker_registry_image" "emby" {

  name = "emby/embyserver:latest"
}

diff --git a/media/jackett.tf b/media/jackett.tf
index 5ed6256..2c0916c 100644
--- a/media/jackett.tf
+++ a/media/jackett.tf
@@ -1,22 +1,24 @@
module "jackett" {

  name   = "jackett"
  source = "../modules/container"
  image  = "linuxserver/jackett:latest"
  # TODO FIXME
  # networks = [data.docker_network.bridge.id]

  networks = "${list(data.docker_network.bridge.id)}"

  web {

  web = {

    expose = true
    port   = 9117
    host   = "jackett.${var.domain}"
  }

  volumes = [{

    host_path      = "/mnt/xwing/config/jackett"
    container_path = "/config"
  }]
  volumes = [

    {
      host_path      = "/mnt/xwing/config/jackett"
      container_path = "/config"
    },
  ]

  resource {

  resource = {

    memory      = "256"
    memory_swap = "512"
  }
@@ -27,3 +29,4 @@
    "TZ=Asia/Kolkata",
  ]
}

diff --git a/media/lidarr.tf b/media/lidarr.tf
index c0e02fb..62e4ea6 100644
--- a/media/lidarr.tf
+++ a/media/lidarr.tf
@@ -1,21 +1,31 @@
data "docker_registry_image" "lidarr" {

  name = "linuxserver/lidarr:latest"
}

resource "docker_image" "lidarr" {

  name          = "${data.docker_registry_image.lidarr.name}"
  pull_triggers = ["${data.docker_registry_image.lidarr.sha256_digest}"]
  name          = data.docker_registry_image.lidarr.name
  pull_triggers = [data.docker_registry_image.lidarr.sha256_digest]
}

locals {

  lidarr_labels = merge(var.traefik-labels, {

    "traefik.port"          = 8686

    "traefik.frontend.rule" = "Host:lidarr.${var.domain}"

  })
}

resource "docker_container" "lidarr" {

  name  = "lidarr"
  image = "${docker_image.lidarr.latest}"
  image = docker_image.lidarr.latest

  dynamic "labels" {

    for_each = local.lidarr_labels
    content {

      label = labels.key
      value = labels.value
    }
  }

  labels = "${merge(

    var.traefik-labels, map(
      "traefik.port", 8686,
      "traefik.frontend.rule","Host:lidarr.${var.domain}"
  ))}"

  memory                = 512
  restart               = "unless-stopped"
@@ -43,5 +53,6 @@
    "TZ=Asia/Kolkata",
  ]

  networks = ["${docker_network.media.id}", "${var.traefik-network-id}"]
  networks = [docker_network.media.id, var.traefik-network-id]
}

diff --git a/media/navidrome.tf b/media/navidrome.tf
index c2191b9..b1ff663 100644
--- a/media/navidrome.tf
+++ a/media/navidrome.tf
@@ -5,12 +5,12 @@

  user = 1004

  resource {

  resource = {

    memory      = "1024"
    memory_swap = "1024"
  }

  web {

  web = {

    port   = 4533
    host   = "music.bb8.fun"
    expose = true
@@ -22,14 +22,14 @@
    "ND_SESSIONTIMEOUT=300h",
    "ND_BASEURL=",
    "ND_AUTOIMPORTPLAYLISTS=false",
    # "ND_UIWELCOMEMESSAGE=Welcome to Scarif Music Archives",
    "ND_LASTFM_APIKEY=${var.lastfm_api_key}",
    "ND_LASTFM_SECRET=${var.lastfm_secret}",
    "ND_SPOTIFY_ID=${var.spotify_id}",
    "ND_SPOTIFY_SECRET=${var.spotify_secret}"
    "ND_SPOTIFY_SECRET=${var.spotify_secret}",
  ]

  networks = "${list(docker_network.media.id, data.docker_network.bridge.id)}"
  # TODO FIXME
  # networks = [docker_network.media.id, data.docker_network.bridge.id]

  volumes = [

    {
@@ -40,6 +40,7 @@
      host_path      = "/mnt/xwing/media/Music"
      container_path = "/music"
      read_only      = true
    }
    },
  ]
}

diff --git a/media/network.tf b/media/network.tf
index 4b3e8ff..8e09d28 100644
--- a/media/network.tf
+++ a/media/network.tf
@@ -7,3 +7,4 @@
    gateway = "172.18.0.1"
  }
}

diff --git a/media/outputs.tf b/media/outputs.tf
index c9ec38f..b32f8dc 100644
--- a/media/outputs.tf
+++ a/media/outputs.tf
@@ -1,7 +1,8 @@
output "names-transmission" {

  value = "${docker_container.transmission.name}"
  value = docker_container.transmission.name
}

output "names-emby" {

  value = "${docker_container.emby.name}"
  value = docker_container.emby.name
}

diff --git a/media/providers.tf b/media/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/media/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/media/radarr.tf b/media/radarr.tf
index d7b7095..9ebffab 100644
--- a/media/radarr.tf
+++ a/media/radarr.tf
@@ -1,17 +1,18 @@
module "radarr" {

  name   = "radarr"
  source = "../modules/container"
  image  = "linuxserver/radarr:latest"

  networks = "${list(docker_network.media.id, data.docker_network.bridge.id)}"
  # TODO FIXME
  # networks = [docker_network.media.id, data.docker_network.bridge.id]

  web {

  web = {

    expose = true
    port   = 7878
    host   = "radarr.${var.domain}"
  }

  resource {

  resource = {

    memory      = 512
    memory_swap = 1024
  }
@@ -37,3 +38,4 @@
    "TZ=Asia/Kolkata",
  ]
}

diff --git a/media/requestrr.tf b/media/requestrr.tf
index 560b821..0a3baf1 100644
--- a/media/requestrr.tf
+++ a/media/requestrr.tf
@@ -1,15 +1,15 @@
module "requestrr" {

  name   = "requestrr"
  source = "../modules/container"
  image  = "darkalfx/requestrr:latest"

  web {

  web = {

    expose = true
    port   = 4545
    host   = "requestrr.${var.domain}"
  }

  resource {

  resource = {

    memory      = 256
    memory_swap = 256
  }
@@ -18,8 +18,10 @@
    {
      host_path      = "/mnt/xwing/config/requestrr"
      container_path = "/root/config"
    }
    },
  ]

  networks = "${list(docker_network.media.id, data.docker_network.bridge.id)}"
  # TODO FIXME
  # networks = [docker_network.media.id, data.docker_network.bridge.id]
}

diff --git a/media/sonarr.tf b/media/sonarr.tf
index 375b811..fb0e823 100644
--- a/media/sonarr.tf
+++ a/media/sonarr.tf
@@ -1,15 +1,15 @@
module "sonarr-container" {

  name   = "sonarr"
  source = "../modules/container"
  image  = "linuxserver/sonarr:latest"

  web {

  web = {

    expose = true
    port   = 8989
    host   = "sonarr.${var.domain}"
  }

  resource {

  resource = {

    memory      = 512
    memory_swap = 1024
  }
@@ -35,5 +35,6 @@
    "TZ=Asia/Kolkata",
  ]

  networks = "${list(docker_network.media.id, data.docker_network.bridge.id)}"
  networks = [docker_network.media.id, data.docker_network.bridge.id]
}

diff --git a/media/transmission.tf b/media/transmission.tf
index 197e92a..b030816 100644
--- a/media/transmission.tf
+++ a/media/transmission.tf
@@ -1,18 +1,26 @@
locals {

  transmission_labels = merge(var.traefik-labels, {

    "traefik.frontend.auth.basic" = var.basic_auth

    "traefik.port"                = 9091

  })
}

resource "docker_container" "transmission" {

  name  = "transmission"
  image = "${docker_image.transmission.latest}"

  labels = "${merge(

    var.traefik-labels,
    map(
      "traefik.frontend.auth.basic", "${var.basic_auth}",
      "traefik.port", 9091,
    ))}"
  image = docker_image.transmission.latest

  dynamic "labels" {

    for_each = local.transmission_labels
    content {

      label = labels.key
      value = labels.value
    }
  }

  ports {

    internal = 51413
    external = 51413
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
    protocol = "udp"
  }

@@ -27,12 +35,17 @@
  }

  volumes {

    host_path      = "/mnt/xwing/media/Music/Audiobooks"
    container_path = "/audiobooks"
  }

  volumes {

    host_path      = "/mnt/xwing/data/watch/transmission"
    container_path = "/watch"
  }

  upload {

    content = "${file("${path.module}/conf/transmission.json")}"
    content = file("${path.module}/conf/transmission.json")
    file    = "/config/settings.json"
  }

@@ -42,7 +55,7 @@
    "TZ=Asia/Kolkata",
  ]

  networks = ["${docker_network.media.id}", "${var.traefik-network-id}"]
  networks = [docker_network.media.id, var.traefik-network-id]

  memory                = 1024
  restart               = "unless-stopped"
@@ -51,10 +64,11 @@
}

resource "docker_image" "transmission" {

  name          = "${data.docker_registry_image.transmission.name}"
  pull_triggers = ["${data.docker_registry_image.transmission.sha256_digest}"]
  name          = data.docker_registry_image.transmission.name
  pull_triggers = [data.docker_registry_image.transmission.sha256_digest]
}

data "docker_registry_image" "transmission" {

  name = "linuxserver/transmission:latest"
}

diff --git a/media/variables.tf b/media/variables.tf
index 06eb255..0ea39b0 100644
--- a/media/variables.tf
+++ a/media/variables.tf
@@ -1,11 +1,11 @@
variable "domain" {

  type = "string"
  type = string
}

# variable "airsonic-smtp-password" {}

variable "traefik-labels" {

  type = "map"
  type = map(string)
}

// TODO: Remove duplication
@@ -14,23 +14,29 @@
}

variable "ips" {

  type = "map"
  type = map(string)
}

variable "traefik-network-id" {}
variable "traefik-network-id" {

}

variable "lastfm_api_key" {

  description = "Navidrome Configuration for lastfm_api_key"
  type        = "string"
  type        = string
}

variable "lastfm_secret" {

  description = "Navidrome Configuration for lastfm_secret"
  type        = "string"
  type        = string
}

variable "spotify_id" {

  description = "Navidrome Configuration for spotify_id"
  type        = "string"
  type        = string
}

variable "spotify_secret" {

  description = "Navidrome Configuration for spotify_secret"
  type        = "string"
  type        = string
}

diff --git a/monitoring/act.tf b/monitoring/act.tf
index b5c2e52..c27d865 100644
--- a/monitoring/act.tf
+++ a/monitoring/act.tf
@@ -1,10 +1,10 @@
data "docker_registry_image" "act-exporter" {

  name = "captn3m0/prometheus-act-exporter:latest"
}

resource "docker_container" "act-exporter" {

  name  = "act-exporter"
  image = "${docker_image.act-exporter.latest}"
  image = docker_image.act-exporter.latest

  entrypoint = ["/usr/local/bin/node", "server.js"]

@@ -14,11 +14,10 @@
  }

  // So it can talk to ACT
  networks_advanced {

    name = "bridge"
  }
  networks = ["bridge"]

  restart               = "unless-stopped"
  destroy_grace_seconds = 10
  must_run              = true
}

diff --git a/monitoring/cadvisor.tf b/monitoring/cadvisor.tf
index c7c1db2..ee326c0 100644
--- a/monitoring/cadvisor.tf
+++ a/monitoring/cadvisor.tf
@@ -1,9 +1,9 @@
module "cadvisor" {

  source = "../modules/container"
  name   = "cadvisor"
  image  = "google/cadvisor:latest"

  resource {

  resource = {

    memory      = 512
    memory_swap = 512
  }
@@ -39,18 +39,12 @@
    },
  ]

  networks_advanced = [

    {
      name = "traefik"
    },
    {
      name = "monitoring"
    },
  ]
  networks = ["monitoring"]

  web {

  web = {

    expose = true
    port   = 8080
    auth   = true
  }
}

diff --git a/monitoring/data.tf b/monitoring/data.tf
index 1a5ae92..f26fcbc 100644
--- a/monitoring/data.tf
+++ a/monitoring/data.tf
@@ -1,3 +1,4 @@
data "docker_registry_image" "prometheus" {

  name = "prom/prometheus:latest"
}

diff --git a/monitoring/grafana.tf b/monitoring/grafana.tf
index c6519ef..03f6f10 100644
--- a/monitoring/grafana.tf
+++ a/monitoring/grafana.tf
@@ -7,32 +7,25 @@
  // grafana:grafana
  user = "984:982"

  web {

  web = {

    port   = 3000
    host   = "grafana.${var.domain}"
    expose = true
  }

  volumes = [{

    host_path      = "/mnt/xwing/data/grafana"
    container_path = "/var/lib/grafana"
  }]

  networks_advanced = [

    {
      name = "traefik"
    },
  volumes = [

    {
      name = "monitoring"
      host_path      = "/mnt/xwing/data/grafana"
      container_path = "/var/lib/grafana"
    },
  ]

  networks = ["monitoring"]

  env = [

    "GF_SERVER_ROOT_URL=https://grafana.${var.domain}",
    "GF_AUTH_ANONYMOUS_ENABLED=true",
    "GF_AUTH_ANONYMOUS_ORG_NAME=Tatooine",

    # Keep this disabled unless bringing up a new grafana instance
    "GF_SECURITY_ADMIN_PASSWORD=${var.gf-security-admin-password}",
  ]

@@ -40,3 +33,4 @@
  destroy_grace_seconds = 10
  must_run              = true
}

diff --git a/monitoring/images.tf b/monitoring/images.tf
index 12fcfdc..1acfc35 100644
--- a/monitoring/images.tf
+++ a/monitoring/images.tf
@@ -1,10 +1,11 @@
resource "docker_image" "prometheus" {

  name          = "${data.docker_registry_image.prometheus.name}"
  pull_triggers = ["${data.docker_registry_image.prometheus.sha256_digest}"]
  name          = data.docker_registry_image.prometheus.name
  pull_triggers = [data.docker_registry_image.prometheus.sha256_digest]
}

resource "docker_image" "act-exporter" {

  name          = "${data.docker_registry_image.act-exporter.name}"
  pull_triggers = ["${data.docker_registry_image.act-exporter.sha256_digest}"]
  name          = data.docker_registry_image.act-exporter.name
  pull_triggers = [data.docker_registry_image.act-exporter.sha256_digest]
  keep_locally  = true
}

diff --git a/monitoring/network.tf b/monitoring/network.tf
index aa53012..9c2cf51 100644
--- a/monitoring/network.tf
+++ a/monitoring/network.tf
@@ -1,5 +1,6 @@
resource "docker_network" "monitoring" {

  name     = "monitoring"
  driver   = "bridge"
  internal = true
}

diff --git a/monitoring/nodeexporter.tf b/monitoring/nodeexporter.tf
index c665f75..0739a8f 100644
--- a/monitoring/nodeexporter.tf
+++ a/monitoring/nodeexporter.tf
@@ -27,14 +27,17 @@
  command = [

    "--path.procfs=/host/proc",
    "--path.sysfs=/host/sys",
    "--collector.filesystem.ignored-mount-points=\"^/(sys|proc|dev|host|etc)($$|/)\"",
    "--collector.filesystem.ignored-mount-points=\"^/(sys|proc|dev|host|etc)($|/)\"",
  ]

  networks = [

    "${docker_network.monitoring.id}",
  ]
  # TODO FIXME

  # networks = [
  #   docker_network.monitoring.id,
  # ]

  restart               = "unless-stopped"
  destroy_grace_seconds = 10
  must_run              = true
}

diff --git a/monitoring/prometheus.tf b/monitoring/prometheus.tf
index 7875f63..1831270 100644
--- a/monitoring/prometheus.tf
+++ a/monitoring/prometheus.tf
@@ -1,6 +1,6 @@
resource "docker_container" "prometheus" {

  name  = "prometheus"
  image = "${docker_image.prometheus.latest}"
  image = docker_image.prometheus.latest

  # prometheus:prometheus
  user = "985:983"
@@ -8,13 +8,13 @@
  ports {

    internal = 9090
    external = 8811
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
  }

  ports {

    internal = 9090
    external = 8811
    ip       = "${var.ips["tun0"]}"
    ip       = var.ips["tun0"]
  }

  command = ["--config.file=/etc/prometheus/prometheus.yml"]
@@ -25,23 +25,12 @@
  }

  upload {

    content = "${file("${path.module}/config/prometheus.yml")}"
    content = file("${path.module}/config/prometheus.yml")
    file    = "/etc/prometheus/prometheus.yml"
  }

  networks_advanced {

    name = "monitoring"
  }

  networks_advanced {

    name = "bridge"
  }
  networks = ["monitoring", "bridge"]

  networks = [

    "${data.docker_network.bridge.id}",
    "${docker_network.monitoring.id}",
  ]

  restart               = "unless-stopped"
  destroy_grace_seconds = 10
  must_run              = true
@@ -50,3 +39,4 @@
data "docker_network" "bridge" {

  name = "bridge"
}

diff --git a/monitoring/providers.tf b/monitoring/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/monitoring/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/monitoring/speedtest.tf b/monitoring/speedtest.tf
index 0a0c11c..8b00a58 100644
--- a/monitoring/speedtest.tf
+++ a/monitoring/speedtest.tf
@@ -7,17 +7,9 @@
  image  = "captn3m0/speedtest-exporter:alpine"
  source = "../modules/container"

  networks_advanced = [

    {
      name    = "monitoring"
      aliases = ["speedtest", "speedtest.docker"]
    },
    {
      name = "bridge"
    },
  ]
  networks = ["monitoring"]

  resource {

  resource = {

    memory      = 256
    memory_swap = 256
  }
@@ -26,3 +18,4 @@
  destroy_grace_seconds = 10
  must_run              = true
}

diff --git a/monitoring/variables.tf b/monitoring/variables.tf
index 57b0281..05deb6b 100644
--- a/monitoring/variables.tf
+++ a/monitoring/variables.tf
@@ -1,17 +1,17 @@
variable "gf-security-admin-password" {

  type = "string"
  type = string
}

variable "domain" {

  type = "string"
  type = string
}

variable "transmission" {

  type = "string"
  type = string
}

variable "links-traefik" {

  type = "string"
  type = string
}

variable "alert-slack-username" {

@@ -31,11 +31,13 @@
}

variable "traefik-labels" {

  type = "map"
  type = map(string)
}

variable "ips" {

  type = "map"
  type = map(string)
}

variable "traefik-network-id" {}
variable "traefik-network-id" {

}

diff --git a/opml/main.tf b/opml/main.tf
index dcf1b0f..437ce83 100644
--- a/opml/main.tf
+++ a/opml/main.tf
@@ -1,12 +1,13 @@
module "opml" {

  name     = "opml"
  source   = "../modules/container"
  image    = "captn3m0/opml-gen:latest"
  networks = ["${docker_network.opml.id}", "${var.traefik-network-id}"]

  web {

  name   = "opml"
  source = "../modules/container"
  image  = "captn3m0/opml-gen:latest"
  # TODO FIXME
  # networks = [docker_network.opml.id, var.traefik-network-id]

  web = {

    expose = true
    host   = "${var.domain}"
    host   = var.domain
  }

  env = [

@@ -15,8 +16,9 @@
    "REDIS_URL=redis://opml-redis:6379/1",
  ]

  resource {

  resource = {

    memory      = 256
    memory_swap = 256
  }
}

diff --git a/opml/network.tf b/opml/network.tf
index dc17e53..6d59776 100644
--- a/opml/network.tf
+++ a/opml/network.tf
@@ -1,4 +1,5 @@
resource "docker_network" "opml" {

  name   = "opml"
  driver = "bridge"
}

diff --git a/opml/providers.tf b/opml/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/opml/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/opml/redis.tf b/opml/redis.tf
index f9c506c..74b77cb 100644
--- a/opml/redis.tf
+++ a/opml/redis.tf
@@ -1,17 +1,19 @@
module "redis" {

  name       = "opml-redis"
  source     = "../modules/container"
  image      = "redis:alpine"
  networks   = ["${docker_network.opml.id}"]
  name   = "opml-redis"
  source = "../modules/container"
  image  = "redis:alpine"
  # TODO FIXME
  # networks   = [docker_network.opml.id]
  keep_image = true

  # ThisSucks
  web {

  web = {

    expose = "false"
  }

  resource {

  resource = {

    memory      = 256
    memory_swap = 256
  }
}

diff --git a/opml/variables.tf b/opml/variables.tf
index 98f8d60..23ea989 100644
--- a/opml/variables.tf
+++ a/opml/variables.tf
@@ -1,5 +1,12 @@
variable "domain" {}
variable "client-id" {}
variable "client-secret" {}

variable "traefik-network-id" {}
variable "domain" {

}

variable "client-id" {

}

variable "client-secret" {

}

variable "traefik-network-id" {

}

diff --git a/radicale/config b/radicale/config
index 86d2d10..3a13e70 100644
--- a/radicale/config
+++ a/radicale/config
@@ -11,6 +11,10 @@
# Value: none | htpasswd | remote_user | http_x_remote_user
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = bcrypt

# Message displayed in the client when a password is needed
realm = Authentication required

[storage]
filesystem_folder = /data/collections
diff --git a/radicale/main.tf b/radicale/main.tf
index 35c05d9..216c5f3 100644
--- a/radicale/main.tf
+++ a/radicale/main.tf
@@ -1,17 +1,20 @@
module "container" {

  name   = "radicale"
  source = "../modules/container"
  image  = "tomsquest/docker-radicale:amd64"

  resource {

  resource = {

    memory      = 2000
    memory_swap = 2000
  }

  web {

  # TODO Drop Capabilities
  # https://github.com/tomsquest/docker-radicale#option-2-recommended-production-grade-instruction-secured-safe-rocket

  web = {

    expose = true
    port   = 5232
    host   = "${var.domain}"
    host   = var.domain
  }

  volumes = [

@@ -27,39 +30,15 @@

  uploads = [

    {
      content = <<EOT

# See radicale.org/configuration/
[server]
hosts = 0.0.0.0:5232

# Max parallel connections
max_connections = 10

[auth]
# Authentication method
# Value: none | htpasswd | remote_user | http_x_remote_user
type = htpasswd
htpasswd_filename = /config/users
htpasswd_encryption = bcrypt

[storage]
filesystem_folder = /data/collections

[logging]
level=warning

[headers]
# Additional HTTP headers
X-Powered-By: Allomancy
Server: Blackbox
EOT
      content = file("${path.module}/config")
      file    = "/config/config"
    },
    {
      content = file("${path.module}/logging.conf")
      file    = "/config/logging"
    },
    {
      content = <<EOT

nemo:$2y$05$vC1WTAuKn2xuDYZ6I3ucxuPnCrtZrVKzdDHSYhqCegi97RM/pdzXW
axy:$2b$10$iqCLs3F1IRDBoSGxGlsOFO9C3peh8QH14hMnHN4o6oqu21PWL9vu2
EOT
      content = file("${path.module}/users")
      file    = "/config/users"
    },
  ]
diff --git a/radicale/providers.tf b/radicale/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/radicale/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/radicale/variables.tf b/radicale/variables.tf
index 10fc457..6e255f8 100644
--- a/radicale/variables.tf
+++ a/radicale/variables.tf
@@ -1,3 +1,4 @@
variable "domain" {

  type = "string"
  type = string
}

diff --git a/timemachine/main.tf b/timemachine/main.tf
index 778f6a0..8c47918 100644
--- a/timemachine/main.tf
+++ a/timemachine/main.tf
@@ -1,15 +1,15 @@
data "docker_registry_image" "timemachine" {

  name = "odarriba/timemachine:latest"
}

resource "docker_image" "timemachine" {

  name          = "${data.docker_registry_image.timemachine.name}"
  pull_triggers = ["${data.docker_registry_image.timemachine.sha256_digest}"]
  name          = data.docker_registry_image.timemachine.name
  pull_triggers = [data.docker_registry_image.timemachine.sha256_digest]
}

resource "docker_container" "timemachine" {

  name  = "timemachine"
  image = "${docker_image.timemachine.latest}"
  image = docker_image.timemachine.latest

  volumes {

    host_path      = "/mnt/xwing/data/timemachine"
@@ -19,17 +19,17 @@
  ports {

    internal = 548
    external = 548
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
  }

  ports {

    internal = 636
    external = 636
    ip       = "${var.ips["eth0"]}"
    ip       = var.ips["eth0"]
  }

  upload {

    content = "${data.template_file.timemachine-entrypoint.rendered}"
    content = data.template_file.timemachine-entrypoint.rendered
    file    = "/entrypoint-custom.sh"
  }

@@ -44,13 +44,13 @@
}

data "template_file" "timemachine-entrypoint" {

  template = "${file("${path.module}/entrypoint.sh.tpl")}"

  vars {

    username-1 = "${var.username-1}"
    password-1 = "${var.password-1}"

    username-2 = "${var.username-2}"
    password-2 = "${var.password-2}"
  template = file("${path.module}/entrypoint.sh.tpl")

  vars = {

    username-1 = var.username-1
    password-1 = var.password-1
    username-2 = var.username-2
    password-2 = var.password-2
  }
}

diff --git a/timemachine/providers.tf b/timemachine/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/timemachine/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/timemachine/variables.tf b/timemachine/variables.tf
index c998ce4..05abf6b 100644
--- a/timemachine/variables.tf
+++ a/timemachine/variables.tf
@@ -1,8 +1,16 @@
variable "ips" {

  type = "map"
  type = map(string)
}

variable "username-1" {}
variable "username-2" {}
variable "password-2" {}
variable "password-1" {}
variable "username-1" {

}

variable "username-2" {

}

variable "password-2" {

}

variable "password-1" {

}

diff --git a/docker/conf/traefik.toml b/docker/conf/traefik.toml
index eb20faa..e5f2a69 100644
--- a/docker/conf/traefik.toml
+++ a/docker/conf/traefik.toml
@@ -38,33 +38,6 @@
# Since I can't apply a authentication
# on this yet

[backends.elibsrv]
[backends.elibsrv.servers.default]
  url = "http://elibsrv.captnemo.in:90"

[backends.scan]
[backends.scan.servers.default]
  url = "http://scan.in.bb8.fun:90"

[frontends]

[frontends.scan]
  backend = "scan"
  [frontends.scan.headers]
    SSLRedirect = true
    SSLTemporaryRedirect = true
    STSSeconds = 2592000
    FrameDeny = true
    ContentTypeNosniff = true
    BrowserXssFilter = true
    ReferrerPolicy = "no-referrer"
  [frontends.scan.headers.customresponseheaders]
    X-Powered-By = "Allomancy"
    Server = "BlackBox"
    X-Clacks-Overhead = "GNU Terry Pratchett"
[frontends.scan.routes.domain]
  rule = "Host:scan.bb8.fun"

[web]
  address = ":1111"
  readOnly = true
diff --git a/gitea/conf/conf.ini.tpl b/gitea/conf/conf.ini.tpl
index 838e864..fa21d27 100644
--- a/gitea/conf/conf.ini.tpl
+++ a/gitea/conf/conf.ini.tpl
@@ -137,7 +137,7 @@
RESET_PASSWD_CODE_LIVE_MINUTES = 30
REGISTER_EMAIL_CONFIRM            = true
ENABLE_NOTIFY_MAIL                = true
DISABLE_REGISTRATION              = false
DISABLE_REGISTRATION              = true
; ; Enable captcha validation for registration
ENABLE_CAPTCHA                    = true
REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA = true
@@ -146,7 +146,7 @@
REQUIRE_SIGNIN_VIEW               = false
; ; Default value for KeepEmailPrivate
; ; New user will get the value of this setting copied into their profile
DEFAULT_KEEP_EMAIL_PRIVATE        = false
DEFAULT_KEEP_EMAIL_PRIVATE        = true
; ; Default value for AllowCreateOrganization
; ; New user will have rights set to create organizations depending on this setting
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
@@ -249,7 +249,7 @@

[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = true
ENABLE_OPENID_SIGNUP = false

[metrics]
; Enables metrics endpoint. True or false; default is false.
diff --git a/media/conf/transmission.json b/media/conf/transmission.json
index 029c854..197afb7 100644
--- a/media/conf/transmission.json
+++ a/media/conf/transmission.json
@@ -11,7 +11,7 @@
    "blocklist-enabled": true,
    "blocklist-url": "http://john.bitsurge.net/public/biglist.p2p.gz",
    "cache-size-mb": 16,
    "dht-enabled": true,
    "dht-enabled": false,
    "download-dir": "/downloads",
    "download-queue-enabled": true,
    "download-queue-size": 5,
@@ -31,14 +31,14 @@
    "peer-port-random-low": 49152,
    "peer-port-random-on-start": false,
    "peer-socket-tos": "default",
    "pex-enabled": true,
    "pex-enabled": false,
    "port-forwarding-enabled": true,
    "preallocation": 1,
    "prefetch-enabled": true,
    "queue-stalled-enabled": true,
    "queue-stalled-minutes": 30,
    "ratio-limit": 0.2,
    "ratio-limit-enabled": true,
    "ratio-limit-enabled": false,
    "rename-partial-files": true,
    "rpc-host-whitelist": "transmission.bb8.fun,transmission",
    "rpc-host-whitelist-enabled": true,
diff --git a/modules/container/image.tf b/modules/container/image.tf
new file mode 100644
index 0000000..9395802 100644
--- /dev/null
+++ a/modules/container/image.tf
@@ -1,0 +1,9 @@
data "docker_registry_image" "image" {

  name = var.image
}

resource "docker_image" "image" {

  name          = var.image
  pull_triggers = [data.docker_registry_image.image.sha256_digest]
  keep_locally  = var.keep_image
}
diff --git a/modules/container/locals.tf b/modules/container/locals.tf
index 8d96c2e..4a107b4 100644
--- a/modules/container/locals.tf
+++ a/modules/container/locals.tf
@@ -1,36 +1,47 @@
locals {

  default_labels {

  default_labels = {

    "managed.by" = "nebula"

  }

  web {

    "traefik.port"          = "${lookup(var.web, "port", "80")}"

    "traefik.frontend.rule" = "Host:${lookup(var.web, "host", "example.invalid")}"

    "traefik.protocol"      = "${lookup(var.web, "protocol", "http")}"

  web = {

    "traefik.port"          = var.web.port != null ? var.web.port : 80

    "traefik.frontend.rule" = var.web.host != null ? "Host:${var.web.host}" : "Host:example.invalid"

    "traefik.protocol"      = var.web.protocol != null ? var.web.protocol : "http"

  }

  resource {

    memory      = "${lookup(var.resource, "memory", 64)}"
    memory_swap = "${lookup(var.resource, "memory_swap", 128)}"
  }

  traefik_common_labels {

  traefik_common_labels = {

    "traefik.enable" = "true"


    // HSTS
    "traefik.frontend.headers.SSLTemporaryRedirect" = "true"

    "traefik.frontend.headers.STSSeconds"           = "2592000"

    "traefik.frontend.headers.STSIncludeSubdomains" = "false"


    // X-Powered-By, Server headers
    "traefik.frontend.headers.customResponseHeaders" = "${var.xpoweredby}"

    "traefik.frontend.headers.customResponseHeaders" = var.xpoweredby

    "traefik.frontend.headers.contentTypeNosniff"    = "true"

    "traefik.frontend.headers.browserXSSFilter"      = "true"

    "traefik.docker.network"                         = "traefik"

  }

    "traefik.docker.network" = "traefik"

  # if var.web.auth == true
  traefik_auth_labels = {

    "traefik.frontend.auth.basic" = var.auth_header

  }

  traefik_auth_labels {

    "traefik.frontend.auth.basic" = "${var.auth_header}"

  resource = {

    memory      = lookup(var.resource, "memory", 64)
    memory_swap = lookup(var.resource, "memory_swap", 128)
  }

  labels = merge(

    # Default labels are applied to every container
    local.default_labels,
    # Add the common traefik labels
    var.web.expose ? local.traefik_common_labels : null,
    # Apply the overwritten web labels only if the container is exposed
    var.web.expose ? local.web : null,
    # And finally a label for Basic Authentication if the service wants it
    var.web.auth != null ? (var.web.auth ? local.traefik_auth_labels : null) : null,
  )

  networks = concat(var.networks, var.web.expose ? ["traefik"] : [])
}
diff --git a/modules/container/main.tf b/modules/container/main.tf
index 4019398..ffa8e04 100644
--- a/modules/container/main.tf
+++ a/modules/container/main.tf
@@ -1,125 +1,82 @@
data "docker_registry_image" "image" {

  name = "${var.image}"
}

resource "docker_image" "image" {

  name          = "${var.image}"
  pull_triggers = ["${data.docker_registry_image.image.sha256_digest}"]
  keep_locally  = "${var.keep_image}"
}

data "docker_network" "traefik" {

  name = "traefik"
}

resource "docker_container" "container" {

  name       = "${var.name}"
  image      = "${docker_image.image.latest}"
  ports      = "${var.ports}"
  restart    = "${var.restart}"
  env        = ["${var.env}"]
  command    = "${var.command}"
  entrypoint = "${var.entrypoint}"
  user       = "${var.user}"

  network_mode = "${var.network_mode}"

  capabilities = ["${var.capabilities}"]

  // Only attach the traefik network if
  // service is exposed to the web
  networks = ["${concat(var.networks,compact(split(",",lookup(var.web, "expose", "false") == "false" ? "" :"${data.docker_network.traefik.id}")))}"]

  networks_advanced = ["${var.networks_advanced}"]

  memory      = "${local.resource["memory"]}"
  memory_swap = "${local.resource["memory_swap"]}"

  volumes = ["${var.volumes}"]
  devices = ["${var.devices}"]

  upload = ["${var.uploads}"]

  # Look at this monstrosity
  # And then https://github.com/hashicorp/terraform/issues/12453#issuecomment-365569618
  # for why this is needed

  labels = "${merge(local.default_labels,

    zipmap(
      concat(
        keys(local.default_labels),
        split("~",
          lookup(var.web, "expose", "false") == "false" ?
            "" :
            join("~", keys(local.traefik_common_labels))
        )
      ),
      concat(
        values(local.default_labels),
        split("~",
          lookup(var.web, "expose", "false") == "false" ?
            "" :
            join("~", values(local.traefik_common_labels))
        )
      )
    ),
    zipmap(
      concat(
        keys(local.default_labels),
        split("~",
          lookup(var.web, "expose", "false") == "false" ?
            "" :
            join("~", keys(local.web))
        )
      ),
      concat(
        values(local.default_labels),
        split("~",
          lookup(var.web, "expose", "false") == "false" ?
            "" :
            join("~", values(local.web))
        )
      )
    ),


    zipmap(
      concat(
        keys(local.default_labels),
        split("~",
          lookup(var.web, "expose", "false") == "false" ?
            "" :
            join("~", keys(local.traefik_common_labels))
        )
      ),
      concat(
        values(local.default_labels),
        split("~",
          lookup(var.web, "expose", "false") == "false" ?
            "" :
            join("~", values(local.traefik_common_labels))
        )
      )
    ),
    zipmap(
      concat(
        keys(local.default_labels),
        split("~",
          lookup(var.web, "auth", "false") == "false" ?
            "" :
            join("~", keys(local.traefik_auth_labels))
        )
      ),
      concat(
        values(local.default_labels),
        split("~",
          lookup(var.web, "auth", "false") == "false" ?
            "" :
            join("~", values(local.traefik_auth_labels))
        )
      )
    )
  )}"
  destroy_grace_seconds = "${var.destroy_grace_seconds}"
  must_run              = "${var.must_run}"
  name  = var.name
  image = docker_image.image.latest

  dynamic "ports" {

    for_each = var.ports
    content {

      external = ports.value.external
      internal = ports.value.internal
      ip       = ports.value.ip
      protocol = lookup(ports.value, "protocol", "tcp")
    }
  }
  restart    = var.restart
  env        = var.env
  command    = var.command
  entrypoint = var.entrypoint
  user       = var.user

  network_mode = var.network_mode

  dynamic "capabilities" {

    for_each = [var.capabilities]
    content {

      add  = lookup(capabilities.value, "add", [])
      drop = lookup(capabilities.value, "drop", [])
    }
  }

  dynamic "networks_advanced" {

    for_each = local.networks
    content {

      name = networks_advanced.value
    }
  }

  memory      = local.resource["memory"]
  memory_swap = local.resource["memory_swap"]

  dynamic "volumes" {

    for_each = var.volumes
    content {

      container_path = lookup(volumes.value, "container_path", null)
      from_container = lookup(volumes.value, "from_container", null)
      host_path      = lookup(volumes.value, "host_path", null)
      read_only      = lookup(volumes.value, "read_only", null)
      volume_name    = lookup(volumes.value, "volume_name", null)
    }
  }

  dynamic "devices" {

    for_each = var.devices
    content {

      host_path      = devices.value["host_path"]
      container_path = devices.value["container_path"]
      permissions    = devices.value["permissions"]
    }
  }

  dynamic "upload" {

    for_each = var.uploads
    content {

      file           = lookup(upload.value, "file", null)
      content        = lookup(upload.value, "content", null)
      content_base64 = lookup(upload.value, "content_base64", null)
      executable     = lookup(upload.value, "executable", null)
      source         = lookup(upload.value, "source", null)
      source_hash    = lookup(upload.value, "source_hash", null)
    }
  }

  dynamic "labels" {

    for_each = local.labels
    content {

      label = labels.key
      value = labels.value
    }
  }

  destroy_grace_seconds = var.destroy_grace_seconds
  must_run              = var.must_run
}
diff --git a/modules/container/providers.tf b/modules/container/providers.tf
new file mode 100644
index 0000000..c8e3cc2 100644
--- /dev/null
+++ a/modules/container/providers.tf
@@ -1,0 +1,9 @@
terraform {

  experiments = [module_variable_optional_attrs]
  required_providers {

    docker = {

      source  = "kreuzwerker/docker"
      version = "2.15.0"
    }
  }
}
diff --git a/modules/container/vars.tf b/modules/container/vars.tf
index c64d63c..82c0239 100644
--- a/modules/container/vars.tf
+++ a/modules/container/vars.tf
@@ -8,19 +8,13 @@

variable "ports" {

  description = "list of port mappings"
  type        = "list"
  type        = list(map(string))
  default     = []
}

variable "networks_advanced" {

  description = "list of networks_advanced"
  type        = "list"
  default     = []
}

variable "networks" {

  description = "list of networks"
  type        = "list"
  description = "list of names of networks to attach to"
  type        = list(string)
  default     = []
}

@@ -32,7 +26,7 @@
variable "must_run" {

  description = "If true, then the Docker container will be kept running. "
  default     = "true"
  type        = "string"
  type        = string
}

variable "user" {

@@ -43,7 +37,7 @@
variable "destroy_grace_seconds" {

  description = "Container will be destroyed after n seconds or on successful stop."
  default     = 10
  type        = "string"
  type        = string
}

variable "command" {

@@ -61,10 +55,9 @@
  default     = []
}

variable "labels" {

  description = "labels"
  default     = {}
}
# variable "labels" {
#   description = "labels"
# }

variable "xpoweredby" {

  default = "X-Powered-By:Allomancy||X-Server:Blackbox"
@@ -72,10 +65,20 @@

variable "web" {

  description = "Web Configuration"
  type = object({

    expose   = bool
    auth     = optional(bool)
    port     = optional(number)
    host     = optional(string)
    protocol = optional(string)
  })

  default = {

    expose = "false"
    auth   = "false"
    expose   = false
    auth     = false
    port     = 80
    host     = ""
    protocol = "http"
  }
}

@@ -95,20 +98,23 @@

variable "volumes" {

  description = "volumes"
  type        = "list"
  default     = []
  default     = {}
}

variable "capabilities" {

  description = "capabilities"
  type        = "list"
  default     = []

  default = {

    add  = []
    drop = []
  }
}

variable "devices" {

  description = "devices"
  type        = "list"
  default     = []
  description = "list of devices"
  type        = list(map(string))

  default = []
}

variable "keep_image" {

@@ -117,5 +123,15 @@
}

variable "uploads" {

  description = "Files to Upload"
  type = list(object({

    file           = string
    content        = optional(string)
    content_base64 = optional(string)
    executable     = optional(bool)
    source         = optional(string)
    source_hash    = optional(string)
  }))

  default = []
}
diff --git a/modules/image/main.tf b/modules/image/main.tf
index 68eb353..cb6d11a 100644
--- a/modules/image/main.tf
+++ a/modules/image/main.tf
@@ -1,16 +1,17 @@
variable "image" {

  description = "image to use"
}

data "docker_registry_image" "image" {

  name = "${var.image}"
  name = var.image
}

resource "docker_image" "image" {

  name          = "${data.docker_registry_image.image.name}"
  pull_triggers = ["${data.docker_registry_image.image.sha256_digest}"]
  name          = data.docker_registry_image.image.name
  pull_triggers = [data.docker_registry_image.image.sha256_digest]
}

output "image" {

  value = "${docker_image.image.latest}"
  value = docker_image.image.latest
}

diff --git a/modules/postgres/main.tf b/modules/postgres/main.tf
index a35ed91..ee31a8d 100644
--- a/modules/postgres/main.tf
+++ a/modules/postgres/main.tf
@@ -1,10 +1,11 @@
resource "postgresql_database" "db" {

  name  = "${var.name}"
  owner = "${var.name}"
  name  = var.name
  owner = var.name
}

resource "postgresql_role" "role" {

  name     = "${var.name}"
  name     = var.name
  login    = true
  password = "${var.password}"
  password = var.password
}

diff --git a/modules/postgres/providers.tf b/modules/postgres/providers.tf
new file mode 100644
index 0000000..c8dd4a4 100644
--- /dev/null
+++ a/modules/postgres/providers.tf
@@ -1,0 +1,19 @@
terraform {

  required_providers {

    pass = {

      source = "camptocamp/pass"
    }
    digitalocean = {

      source = "digitalocean/digitalocean"
    }
    postgresql = {

      source = "cyrilgdn/postgresql"
    }
    cloudflare = {

      source = "cloudflare/cloudflare"
    }
    docker = {

      source = "kreuzwerker/docker"
    }
  }
}
diff --git a/modules/postgres/vars.tf b/modules/postgres/vars.tf
index 52cfbc8..9e7446e 100644
--- a/modules/postgres/vars.tf
+++ a/modules/postgres/vars.tf
@@ -1,9 +1,10 @@
variable "name" {

  description = "database/role name"
  type        = "string"
  type        = string
}

variable "password" {

  description = "role password"
  type        = "string"
  type        = string
}