Compare commits

..

7 Commits

152 changed files with 1452 additions and 4690 deletions

5
.gitignore vendored
View File

@ -3,11 +3,6 @@
.terraform .terraform
*.tfstate *.tfstate
*.tfstate.backup *.tfstate.backup
*.terraform.lock.hcl
*.out *.out
*.backup *.backup
secrets secrets
k8s/
k8s2/
docker/conf/wiki.yml
plan

View File

@ -1 +0,0 @@
1.3.6

View File

@ -1,18 +0,0 @@
# 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
````

141
README.md
View File

@ -2,106 +2,103 @@
![Nebula header image](https://cdn.spacetelescope.org/archives/images/thumb700x/heic0707a.jpg) ![Nebula header image](https://cdn.spacetelescope.org/archives/images/thumb700x/heic0707a.jpg)
> Where stars are born. >Where stars are born.
Manages the local infrastructure of my home server. I'm also doing blog posts around the same: Manages the local infrastructure of my home server. I'm also doing blog posts around the same:
1. [Part 1, Hardware](https://captnemo.in/blog/2017/09/17/home-server-build/) 1. [Part 1, Hardware](https://captnemo.in/blog/2017/09/17/home-server-build/)
2. [Part 2, Terraform/Docker](https://captnemo.in/blog/2017/11/09/home-server-update/) 2. [Part 2, Terraform/Docker](https://captnemo.in/blog/2017/11/09/home-server-update/)
3. [Part 3, Learnings](https://captnemo.in/blog/2017/12/18/home-server-learnings/) 3. [Part 3, Learnings](https://captnemo.in/blog/2017/12/18/home-server-learnings/)
4. [Part 4, Migrating from Google (and more)](https://captnemo.in/blog/2017/12/31/migrating-from-google/) 4. [Part 4, Migrating from Google (and more)](https://captnemo.in/blog/2017/12/31/migrating-from-google/)
5. [Part 5, Networking](https://captnemo.in/blog/2018/04/22/home-server-networking/)
6. [Part 6, RAID](https://captnemo.in/blog/2019/02/24/btrfs-raid-device-replacement-story/)
The canonical URL for this repo is https://git.captnemo.in/nemo/nebula/. A mirror is maintained on GitHub at <https://github.com/captn3m0/nebula> The canonical URL for this repo is https://git.captnemo.in/nemo/nebula/. A mirror is maintained on GitHub.
# modules # modules
1. docker: to actually run the services. Catch-all for miscellaneous containers 1. docker: to actually run the services.
2. cloudflare: to manage the DNS. 2. cloudflare: to manage the DNS.
3. mysql: to create mysql users and databases. 3. mysql: to create mysql users and databases.
4. media: Media related containers (Jackett, Lidarr, Radarr, Sonarr)
5. Monitoring: Monitoring related resources (Cadvisor, Grafana, NodeExporter, Prometheus, Transmission-Exporter)
6. Gitea: Just git.captnemo.in
7. miniflux: RSS Web reader
8. Radicale: CardDav/CalDav webserver
Self-learning project for terraform/docker. Self-learning project for terraform/docker.
# Planned # Planned
1. ~Setup DigitalOcean~ 1. Setup DigitalOcean
2. Add DO infrastructure via ansible 2. Add DO infrastructure via ansible
3. ~Add traefik for proper proxying~ 3. ~Add traefik for proper proxying~
4. Maybe add docker swarm (or k8s?) across both the servers. Might setup the k8s API on the Raspberry Pi. 4. Maybe add docker swarm (or k8s?) across both the servers. Might setup the k8s API on the Raspberry Pi.
# Service List # Service List
Currently running the following (all links are to the `store.docker.com` links for the docker images that I'm using: Currently running the following (all links are to the `store.docker.com` links for the docker images that I'm using:
| image | tag | module/link | ## Databases
| -------------------------------- | ---------- | ---------------------------------------------------- |
| captn3m0/opml-gen | latest | https://opml.bb8.fun |
| captn3m0/rss-bridge | latest | https://github.com/RSS-Bridge/rss-bridge |
| captn3m0/speedtest-exporter | alpine | https://github.com/stefanwalther/speedtest-exporter |
| emby/embyserver | latest | https://emby.media |
| gitea/gitea | 1.5.0-rc1 | services |
| google/cadvisor | latest | monitoring |
| grafana/grafana | latest | monitoring |
| jankysolutions/requestbin | latest | tools |
| linuxserver/airsonic | latest | media |
| linuxserver/jackett | latest | media |
| linuxserver/lidarr | latest | media |
| linuxserver/lychee | latest | media |
| linuxserver/radarr | latest | media |
| linuxserver/sonarr | latest | media |
| linuxserver/transmission | latest | media |
| linuxserver/ubooquity | latest | media |
| miniflux/miniflux | 2.0.9 | tools |
| postgres | 10-alpine | database |
| prom/node-exporter | v0.15.2 | monitoring |
| prom/prometheus | latest | monitoring |
| requarks/wiki | latest | services |
| serjs/go-socks5-proxy | latest | tools |
| tocttou/gotviz | latest | na |
| tomsquest/docker-radicale | latest | services |
| traefik | 1.6-alpine | plumbing |
## Docker Notes - [MariaDB](https://store.docker.com/images/mariadb) for a simple database backend
- [MongoRocks](https://store.docker.com/community/images/jadsonlourenco/mongo-rocks) as a mongoDB server. Uses RocksDB as the backend
- Lots of the above images are from the excellent [LinuxServer.io](https://www.linuxserver.io), and they're doing great work :+1: ## Media
- Most images are running the latest beta (if available) or stable versions.
- Traefik is running with wildcard certificates. - [Emby](https://store.docker.com/community/images/emby/embyserver) Media Server
- [CouchPotato](https://store.docker.com/community/images/linuxserver/couchpotato), auto-download movies
- [SickRage](https://store.docker.com/community/images/linuxserver/sickrage), auto-download TV shows
- [Transmission](https://store.docker.com/community/images/linuxserver/transmission), to download torrents
- [AirSonic](https://store.docker.com/community/images/airsonic/airsonic), for a music server
- [Ubooquity](https://store.docker.com/community/images/linuxserver/ubooquity), EBooks server with OPDS support
- [Lychee](https://store.docker.com/community/images/linuxserver/lychee), as a simple image-sharing/hosting service
## Plumbing
- [Traefik](https://store.docker.com/images/traefik) as a reverse-proxy server, and TLS termination
- [CAdvisor](https://store.docker.com/community/images/google/cadvisor), for basic monitoring
## Misc
- [Wiki.JS](https://store.docker.com/community/images/requarks/wiki) as a simple home-wiki
- [Muximux](https://store.docker.com/community/images/linuxserver/muximux) as a landing page for the entire setup
- [Radicale](https://store.docker.com/community/images/tomsquest/docker-radicale), for a CalDav/Carddav server
- [Gitea](https://store.docker.com/community/images/gitea/gitea), git server
6 out of the above images are from the excellent [LinuxServer.io](https://www.linuxserver.io), and they're doing great work :+1:
## Security Headers Note
The following security headers are applied using traefik on all traefik frontend docker backends:
- HSTS
- Redirect HTTP->HTTPS
- contentTypeNosniff: true
- browserXSSFilter: true
- XFO: Allow-From home.bb8.fun
- referrerPolicy: no-referrer
- X-Powered-By: Allomancy
- X-Server: BlackBox
- X-Clacks-Overhead "GNU Terry Pratchett" (On some domains)
~~Currently waiting on traefik 1.5.0-rc2 to fix security specific headers issue (marked as TODO above).~~ (Now resolved with new traefik release)
## Upstream ## Upstream
I've been using this as a contributing opportunity and reporting/fixing issues upstream: Issues I've faced/reported as a result of this project:
1. Airsonic HTTPS proxying is broken. Reported: https://github.com/airsonic/airsonic/issues/641. Turned out to be a known issue: https://github.com/airsonic/airsonic/issues/594. Now fixed. 1. Airsonic HTTPS proxying is broken. Reported: https://github.com/airsonic/airsonic/issues/641. Turned out to be a known issue: https://github.com/airsonic/airsonic/issues/594.
2. Traefik docker backend security headers were broken with dashes. I [reported it here](https://github.com/containous/traefik/issues/2493), and fixed by https://github.com/containous/traefik/pull/2496 :white_check_mark: 2. Traefik docker backend security headers were broken with dashes. Reported at https://github.com/containous/traefik/issues/2493, and fixed by https://github.com/containous/traefik/pull/2496 :white_check_mark:
3. Headphones dies repeatedly with no error logs. Yet-to-report. (Already reported, fails due to classical artists) 3. Headphones dies repeatedly with no error logs. Yet-to-report.
4. Terraform doesn't parse mariadb version numbers. Report: https://github.com/terraform-providers/terraform-provider-mysql/issues/6. Filed a [PR to fix](https://github.com/hashicorp/go-version/pull/34) and [to bump the go-version dependency](https://github.com/terraform-providers/terraform-provider-mysql/pull/27) :white_check_mark: 4. Terraform doesn't parse mariadb version numbers. Report: https://github.com/terraform-providers/terraform-provider-mysql/issues/6. Got this fixed myself by filing a PR: https://github.com/hashicorp/go-version/pull/34. Another PR pending in the [provider](https://github.com/terraform-providers/terraform-provider-mysql/pull/27) to bump the go-version dependency. :white_check_mark:
5. `elibsrv` didn't support ebook-convert, only mobigen. PR is at https://github.com/captn3m0/elibsrv/pull/1. Merged to `elibsrv` trunk, will be part of next release. 5. `elibsrv` didn't support ebook-convert, only mobigen. PR is at https://github.com/captn3m0/elibsrv/pull/1. I've to get this merged upstream for the next release.
6. `ubooquity` docker container doesn't let you set admin password: https://github.com/linuxserver/docker-ubooquity/issues/17. (Couldn't reproduce, closed) :white_check_mark: 6. `ubooquity` docker container doesn't let you set admin password: https://github.com/linuxserver/docker-ubooquity/issues/17. (Couldn't reproduce, closed) :white_check_mark:
7. Traefik customresponseheaders can't contain colons on the docker backend: https://github.com/containous/traefik/issues/2517. Fixed with https://github.com/containous/traefik/pull/2509 :white_check_mark: 7. Traefik customresponseheaders can't contain colons on the docker backend: https://github.com/containous/traefik/issues/2517. Fixed with https://github.com/containous/traefik/pull/2509 :white_check_mark:
8. Traefik Security headers don't overwrite upstream headers: https://github.com/containous/traefik/issues/2618 :white_check_mark: 8. Traefik Security headers don't overwrite upstream headers: https://github.com/containous/traefik/issues/2618
9. Transmission exporter broke with different data types while unmarshalling JSON in go. I filed a PR https://github.com/metalmatze/transmission-exporter/pull/2 :white_check_mark:
10. Radarr official docker container was [running a very old `mediainfo`](https://github.com/Radarr/Radarr/issues/2668#issuecomment-376310514). [Filed a fix to upgrade `mediainfo` on the official radarr image](https://github.com/linuxserver/docker-baseimage-mono/pull/3) :white_check_mark:
11. Patched the [speedtest-exporter](https://github.com/stefanwalther/speedtest-exporter/pull/7) to use Alpine and upgraded Node.JS for a smaller updated build.
12. Faced (4) above again because mariadb decided to add `:` in the version response. [Workaround was to force set `--version=10.3-mariadb`](https://git.captnemo.in/nemo/nebula/commit/5f47a08bb55eea2c708c41668657ac1efa84c72a)
13. Reported [2 critical security issues in Abstruse CI](https://github.com/bleenco/abstruse/issues/363). :white_check_mark:
14. Faced (13) above again with postgres, thankfully [someone already fixed version parsing](https://github.com/terraform-providers/terraform-provider-postgresql/pull/31) :white_check_mark:
15. RSS Bridge was missing an official Docker Image. [I Filed a PR](https://github.com/RSS-Bridge/rss-bridge/pull/720) :white_check_mark:
# Plumbing # Plumbing
Their is a lot of additional infrastructure that is _not-yet_ part of this repo. This includes: Their is a lot of additional infrastructure that is _not-yet_ part of this repo. This includes:
1. The Digital Ocean droplet running DNSCrypt and simpleproxy to proxy over a openvpn connection to this box. 1. The Digital Ocean droplet running DNSCrypt and simpleproxy to proxy over a openvpn connection to this box.
2. openbox, kodi configuration to run on boot along with the Steam Controller for the HTPC setup 2. openbox, kodi configuration to run on boot along with the Steam Controller for the HTPC setup
3. Docker main configuration with half-baked CA setup 3. Docker main configuration with half-baked CA setup
4. btrfs-backed subvolumes and snapshotting for most things in /mnt/xwing/ (in-progress) 4. btrfs-backed subvolumes and snapshotting for most things in /mnt/xwing/ (in-progress)
5. User-creation on the main server. (I'm using a common user for media applications and specific users for other applications) 5. User-creation on the main server. (I'm using a common user for media applications and specific users for other applications)
# License # License

View File

@ -1,5 +0,0 @@
<?php
// Generates the Ubooquity preferences.json file
$template = "ubooquity.tpl.json";

View File

@ -4,18 +4,18 @@
*/ */
resource "cloudflare_record" "home" { resource "cloudflare_record" "home" {
zone_id = var.zone_id domain = "${var.domain}"
name = "in" name = "in"
value = var.ips["eth0"] value = "${var.ips["eth0"]}"
type = "A" type = "A"
} }
resource "cloudflare_record" "home-wildcard" { resource "cloudflare_record" "home-wildcard" {
zone_id = var.zone_id domain = "${var.domain}"
name = "*.in" name = "*.in"
value = cloudflare_record.home.hostname value = "${cloudflare_record.home.hostname}"
type = "CNAME" type = "CNAME"
ttl = 3600 ttl = 3600
} }
/** /**
@ -23,42 +23,18 @@ resource "cloudflare_record" "home-wildcard" {
* *.bb8.fun -> bb8.fun * *.bb8.fun -> bb8.fun
*/ */
resource "cloudflare_record" "internet" { resource "cloudflare_record" "internet" {
zone_id = var.zone_id domain = "${var.domain}"
name = "@" name = "@"
value = var.droplet_ip value = "${var.ips["static"]}"
type = "A" type = "A"
} }
resource "cloudflare_record" "internet-wildcard" { resource "cloudflare_record" "internet-wildcard" {
zone_id = var.zone_id domain = "${var.domain}"
name = var.domain name = "*.${var.domain}"
value = cloudflare_record.internet.hostname value = "${cloudflare_record.internet.hostname}"
type = "CNAME" type = "CNAME"
ttl = 3600 ttl = 3600
}
resource "cloudflare_record" "dns" {
zone_id = var.zone_id
name = "dns"
value = var.ips["static"]
type = "A"
}
resource "cloudflare_record" "doh" {
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" {
zone_id = var.zone_id
name = "_acme-challenge.${var.domain}"
type = "A"
value = "127.0.0.1"
ttl = "300"
} }
/** /**
@ -66,44 +42,18 @@ resource "cloudflare_record" "acme-no-cname-1" {
* *.vpn.bb8.fun * *.vpn.bb8.fun
*/ */
resource "cloudflare_record" "vpn" { resource "cloudflare_record" "vpn" {
zone_id = var.zone_id domain = "${var.domain}"
name = "vpn" name = "vpn"
value = var.ips["tun0"] value = "${var.ips["tun0"]}"
type = "A" type = "A"
} }
resource "cloudflare_record" "vpn_wildcard" { resource "cloudflare_record" "vpn_wildcard" {
zone_id = var.zone_id domain = "${var.domain}"
name = "*.vpn.${var.domain}" name = "*.vpn.${var.domain}"
value = cloudflare_record.vpn.hostname value = "${cloudflare_record.vpn.hostname}"
type = "CNAME" type = "CNAME"
ttl = 3600 ttl = 3600
}
/**
* vpn.bb8.fun
* *.vpn.bb8.fun
*/
resource "cloudflare_record" "dovpn" {
zone_id = var.zone_id
name = "dovpn"
value = var.ips["dovpn"]
type = "A"
}
resource "cloudflare_record" "dovpn_wildcard" {
zone_id = var.zone_id
name = "*.dovpn.${var.domain}"
value = cloudflare_record.dovpn.hostname
type = "CNAME"
ttl = 3600
}
resource "cloudflare_record" "etcd" {
zone_id = var.zone_id
name = "etcd"
value = var.ips["dovpn"]
type = "A"
} }
######################## ########################
@ -111,21 +61,21 @@ resource "cloudflare_record" "etcd" {
######################## ########################
resource "cloudflare_record" "mailgun-spf" { resource "cloudflare_record" "mailgun-spf" {
zone_id = var.zone_id domain = "${var.domain}"
name = "l" name = "l"
value = "v=spf1 include:mailgun.org ~all" value = "v=spf1 include:mailgun.org ~all"
type = "TXT" type = "TXT"
} }
resource "cloudflare_record" "mailgun-dkim" { resource "cloudflare_record" "mailgun-dkim" {
zone_id = var.zone_id domain = "${var.domain}"
name = "k1._domainkey.l" name = "k1._domainkey.l"
value = "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnbP+IQkuPkgmUhpqCKzIdDSZ0HazaMp+cdBH++LBed8oY8/jmV8BhxMp5JwyePzRTxneT8ASsRtcp7CQ3z4nMC7aFX0kH6Bnu2v+u2JWudxs8x0I02OrPbSaQ5QVQdbAaCUCEfCQ06LJsn8aqPNrRIOWEMnxln+ebFJ0wKGscFQIDAQAB" value = "k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnbP+IQkuPkgmUhpqCKzIdDSZ0HazaMp+cdBH++LBed8oY8/jmV8BhxMp5JwyePzRTxneT8ASsRtcp7CQ3z4nMC7aFX0kH6Bnu2v+u2JWudxs8x0I02OrPbSaQ5QVQdbAaCUCEfCQ06LJsn8aqPNrRIOWEMnxln+ebFJ0wKGscFQIDAQAB"
type = "TXT" type = "TXT"
} }
resource "cloudflare_record" "mailgun-mxa" { resource "cloudflare_record" "mailgun-mxa" {
zone_id = var.zone_id domain = "${var.domain}"
name = "l" name = "l"
value = "mxa.mailgun.org" value = "mxa.mailgun.org"
type = "MX" type = "MX"
@ -133,17 +83,9 @@ resource "cloudflare_record" "mailgun-mxa" {
} }
resource "cloudflare_record" "mailgun-mxb" { resource "cloudflare_record" "mailgun-mxb" {
zone_id = var.zone_id domain = "${var.domain}"
name = "l" name = "l"
value = "mxb.mailgun.org" value = "mxb.mailgun.org"
type = "MX" type = "MX"
priority = 20 priority = 20
} }
resource "cloudflare_record" "k8s" {
zone_id = var.zone_id
name = "k8s"
value = "10.8.0.1"
type = "A"
ttl = 3600
}

View File

@ -1,7 +0,0 @@
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
}
}
}

View File

@ -1,10 +1,7 @@
variable "domain" { variable "domain" {
type = string type = "string"
} }
variable "ips" { variable "ips" {
type = map type = "map"
} }
variable "droplet_ip" {}
variable "zone_id" {}

11
data.tf
View File

@ -1,11 +0,0 @@
data "docker_network" "bridge" {
name = "bridge"
}
data "cloudflare_zones" "bb8" {
filter {
name = "bb8"
lookup_type = "exact"
match = "bb8.fun"
}
}

View File

@ -1,10 +0,0 @@
resource "docker_network" "postgres" {
name = "postgres"
driver = "bridge"
internal = true
ipam_config {
subnet = "172.20.0.8/27"
gateway = "172.20.0.9"
}
}

View File

@ -1,4 +0,0 @@
output "postgres-network-id" {
value = docker_network.postgres.name
}

View File

@ -1,58 +0,0 @@
resource "docker_container" "postgres" {
name = "postgres"
image = docker_image.postgres.image_id
command = [
"postgres",
"-c",
"max_connections=250",
"-c",
"shared_buffers=500MB",
]
volumes {
volume_name = docker_volume.pg_data.name
container_path = "/var/lib/postgresql/data"
read_only = false
}
// This is so that other host-only services can share this
ports {
internal = 5432
external = 5432
ip = var.ips["eth0"]
}
// This is a not-so-great idea
// TODO: Figure out a better way to make terraform SSH and then connect to localhost
ports {
internal = 5432
external = 5432
ip = var.ips["tun0"]
}
memory = 2048
memory_swap = 2048
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
env = [
"POSTGRES_PASSWORD=${var.postgres-root-password}",
]
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]
}
data "docker_registry_image" "postgres" {
name = "postgres:${var.postgres-version}"
}
data "docker_network" "bridge" {
name = "bridge"
}

View File

@ -1,10 +0,0 @@
terraform {
required_providers {
postgresql = {
source = "cyrilgdn/postgresql"
}
docker = {
source = "kreuzwerker/docker"
}
}
}

View File

@ -1,11 +0,0 @@
variable "postgres-version" {
description = "postgres version to use for fetching the docker image"
default = "14-alpine"
}
variable "ips" {
type = map(string)
}
variable "postgres-root-password" {
}

View File

@ -1,3 +0,0 @@
resource "docker_volume" "pg_data" {
name = "pg_data"
}

View File

@ -1,23 +0,0 @@
resource "digitalocean_droplet" "sydney" {
image = "??"
name = "sydney.captnemo.in"
region = "blr1"
size = "s-1vcpu-2gb"
ipv6 = true
private_networking = true
resize_disk = true
volume_ids = ["eae03502-9279-11e8-ab31-0242ac11470b"]
tags = [
"bangalore",
"proxy",
"sydney",
"vpn",
]
}
output "droplet_ipv4" {
value = digitalocean_droplet.sydney.ipv4_address
}

View File

@ -1,35 +0,0 @@
resource "digitalocean_firewall" "web" {
name = "web-inbound"
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"]
}
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"]
}
}

View File

@ -1,5 +0,0 @@
resource "digitalocean_floating_ip" "sydney" {
droplet_id = digitalocean_droplet.sydney.id
region = digitalocean_droplet.sydney.region
}

View File

@ -1,19 +0,0 @@
terraform {
required_providers {
pass = {
source = "camptocamp/pass"
}
digitalocean = {
source = "digitalocean/digitalocean"
}
postgresql = {
source = "cyrilgdn/postgresql"
}
cloudflare = {
source = "cloudflare/cloudflare"
}
docker = {
source = "kreuzwerker/docker"
}
}
}

158
docker/conf/headphones.ini Normal file
View File

@ -0,0 +1,158 @@
[General]
nzb_downloader = 0
cue_split = 1
libraryscan = 1
music_encoder = 0
open_magnet_links = 0
preferred_quality = 0
customport = 5000
download_scan_interval = 5
preferred_bitrate = 320
destination_dir = /music
encodervbrcbr = cbr
xldprofile = ""
delete_lossless_files = 1
encoderquality = 2
autowant_all = 0
ignored_words = "WMA,FLAC"
freeze_db = 0
encoder_multicore_count = 0
keep_torrent_files = 0
http_root = /
download_dir = ""
http_proxy = 1
launch_browser = 0
http_username = ""
lossless_destination_dir = ""
mb_ignore_age = 365
file_underscores = 0
cue_split_flac_path = ""
lossless_bitrate_from = 0
do_not_override_git_branch = 0
include_extras = 0
samplingfrequency = 44100
http_host = 0.0.0.0
album_art_format = cover
encoder_path = ""
hpuser = ""
detect_bitrate = 0
enable_https = 0
check_github = 0
custompass = ""
mirror = musicbrainz.org
api_enabled = 0
correct_metadata = 1
customuser = ""
cue_split_shntool_path = /usr/bin/shntool
lastfm_username = captn3m0
autowant_upcoming = 1
config_version = 5
check_github_interval = 999999
customauth = 0
file_permissions = 0644
prefer_torrents = 1
encoderoutputformat = mp3
search_interval = 1440
preferred_bitrate_low_buffer = 0
ignore_clean_releases = 0
http_password = ""
usenet_retention = 1500
add_album_art = 1
headphones_indexer = 0
wait_until_release_date = 0
rename_files = 0
file_format = $Track - $Title
customhost = headphones.bb8.fun
interface = default
folder_format = $Artist/$Album
move_files = 1
cleanup_files = 1
replace_existing_folders = 0
preferred_bitrate_allow_lossless = 0
embed_album_art = 1
check_github_on_startup = 0
http_port = 8181
download_torrent_dir = /downloads
folder_permissions = 0755
official_releases_only = 0
magnet_links = 0
log_dir = /config/logs
update_db_interval = 24
git_path = ""
required_words = ""
advancedencoder = ""
numberofseeders = 5
torrentblackhole_dir = ""
cache_dir = /config/cache
blackhole = 0
libraryscan_interval = 300
keep_nfo = 0
preferred_bitrate_high_buffer = 0
https_cert = /config/server.crt
hppass = ""
customsleep = 1
api_key = ""
encoderlossless = 1
torrent_downloader = 0
torrent_removal_interval = 720
blackhole_dir = ""
keep_original_folder = 1
extras = ""
autowant_manually_added = 1
encoder_multicore = 0
encoderfolder = ""
preferred_words = ""
https_key = /config/server.key
encoder = ffmpeg
git_user = rembo10
bitrate = 192
music_dir = /music
auto_add_artists = 1
git_branch = master
embed_lyrics = 0
lossless_bitrate_to = 0
keep_torrent_files_dir = ""
file_permissions_enabled = 1
album_art_max_width = 512
album_art_min_width = 512
soft_chroot = ""
rename_unprocessed = 1
rename_frozen = 1
folder_permissions_enabled = 1
do_not_process_unmatched = 0
[Subsonic]
subsonic_host = ""
subsonic_password = ""
subsonic_enabled = 0
subsonic_username = ""
[Email]
email_onsnatch = 0
email_smtp_password = ""
email_tls = 0
email_smtp_port = 25
email_smtp_server = ""
email_enabled = 0
email_smtp_user = ""
email_ssl = 0
email_to = ""
email_from = ""
[Advanced]
verify_ssl_cert = 1
ignored_files = ,
journal_mode = wal
album_completion_pct = 80
ignored_folders = ,
cache_sizemb = 32
[Piratebay]
piratebay_ratio = 0.1
piratebay = 1
piratebay_proxy_url = https://duckingproxy.eu/
[MPC]
mpc_enabled = 0
[XBMC]
xbmc_username = ""
xbmc_host = http://${var.ips["eth0"]}:8080
xbmc_enabled = 1
xbmc_update = 1
xbmc_password = ""
xbmc_notify = 1

View File

@ -1,11 +0,0 @@
/* TEAM */
Captain: Nemo (Abhay Rana)
Contact: git [at] captnemo.in
Twitter: @captn3m0
/* SITE */
Last update: Feb 2018
Language: English
IDE: Sublime Text, Vim
Runs-On: Docker
Details: https://git.captnemo.in/nemo/nebula/

View File

@ -1,72 +0,0 @@
# Web must be converted manually. See https://docs.traefik.io/operations/api/
# Redirect on entry point "http" must be converted manually. See https://docs.traefik.io/middlewares/http/redirectscheme/
# TLS on entry point "https" must be converted manually. See https://docs.traefik.io/routing/routers/#tls
# The domain (bb8.fun) defined in the Docker provider must be converted manually. See https://docs.traefik.io/providers/docker/#defaultrule
# All the elements related to dynamic configuration (backends, frontends, ...) must be converted manually. See https://docs.traefik.io/routing/overview/
# The entry point (https) defined in the ACME configuration must be converted manually. See https://docs.traefik.io/routing/routers/#certresolver
[global]
sendAnonymousUsage = true
[tls.options]
[tls.options.default]
minVersion = "VersionTLS12"
[[tls.certificates]]
certFile = "/etc/traefik/git.captnemo.in.crt"
keyFile = "/etc/traefik/git.captnemo.in.key"
[[tls.certificates]]
certFile = "/etc/traefik/rss.captnemo.in.crt"
keyFile = "/etc/traefik/rss.captnemo.in.key"
# This forces port 8080
[api]
# https://doc.traefik.io/traefik/operations/dashboard/#insecure-mode
dashboard = true
# Enable the API in insecure mode, which means that the API will be available directly on the entryPoint named traefik.
insecure = true
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.http]
[entryPoints.https]
address = ":443"
[entryPoints.https.http]
[providers]
providersThrottleDuration = "2s"
[providers.docker]
watch = true
endpoint = "unix:///var/run/docker.sock"
swarmModeRefreshSeconds = "15s"
httpClientTimeout = "0s"
[providers.file]
[log]
[accessLog]
bufferingSize = 0
[certificatesResolvers]
[certificatesResolvers.default]
[certificatesResolvers.default.acme]
email = "acme@captnemo.in"
storage = "/acme/acme.json"
certificatesDuration = 0
[certificatesResolvers.default.acme.dnsChallenge]
provider = "cloudflare"
delayBeforeCheck = "2m0s"
[certificatesResolvers.default.acme.httpChallenge]
entryPoint = "http"
[certificatesResolvers.t]
[certificatesResolvers.t.acme]
email = "acme@captnemo.in"
storage = "/acme/acme.json"
[certificatesResolvers.myresolver.acme.tlsChallenge]
[http.middlewares]
[http.middlewares.test-redirectscheme.redirectScheme]
scheme = "https"
permanent = true

View File

@ -1,26 +0,0 @@
global:
sendAnonymousUsage: true
entryPoints:
http:
address: :80
https:
address: :443
providers:
providersThrottleDuration: 2s
docker:
watch: true
endpoint: unix:///var/run/docker.sock
swarmModeRefreshSeconds: 15s
file: {}
log: {}
accessLog: {}
certificatesResolvers:
default:
acme:
email: acme@captnemo.in
storage: /acme/acme.json
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 2m0s
httpChallenge:
entryPoint: http

View File

@ -1,11 +1,9 @@
defaultEntryPoints = ["http", "https"] defaultEntryPoints = ["http", "https"]
sendAnonymousUsage = true
checkNewVersion = false
[traefikLog]
[accessLog]
[entryPoints] [entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https] [entryPoints.https]
address = ":443" address = ":443"
# This is required for ACME support # This is required for ACME support
@ -13,18 +11,9 @@ checkNewVersion = false
[[entryPoints.https.tls.certificates]] [[entryPoints.https.tls.certificates]]
certFile = "/etc/traefik/git.captnemo.in.crt" certFile = "/etc/traefik/git.captnemo.in.crt"
keyFile = "/etc/traefik/git.captnemo.in.key" keyFile = "/etc/traefik/git.captnemo.in.key"
[[entryPoints.https.tls.certificates]]
certFile = "/etc/traefik/rss.captnemo.in.crt"
keyFile = "/etc/traefik/rss.captnemo.in.key"
[[entryPoints.https.tls.certificates]]
certFile = "/etc/traefik/tatooine.club.crt"
keyFile = "/etc/traefik/tatooine.club.key"
[docker] [docker]
# Make sure you mount this as readonly # Make sure you mount this as readonly
# NOTE: readonly doesn't reduce the risk because
# it is a unix socket - it doesn't automatically translate
# read|write perms to GET/POST requests.
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"
domain = "bb8.fun" domain = "bb8.fun"
watch = true watch = true
@ -36,33 +25,81 @@ checkNewVersion = false
# This is currently not exposed # This is currently not exposed
# Since I can't apply a authentication # Since I can't apply a authentication
# on this yet # 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] [web]
address = ":1111" address = ":1111"
readOnly = true readOnly = true
# To enable Traefik to export internal metrics to Prometheus
[web.metrics.prometheus]
[acme] [acme]
email = "acme@captnemo.in" email = "acme@captnemo.in"
storage = "/acme/acme.json" storage = "/acme/acme.json"
entryPoint = "https" entryPoint = "https"
onHostRule = false dnsProvider = "cloudflare"
onHostRule = true
onDemand = false onDemand = false
acmelogging = true acmelogging = true
[acme.httpChallenge] # Waiting till Jan '18 to get wildcard SSL on LE
entryPoint = "http"
[acme.dnsChallenge]
provider = "cloudflare"
delayBeforeCheck = 120
resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
# Primary 2 wildcard certs
[[acme.domains]] [[acme.domains]]
main = "*.bb8.fun" main = "bb8.fun"
# Internal services are also protected! sans = [
[[acme.domains]] "airsonic.bb8.fun",
main = "*.in.bb8.fun" "airsonic.in.bb8.fun",
"cadvisor.bb8.fun",
"couchpotato.bb8.fun",
"debug.in.bb8.fun",
"ebooks.bb8.fun",
"ebooks.in.bb8.fun",
"emby.bb8.fun",
"emby.in.bb8.fun",
"flexget.bb8.fun",
"git.bb8.fun",
"gitea.bb8.fun",
"grafana.bb8.fun",
"headphones.bb8.fun",
"home.bb8.fun",
"home.in.bb8.fun",
"library.bb8.fun",
"luke.bb8.fun",
"monitoring.bb8.fun",
"muximux.bb8.fun",
"muximux.in.bb8.fun",
"pics.bb8.fun",
"pics.in.bb8.fun",
"read.bb8.fun",
"read.in.bb8.fun",
"rey.bb8.fun",
"scan.bb8.fun",
"tatooine.bb8.fun",
"traefik.bb8.fun",
"transmission.bb8.fun",
"wiki.bb8.fun"
]

View File

@ -10,17 +10,17 @@
"bind-address-ipv6": "::", "bind-address-ipv6": "::",
"blocklist-enabled": true, "blocklist-enabled": true,
"blocklist-url": "http://john.bitsurge.net/public/biglist.p2p.gz", "blocklist-url": "http://john.bitsurge.net/public/biglist.p2p.gz",
"cache-size-mb": 256, "cache-size-mb": 16,
"dht-enabled": true, "dht-enabled": true,
"download-dir": "/downloads", "download-dir": "/downloads",
"download-queue-enabled": false, "download-queue-enabled": true,
"download-queue-size": 5, "download-queue-size": 5,
"encryption": 1, "encryption": 1,
"idle-seeding-limit": 30, "idle-seeding-limit": 30,
"idle-seeding-limit-enabled": false, "idle-seeding-limit-enabled": false,
"incomplete-dir": "/downloads", "incomplete-dir": "/downloads",
"incomplete-dir-enabled": true, "incomplete-dir-enabled": true,
"lpd-enabled": true, "lpd-enabled": false,
"message-level": 2, "message-level": 2,
"peer-congestion-algorithm": "", "peer-congestion-algorithm": "",
"peer-id-ttl-hours": 6, "peer-id-ttl-hours": 6,
@ -31,17 +31,15 @@
"peer-port-random-low": 49152, "peer-port-random-low": 49152,
"peer-port-random-on-start": false, "peer-port-random-on-start": false,
"peer-socket-tos": "default", "peer-socket-tos": "default",
"pex-enabled": false, "pex-enabled": true,
"port-forwarding-enabled": true, "port-forwarding-enabled": true,
"preallocation": 1, "preallocation": 1,
"prefetch-enabled": true, "prefetch-enabled": true,
"queue-stalled-enabled": false, "queue-stalled-enabled": true,
"queue-stalled-minutes": 30, "queue-stalled-minutes": 30,
"ratio-limit": 1.2, "ratio-limit": 0.2,
"ratio-limit-enabled": true, "ratio-limit-enabled": true,
"rename-partial-files": true, "rename-partial-files": true,
"rpc-host-whitelist": "transmission.bb8.fun,transmission",
"rpc-host-whitelist-enabled": true,
"rpc-authentication-required": false, "rpc-authentication-required": false,
"rpc-bind-address": "0.0.0.0", "rpc-bind-address": "0.0.0.0",
"rpc-enabled": true, "rpc-enabled": true,
@ -51,19 +49,19 @@
"rpc-username": "", "rpc-username": "",
"rpc-whitelist": "127.0.0.1", "rpc-whitelist": "127.0.0.1",
"rpc-whitelist-enabled": false, "rpc-whitelist-enabled": false,
"scrape-paused-torrents-enabled": false, "scrape-paused-torrents-enabled": true,
"script-torrent-done-enabled": false, "script-torrent-done-enabled": false,
"script-torrent-done-filename": "", "script-torrent-done-filename": "",
"seed-queue-enabled": true, "seed-queue-enabled": false,
"seed-queue-size": 50, "seed-queue-size": 10,
"speed-limit-down": 100, "speed-limit-down": 100,
"speed-limit-down-enabled": false, "speed-limit-down-enabled": false,
"speed-limit-up": 50, "speed-limit-up": 50,
"speed-limit-up-enabled": false, "speed-limit-up-enabled": true,
"start-added-torrents": true, "start-added-torrents": true,
"trash-original-torrent-files": false, "trash-original-torrent-files": false,
"umask": 2, "umask": 2,
"upload-slots-per-torrent": 10, "upload-slots-per-torrent": 14,
"utp-enabled": true, "utp-enabled": true,
"watch-dir": "/watch", "watch-dir": "/watch",
"watch-dir-enabled": true "watch-dir-enabled": true

View File

@ -3,22 +3,8 @@
{ {
"pathString": "/files", "pathString": "/files",
"userName": [ "userName": [
"arvind",
"diya",
"gappan",
"himanshu",
"konarak",
"pratyush",
"reddit", "reddit",
"riccu", "tatooine"
"sankalp",
"shreyas",
"tatooine",
"vignesh",
"harman",
"pranav",
"swapnil",
"noopur"
] ]
} }
], ],
@ -26,22 +12,8 @@
{ {
"pathString": "/comics", "pathString": "/comics",
"userName": [ "userName": [
"arvind",
"diya",
"gappan",
"himanshu",
"konarak",
"pratyush",
"reddit", "reddit",
"riccu", "tatooine"
"sankalp",
"shreyas",
"tatooine",
"vignesh",
"harman",
"pranav",
"swapnil",
"noopur"
] ]
} }
], ],
@ -49,139 +21,51 @@
{ {
"pathString": "/books", "pathString": "/books",
"userName": [ "userName": [
"arvind",
"diya",
"gappan",
"himanshu",
"konarak",
"pratyush",
"reddit", "reddit",
"riccu", "tatooine"
"sankalp",
"shreyas",
"tatooine",
"vignesh",
"harman",
"pranav",
"swapnil",
"noopur"
] ]
} }
], ],
"users": [ "users": [
{ {
"name": "reddit", "name": "reddit",
"passwordHash": "passwordHash": "396731fff7f1931aeba6e69d3443d5ef7971569e3b9d64a3a4deca655789917a"
"396731fff7f1931aeba6e69d3443d5ef7971569e3b9d64a3a4deca655789917a"
}, },
{ {
"name": "tatooine", "name": "tatooine",
"passwordHash": "passwordHash": "ca0c540641a9e34c47cbd1866443ca181202aaa422fcc5ad4cbf75095aab7da0"
"ca0c540641a9e34c47cbd1866443ca181202aaa422fcc5ad4cbf75095aab7da0"
},
{
"name": "riccu",
"passwordHash":
"ff66d15e21624763cb2d65a21a7aa275ae65219d6f5ed0e5c5583c9be2fc3b12"
},
{
"name": "sankalp",
"passwordHash":
"b3c219dffa8a379c4daaed75c63141ebefa2a6f0a872e9cd7f328ad6511fb863"
},
{
"name": "pratyush",
"passwordHash":
"e63af1a184949abfd3666ef2c60c462191619fdcefadf9021a5d24f236d302fe"
},
{
"name": "arvind",
"passwordHash":
"126f31712138ea8e5f77c0e2565be848ec87a4057dfe1c4070a6c9d1f3de8ded"
},
{
"name": "harman",
"passwordHash":
"f9bd71d0a8cee05a724efae4f5636123f18d8c9c531c470892f8681375726bd2"
},
{
"name": "shreyas",
"passwordHash":
"ee4501f0aa63453f4360e974c3220c2c7a4c58d2125d989b80ef855e1471535d"
},
{
"name": "himanshu",
"passwordHash":
"c8da693b24c20921b16a55b8bd21b9e0c76e3bdfba81df20f1a0e6b010e0c3a5"
},
{
"name": "diya",
"passwordHash":
"96d39fafe6c1cfb8504ba8f438ab3e11a972f7a3bb3908287b9e3fa5bd28e19a"
},
{
"name": "vignesh",
"passwordHash":
"a1589cab7d5123af4fb19ccaea31e586348756944b1dca759a16a4a0b8e1243d"
},
{
"name": "konarak",
"passwordHash":
"49afa1013d2be0498107e12fc755f27edb90787161f00a2ef579bb6ad8c59b63"
},
{
"name": "gappan",
"passwordHash":
"681825c273d75dce4151f6c61358038e099fec2c3540369267f1fa28d607ce1d"
},
{
"name": "swapnil",
"passwordHash":
"f916f120f09ec561ff1d76e19e2749d1a6078e92051f1f5fcca884489fd43745"
},
{
"name": "noopur",
"passwordHash":
"f49e49db9893a187773fb08e8671ff2f9cd83b8d43b657fbf0abe67b3dfc0e9d"
},
{
"name": "pranav",
"passwordHash":
"9df97ced8b4de090c469244230ca64f5164ff37e9fde2314cf8c2e87db6d033b"
} }
], ],
"isFilesProviderEnabled" : true, "isFilesProviderEnabled": true,
"isComicsProviderEnabled" : true, "isComicsProviderEnabled": true,
"isBooksProviderEnabled" : true, "isBooksProviderEnabled": true,
"isUserManagementEnabled" : true, "isUserManagementEnabled": true,
"libraryPortNumber" : 2202, "libraryPortNumber": 2202,
"adminPortNumber" : 2203, "adminPortNumber": 2203,
"comicWidth" : 160, "comicWidth": 160,
"comicHeight" : 230, "comicHeight": 230,
"comicsPaginationNumber" : 30, "comicsPaginationNumber": 30,
"bookWidth" : 160, "bookWidth": 160,
"bookHeight" : 230, "bookHeight": 230,
"booksPaginationNumber" : 30, "booksPaginationNumber": 30,
"minimizeToTray" : false, "minimizeToTray": false,
"minimizeOnStartup" : false, "minimizeOnStartup": false,
"autoscanPeriod" : 1440, "autoscanPeriod": 0,
"isRemoteAdminEnabled" : true, "isRemoteAdminEnabled": true,
"theme" : "default", "theme": "default",
"isShrinkingCacheEnabled" : false, "isShrinkingCacheEnabled": false,
"shrunkPageWidth" : 1536, "shrunkPageWidth": 1536,
"shrunkPageHeight" : 2500, "shrunkPageHeight": 2500,
"shrinkingCachePath" : "", "shrinkingCachePath": "",
"autoScanAtLaunch" : false, "autoScanAtLaunch": false,
"reverseProxyPrefix" : "", "reverseProxyPrefix": "",
"keystorePath" : "", "keystorePath": "",
"keystorePassword" : "", "keystorePassword": "",
"isOpdsProviderEnabled" : true, "isOpdsProviderEnabled": true,
"folderExclusionPattern" : "", "folderExclusionPattern": "",
"bypassSingleRootFolder" : false, "bypassSingleRootFolder": false,
"enableFolderMetadataDisplay" : true, "enableFolderMetadataDisplay": true,
"bookmarkUsingCookies" : false, "bookmarkUsingCookies": false,
"displayTitleInsteadOfFileName" : true, "displayTitleInsteadOfFileName": true,
"keepUnreachableSharedFolders" : false, "keepUnreachableSharedFolders": false
"isCalibreLibrary" : false,
"instanceId" : "3a0e4425a8e14c719ca2eb382f85292e"
} }

View File

@ -1,15 +0,0 @@
port: 3000
db:
type: postgres
host: postgres
port: 5432
user: wikijs
db: wikijs
pass: ${DB_PASSWORD}
ssl:
enabled: false
bindIP: 0.0.0.0
logLevel: silly
offline: true
ha: false
dataPath: /data

141
docker/conf/wiki.yml Normal file
View File

@ -0,0 +1,141 @@
#######################################################################
# Wiki.js - CONFIGURATION #
#######################################################################
# Full explanation + examples in the documentation:
# https://docs.requarks.io/wiki/install
# You can use an ENV variable by using $(ENV_VAR_NAME) as the value
# ---------------------------------------------------------------------
# Title of this site
# ---------------------------------------------------------------------
title: Scarif Wiki
# ---------------------------------------------------------------------
# Full public path to the site, without the trailing slash
# ---------------------------------------------------------------------
# INCLUDE CLIENT PORT IF NOT 80/443!
host: https://wiki.bb8.fun
# ---------------------------------------------------------------------
# Port the main server should listen to (80 by default)
# ---------------------------------------------------------------------
# To use process.env.PORT, comment the line below:
port: 9999
# ---------------------------------------------------------------------
# Data Directories
# ---------------------------------------------------------------------
paths:
repo: /repo
data: /data
# ---------------------------------------------------------------------
# Upload Limits
# ---------------------------------------------------------------------
# In megabytes (MB)
uploads:
maxImageFileSize: 5
maxOtherFileSize: 100
# ---------------------------------------------------------------------
# Site Language
# ---------------------------------------------------------------------
# Possible values: en, de, es, fa, fr, ja, ko, nl, pt, ru, sr, tr or zh
lang: en
# Enable for right to left languages (e.g. arabic):
langRtl: false
# ---------------------------------------------------------------------
# Site Authentication
# ---------------------------------------------------------------------
public: true
auth:
defaultReadAccess: false
local:
enabled: true
google:
enabled: false
clientId: GOOGLE_CLIENT_ID
clientSecret: GOOGLE_CLIENT_SECRET
# ---------------------------------------------------------------------
# Secret key to use when encrypting sessions
# ---------------------------------------------------------------------
# Use a long and unique random string (256-bit keys are perfect!)
sessionSecret: $(SESSION_SECRET)
# ---------------------------------------------------------------------
# Database Connection String
# ---------------------------------------------------------------------
db: mongodb://mongorocks:27017/wiki
# ---------------------------------------------------------------------
# Git Connection Info
# ---------------------------------------------------------------------
# git:
# url: https://github.com/Organization/Repo
# branch: master
# auth:
# # Type: basic or ssh
# type: ssh
# # Only for Basic authentication:
# username: marty
# password: MartyMcFly88
# # Only for SSH authentication:
# privateKey: /etc/wiki/keys/git.pem
# sslVerify: true
# # Default email to use as commit author
# serverEmail: marty@example.com
# # Whether to use user email as author in commits
# showUserEmail: true
# ---------------------------------------------------------------------
# Features
# ---------------------------------------------------------------------
# You can enable / disable specific features below
features:
linebreaks: true
mathjax: false
# ---------------------------------------------------------------------
# External Logging
# ---------------------------------------------------------------------
externalLogging:
bugsnag: false
loggly: false
papertrail: false
rollbar: false
sentry: false
# ---------------------------------------------------------------------
# Color Theme
# ---------------------------------------------------------------------
theme:
primary: indigo
alt: blue-grey
viewSource: all # all | write | false
footer: blue-grey
code:
dark: true
colorize: true

View File

@ -1,12 +1,68 @@
# Database versions shouldn't be upgraded
data "docker_registry_image" "mariadb" {
name = "mariadb:10.3"
}
data "docker_registry_image" "mongorocks" {
name = "jadsonlourenco/mongo-rocks:latest"
}
# Leave all other apps at latesst
data "docker_registry_image" "emby" {
name = "emby/embyserver:latest"
}
data "docker_registry_image" "transmission" {
name = "linuxserver/transmission:latest"
}
data "docker_registry_image" "couchpotato" {
name = "linuxserver/couchpotato:latest"
}
data "docker_registry_image" "traefik" { data "docker_registry_image" "traefik" {
name = "traefik:1.7" name = "traefik:cancoillotte-alpine"
}
# https://github.com/go-gitea/gitea/releases
data "docker_registry_image" "gitea" {
name = "gitea/gitea:1.3.2"
}
data "docker_registry_image" "sickrage" {
name = "linuxserver/sickrage:latest"
}
data "docker_registry_image" "airsonic" {
name = "airsonic/airsonic:latest"
}
data "docker_registry_image" "wikijs" {
name = "requarks/wiki:latest"
}
data "docker_registry_image" "headphones" {
name = "linuxserver/headphones:latest"
}
data "docker_registry_image" "muximux" {
name = "linuxserver/muximux:latest"
} }
data "docker_registry_image" "ubooquity" { data "docker_registry_image" "ubooquity" {
name = "linuxserver/ubooquity:latest" name = "linuxserver/ubooquity:latest"
} }
data "docker_registry_image" "headerdebug" {
name = "brndnmtthws/nginx-echo-headers:latest"
}
data "docker_registry_image" "cadvisor" {
name = "google/cadvisor:latest"
}
data "docker_registry_image" "lychee" { data "docker_registry_image" "lychee" {
name = "linuxserver/lychee:latest" name = "linuxserver/lychee:latest"
} }

56
docker/db.tf Normal file
View File

@ -0,0 +1,56 @@
resource "docker_container" "mongorocks" {
name = "mongorocks"
image = "${docker_image.mongorocks.latest}"
restart = "unless-stopped"
destroy_grace_seconds = 30
must_run = true
memory = 256
volumes {
volume_name = "${docker_volume.mongorocks_data_volume.name}"
container_path = "/data/db"
host_path = "${docker_volume.mongorocks_data_volume.mountpoint}"
}
env = [
"AUTH=no",
"DATABASE=wiki",
"OPLOG_SIZE=50",
]
}
resource "docker_container" "mariadb" {
name = "mariadb"
image = "${docker_image.mariadb.latest}"
volumes {
volume_name = "${docker_volume.mariadb_volume.name}"
container_path = "/var/lib/mysql"
host_path = "${docker_volume.mariadb_volume.mountpoint}"
}
// This is so that other host-only services can share this
ports {
internal = 3306
external = 3306
ip = "${var.ips["eth0"]}"
}
// This is a not-so-great idea
// TODO: Figure out a better way to make terraform SSH and then connect to localhost
ports {
internal = 3306
external = 3306
ip = "${var.ips["tun0"]}"
}
memory = 512
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
env = [
"MYSQL_ROOT_PASSWORD=${var.mysql_root_password}",
]
}

40
docker/gitea.tf Normal file
View File

@ -0,0 +1,40 @@
resource docker_container "gitea" {
name = "gitea"
image = "${docker_image.gitea.latest}"
labels {
"traefik.port" = 3000
"traefik.enable" = "true"
"traefik.frontend.rule" = "Host:git.captnemo.in"
"traefik.frontend.headers.STSSeconds" = "2592000"
"traefik.frontend.headers.browserXSSFilter" = "true"
"traefik.frontend.headers.contentTypeNosniff" = "true"
"traefik.frontend.headers.SSLTemporaryRedirect" = "true"
"traefik.frontend.headers.STSIncludeSubdomains" = "false"
"traefik.frontend.headers.customResponseHeaders" = "${var.xpoweredby}"
"traefik.frontend.headers.customFrameOptionsValue" = "${var.xfo_allow}"
}
ports {
internal = 22
external = 2222
ip = "${var.ips["eth0"]}"
}
ports {
internal = 22
external = 2222
ip = "${var.ips["tun0"]}"
}
volumes {
volume_name = "${docker_volume.gitea_volume.name}"
container_path = "/data"
host_path = "${docker_volume.gitea_volume.mountpoint}"
}
memory = 256
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
}

View File

@ -1,20 +0,0 @@
data "docker_registry_image" "gotviz" {
name = "tocttou/gotviz:latest"
}
# resource "docker_image" "gotviz" {
# 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.image_id}"
# 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
# }

View File

@ -1,14 +1,84 @@
resource "docker_image" "traefik17" { resource "docker_image" "emby" {
name = data.docker_registry_image.traefik.name name = "${data.docker_registry_image.emby.name}"
pull_triggers = [data.docker_registry_image.traefik.sha256_digest] pull_triggers = ["${data.docker_registry_image.emby.sha256_digest}"]
}
resource "docker_image" "mariadb" {
name = "${data.docker_registry_image.mariadb.name}"
pull_triggers = ["${data.docker_registry_image.mariadb.sha256_digest}"]
}
resource "docker_image" "transmission" {
name = "${data.docker_registry_image.transmission.name}"
pull_triggers = ["${data.docker_registry_image.transmission.sha256_digest}"]
}
resource "docker_image" "couchpotato" {
name = "${data.docker_registry_image.couchpotato.name}"
pull_triggers = ["${data.docker_registry_image.couchpotato.sha256_digest}"]
}
resource "docker_image" "traefik" {
name = "${data.docker_registry_image.traefik.name}"
pull_triggers = ["${data.docker_registry_image.traefik.sha256_digest}"]
}
resource "docker_image" "gitea" {
name = "${data.docker_registry_image.gitea.name}"
pull_triggers = ["${data.docker_registry_image.gitea.sha256_digest}"]
}
resource "docker_image" "sickrage" {
name = "${data.docker_registry_image.sickrage.name}"
pull_triggers = ["${data.docker_registry_image.sickrage.sha256_digest}"]
}
resource "docker_image" "airsonic" {
name = "${data.docker_registry_image.airsonic.name}"
pull_triggers = ["${data.docker_registry_image.airsonic.sha256_digest}"]
}
resource "docker_image" "wikijs" {
name = "${data.docker_registry_image.wikijs.name}"
pull_triggers = ["${data.docker_registry_image.wikijs.sha256_digest}"]
}
# Attempting to use mongorocks to work around reboot issue
# Hoping that this will not face reboot-recovery issues
# Wrote about this: https://captnemo.in/blog/2017/12/18/home-server-learnings/
resource "docker_image" "mongorocks" {
name = "${data.docker_registry_image.mongorocks.name}"
pull_triggers = ["${data.docker_registry_image.mongorocks.sha256_digest}"]
}
resource "docker_image" "headphones" {
name = "${data.docker_registry_image.headphones.name}"
pull_triggers = ["${data.docker_registry_image.headphones.sha256_digest}"]
}
resource "docker_image" "muximux" {
name = "${data.docker_registry_image.muximux.name}"
pull_triggers = ["${data.docker_registry_image.muximux.sha256_digest}"]
} }
resource "docker_image" "ubooquity" { resource "docker_image" "ubooquity" {
name = data.docker_registry_image.ubooquity.name name = "${data.docker_registry_image.ubooquity.name}"
pull_triggers = [data.docker_registry_image.ubooquity.sha256_digest] pull_triggers = ["${data.docker_registry_image.ubooquity.sha256_digest}"]
} }
# resource "docker_image" "lychee" { # Helps debug traefik reverse proxy headers
# name = "${data.docker_registry_image.lychee.name}" # Highly recommended!
# pull_triggers = ["${data.docker_registry_image.lychee.sha256_digest}"] resource "docker_image" "headerdebug" {
# } name = "${data.docker_registry_image.headerdebug.name}"
pull_triggers = ["${data.docker_registry_image.headerdebug.sha256_digest}"]
}
resource "docker_image" "cadvisor" {
name = "${data.docker_registry_image.cadvisor.name}"
pull_triggers = ["${data.docker_registry_image.cadvisor.sha256_digest}"]
}
resource "docker_image" "lychee" {
name = "${data.docker_registry_image.lychee.name}"
pull_triggers = ["${data.docker_registry_image.lychee.sha256_digest}"]
}

View File

@ -1,17 +1,15 @@
locals { locals {
traefik_common_labels = { traefik_common_labels {
"traefik.enable" = "true" "traefik.enable" = "true"
// HSTS // HSTS
"traefik.frontend.headers.SSLTemporaryRedirect" = "true" "traefik.frontend.headers.SSLTemporaryRedirect" = "true"
"traefik.frontend.headers.STSSeconds" = "2592000" "traefik.frontend.headers.STSSeconds" = "2592000"
"traefik.frontend.headers.STSIncludeSubdomains" = "false" "traefik.frontend.headers.STSIncludeSubdomains" = "false"
// X-Powered-By, Server headers // X-Powered-By, Server headers
"traefik.frontend.headers.customResponseHeaders" = var.xpoweredby "traefik.frontend.headers.customResponseHeaders" = "${var.xpoweredby}"
// X-Frame-Options // 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.contentTypeNosniff" = "true"
"traefik.frontend.headers.browserXSSFilter" = "true" "traefik.frontend.headers.browserXSSFilter" = "true"
"traefik.docker.network" = "traefik"
} }
} }

View File

@ -1,30 +1,44 @@
# resource "docker_container" "lychee" { resource "docker_container" "lychee" {
# name = "lychee" name = "lychee"
# image = "${docker_image.lychee.image_id}" image = "${docker_image.lychee.latest}"
# restart = "unless-stopped"
# destroy_grace_seconds = 10 restart = "unless-stopped"
# must_run = true destroy_grace_seconds = 10
# volumes { must_run = true
# host_path = "/mnt/xwing/config/lychee"
# container_path = "/config" volumes {
# } host_path = "/mnt/xwing/config/lychee"
# volumes { container_path = "/config"
# host_path = "/mnt/xwing/data/lychee" }
# container_path = "/pictures"
# } volumes {
# upload { host_path = "/mnt/xwing/data/lychee"
# content = "${file("${path.module}/conf/lychee.php.ini")}" container_path = "/pictures"
# file = "/config/lychee/user.ini" }
# }
# labels = "${merge( upload {
# local.traefik_common_labels, content = "${file("${path.module}/conf/lychee.php.ini")}"
# map( file = "/config/lychee/user.ini"
# "traefik.port", 80, }
# "traefik.frontend.rule", "Host:pics.${var.domain}",
# ))}" labels {
# env = [ "traefik.port" = 80
# "PUID=986", "traefik.frontend.passHostHeader" = "false"
# "PGID=984", "traefik.enable" = "true"
# ] "traefik.frontend.headers.SSLTemporaryRedirect" = "true"
# # links = ["${var.links-mariadb}"] "traefik.frontend.headers.STSIncludeSubdomains" = "false"
# } "traefik.frontend.headers.contentTypeNosniff" = "true"
"traefik.frontend.headers.browserXSSFilter" = "true"
"traefik.frontend.headers.STSSeconds" = "2592000"
"traefik.frontend.headers.customFrameOptionsValue" = "${var.xfo_allow}"
"traefik.frontend.headers.customResponseHeaders" = "${var.xpoweredby}"
"traefik.frontend.rule" = "Host:pics.${var.domain},pics.in.${var.domain}"
}
env = [
"PUID=986",
"PGID=984",
]
links = ["mariadb"]
}

457
docker/main.tf Normal file
View File

@ -0,0 +1,457 @@
resource docker_container "transmission" {
name = "transmission"
image = "${docker_image.transmission.latest}"
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.auth.basic", "${var.basic_auth}",
"traefik.port", 9091,
))}"
ports {
internal = 51413
external = 51413
ip = "${var.ips["eth0"]}"
protocol = "udp"
}
volumes {
host_path = "/mnt/xwing/config/transmission"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
}
volumes {
host_path = "/mnt/xwing/data/watch/transmission"
container_path = "/watch"
}
upload {
content = "${file("${path.module}/conf/transmission.json")}"
file = "/config/settings.json"
}
env = [
"PGID=1003",
"PUID=1000",
"TZ=Asia/Kolkata",
]
memory = 256
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
}
resource "docker_container" "emby" {
name = "emby"
image = "${docker_image.emby.latest}"
volumes {
host_path = "/mnt/xwing/config/emby"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media"
container_path = "/media"
}
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.rule", "Host:emby.in.${var.domain},emby.${var.domain}",
"traefik.frontend.passHostHeader", "true",
"traefik.frontend.auth.basic", "${var.basic_auth}",
"traefik.port", 8096,
))}"
memory = 2048
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
# Running as lounge:tatooine
env = [
"APP_USER=lounge",
"APP_UID=1004",
"APP_GID=1003",
"APP_CONFIG=/mnt/xwing/config",
"TZ=Asia/Kolkata",
]
}
resource "docker_container" "couchpotato" {
name = "couchpotato"
image = "${docker_image.couchpotato.latest}"
volumes {
host_path = "/mnt/xwing/config/couchpotato"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
}
volumes {
host_path = "/mnt/xwing/media/Movies"
container_path = "/movies"
}
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.auth.basic", "${var.basic_auth}",
"traefik.port", 5050,
))}"
memory = 256
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
# Running as lounge:tatooine
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
links = ["transmission"]
}
resource "docker_container" "airsonic" {
name = "airsonic"
image = "${docker_image.airsonic.latest}"
restart = "unless-stopped"
destroy_grace_seconds = 30
must_run = true
user = "1004"
memory = 800
volumes {
host_path = "/mnt/xwing/config/airsonic/data"
container_path = "/airsonic/data"
}
volumes {
host_path = "/mnt/xwing/media/Music"
container_path = "/airsonic/music"
}
volumes {
host_path = "/mnt/xwing/config/airsonic/playlists"
container_path = "/airsonic/playlists"
}
volumes {
host_path = "/mnt/xwing/config/airsonic/podcasts"
container_path = "/airsonic/podcasts"
}
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.rule", "Host:airsonic.in.${var.domain},airsonic.${var.domain}",
"traefik.frontend.passHostHeader", "true",
"traefik.port", 4040,
))}"
}
resource "docker_container" "headerdebug" {
name = "headerdebug"
image = "${docker_image.headerdebug.latest}"
restart = "unless-stopped"
destroy_grace_seconds = 30
must_run = true
memory = 16
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.rule", "Host:debug.in.${var.domain},debug.${var.domain}",
"traefik.port", 8080,
"traefik.enable", "true",
))}"
}
resource "docker_container" "sickrage" {
name = "sickrage"
image = "${docker_image.sickrage.latest}"
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
memory = 512
volumes {
host_path = "/mnt/xwing/config/sickrage"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
}
volumes {
host_path = "/mnt/xwing/media/TV"
container_path = "/tv"
}
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.passHostHeader", "false",
"traefik.frontend.auth.basic", "${var.basic_auth}",
"traefik.port", 8081,
))}"
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
}
resource "docker_container" "headphones" {
name = "headphones"
image = "${docker_image.headphones.latest}"
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
memory = 128
volumes {
host_path = "/mnt/xwing/config/headphones"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
}
volumes {
host_path = "/mnt/xwing/media/Music"
container_path = "/music"
}
upload {
content = "${file("${path.module}/conf/headphones.ini")}"
file = "/config/config.ini"
}
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.auth.basic", "${var.basic_auth}",
"traefik.port", 8181,
))}"
# lounge:tatooine
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
}
resource "docker_container" "ubooquity" {
name = "ubooquity"
image = "${docker_image.ubooquity.latest}"
restart = "unless-stopped"
destroy_grace_seconds = 30
must_run = true
volumes {
host_path = "/mnt/xwing/config/ubooquity"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/EBooks"
container_path = "/books"
}
volumes {
host_path = "/mnt/xwing/media/EBooks"
container_path = "/files"
}
volumes {
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}"
"traefik.admin.frontend.auth.basic" = "${var.basic_auth}"
"traefik.read.port" = 2202
"traefik.read.frontend.rule" = "Host:read.${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}"
}
upload {
content = "${file("${path.module}/conf/ubooquity.json")}"
file = "/config/preferences.json"
}
# lounge:tatooine
env = [
"PUID=1004",
"PGID=1003",
"MAXMEM=800",
]
}
resource "docker_container" "wiki" {
name = "wiki"
image = "${docker_image.wikijs.latest}"
restart = "unless-stopped"
destroy_grace_seconds = 30
must_run = true
memory = 300
upload {
content = "${file("${path.module}/conf/wiki.yml")}"
file = "/var/wiki/config.yml"
}
volumes {
host_path = "/mnt/xwing/logs/wiki"
container_path = "/logs"
}
volumes {
host_path = "/mnt/xwing/data/wiki/repo"
container_path = "/repo"
}
volumes {
host_path = "/mnt/xwing/data/wiki/data"
container_path = "/data"
}
// The last header is a workaround for double header traefik bug
// This might be actually breaking iframe till the 1.5 Final release.
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.rule", "Host:wiki.${var.domain}",
"traefik.frontend.passHostHeader", "true",
"traefik.port", 9999,
"traefik.frontend.headers.customResponseHeaders", "${var.xpoweredby}||Referrer-Policy:${var.refpolicy}||X-Frame-Options:${var.xfo_allow}",
))}"
links = ["mongorocks"]
env = [
"WIKI_ADMIN_EMAIL=me@captnemo.in",
"SESSION_SECRET=${var.wiki_session_secret}",
]
}
resource "docker_container" "muximux" {
name = "muximux"
image = "${docker_image.muximux.latest}"
memory = 64
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
volumes {
host_path = "/mnt/xwing/config/muximux"
container_path = "/config"
}
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.port", 80,
"traefik.frontend.headers.frameDeny", "true",
"traefik.frontend.passHostHeader", "false",
"traefik.frontend.auth.basic", "${var.basic_auth}",
"traefik.frontend.rule", "Host:home.in.${var.domain},home.${var.domain}",
))}"
# lounge:tatooine
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
}
resource "docker_container" "cadvisor" {
name = "cadvisor"
image = "${docker_image.cadvisor.latest}"
memory = 512
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
volumes {
host_path = "/"
container_path = "/rootfs"
read_only = true
}
volumes {
host_path = "/sys"
container_path = "/sys"
read_only = true
}
volumes {
host_path = "/var/lib/docker"
container_path = "/var/lib/docker"
read_only = true
}
volumes {
host_path = "/dev/disk"
container_path = "/dev/disk"
read_only = true
}
volumes {
host_path = "/var/run"
container_path = "/var/run"
}
labels = "${merge(
local.traefik_common_labels,
map(
"traefik.frontend.passHostHeader", "true",
"traefik.frontend.auth.basic", "${var.basic_auth}",
"traefik.port", 8080,
))}"
}

View File

@ -1,6 +0,0 @@
resource "docker_network" "traefik" {
name = "traefik"
driver = "bridge"
internal = true
}

View File

@ -1,16 +1,3 @@
# output "lychee-ip" { output "lychee-ip" {
# value = "${docker_container.lychee.ip_address}" value = "${docker_container.lychee.ip_address}"
# }
output "names-traefik" {
value = docker_container.traefik.name
} }
output "traefik-network-id" {
value = docker_network.traefik.id
}
output "auth-header" {
value = var.basic_auth
}

View File

@ -1,19 +0,0 @@
terraform {
required_providers {
pass = {
source = "camptocamp/pass"
}
digitalocean = {
source = "digitalocean/digitalocean"
}
postgresql = {
source = "cyrilgdn/postgresql"
}
cloudflare = {
source = "cloudflare/cloudflare"
}
docker = {
source = "kreuzwerker/docker"
}
}
}

View File

@ -1,95 +1,60 @@
resource "docker_container" "traefik" { resource "docker_container" "traefik" {
name = "traefik" name = "traefik"
image = docker_image.traefik17.image_id image = "${docker_image.traefik.latest}"
# Admin Backend
labels { ports {
label = "traefik.enable" internal = 1111
value = "true" external = 1111
ip = "${var.ips["eth0"]}"
} }
labels { ports {
label = "traefik.http.routers.api.rule" internal = 1111
value = "Host('traefik.in.bb8.fun')" external = 1111
} ip = "${var.ips["tun0"]}"
labels {
label = "traefik.http.routers.api.service"
value = "api@internal"
} }
# Local Web Server # Local Web Server
ports { ports {
internal = 80 internal = 80
external = 80 external = 80
ip = var.ips["eth0"] ip = "${var.ips["eth0"]}"
} }
# Local Web Server (HTTPS) # Local Web Server (HTTPS)
ports { ports {
internal = 443 internal = 443
external = 443 external = 443
ip = var.ips["eth0"] ip = "${var.ips["eth0"]}"
} }
# Proxied via sydney.captnemo.in # Proxied via sydney.captnemo.in
ports { ports {
internal = 443 internal = 443
external = 443 external = 443
ip = var.ips["tun0"] ip = "${var.ips["tun0"]}"
} }
ports { ports {
internal = 80 internal = 80
external = 80 external = 80
ip = var.ips["tun0"] ip = "${var.ips["tun0"]}"
} }
upload { upload {
content = file("${path.module}/conf/traefik.toml") content = "${file("${path.module}/conf/traefik.toml")}"
file = "/etc/traefik/traefik.toml" file = "/etc/traefik/traefik.toml"
} }
upload { upload {
content = file( content = "${file("/home/nemo/projects/personal/certs/git.captnemo.in/fullchain.pem")}"
"/home/nemo/projects/personal/certs/git.captnemo.in/fullchain.pem", file = "/etc/traefik/git.captnemo.in.crt"
)
file = "/etc/traefik/git.captnemo.in.crt"
} }
upload { upload {
content = file( content = "${file("/home/nemo/projects/personal/certs/git.captnemo.in/privkey.pem")}"
"/home/nemo/projects/personal/certs/git.captnemo.in/privkey.pem", file = "/etc/traefik/git.captnemo.in.key"
)
file = "/etc/traefik/git.captnemo.in.key"
}
upload {
content = file(
"/home/nemo/projects/personal/certs/lego/certificates/tatooine.club.key",
)
file = "/etc/traefik/tatooine.club.key"
}
upload {
content = file(
"/home/nemo/projects/personal/certs/lego/certificates/tatooine.club.crt",
)
file = "/etc/traefik/tatooine.club.crt"
}
upload {
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"
} }
volumes { volumes {
@ -104,20 +69,10 @@ resource "docker_container" "traefik" {
} }
memory = 256 memory = 256
restart = "always" restart = "unless-stopped"
destroy_grace_seconds = 10 destroy_grace_seconds = 10
must_run = true must_run = true
// `bridge` is auto-connected for now
// https://github.com/terraform-providers/terraform-provider-docker/issues/10
networks_advanced {
name = "traefik"
}
networks_advanced {
name = "bridge"
}
env = [ env = [
"CLOUDFLARE_EMAIL=${var.cloudflare_email}", "CLOUDFLARE_EMAIL=${var.cloudflare_email}",
"CLOUDFLARE_API_KEY=${var.cloudflare_key}", "CLOUDFLARE_API_KEY=${var.cloudflare_key}",

View File

@ -1,76 +0,0 @@
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.image_id
restart = "unless-stopped"
destroy_grace_seconds = 30
must_run = true
volumes {
host_path = "/mnt/xwing/config/ubooquity"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/EBooks"
container_path = "/books"
}
volumes {
host_path = "/mnt/xwing/media/EBooks"
container_path = "/files"
}
volumes {
host_path = "/mnt/xwing/media/EBooks/Comics"
container_path = "/comics"
}
labels {
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")
file = "/config/preferences.json"
}
# lounge:tatooine
env = [
"PUID=1004",
"PGID=1003",
"MAXMEM=800",
]
}

View File

@ -1,18 +1,22 @@
variable "web_username" { variable "web_username" {
type = string type = "string"
} }
variable "web_password" { variable "web_password" {
type = string type = "string"
}
variable "mysql_root_password" {
type = "string"
} }
variable "cloudflare_key" { variable "cloudflare_key" {
type = string type = "string"
description = "cloudflare API Key" description = "cloudflare API Key"
} }
variable "cloudflare_email" { variable "cloudflare_email" {
type = string type = "string"
description = "cloudflare email address" description = "cloudflare email address"
} }
@ -39,15 +43,13 @@ variable "refpolicy" {
} }
variable "wiki_session_secret" { variable "wiki_session_secret" {
type = string type = "string"
} }
variable "domain" { variable "domain" {
type = string type = "string"
} }
variable "ips" { variable "ips" {
type = map(string) type = "map"
} }
# variable "links-mariadb" {}

11
docker/volumes.tf Normal file
View File

@ -0,0 +1,11 @@
resource "docker_volume" "mariadb_volume" {
name = "mariadb_volume"
}
resource "docker_volume" "gitea_volume" {
name = "gitea_volume"
}
resource "docker_volume" "mongorocks_data_volume" {
name = "mongorocks_data_volume"
}

View File

@ -1,12 +0,0 @@
module "echo-server" {
source = "./modules/container"
name = "echo-server"
image = "jmalloc/echo-server:latest"
web = {
expose = "true"
port = 8080
host = "debug.${var.root-domain},debug.in.${var.root-domain}"
}
}

View File

@ -1,45 +0,0 @@
module "elibsrv" {
name = "elibsrv"
source = "./modules/container"
image = "captn3m0/elibsrv"
resource = {
memory = 512
memory_swap = 512
}
web = {
expose = true
host = "ebooks.${var.root-domain}"
auth = true
}
volumes = [
{
host_path = "/mnt/xwing/media/EBooks"
container_path = "/books"
read_only = true
},
{
host_path = "/mnt/xwing/config/elibsrv"
container_path = "/config"
read_only = true
},
{
host_path = "/mnt/xwing/cache/elibsrv"
container_path = "/cache"
},
]
# The corresponding scan command is run using a cronjob
# `docker run --volume "/mnt/xwing/media/EBooks:/books:ro" --volume "/mnt/xwing/config/elibsrv:/config" --env "elibsrv_thumbheight=320" captn3m0/elibsrv scan
command = ["serve"]
keep_image = true
env = [
"elibsrv_thumbheight=320",
"elibsrv_title=Scarif Media Archives",
]
networks = ["bridge"]
}

View File

@ -1,313 +0,0 @@
; This file lists the default values used by Gitea
; Copy required sections to your own app.ini (default is custom/conf/app.ini)
; and modify as needed.
; See the cheatsheet at https://docs.gitea.io/en-us/config-cheat-sheet/
; A sample file with all configuration documented is at https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini
; App name that shows on every page title
APP_NAME = Nemo's code
RUN_MODE = prod
RUN_USER = git
WORK_PATH=/data/gitea
[repository]
ROOT = /data/git/repositories
USE_COMPAT_SSH_URI = false
[repository.upload]
TEMP_PATH = /data/gitea/uploads
[ui]
;; Number of issues that are displayed on one page
ISSUE_PAGING_NUM = 20
; Value of `theme-color` meta tag, used by Android >= 5.0
; An invalid color like "none" or "disable" will have the default style
; More info: https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android
THEME_COLOR_META_TAG = `#192a56`
; Max size of files to be displayed (defaults is 8MiB)
MAX_DISPLAY_FILE_SIZE = 1000000
; Whether show the user email in the Explore Users page
SHOW_USER_EMAIL = false
[ui.admin]
; Number of users that are showed in one page
USER_PAGING_NUM = 50
; Number of repos that are showed in one page
REPO_PAGING_NUM = 50
; Number of notices that are showed in one page
NOTICE_PAGING_NUM = 25
; Number of organization that are showed in one page
ORG_PAGING_NUM = 50
;; Whether to only show relevant repos on the explore page when no keyword is specified and default sorting is used.
;; A repo is considered irrelevant if it's a fork or if it has no metadata (no description, no icon, no topic).
ONLY_SHOW_RELEVANT_REPOS = true
[ui.user]
; Number of repos that are showed in one page
REPO_PAGING_NUM = 15
[ui.meta]
AUTHOR = Nemo
DESCRIPTION = Nemo's self-hosted code
KEYWORDS = git, captnemo, git.captnemo.in, piratecoders
[markdown]
; Enable hard line break extension
ENABLE_HARD_LINE_BREAK = false
; List of custom URL-Schemes that are allowed as links when rendering Markdown
; for example git,magnet
CUSTOM_URL_SCHEMES = git,magnet,steam,irc,slack
FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
;; Enables math inline and block detection
ENABLE_MATH = true
; Define allowed algorithms and their minimum key length (use -1 to disable a type)
[ssh.minimum_key_sizes]
ED25519 = 256
ECDSA = 256
RSA = 2048
DSA = 1024
[lfs]
PATH=/data/gitea/lfs
[server]
APP_DATA_PATH = /data/gitea
HTTP_PORT = 3000
ROOT_URL = https://git.captnemo.in/
DISABLE_SSH = true
DOMAIN = git.captnemo.in
LFS_START_SERVER = true
LFS_JWT_SECRET = "${lfs-jwt-secret}"
OFFLINE_MODE = true
LANDING_PAGE = explore
MINIMUM_KEY_SIZE_CHECK = true
# Uses the Mozilla Modern SSH Config params
SSH_SERVER_CIPHERS = chacha20-poly1305@openssh.com, aes256-gcm@openssh.com, aes128-gcm@openssh.com, aes256-ctr, aes192-ctr, aes128-ctr
SSH_SERVER_KEY_EXCHANGES = curve25519-sha256@libssh.org, ecdh-sha2-nistp521, ecdh-sha2-nistp384, ecdh-sha2-nistp256, diffie-hellman-group-exchange-sha256
SSH_SERVER_MACS = hmac-sha2-512-etm@openssh.com, hmac-sha2-256-etm@openssh.com, umac-128-etm@openssh.com, hmac-sha2-512, hmac-sha2-256, umac-128@openssh.com
DISABLE_ROUTER_LOG = true
ENABLE_GZIP = true
[database]
; TODO
; ; Either "mysql", "postgres", "mssql" or "sqlite3", it's your choice
DB_TYPE = sqlite3
HOST = mariadb:3306
NAME = gitea
USER = gitea
; PASSWD = "mysql-password"
; ; For "postgres" only, either "disable", "require" or "verify-full"
; SSL_MODE = disable
; ; For "sqlite3" and "tidb", use absolute path when you start as service
PATH = /data/gitea/gitea.db
; ; For "sqlite3" only. Query timeout
SQLITE_TIMEOUT = 500
; ; For iterate buffer, default is 50
; ITERATE_BUFFER_SIZE = 50
; Show the database generated SQL
LOG_SQL = false
SQLITE_JOURNAL_MODE = WAL
[picture]
AVATAR_UPLOAD_PATH = /data/gitea/avatars
DISABLE_GRAVATAR = true
ENABLE_FEDERATED_AVATAR = false
[indexer]
ISSUE_INDEXER_PATH = indexers/issues.bleve
; repo indexer by default disabled, since it uses a lot of disk space
REPO_INDEXER_ENABLED = true
REPO_INDEXER_PATH = indexers/repos.bleve
MAX_FILE_SIZE = 1048576
[queue.issue_indexer]
LENGTH = 100
[admin]
; Disable regular (non-admin) users to create organizations
DISABLE_REGULAR_ORG_CREATION = false
[security]
INSTALL_LOCK = true
LOGIN_REMEMBER_DAYS = 30
MIN_PASSWORD_LENGTH = 10
IMPORT_LOCAL_PATHS = true
DISABLE_GIT_HOOKS = true
SECRET_KEY = "${secret_key}"
INTERNAL_TOKEN = "${internal_token}"
[service]
; ; More detail: https://github.com/gogits/gogs/issues/165
; ENABLE_REVERSE_PROXY_AUTHENTICATION = false
; ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
; ; Time limit to confirm account/email registration
ACTIVE_CODE_LIVE_MINUTES = 15
; ; Time limit to confirm forgot password reset process
RESET_PASSWD_CODE_LIVE_MINUTES = 30
REGISTER_EMAIL_CONFIRM = true
ENABLE_NOTIFY_MAIL = true
DISABLE_REGISTRATION = true
; ; Enable captcha validation for registration
ENABLE_CAPTCHA = true
REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA = true
CAPTCHA_TYPE = image
; ; User must sign in to view anything.
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 = true
; ; Default value for AllowCreateOrganization
; ; New user will have rights set to create organizations depending on this setting
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
DEFAULT_ENABLE_TIMETRACKING = false
DEFAULT_ALLOW_ONLY_CONTRIBUTORS_TO_TRACK_TIME = true
; ; Default value for the domain part of the user's email address in the git log
; ; if he has set KeepEmailPrivate true. The user's email replaced with a
; ; concatenation of the user name in lower case, "@" and NO_REPLY_ADDRESS.
NO_REPLY_ADDRESS = noreply.example.org
ENABLE_REVERSE_PROXY_AUTHENTICATION = false
ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
[mailer]
ENABLED = true
FROM = git@captnemo.in
USER = git@captnemo.in
PASSWD = ${smtp_password}
PROTOCOL = smtps
SMTP_ADDR = smtp.migadu.com
SMTP_PORT = 465
SEND_AS_PLAIN_TEXT = true
SUBJECT_PREFIX = "[git.captnemo.in] "
[cache]
ADAPTER = redis
HOST = "network=tcp,addr=gitea-redis:6379,db=0,pool_size=100,idle_timeout=180"
[session]
; ; Either "memory", "file", or "redis", default is "memory"
PROVIDER = redis
; Provider config options
; redis: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180
PROVIDER_CONFIG = "network=tcp,addr=gitea-redis:6379,db=1,pool_size=100,idle_timeout=180"
; ; If you use session in https only, default is false
COOKIE_SECURE = true
; SameSite settings. Either "none", "lax", or "strict"
SAME_SITE = strict
[migrations]
ALLOWED_DOMAINS = github.com
ALLOW_LOCALNETWORKS = false
[attachment]
; ; Whether attachments are enabled. Defaults to `true`
ENABLE = true
; ; One or more allowed types, e.g. image/jpeg|image/png
ALLOWED_TYPES = image/jpeg|image/png|application/zip|application/gzip|application/pdf|text/csv
; ; Max size of each file. Defaults to 32MB
MAX_SIZE = 200
; ; Max number of files per upload. Defaults to 10
MAX_FILES = 10
[log]
; Either "console", "file", "conn", "smtp" or "database", default is "console"
; Use comma to separate multiple modes, e.g. "console, file"
MODE = console
; Buffer length of the channel, keep it as it is if you don't know what it is.
BUFFER_LEN = 10000
; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
LEVEL = Warn
REDIRECT_MACARON_LOG = true
ROUTER_LOG_LEVEL = Critical
logger.access.MODE=,
logger.xorm.MODE=,
[cron]
; Enable running cron tasks periodically.
ENABLED = true
; ; Run cron tasks when Gitea starts.
RUN_AT_START = false
[cron.archive_cleanup]
RUN_AT_START = true
SCHEDULE = @midnight
; Archives created more than OLDER_THAN ago are subject to deletion
OLDER_THAN = 24h
; ; Update mirrors
[cron.update_mirrors]
SCHEDULE = @every 3h
; Repository health check
[cron.repo_health_check]
SCHEDULE = @midnight
TIMEOUT = 60s
; Arguments for command 'git fsck', e.g. "--unreachable --tags"
; see more on http://git-scm.com/docs/git-fsck
ARGS =
; Check repository statistics
[cron.check_repo_stats]
RUN_AT_START = true
SCHEDULE = @midnight
[api]
; Max number of items will response in a page
MAX_RESPONSE_ITEMS = 100
[other]
SHOW_FOOTER_BRANDING = false
; Show version information about Gitea and Go in the footer
SHOW_FOOTER_VERSION = true
; Show time of template execution in the footer
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
[openid]
ENABLE_OPENID_SIGNIN = true
ENABLE_OPENID_SIGNUP = false
[metrics]
; Enables metrics endpoint. True or false; default is false.
ENABLED = true
[oauth2]
ENABLE = false
; this is same as JWT secret above
JWT_SECRET = "${oauth2-jwt-secret}"
[federation]
ENABLED=false
;; Enable/Disable user statistics for nodeinfo if federation is enabled
;SHARE_USER_STATISTICS = true
;;
;; Maximum federation request and response size (MB)
;MAX_SIZE = 4
;;
;; WARNING: Changing the settings below can break federation.
;;
;; HTTP signature algorithms
;ALGORITHMS = rsa-sha256, rsa-sha512, ed25519
;;
;; HTTP signature digest algorithm
;DIGEST_ALGORITHM = SHA-256
;;
;; GET headers for federation requests
;GET_HEADERS = (request-target), Date
;;
;; POST headers for federation requests
;POST_HEADERS = (request-target), Date, Digest
[packages]
;; Enable/Disable package registry capabilities
ENABLED = true

View File

@ -1,7 +0,0 @@
<a class="item" href="https://captnemo.in/contact/">
Contact
</a>
<a class="item" href="https://status.captnemo.in/">
Status
</a>

View File

@ -1,36 +0,0 @@
{{template "base/head" .}}
<div class="home">
<div class="ui stackable middle very relaxed page grid">
<div class="sixteen wide center aligned centered column">
<div>
<img class="logo" src="{{AppSubUrl}}/img/gitea-lg.png" />
</div>
<div class="hero">
<h1 class="ui icon header title">
Nemo's Code
</h1>
<h2>under-the-tv code hosting service</h2>
</div>
</div>
</div>
<div class="ui stackable middle very relaxed page grid">
<div class="eight wide center column">
<h1 class="hero ui icon header">
<i class="octicon octicon-rocket"></i>
Run by friends
</h1>
<p class="large">
This service is run by <a href="https://captnemo.in">Nemo</a>
</p>
</div>
<div class="eight wide center column">
<h1 class="hero ui icon header">
<i class="octicon octicon-flame"></i> Uptime not guaranteed
</h1>
<p class="large">
Run for fun and not profit.
</p>
</div>
</div>
</div>
{{template "base/footer" .}}

View File

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.0"
width="2000.000000pt"
height="2000.000000pt"
viewBox="0 0 2000.000000 2000.000000"
preserveAspectRatio="xMidYMid meet"
id="svg10"
sodipodi:docname="favicon.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs14" />
<sodipodi:namedview
id="namedview12"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="pt"
showgrid="false"
inkscape:zoom="0.23281491"
inkscape:cx="1232.7389"
inkscape:cy="1415.2874"
inkscape:window-width="1920"
inkscape:window-height="1037"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="g8" />
<metadata
id="metadata2">
Created by potrace 1.16, written by Peter Selinger 2001-2019
</metadata>
<g
transform="translate(0.000000,2000.000000) scale(0.100000,-0.100000)"
fill="#000000"
stroke="none"
id="g8"
style="fill:#000080">
<path
d="M0 10000 l0 -10000 10000 0 10000 0 0 10000 0 10000 -10000 0 -10000 0 0 -10000z m11852 5356 c553 -141 882 -698 738 -1251 -27 -102 -104 -261 -168 -346 -71 -94 -197 -207 -296 -265 -109 -65 -274 -121 -405 -136 l-100 -12 -90 -221 c-69 -169 -87 -222 -76 -227 12 -7 1420 -613 2145 -923 162 -69 310 -132 328 -140 31 -14 32 -16 20 -44 -7 -15 -55 -127 -107 -247 -52 -121 -96 -221 -98 -223 -4 -5 -131 48 -1385 585 -608 261 -1111 474 -1116 474 -5 0 -19 -24 -31 -52 -76 -181 -1440 -3576 -1474 -3667 -16 -43 -21 -81 -21 -166 -1 -99 2 -116 26 -167 38 -81 83 -133 158 -182 160 -104 244 -120 614 -113 383 7 564 38 731 125 90 47 218 173 263 258 67 128 79 306 32 471 -11 40 -20 75 -20 78 0 3 91 5 203 5 402 0 680 43 1017 156 226 76 540 218 768 348 35 20 66 36 68 36 9 0 3 -122 -11 -237 -57 -462 -214 -845 -470 -1152 -100 -119 -281 -291 -403 -383 -92 -70 -344 -228 -363 -228 -4 0 -35 29 -70 65 -105 109 -219 151 -345 125 -36 -7 -108 -38 -184 -78 -489 -261 -847 -376 -1345 -434 -180 -21 -653 -15 -955 11 -140 13 -338 25 -440 28 l-185 5 125 -126 125 -126 75 6 c97 8 170 -17 234 -82 65 -64 90 -137 82 -234 l-6 -75 206 -206 206 -207 83 6 c73 4 89 2 137 -20 184 -84 232 -302 100 -449 -67 -75 -110 -94 -212 -94 -68 0 -93 5 -125 22 -51 27 -119 100 -140 150 -19 43 -24 127 -12 188 l8 40 -201 200 -200 200 0 -531 0 -531 26 -14 c41 -22 101 -96 118 -146 38 -110 8 -222 -80 -302 -96 -88 -207 -103 -321 -45 -134 69 -192 232 -130 367 26 58 83 119 128 139 l29 13 0 531 0 532 -28 11 c-42 18 -108 89 -132 144 -24 53 -28 144 -10 191 11 30 9 32 -203 245 -117 119 -219 230 -226 246 -44 103 -170 239 -486 523 -412 371 -643 616 -819 865 -236 336 -397 688 -501 1092 -44 170 -80 249 -141 305 -76 70 -138 92 -263 95 l-106 2 -28 109 c-49 190 -70 366 -71 589 -1 220 10 324 54 505 61 246 161 471 308 691 134 200 315 410 326 378 3 -8 19 -68 36 -134 136 -520 354 -976 638 -1332 95 -119 277 -319 322 -354 l33 -25 -89 -49 c-166 -93 -288 -240 -324 -393 -15 -66 -15 -212 0 -289 24 -120 126 -300 267 -476 139 -172 374 -408 455 -456 181 -106 352 -125 492 -54 66 33 153 123 193 198 17 33 365 794 773 1690 408 897 760 1668 782 1715 72 154 120 262 116 265 -1 1 -91 40 -198 85 -321 136 -871 371 -935 400 -33 15 -161 70 -285 123 -735 312 -1076 459 -1082 466 -11 10 201 501 216 501 6 0 200 -81 431 -181 1489 -641 2052 -882 2070 -886 17 -3 33 24 118 212 l97 217 -48 61 c-96 122 -154 239 -194 392 -30 114 -36 323 -13 436 23 114 72 242 131 341 58 96 199 245 293 308 124 83 285 146 437 170 97 15 276 4 386 -24z"
id="path4"
style="fill:#192a56;fill-opacity:1" />
<path
d="M11496 14790 c-110 -28 -228 -126 -281 -234 -103 -210 -11 -470 203 -577 65 -33 71 -34 187 -34 116 0 122 1 187 34 82 41 169 128 204 206 99 217 11 466 -202 571 -66 33 -83 37 -166 40 -51 2 -110 -1 -132 -6z"
id="path6"
style="fill:#192a56;fill-opacity:1" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -1,22 +0,0 @@
User-agent: MJ12bot
Disallow: /
User-agent: SemrushBot
Disallow: /
User-agent: SemrushBot-SA
Disallow: /
User-agent: rogerbot
Disallow:/
User-agent: dotbot
Disallow:/
User-agent: AhrefsBot
Disallow: /
User-agent: Alexibot
Disallow: /
User-agent: SurveyBot
Disallow: /
User-agent: Xenus
Disallow: /
User-agent: Xenus Link Sleuth 1.1c
Disallow: /
User-agent: AhrefsBot
Disallow: /

View File

@ -1,22 +0,0 @@
User-agent: MJ12bot
Disallow: /
User-agent: SemrushBot
Disallow: /
User-agent: SemrushBot-SA
Disallow: /
User-agent: rogerbot
Disallow:/
User-agent: dotbot
Disallow:/
User-agent: AhrefsBot
Disallow: /
User-agent: Alexibot
Disallow: /
User-agent: SurveyBot
Disallow: /
User-agent: Xenus
Disallow: /
User-agent: Xenus Link Sleuth 1.1c
Disallow: /
User-agent: AhrefsBot
Disallow: /

View File

@ -1,21 +0,0 @@
# https://github.com/go-gitea/gitea/releases
data "docker_registry_image" "gitea" {
name = "gitea/gitea:1.21"
}
data "docker_registry_image" "redis" {
name = "redis:alpine"
}
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
}
}

View File

@ -1,89 +0,0 @@
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.image_id
dynamic "labels" {
for_each = local.l
content {
label = labels.key
value = labels.value
}
}
volumes {
volume_name = docker_volume.gitea_volume.name
container_path = "/data"
host_path = docker_volume.gitea_volume.mountpoint
}
# For the following uploads, note that
# /data/gitea is GITEA_CUSTOM_PATH
# Logos
# https://docs.gitea.com/next/administration/customizing-gitea#changing-the-logo
# PNG images
upload {
content_base64 = filebase64("${path.module}/conf/public/img/gitea-lg.png")
file = "/data/gitea/public/img/logo.png"
}
upload {
content_base64 = filebase64("${path.module}/conf/public/img/gitea-lg.png")
file = "/data/gitea/public/img/apple-touch-icon.png"
}
upload {
content_base64 = filebase64("${path.module}/conf/public/img/gitea-sm.png")
file = "/data/gitea/public/img/favicon.png"
}
# SVG images
upload {
content_base64 = filebase64("${path.module}/conf/public/img/favicon.svg")
file = "/data/gitea/public/img/logo.svg"
}
upload {
content_base64 = filebase64("${path.module}/conf/public/img/favicon.svg")
file = "/data/gitea/public/img/favicon.svg"
}
# Some files at top-level
upload {
content = file("${path.module}/../docker/conf/humans.txt")
file = "/data/gitea/humans.txt"
}
upload {
content = file("${path.module}/conf/public/robots.txt")
file = "/data/gitea/robots.txt"
}
# Extra Links in header
upload {
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
file = "/data/gitea/conf/app.ini"
}
memory = 800
restart = "always"
destroy_grace_seconds = 10
must_run = true
networks = ["gitea", "traefik"]
}
resource "docker_image" "gitea" {
name = data.docker_registry_image.gitea.name
pull_triggers = [data.docker_registry_image.gitea.sha256_digest]
}

View File

@ -1,5 +0,0 @@
resource "docker_network" "gitea" {
name = "gitea"
driver = "bridge"
}

View File

@ -1,19 +0,0 @@
terraform {
required_providers {
pass = {
source = "camptocamp/pass"
}
digitalocean = {
source = "digitalocean/digitalocean"
}
postgresql = {
source = "cyrilgdn/postgresql"
}
cloudflare = {
source = "cloudflare/cloudflare"
}
docker = {
source = "kreuzwerker/docker"
}
}
}

View File

@ -1,23 +0,0 @@
resource "docker_container" "redis" {
name = "gitea-redis"
image = docker_image.redis.image_id
volumes {
host_path = "/mnt/xwing/cache/gitea"
container_path = "/data"
}
memory = 64
restart = "always"
destroy_grace_seconds = 10
must_run = true
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]
keep_locally = true
}

View File

@ -1,32 +0,0 @@
variable "traefik-labels" {
type = map(string)
}
variable "domain" {
}
variable "ips" {
type = map(string)
}
variable "secret-key" {
}
variable "internal-token" {
}
variable "smtp-password" {
}
variable "lfs-jwt-secret" {
}
variable "oauth2-jwt-secret" {
}
variable "mysql-password" {
}
variable "traefik-network-id" {
}

View File

@ -1,4 +0,0 @@
resource "docker_volume" "gitea_volume" {
name = "gitea_volume"
}

View File

@ -1,24 +0,0 @@
module "home-assistant" {
name = "home-assistant"
source = "../modules/container"
image = "ghcr.io/home-assistant/home-assistant:stable"
resource = {
memory = 1024
memory_swap = 1024
}
env = [
"TZ=Asia/Kolkata",
]
network_mode = "host"
volumes = [
{
container_path = "/config"
host_path = "/mnt/zwing/config/home-assistant"
},
]
}

View File

@ -1,16 +0,0 @@
module "jupyter" {
name = "jupyter"
source = "./modules/container"
image = "jupyter/scipy-notebook"
resource = {
memory = 1024
memory_swap = 4096
}
web = {
expose = "true"
host = "j.${var.root-domain}"
port = 8888
}
networks = ["bridge"]
gpu = true
}

View File

@ -1,21 +0,0 @@
# 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
# }
# }
data "pass_password" "kaarana-root-db-password" {
path = "KAARANA_DB_ROOT_PASSWORD"
}
data "pass_password" "kaarana-db-password" {
path = "KAARANA_DB_PASSWORD"
}

View File

@ -1,40 +0,0 @@
// Create a small database network
resource "docker_network" "kaarana-db" {
name = "kaarana-db"
labels = {
internal = "true"
role = "database"
}
internal = true
ipam_config {
subnet = "172.20.0.0/29"
gateway = "172.20.0.1"
}
}
// Run a small mySQL container in this subnet
resource "docker_container" "mysql" {
image = docker_image.db.image_id
name = "kaarana-mariadb"
restart = "always"
must_run = true
env = [
"MYSQL_ROOT_PASSWORD=${var.root_db_password}",
"MYSQL_USER=${local.username}",
"MYSQL_PASSWORD=${var.db_password}",
"MYSQL_DATABASE=${local.database}",
]
volumes {
host_path = "/mnt/disk/kaarana-db"
container_path = "/var/lib/mysql"
}
networks = ["kaarana-db"]
}

View File

@ -1,27 +0,0 @@
data "docker_registry_image" "wp" {
name = "wordpress:latest"
}
resource "docker_image" "wp" {
name = "wordpress"
pull_triggers = [data.docker_registry_image.wp.sha256_digest]
}
data "docker_registry_image" "db" {
name = "mariadb:10.4"
}
resource "docker_image" "db" {
name = "mariadb"
pull_triggers = [data.docker_registry_image.db.sha256_digest]
}
data "docker_registry_image" "traefik" {
name = "traefik:v2.0"
}
resource "docker_image" "traefik" {
name = "traefik"
pull_triggers = [data.docker_registry_image.db.sha256_digest]
}

View File

@ -1,64 +0,0 @@
// Create a small database network
resource "docker_network" "traefik" {
name = "traefik"
labels = {
internal = "true"
role = "ingress"
}
internal = true
}
resource "docker_container" "traefik" {
name = "traefik"
image = docker_image.traefik.image_id
# Do not offer HTTP2
# https://community.containo.us/t/traefikv2-http-2-0/1199
env = [
"GODEBUG=http2client=0",
]
upload {
content = file("${path.module}/traefik.toml")
file = "/etc/traefik/traefik.toml"
}
volumes {
host_path = "/var/run/docker.sock"
container_path = "/var/run/docker.sock"
read_only = true
}
volumes {
host_path = "/mnt/disk/traefik"
container_path = "/acme"
}
ports {
internal = 443
external = 8443
ip = "139.59.22.234"
}
ports {
internal = 80
external = 80
ip = "139.59.22.234"
}
memory = 256
restart = "always"
destroy_grace_seconds = 10
must_run = true
networks_advanced {
name = "bridge"
}
networks_advanced {
name = "traefik"
}
}

View File

@ -1,45 +0,0 @@
# This configures docker service discovery
[providers.docker]
exposedByDefault = false
network = "traefik"
defaultRule = ""
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web-secure]
address = ":443"
[http.middlewares]
[http.middlewares.everything.redirectScheme]
scheme = "https"
[tcp.routers]
[tcp.routers.forwardtohome]
entryPoints = ["web-secure"]
rule = "HostSNI(`emby.bb8.fun`, `git.captnemo.in`)"
service = "homeserver"
[tcp.routers.forwardtohome.tls]
passthrough = true
[tcp.services]
[tcp.services.homeserver.loadBalancer]
[[tcp.services.homeserver.loadBalancer.servers]]
address = "10.8.0.14:443"
[certificatesResolvers.default.acme]
email = "certs@captnemo.in"
storage = "/acme/acme.json"
[certificatesResolvers.default.acme.httpChallenge]
# used during the challenge
entryPoint = "web"
[tls.options]
[tls.options.foo]
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]

View File

@ -1,12 +0,0 @@
variable "root_db_password" {
}
variable "db_password" {
}
locals {
username = "wordpress"
database = "wordpress"
db_hostname = "kaarana.db"
}

View File

@ -1,40 +0,0 @@
resource "docker_container" "wp" {
image = docker_image.wp.image_id
name = "kaarana-wordpress"
restart = "always"
must_run = true
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"
}
env = [
"WORDPRESS_DB_HOST=${local.db_hostname}",
"WORDPRESS_DB_USER=${local.username}",
"WORDPRESS_DB_PASSWORD=${var.db_password}",
"WORDPRESS_DB_NAME=${local.database}",
"WORDPRESS_TABLE_PREFIX=",
]
volumes {
host_path = "/mnt/disk/kaarana-wp"
container_path = "/var/www/html"
}
ports {
internal = 80
external = 8213
ip = "10.8.0.1"
}
networks = ["bridge", "kaarana-db"]
}

View File

@ -1,33 +0,0 @@
# module "kavita" {
# name = "kavita"
# source = "./modules/container"
# image = "kizaing/kavita:latest"
# web = {
# expose = true
# port = 5000
# host = "kavita.bb8.fun"
# }
# resource = {
# memory = 1024
# memory_swap = 1024
# }
# volumes = [
# {
# host_path = "/mnt/xwing/media/EBooks"
# container_path = "/ebooks"
# },
# {
# host_path = "/mnt/xwing/config/kavita"
# container_path = "/kavita/config"
# }
# ]
# networks = ["traefik"]
# env = [
# "TZ=Asia/Kolkata",
# ]
# }

View File

@ -1,41 +0,0 @@
# // Points to the local working directory instead of
# // the published version
# module "kayak" {
# source = "../terraform-digitalocean-kayak"
# cert_path = "${path.root}/secrets/kayak"
# domain = "kayak.${var.root-domain}"
# ssh_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD0Getey8585AqdgIl9mqQ3SH9w6z7NZUW4HXdOqZwC7sYEaDrLOBV014gtFS8h8ymm4dcw6xEGUkaavcHC8W9ChTLKBMK4N1/sUS/umLy+Wi/K//g13y0VHSdvcc+gMQ27b9n/DwDY4ZKkaf6t+4HWyFWNh6gp0cT1WCyLNlsER55KUdy+C1lCOpv1SMepOaYc7uyBlC9FfgewJho/OfxnoTztQV6QeSGfr2Xr94Ip1FUPoLoBLLilh4ZbCe6F6bqn0kNgVBTkrVwWJv5Z0jCJpUjER69cqjASRao9KCHkyPtybzKKhCLZIlB3QMggEv0xnlHMpeeuDWcGrBVPKI8V"
# asset_dir = "${path.root}/k8s"
# providers {
# docker = "docker.kayak"
# }
# }
# provider "docker" {
# host = "tcp://${cloudflare_record.kayak-docker.hostname}:2376"
# version = "~> 2.0.0"
# alias = "kayak"
# ca_material = "${module.kayak.docker_ca_cert}"
# cert_material = "${module.kayak.docker_client_cert}"
# key_material = "${module.kayak.docker_client_key}"
# }
# resource "cloudflare_record" "kayak-docker" {
# name = "docker.kayak"
# value = "${module.kayak.droplet_ipv4}"
# domain = "${var.root-domain}"
# type = "A"
# ttl = 120
# }
# resource "cloudflare_record" "kayak" {
# name = "kayak"
# value = "${module.kayak.droplet_ipv4}"
# domain = "${var.root-domain}"
# type = "A"
# ttl = 120
# }
# resource "cloudflare_record" "kayak-etcd" {
# name = "etcd.kayak"
# value = "${module.kayak.droplet_ipv4_private}"
# domain = "${var.root-domain}"
# type = "A"
# ttl = 120
# }

View File

@ -1,40 +0,0 @@
module "klaxon-db" {
source = "./modules/postgres"
name = "klaxon"
password = data.pass_password.klaxon-db-password.password
}
module "klaxon" {
name = "klaxon"
source = "./modules/container"
web = {
expose = true
port = "3000"
host = "klaxon.${var.root-domain}"
}
resource = {
memory = 1024
memory_swap = 1024
}
env = [
"DATABASE_URL=postgres://klaxon:${data.pass_password.klaxon-db-password.password}@postgres/klaxon",
"ADMIN_EMAILS=klaxon.admin@captnemo.in",
"RAILS_ENV=production",
"SECRET_KEY_BASE=${data.pass_password.klaxon-secret-key.password}",
"SENDGRID_USERNAME=apikey",
"SENDGRID_PASSWORD=${data.pass_password.klaxon-sendgrid-password.password}",
"KLAXON_FORCE_SSL=false",
"KLAXON_COMPILE_ASSETS=true",
"ADMIN_EMAILS=klaxon@captnemo.in",
"MAILER_FROM_ADDRESS=klaxon@sendgrid.captnemo.in",
]
restart = "always"
image = "themarshallproject/klaxon"
networks = ["postgres", "external"]
}

View File

@ -1,19 +0,0 @@
// Bring up a simple test container
// In the controller node
# resource "kubernetes_pod" "nginx" {
# metadata {
# name = "terraform-example"
# namespace = "default"
# }
# spec {
# toleration {
# key = "node-role.kubernetes.io/master"
# operator = "Exists"
# effect = "NoSchedule"
# }
# container {
# image = "nginx:latest"
# name = "nginx"
# }
# }
# }

118
main.tf
View File

@ -1,112 +1,30 @@
module "cloudflare" { module "cloudflare" {
source = "./cloudflare" source = "cloudflare"
domain = "bb8.fun" domain = "bb8.fun"
zone_id = lookup(data.cloudflare_zones.bb8.zones[0], "id") ips = "${var.ips}"
ips = var.ips }
droplet_ip = module.digitalocean.droplet_ipv4 module "mysql" {
source = "mysql"
mysql_root_password = "${var.mysql_root_password}"
mysql_lychee_password = "${var.mysql_lychee_password}"
mysql_kodi_password = "${var.mysql_kodi_password}"
lychee_ip = "${module.docker.lychee-ip}"
} }
module "docker" { module "docker" {
source = "./docker" source = "docker"
web_username = data.pass_password.web_username.password web_username = "${var.web_username}"
web_password = data.pass_password.web_password.password web_password = "${var.web_password}"
cloudflare_key = data.pass_password.cloudflare_key.password mysql_root_password = "${var.mysql_root_password}"
cloudflare_key = "${var.cloudflare_key}"
cloudflare_email = "bb8@captnemo.in" cloudflare_email = "bb8@captnemo.in"
wiki_session_secret = data.pass_password.wiki_session_secret.password wiki_session_secret = "${var.wiki_session_secret}"
ips = var.ips ips = "${var.ips}"
domain = "bb8.fun" domain = "bb8.fun"
} }
module "db" {
source = "./db"
postgres-root-password = data.pass_password.postgres-root-password.password
ips = var.ips
}
module "timemachine" {
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
}
module "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
//passed, but not used
mysql-password = ""
traefik-network-id = module.docker.traefik-network-id
}
module "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
}
module "radicale" { module "radicale" {
source = "./radicale" source = "radicale"
domain = "radicale.bb8.fun" domain = "radicale.bb8.fun"
} }
module "media" {
source = "./media"
domain = "bb8.fun"
traefik-labels = var.traefik-common-labels
ips = var.ips
# ToDO: Change this to lookup
traefik-network-id = "ffc1e366849e"
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
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
}
module "digitalocean" {
source = "./digitalocean"
}
module "home-assistant" {
source = "./home-assistant"
}
module "mastodon" {
source = "./mastodon"
db-password = data.pass_password.mastodon-db-password.password
secret-key-base = data.pass_password.mastodon-secret-key-base.password
otp-secret = data.pass_password.mastodon-otp-secret.password
vapid-private-key = data.pass_password.mastodon-vapid-private-key.password
vapid-public-key = data.pass_password.mastodon-vapid-public-key.password
smtp-password = data.pass_password.mastodon-smtp-password.password
}
// Used to force access to ISP related resources
# module "tinyproxy" {
# source = "./tinyproxy"
# ips = "${var.ips}"
# }

View File

@ -1,29 +0,0 @@
module "mastodon-redis" {
name = "mastodon-redis"
source = "../modules/container"
image = "redis:alpine"
networks = ["mastodon"]
keep_image = true
resource = {
memory = 256
memory_swap = 256
}
# In case the cache dies,
# tootctl feeds build
# regenerates the feeds, run it from
# inside a mastodon container
volumes = [
{
host_path = "/mnt/zwing/cache/mastodon-redis"
container_path = "/data"
}
]
}
module "mastodon-db" {
source = "../modules/postgres"
name = "mastodon"
password = var.db-password
}

View File

@ -1,23 +0,0 @@
locals {
version = "4.1.10"
env = [
"LOCAL_DOMAIN=tatooine.club",
"REDIS_HOST=mastodon-redis",
"REDIS_PORT=6379",
"DB_HOST=postgres",
"DB_USER=mastodon",
"DB_NAME=mastodon",
"DB_PASS=${var.db-password}",
"DB_PORT=5432",
"ES_ENABLED=false",
"SECRET_KEY_BASE=${var.secret-key-base}",
"OTP_SECRET=${var.otp-secret}",
"VAPID_PRIVATE_KEY=${var.vapid-private-key}",
"VAPID_PUBLIC_KEY=${var.vapid-public-key}",
"SMTP_SERVER=smtp.eu.mailgun.org",
"SMTP_PORT=587",
"SMTP_LOGIN=mastodon@mail.tatooine.club",
"SMTP_PASSWORD=${var.smtp-password}",
"SMTP_FROM_ADDRESS=mastodon@mail.tatooine.club",
]
}

View File

@ -1,103 +0,0 @@
module "mastodon-web" {
name = "mastodon-web"
source = "../modules/container"
image = "ghcr.io/mastodon/mastodon:v${local.version}"
keep_image = true
networks = ["mastodon", "traefik", "external", "postgres"]
labels = {
"traefik.frontend.headers.STSPreload" = "true"
"traefik.frontend.headers.STSIncludeSubdomains" = "true"
"traefik.frontend.headers.STSSeconds" = "31536000"
}
env = concat(local.env,[
"MAX_THREADS=4",
"WEB_CONCURRENCY=5"
])
command = [
"bash",
"-c",
"rm -f /mastodon/tmp/pids/server.pid; bundle exec rake db:migrate; bundle exec rails s -p 3000"
]
volumes = [{
container_path = "/mastodon/public/system"
host_path = "/mnt/xwing/data/mastodon"
}]
web = {
expose = "true"
host = "tatooine.club"
port = 3000
}
resource = {
memory = 2048
memory_swap = 2048
}
}
module "mastodon-streaming" {
name = "mastodon-streaming"
source = "../modules/container"
image = "ghcr.io/mastodon/mastodon:v${local.version}"
keep_image = true
# 24 threads for Streaming
env = concat(local.env,[
"DB_POOL=8",
"STREAMING_CLUSTER_NUM=4"
])
networks = ["postgres", "external", "mastodon"]
command = [
"node",
"./streaming"
]
web = {
expose = "false"
}
resource = {
memory = 512
memory_swap = 512
}
}
module "mastodon-sidekiq" {
name = "mastodon-sidekiq"
source = "../modules/container"
image = "ghcr.io/mastodon/mastodon:v${local.version}"
keep_image = true
env = concat(local.env,[
"DB_POOL=50"
])
web = {
expose = "false"
}
networks = ["postgres", "external", "mastodon"]
command = [
"bundle",
"exec",
"sidekiq"
]
volumes = [{
container_path = "/mastodon/public/system"
host_path = "/mnt/xwing/data/mastodon"
}]
resource = {
memory = 2048
memory_swap = 2048
}
}

View File

@ -1,5 +0,0 @@
resource "docker_network" "mastodon" {
name = "mastodon"
driver = "bridge"
internal = true
}

View File

@ -1,10 +0,0 @@
terraform {
required_providers {
postgresql = {
source = "cyrilgdn/postgresql"
}
docker = {
source = "kreuzwerker/docker"
}
}
}

View File

@ -1,18 +0,0 @@
variable "db-password" {
type = string
}
variable "secret-key-base" {
type = string
}
variable "otp-secret" {
type = string
}
variable "vapid-private-key" {
type = string
}
variable "vapid-public-key" {
type = string
}
variable "smtp-password" {
type = string
}

View File

@ -1,62 +0,0 @@
# 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"
# container_path = "/config"
# },
# {
# host_path = "/mnt/xwing/media/Music"
# container_path = "/music"
# },
# {
# host_path = "/mnt/xwing/config/airsonic/playlists"
# container_path = "/playlists"
# },
# {
# host_path = "/mnt/xwing/config/airsonic/podcasts"
# container_path = "/podcasts"
# },
# {
# host_path = "/mnt/xwing/config/airsonic/jre"
# container_path = "/usr/lib/jvm/java-1.8-openjdk/jre/lib/"
# },
# ]
# }
# 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}"
# }
# }

View File

@ -1,35 +0,0 @@
MediaLibraryStatistics=1512 4850 62662 486912890569 15485585
IndexString=A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ)
IgnoredArticles=The El La Los Las Le Les
Shortcuts=New Incoming Podcast
PlaylistFolder=/var/playlists
MusicFileTypes=mp3 ogg oga aac m4a flac wav wma aif aiff ape mpc shn
VideoFileTypes=flv avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts
CoverArtFileTypes2=cover.jpg cover.png cover.gif folder.jpg jpg jpeg gif png
SortAlbumsByYear=true
GettingStartedEnabled=false
WelcomeTitle=Airsonic
WelcomeSubtitle=
WelcomeMessage2=
LoginMessage=
Theme=default
LocaleLanguage=en
LocaleCountry=in
LocaleVariant=
IndexCreationInterval=3
IndexCreationHour=3
FastCacheEnabled=false
OrganizeByFolderStructure=true
DownloadBitrateLimit=0
UploadBitrateLimit=0
LdapEnabled=false
LdapUrl=ldap://host.domain.com:389/cn=Users,dc=domain,dc=com
LdapSearchFilter=(sAMAccountName={0})
LdapManagerDn=
LdapAutoShadowing=false
SmtpServer=smtp.mailgun.com
SmtpEncryption=SSL/TLS
SmtpPort=465
SmtpUser=airsonic@captnemo.in
SmtpFrom=airsonic@captnemo.in
SmtpPassword=${smtp-password}

View File

@ -1,5 +0,0 @@
# https://airsonic.github.io/docs/jukebox/
javax.sound.sampled.Clip=#PCH [plughw:1,0]
javax.sound.sampled.Port=#Port PCH [hw:1]
javax.sound.sampled.SourceDataLine=#PCH [plughw:1,0]
javax.sound.sampled.TargetDataLine=#PCH [plughw:1,0]

View File

@ -1,4 +0,0 @@
data "docker_network" "bridge" {
name = "bridge"
}

View File

@ -1,74 +0,0 @@
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.image_id
# SSD holds both the cache and data
volumes {
host_path = "/mnt/zwing/config/emby"
container_path = "/config"
}
# We keep the cache separate
# So the config directory isn't bloated
volumes {
host_path = "/mnt/zwing/cache/emby"
container_path = "/config/cache"
}
# We want backups on the HDD
volumes {
host_path = "/mnt/xwing/backups/config/emby"
container_path = "/backups"
}
# And mount the media as well
volumes {
host_path = "/mnt/xwing/media"
container_path = "/media"
}
dynamic "labels" {
for_each = local.emby_labels
content {
label = labels.key
value = labels.value
}
}
networks = [docker_network.media.id, var.traefik-network-id]
memory = 2048
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
# This breaks every time we upgrade the kernel
# or the nvidia driver, and needs a reboot.
gpus = "all"
# Running as lounge:tatooine
env = [
"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]
}
data "docker_registry_image" "emby" {
name = "emby/embyserver:latest"
}

View File

@ -1,32 +0,0 @@
module "jackett" {
name = "jackett"
source = "../modules/container"
image = "linuxserver/jackett:latest"
# TODO FIXME
# networks = [data.docker_network.bridge.id]
web = {
expose = true
port = 9117
host = "jackett.${var.domain}"
}
volumes = [
{
host_path = "/mnt/xwing/config/jackett"
container_path = "/config"
},
]
resource = {
memory = "256"
memory_swap = "512"
}
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
}

View File

@ -1,58 +0,0 @@
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]
}
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.image_id
dynamic "labels" {
for_each = local.lidarr_labels
content {
label = labels.key
value = labels.value
}
}
memory = 512
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
volumes {
host_path = "/mnt/xwing/config/lidarr"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
}
volumes {
host_path = "/mnt/xwing/media/Music"
container_path = "/music"
}
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
networks = [docker_network.media.id, var.traefik-network-id]
}

View File

@ -1,50 +0,0 @@
module "navidrome" {
source = "../modules/container"
image = "deluan/navidrome"
name = "navidrome"
user = 1004
resource = {
memory = "1024"
memory_swap = "1024"
}
web = {
port = 4533
host = "music.bb8.fun"
expose = true
}
env = [
"ND_SCANINTERVAL=6h",
"ND_LOGLEVEL=info",
"ND_SESSIONTIMEOUT=300h",
"ND_BASEURL=",
"ND_AUTOIMPORTPLAYLISTS=false",
"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}",
]
# TODO FIXME
# networks = [docker_network.media.id, data.docker_network.bridge.id]
# Keep cache and data config so we can do easier backups
volumes = [
{
host_path = "/mnt/zwing/config/navidrome"
container_path = "/data"
},{
host_path = "/mnt/zwing/cache/navidrome"
container_path = "/data/cache"
},
{
host_path = "/mnt/xwing/media/Music"
container_path = "/music"
read_only = true
},
]
}

View File

@ -1,9 +0,0 @@
resource "docker_network" "media" {
name = "media"
driver = "bridge"
ipam_config {
subnet = "172.18.0.0/24"
gateway = "172.18.0.1"
}
}

View File

@ -1,8 +0,0 @@
output "names-transmission" {
value = docker_container.transmission.name
}
output "names-emby" {
value = docker_container.emby.name
}

View File

@ -1,19 +0,0 @@
terraform {
required_providers {
pass = {
source = "camptocamp/pass"
}
digitalocean = {
source = "digitalocean/digitalocean"
}
postgresql = {
source = "cyrilgdn/postgresql"
}
cloudflare = {
source = "cloudflare/cloudflare"
}
docker = {
source = "kreuzwerker/docker"
}
}
}

View File

@ -1,33 +0,0 @@
module "prowlarr" {
name = "prowlarr"
source = "../modules/container"
image = "linuxserver/prowlarr:nightly"
web = {
expose = true
port = 9696
host = "prowlarr.${var.domain}"
auth = true
}
resource = {
memory = 512
memory_swap = 1024
}
volumes = [
{
host_path = "/mnt/xwing/config/prowlarr"
container_path = "/config"
}
]
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
networks = [docker_network.media.id, data.docker_network.bridge.id]
}

View File

@ -1,45 +0,0 @@
module "radarr" {
name = "radarr"
source = "../modules/container"
image = "linuxserver/radarr:latest"
networks = [docker_network.media.id, data.docker_network.bridge.id]
web = {
expose = true
port = 7878
host = "radarr.${var.domain}"
}
resource = {
memory = 512
memory_swap = 1024
}
volumes = [
{
host_path = "/mnt/zwing/config/radarr"
container_path = "/config"
},
# Backups stay on spinning disks
{
host_path = "/mnt/xwing/backups/config/sonarr"
container_path = "/config/Backups"
},
{
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
},
{
host_path = "/mnt/xwing/media/Movies"
container_path = "/movies"
},
]
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
}

View File

@ -1,26 +0,0 @@
module "requestrr" {
name = "requestrr"
source = "../modules/container"
image = "darkalfx/requestrr:latest"
web = {
expose = true
port = 4545
host = "requestrr.${var.domain}"
}
resource = {
memory = 256
memory_swap = 256
}
volumes = [
{
host_path = "/mnt/xwing/config/requestrr"
container_path = "/root/config"
},
]
networks = [docker_network.media.id, data.docker_network.bridge.id]
}

View File

@ -1,45 +0,0 @@
module "sonarr-container" {
name = "sonarr"
source = "../modules/container"
image = "linuxserver/sonarr:latest"
web = {
expose = true
port = 8989
host = "sonarr.${var.domain}"
}
resource = {
memory = 512
memory_swap = 1024
}
volumes = [
{
host_path = "/mnt/zwing/config/sonarr"
container_path = "/config"
},
# Backups stay on spinning disks
{
host_path = "/mnt/xwing/backups/config/sonarr"
container_path = "/config/Backups"
},
{
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
},
{
host_path = "/mnt/xwing/media/TV"
container_path = "/tv"
},
]
env = [
"PUID=1004",
"PGID=1003",
"TZ=Asia/Kolkata",
]
networks = [docker_network.media.id, data.docker_network.bridge.id]
}

View File

@ -1,74 +0,0 @@
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.image_id
dynamic "labels" {
for_each = local.transmission_labels
content {
label = labels.key
value = labels.value
}
}
ports {
internal = 51413
external = 51413
ip = var.ips["eth0"]
protocol = "udp"
}
volumes {
host_path = "/mnt/xwing/config/transmission"
container_path = "/config"
}
volumes {
host_path = "/mnt/xwing/media/DL"
container_path = "/downloads"
}
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")
file = "/config/settings.json"
}
env = [
"PGID=1003",
"PUID=1000",
"TZ=Asia/Kolkata",
]
networks = [docker_network.media.id, var.traefik-network-id]
memory = 1024
restart = "unless-stopped"
destroy_grace_seconds = 10
must_run = true
}
resource "docker_image" "transmission" {
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"
}

View File

@ -1,42 +0,0 @@
variable "domain" {
type = string
}
# variable "airsonic-smtp-password" {}
variable "traefik-labels" {
type = map(string)
}
// TODO: Remove duplication
variable "basic_auth" {
default = "tatooine:$2y$05$iPbatint3Gulbs6kUtyALO9Yq5sBJ..aiF82bcIziH4ytz9nFoPr6,reddit:$2y$05$ghKxSydYCpAT8r2VVMDmWO/BBecghGfLsRJUkr3ii7XxPyxBqp8Oy"
}
variable "ips" {
type = map(string)
}
variable "traefik-network-id" {
}
variable "lastfm_api_key" {
description = "Navidrome Configuration for lastfm_api_key"
type = string
}
variable "lastfm_secret" {
description = "Navidrome Configuration for lastfm_secret"
type = string
}
variable "spotify_id" {
description = "Navidrome Configuration for spotify_id"
type = string
}
variable "spotify_secret" {
description = "Navidrome Configuration for spotify_secret"
type = string
}

View File

@ -1,30 +0,0 @@
module "miniflux-container" {
name = "miniflux"
source = "./modules/container"
image = "miniflux/miniflux:2.0.50"
web = {
expose = true
port = 8080
host = "rss.captnemo.in"
}
networks = ["bridge", "postgres"]
env = [
"DATABASE_URL=postgres://miniflux:${data.pass_password.miniflux-db-password.password}@postgres/miniflux?sslmode=disable",
"RUN_MIGRATIONS=1",
]
resource = {
memory = 512
memory_swap = 1024
}
}
module "miniflux-db" {
source = "./modules/postgres"
name = "miniflux"
password = data.pass_password.miniflux-db-password.password
}

View File

@ -1,9 +0,0 @@
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
}

View File

@ -1,49 +0,0 @@
locals {
default_labels = {
"managed.by" = "nebula"
}
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"
}
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.contentTypeNosniff" = "true"
"traefik.frontend.headers.browserXSSFilter" = "true"
"traefik.docker.network" = "traefik"
}
# if var.web.auth == true
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,
var.labels,
)
networks = concat(var.networks, var.web.expose ? ["traefik"] : [])
}

Some files were not shown because too many files have changed in this diff Show More