Compare commits

...

543 Commits

Author SHA1 Message Date
IamTheFij bdfde48bec Add some more monitors to nomad minitor 2024-05-06 14:29:17 -07:00
IamTheFij 9af55580e7 Update diun config to read from task socket 2024-05-01 10:18:54 -07:00
IamTheFij b9c35bf18f Add ability to set task identities for service module 2024-05-01 10:18:24 -07:00
IamTheFij e7f740a2d9 Add languagetool server 2024-05-01 09:43:28 -07:00
IamTheFij 57efee14e9 Update Ansible inventory to split node roles
Splits servers and clients to their own groups so that plays can target
specific roles.

Prior, everything was "both", but i want to and another server for
recovery purposes but not host containers on it.
2024-05-01 09:40:21 -07:00
IamTheFij c711c25737 Always use CF for dns when renewing lego certs
Makes it more resilient if my servers are down, but also cuts out a hop
because CF is the nameserver as well.
2024-04-27 19:33:10 -07:00
IamTheFij 24122c2a3e Split fixers to their own groups
Allow them to deploy as different allocs on different hosts
2024-04-22 09:07:03 -07:00
IamTheFij 13121862ec Add new host on qnap nas 2024-04-22 09:06:33 -07:00
IamTheFij 28da3f425b Move nomad default interface to host vars 2024-04-22 09:06:11 -07:00
IamTheFij 2d59886378 Update diun to include ability to read nomad socket 2024-04-17 10:46:28 -07:00
IamTheFij da0f52dab3 Improve change detection for cluster bootstrap 2024-04-17 10:46:10 -07:00
IamTheFij beac302a53 Upgrade nomad to 1.7.6 2024-04-17 10:45:27 -07:00
IamTheFij 5edcb86e7e Remove traefik grafana dashboard
Now in data backups rather than git.
2024-03-26 14:56:14 -07:00
IamTheFij 3dcd4c44b3 Tune memory after reviewing grafana 2024-03-26 09:48:31 -07:00
IamTheFij e6653f6495 Migrate sonarr to postgresql
And increase postgresql memory to accomodate
2024-03-25 16:05:58 -07:00
IamTheFij a9a919b8f2 Increase priority for sevices with highee resources
Photoprism requires lots if mem and sonar a specific volume
2024-03-22 21:09:19 -07:00
IamTheFij cc66bfdbcb Update photoprism 2024-03-22 21:07:55 -07:00
IamTheFij b02050112e Tune some service memeory 2024-03-22 21:07:07 -07:00
IamTheFij d5c2a0d185 Use default diun for syslogng 2024-03-22 21:05:53 -07:00
IamTheFij 6a3ae49d8e Update terraform modules 2024-03-11 22:02:07 -07:00
IamTheFij 75ee09d7e6 Remove bazarr
Plex does this automatically now
2024-02-20 10:13:40 -08:00
IamTheFij 8b90aa0d74 Add 1.1.1.1 dns back to blocky for better resiliance 2024-02-20 10:10:41 -08:00
IamTheFij 62e120ce51 Add radarr 2024-02-20 10:09:48 -08:00
IamTheFij 5fb510202d Fix indent for Authelia rules 2024-02-20 10:05:25 -08:00
IamTheFij 64a085ef80 Reatart failing services
Restart services that fail checks
2024-02-18 07:49:16 -08:00
IamTheFij f2f415aeac Fix traefik metrics 2024-02-18 07:47:31 -08:00
IamTheFij bb291b1f01 Move databases to their own tf files and improve first start 2024-02-13 12:05:55 -08:00
IamTheFij 056eac976c lldap: Make it work on first bootstrap
Can't use the job id for creating the variables and permissions because we end up
with circular dependencies. The job won't return until it's successful in Nomad and it won't
start in nomad without access to varibles
2024-02-13 12:05:21 -08:00
IamTheFij 198f96f3f7 Add back other traefik ports and metrics 2024-02-13 12:03:03 -08:00
IamTheFij 6b5adbdf39 Remove 404 block list 2024-02-13 12:02:35 -08:00
IamTheFij 77ef4b4167 Use quad9 encrypted dns 2024-02-13 12:02:14 -08:00
IamTheFij b35b8cecd5 Blocky: Remove mysql and redis configs from stunnel if server isn't found 2024-02-13 12:01:45 -08:00
IamTheFij b9dfeff6d8 Have blocky use router for upstream in nomad 2024-02-13 12:01:08 -08:00
IamTheFij 2ff954b4b5 Bump nomad 2024-02-13 12:00:43 -08:00
IamTheFij 2528dafcc6 Make nomad restart playbook more resilient 2024-02-13 12:00:24 -08:00
IamTheFij 0e168376b8 Add terraform destroy to makefile 2024-02-13 11:59:47 -08:00
IamTheFij a16dc204fe Run dummy backup more frequently to make graphs easier to read 2024-01-24 20:10:14 -08:00
IamTheFij 93d340c182 Make sure gitea ingress uses system wesher config
It was always using wesher
2024-01-23 12:09:59 -08:00
IamTheFij 37ee67b2e6 fix: Add job_id output to services
This should be earlier in history
2024-01-23 12:09:29 -08:00
IamTheFij 35dfeb3093 Add service healthchecks 2024-01-23 12:08:47 -08:00
IamTheFij 0a2eace3dd Fix lldap secrets 2024-01-23 12:07:42 -08:00
IamTheFij 6fe1b200f2 Update loki 2024-01-23 12:06:25 -08:00
IamTheFij c5d5ab42b8 Add some nomad actions for backups to test different formatting 2024-01-23 12:05:56 -08:00
IamTheFij efe7864cc9 Delay shutdowns of backup jobs to reduce killing those in progress 2024-01-23 12:05:20 -08:00
IamTheFij 9ba74ce698 Use return vars for service acl 2024-01-16 14:16:21 -08:00
IamTheFij 4fe3d46d5f Add external service acls for authelia 2024-01-16 14:15:56 -08:00
IamTheFij cf8bde7920 Add external traefik routes to nomad vars 2024-01-16 14:15:18 -08:00
IamTheFij bc87688f1a Move ldap secrets 2024-01-16 14:14:39 -08:00
IamTheFij 3491c1f679 Add refresh make target 2024-01-16 14:04:44 -08:00
IamTheFij 7b019e0787 Add auth to sonarr 2024-01-08 14:57:06 -08:00
IamTheFij 0f19e2433f Upgrade sonarr to version 4 2024-01-08 10:14:53 -08:00
IamTheFij c01d45c7a2 Upgrade grafana to version 10 2024-01-08 10:11:42 -08:00
IamTheFij d07afe2319 Update traffic routes to handle null IPs
Eg. 0.0.0.0 for blocked domains
2024-01-06 16:23:45 -08:00
IamTheFij b025e4a87e Add repo unlock via Nomad action to backups 2024-01-06 16:22:20 -08:00
IamTheFij 9be16fef1f Upgrade traefik to 2.10 2024-01-04 13:25:10 -08:00
IamTheFij c26da678b3 Small traefik cleanup
Remove fallback DNS since we only care about internal DNS

Use loopback address for accessing Nomad UI
2024-01-04 13:24:49 -08:00
IamTheFij 6b9533ef71 Run traefik on multiple hosts 2024-01-04 13:24:15 -08:00
IamTheFij 0bd995ec2b Traefik: Use nomad vars for dynamic certs
Rather than having Traefik handle cert fetching, instead
it is delegated to a separate job so that multiple Traefik
instances can share certs
2024-01-04 10:55:49 -08:00
IamTheFij 0d340f3349 Periodic job to renew lego certs and store them in Nomad Variables
This will allow multiple instance of Traefik to serve certs.
2024-01-04 10:53:25 -08:00
IamTheFij bcad131aa7 Use job id for lldap acls 2024-01-04 10:53:23 -08:00
IamTheFij cda2842f8f Switch to image containing stunnel
Rather than installing on container startup, using an image with
stunnel pre-installed. This avoids issues with DNS breaking
the container on startup.
2024-01-03 13:50:49 -08:00
IamTheFij 9544222961 Bump to 1.7.2 2023-12-29 20:47:58 -08:00
IamTheFij 7bc4ae1f8b Reserve node memory to reduce OOM kills 2023-12-29 07:36:23 -08:00
IamTheFij 1a3c096b65 Fix nomad fixers 2023-12-29 07:35:07 -08:00
IamTheFij 25e533287d Fix gitea backups syntax 2023-12-18 12:23:21 -08:00
IamTheFij 7e87002be2 Nomad 1.7 2023-12-18 12:22:19 -08:00
IamTheFij ab6906e989 Gitea backups 2023-12-10 20:39:33 -08:00
IamTheFij ca55209316 Fix blocky redis 2023-12-10 20:37:43 -08:00
IamTheFij 1b49f015c5 Update blocky config to v0.22 schema 2023-11-30 14:00:27 -08:00
IamTheFij eb25138675 Remove defunct lists 2023-11-30 13:39:22 -08:00
IamTheFij 69a0f760b4 Remove defunct lists 2023-11-30 13:39:01 -08:00
IamTheFij 3fcedaddb7 Remove todo from traefik 2023-11-30 13:26:15 -08:00
IamTheFij bb34b434b8 Add custom blocklists hosted on my gitea server 2023-11-30 13:23:54 -08:00
IamTheFij 36cdb8f41b Add Gitea
Currently it won't auto bootstrap auth. A command has to be executed one
time to get it to be added to the database.
2023-11-30 13:22:54 -08:00
IamTheFij cdd4e9b5d5 Fix custom ports for services 2023-11-30 13:22:53 -08:00
IamTheFij f06e90ab0d Remove hw transcode constraints from photoprism 2023-11-30 10:05:39 -08:00
IamTheFij 2d733b278c Make backup jobids static so they work on clean deploy 2023-11-30 09:55:08 -08:00
IamTheFij b218633c2c Add scheduled job to update UniFi Traffic Routes
Because I use a custom DNS server, Domain based routing rules
don't work. This instead resolves the domains and then adds
the IP addresses to the rules.
2023-11-20 10:37:03 -08:00
IamTheFij e21ec11eb5 Fix grafana
Broken template
2023-11-20 10:35:49 -08:00
IamTheFij d6f9c2a7e4 Fix diun include tags variable
This fixes a configuration bug causing diun to include all tags by default.
2023-11-16 12:22:44 -08:00
IamTheFij 891cfa7b2d Update blocky dashboard to not use consul tags 2023-11-16 12:21:59 -08:00
IamTheFij c11b8e157b Fix grafana dashboard provisioning
A path mismatch existed after migrating to alloc storage
2023-11-16 12:21:40 -08:00
IamTheFij 0d208b7394 Add dummy backup job to keep backup task running on all hosts
Otherwise, if a client is not running any stateful services, the task
will fail and Nomad will eventually stop retrying. If a service gets
relocated to the host, the task is not restarted. This makes sure the
task will cover moved services and make it more easy to determine that
backups are healthy.
2023-11-16 12:19:19 -08:00
IamTheFij 9b347880cc Ignore some regeneratable files in backups
Reduces the size of Lidarr and Photoprism backups
2023-11-07 16:50:02 -08:00
IamTheFij a0185d9642 Bump resources for backups to allow more memory
Was getting OOM killed
2023-11-07 16:49:27 -08:00
IamTheFij f2f5f4407c Add TZ to restic-scheduler 2023-11-07 16:48:57 -08:00
IamTheFij 0b3d3caff6 Update restic-scheduler to fix index out of range error 2023-11-07 16:48:41 -08:00
IamTheFij 52abd94a38 Use minio as restic repo rather than sftp
I've been getting a lot of restic lock errors using sftp
2023-11-06 16:35:13 -08:00
IamTheFij 0391fd95ad Allow fixers to actually fix things 2023-11-06 14:41:54 -08:00
IamTheFij df1ae60936 Add change_script to service module 2023-11-06 14:41:13 -08:00
IamTheFij a2d33ac309 Add proxmox influxdb to Grafana 2023-10-23 13:10:01 -07:00
IamTheFij 1b48892172 Add read-only implementation of fixers as scheduled batches 2023-10-23 12:59:45 -07:00
IamTheFij 48a48bb080 Move sonarr and nzbget to their own jobs 2023-10-23 12:59:11 -07:00
IamTheFij bd2c5ca3db Put restic cache on ephemeral disk 2023-10-23 08:54:05 -07:00
IamTheFij 48074bdc39 Fix log from orphaned services to not say deleting when it's in dry run. 2023-10-19 12:08:28 -07:00
IamTheFij 2f3fc87f12 Consider job status when detecting missing services
Prevents false alarms and attempts to restart failed or stopped allocs.
2023-10-19 12:07:57 -07:00
IamTheFij 369802cacc Bump Postgres memory to 500mb 2023-10-19 12:07:14 -07:00
IamTheFij 0c3f98d5c3 Pin Grafana to amd64 since renderer requires it.
This could be mitigated by moving the renderer to another task group.
2023-10-19 12:06:47 -07:00
IamTheFij b97cfb68ad Minor Nomad bmp 1.6.2 2023-10-19 12:05:52 -07:00
IamTheFij d83591cfd4 Make diun disk sticky 2023-09-27 21:34:14 -07:00
IamTheFij f80eccdfa2 Update diun to use global defaults 2023-09-27 21:33:55 -07:00
IamTheFij 5fe30d005b Update missing services script to restart allocs 2023-09-27 21:30:48 -07:00
IamTheFij ad439d48f3 Add waiting for loki and prom dependencies in core 2023-09-27 21:30:22 -07:00
IamTheFij df4737655a Remount network shares when recovering cluster 2023-09-27 21:26:44 -07:00
IamTheFij b29f405090 Bump prometheus versiosn and pin blocky 2023-09-18 21:58:43 -07:00
IamTheFij cf90248430 Remove old Consul and Vault references 2023-09-17 21:43:04 -07:00
IamTheFij 72a108753b Bump lldap to latest release 2023-09-14 12:14:07 -07:00
IamTheFij 8dd00c1249 authelia and grafana to shared smtp secrets 2023-08-29 15:11:40 -07:00
IamTheFij edeb6cf444 lldap: access shared smtp secrets 2023-08-29 14:56:06 -07:00
IamTheFij 2bd939e651 Remove deprecated hcl2 enabled 2023-08-29 13:02:04 -07:00
IamTheFij ea8ca478c6 Fix blocky acl 2023-08-29 12:59:14 -07:00
IamTheFij 769965ae6b Fix lidarr to use postgres db creds 2023-08-29 12:52:30 -07:00
IamTheFij f5898b0283 Add workload ACL management for mysql and postgres access
Allows required jobs to access shared secrets and auto generates psks
for stunnel.

Currently supporting MySQL, Postgres, and LDAP.
2023-08-29 12:48:48 -07:00
IamTheFij cdba6aa24f Add script to check for missing services 2023-08-28 13:54:24 -07:00
IamTheFij 50447b9a7d Add a read-only check for orphaned service script 2023-08-28 13:54:06 -07:00
IamTheFij 08f92f8ba5 Add new backup and orphaned service scripts 2023-08-26 15:59:50 -07:00
IamTheFij b13e31d9f8 Move scripts to subdir 2023-08-26 15:58:57 -07:00
IamTheFij a57e87d21f Fix var path for adhoc backup jobs 2023-08-26 15:56:21 -07:00
IamTheFij 2efc7f8c2f Add ability to set job meta for services 2023-08-24 15:41:18 -07:00
IamTheFij 7aa5b800ba Clean up finally rendered templates for services 2023-08-24 15:37:42 -07:00
IamTheFij 013dd8248b Make base_hostname more configurable 2023-08-24 15:03:36 -07:00
IamTheFij f6dd3f4284 Clean up root module and move lldap to databases 2023-08-24 13:52:03 -07:00
IamTheFij 4a7bff7611 Move metrics out of a module and into core 2023-08-24 13:00:36 -07:00
IamTheFij b4a6901687 Bump up sonarr memory a little more 2023-08-24 12:51:32 -07:00
IamTheFij d5078b24da Refactor use of wesher to be behind a variable toggle
Occasionally I run into issues with Wesher. This makes it easier to
disable use of Wesher by setting TF_VAR_use_wesher to false.
2023-08-24 12:51:32 -07:00
IamTheFij e2c35a82a9 Fix grafana config loading
For some reason, the env variable method stoped working.
2023-08-24 11:59:10 -07:00
IamTheFij 50507b2aa8 Fix eol on readme 2023-08-24 11:53:54 -07:00
IamTheFij 0cfd052a6e Move backup module to a module under root 2023-08-24 11:53:08 -07:00
IamTheFij 1715b58ca9 Pin image versions for more critical services 2023-08-24 11:39:00 -07:00
IamTheFij 440c0b0c4c Move redis commander 2023-08-24 11:37:13 -07:00
IamTheFij 47da10febf Remove unused caddy module 2023-08-24 11:11:36 -07:00
IamTheFij 0a8395e8fa Add bazarr configs 2023-08-21 10:54:57 -07:00
IamTheFij 05c367e531 Try to format time zones from minitor 2023-08-18 12:15:34 -07:00
IamTheFij 32b5e420bc Fix incorrect README
Referenced Ansible when it should have been Nomad variables.
2023-08-13 20:54:46 -07:00
IamTheFij 9d8aa49b31 Improve README.md documentation
Gove more details in README.md
2023-08-13 20:53:11 -07:00
IamTheFij dcb9f7d26f Add log alert back to minitor 2023-08-11 07:08:00 -04:00
IamTheFij 64d61d69a1 Fix Plex minitor check 2023-08-11 06:52:51 -04:00
IamTheFij 92e42b5605 Update and add time format to minitor 2023-08-11 03:49:55 -07:00
IamTheFij b62029be0a Lower photoprism resources to make it easier to schedule 2023-08-10 15:56:12 -07:00
IamTheFij ddeb8fffbc Move services to their own tf files for easier locating 2023-08-07 11:37:19 -07:00
IamTheFij 41c9d3d6f6 Adjust down default service stunnel sidecar resources
Keep photoprism and lidarr, database heavy tools, at the same level
2023-08-07 11:31:35 -07:00
IamTheFij 02959c7673 Update minitor with new apps and global options 2023-08-03 14:39:50 -07:00
IamTheFij 3e0533954f Add authelia backup job 2023-08-03 10:36:42 -07:00
IamTheFij df5ed00f05 Update backup job config to iterate over job files
This will prevent new ones from being added and not included
2023-08-03 10:33:11 -07:00
IamTheFij 0a5480129e Remove nextcloud since it's not used 2023-08-03 10:32:34 -07:00
IamTheFij 946873e5ad Make sure existing jobs are loaded 2023-08-03 10:21:34 -07:00
IamTheFij d8f8884cb8 Improve backup job configuration
Add lidarr, fix hosts to 'nomad' since host names change with containers and nodes
and don't make a difference, make most jobs daily, exclude sonarr and lidarr zip backups
from restic backups.
2023-08-03 10:11:57 -07:00
IamTheFij e63327428f Update backups to v0.2.0 to include postgres 2023-08-03 09:53:31 -07:00
IamTheFij a2d24e03cd Deploy adhoc backups to all hosts 2023-08-03 09:53:03 -07:00
IamTheFij f66bd95fbb Run backup batches on all hosts 2023-08-02 21:33:16 -07:00
IamTheFij fa0da05343 Change authelia port to avoid conflict with prometheus 2023-08-02 21:31:08 -07:00
IamTheFij 2844493fa1 Increase pgsql and lidarr memory to prevent crashes on library 2023-07-31 10:43:51 -07:00
IamTheFij 4b94f66786 Increase Traefik memory 2023-07-31 10:43:03 -07:00
IamTheFij c2632ee7c0 Mount pgdata to propper path 2023-07-26 23:24:09 -07:00
IamTheFij f333031c25 bootstrap blocky with stunnel 2023-07-26 23:23:23 -07:00
IamTheFij 254ef01de9 Increase lidarr resources 2023-07-26 15:30:05 -07:00
IamTheFij b5ab68e6f3 Fix postgres host volume 2023-07-26 15:29:52 -07:00
IamTheFij 8f6bed297c Upgrade to nomad 1.6.1 2023-07-26 15:29:39 -07:00
IamTheFij 882b93a4c5 Abort nomad recovery if any hosts fail 2023-07-26 15:27:46 -07:00
IamTheFij 0d37652447 Add pre-commit hook to make sure variable sample is up to date 2023-07-25 16:57:44 -07:00
IamTheFij a52c2bc6c7 Run pre-commit on everything 2023-07-25 16:57:44 -07:00
IamTheFij 70098930f8 Add lidarr 2023-07-25 16:57:33 -07:00
IamTheFij e7c985d276 Allow adminer to connect to postgres 2023-07-25 16:57:33 -07:00
IamTheFij 0ea9da3a53 Update postgres bootstrap allowing multiple databases 2023-07-25 16:57:33 -07:00
IamTheFij ac29343d96 Add postgres stunnel and service bootstrap 2023-07-25 10:59:33 -07:00
IamTheFij f8478ae6c9 Service Template: Make sure stunnel is there for ldap 2023-07-25 10:30:28 -07:00
IamTheFij f0d31ff13c Move stunnel psks to a more restrictive path 2023-07-25 10:16:30 -07:00
IamTheFij c33f877af8 Allow specifying port `from` value for local host binding 2023-07-24 15:23:40 -07:00
IamTheFij b9fb2d4b07 Add ability to specify custom services for service module 2023-07-24 15:23:31 -07:00
IamTheFij 6524631a53 Upgrade to nomad 1.6 2023-07-19 10:42:38 -07:00
IamTheFij 10a9689eef Bump up MySQL memory 2023-07-19 09:37:23 -07:00
IamTheFij 9b11ad9a69 Add Nomad var example and remove old examples 2023-07-11 12:46:47 -07:00
IamTheFij 72c30d4d74 Add basic readme 2023-07-11 17:32:50 +00:00
IamTheFij 2e7dc0315e Use shorthand for ingress middlware for photoprism 2023-07-07 16:35:07 -07:00
IamTheFij 744466bf07 Use static port for Authelia so that nomad middleware config is the same for each service 2023-07-07 16:34:50 -07:00
IamTheFij d0641f8edf Enable setting static ports for service template 2023-07-07 16:33:36 -07:00
IamTheFij cb73e2b205 Remove whitespace 2023-07-07 15:56:25 -07:00
IamTheFij 486df58bac Use nomad-python client for setting nomad vars 2023-07-07 15:56:25 -07:00
IamTheFij b75f8fce7b Clean photoprism config 2023-07-07 15:56:24 -07:00
IamTheFij df062000e7 Run two authelia instances now that it's stateless 2023-07-07 15:56:23 -07:00
IamTheFij 2b91b6dc8f Add instance count to service template 2023-07-07 15:51:19 -07:00
IamTheFij 85db434c1f Minor cleanup to backups module 2023-07-07 15:50:58 -07:00
IamTheFij 0a7ad7a9dc Enable redis for authelia
This also splits redis instances by service
2023-07-07 15:50:23 -07:00
IamTheFij b0c1aca497 Increase token time for Nomad OIDC 2023-07-07 15:47:08 -07:00
IamTheFij 60a4051988 Enable Authelia OIDC for Nomad 2023-07-07 00:41:44 -07:00
IamTheFij 0ceb513216 Switch Grafana to OIDC from proxy auth 2023-07-07 00:40:19 -07:00
IamTheFij 9d5aeeec96 Enable Authelia OIDC provider 2023-07-07 00:39:44 -07:00
IamTheFij 6dbe0f7f45 Add nomad ACLs and roles for use in oidc auth 2023-07-07 00:30:02 -07:00
IamTheFij eae5b201b6 Add two factor for external IPs 2023-07-06 21:25:31 -07:00
IamTheFij 532d7f9a4c Use Authelia for Grafana login 2023-07-06 18:00:06 -07:00
IamTheFij 88e91e5e5d Deploy authelia
Backed by lldap and mysql and deployed on whoami for now as a forward
proxy example

Would be good to add oidc for Nomad as well as make policies configurable
via Nomad variables.
2023-07-06 18:00:06 -07:00
IamTheFij a90b3bbdbe Make it easier to enable bootstrap now that key isn't included 2023-07-06 17:25:13 -07:00
IamTheFij cdbd6a9cb3 Add ability to set priority for service templates 2023-07-06 17:25:13 -07:00
IamTheFij 2a1a7fb6b7 Clean mysql stunnel config 2023-07-06 17:25:13 -07:00
IamTheFij 8650ab973a Add stunnel for ldap as part of service template 2023-07-06 17:25:13 -07:00
IamTheFij acc80868f9 Switch lldap storage to mysql 2023-07-05 17:30:54 -07:00
IamTheFij f606e0a17e Remove blocky client groups because fallback server masks them 2023-07-05 15:45:55 -07:00
IamTheFij 44467d1075 Add playbook to restart wesher and nomad 2023-06-20 09:45:01 -07:00
IamTheFij 8b0495c6c8 Take mysql off wesher network 2023-06-20 09:44:21 -07:00
IamTheFij 2df43584cf Grafana config reloading: Use explicit path and echo
Was running into some issues with this not running. Using an explicit
path seems to help, so I'll try it for now. Also added some echo statements
to make it easier to discern when run.
2023-06-20 09:44:04 -07:00
IamTheFij 2c128b25f3 Add additional blocking for wemo 2023-06-20 09:42:33 -07:00
IamTheFij 1df5545835 Promtail: use local task dir rather than bind mount 2023-05-12 10:11:30 -07:00
IamTheFij d4cb91d58d Rename metrics job to exporters 2023-05-12 10:11:11 -07:00
IamTheFij 48322d9a78 Document what the nomad stalker is for 2023-05-12 10:10:31 -07:00
IamTheFij 73e9977d41 Use variable secrets location for mysql 2023-05-09 15:57:09 -07:00
IamTheFij 5dc0e4bcaf Use pushgateway with restic scheduler batches
Batches can't run due to incorrectly scoped variables
2023-05-09 15:56:53 -07:00
IamTheFij 5169aecc6d Add pushgateway to prometheus 2023-05-09 15:56:20 -07:00
IamTheFij 69c8322d50 Better error handling in nomad variable bootstrap 2023-05-09 13:21:00 -07:00
IamTheFij f11fad30a5 Use stunnel for mysql
Doesn't remove wesher or normal mysql service
2023-05-09 13:20:36 -07:00
IamTheFij a5efe0c21b Fix variable bootstrap address 2023-05-09 13:13:28 -07:00
IamTheFij 30766cce39 Bump up sonarr memory 2023-05-09 11:40:31 -07:00
IamTheFij 33ec66346b Make target to stop Nomad cluster 2023-05-09 11:39:27 -07:00
IamTheFij 8d63c50ffb Add Postgres database to cluster 2023-05-03 14:16:47 -07:00
IamTheFij cf0a415179 Revert "Upgrade cni to 1.1.2"
This reverts commit bbc8ba5c6b.
2023-05-02 21:29:27 -07:00
IamTheFij 27fd60d84d Add missing service to Wesher
Promtail, Backups, service module
2023-05-02 21:14:36 -07:00
IamTheFij 0a84fd04bc Automatically re-provision grafana when data source addresses change 2023-05-02 21:13:59 -07:00
IamTheFij 1c14430c99 Preliminary hw transcode support for Photoprism on pi4 2023-04-20 16:48:04 -07:00
IamTheFij f75d149f32 Add constraints and docker devices to service template 2023-04-20 16:47:07 -07:00
IamTheFij bbc8ba5c6b Upgrade cni to 1.1.2 2023-04-20 16:46:35 -07:00
IamTheFij 973388e109 Add hw_transcode meta to nodes 2023-04-20 16:43:57 -07:00
IamTheFij 42054d8fa6 More whoami instances 2023-04-14 14:24:33 -07:00
IamTheFij 4122d92f78 Make sure adminer is using nomad service discovery 2023-04-14 14:24:17 -07:00
IamTheFij fb25b52e7a Restrict permissions to wesher config 2023-04-14 14:23:58 -07:00
IamTheFij fb6a899a26 Ignore nomad variables file 2023-04-14 13:54:43 -07:00
IamTheFij 8cbc9145c0 Minor nomad upgrade 2023-04-14 13:41:40 -07:00
IamTheFij 7d8bc45090 Move blocky custom mappings above catchall 2023-04-04 13:12:34 -07:00
IamTheFij 87d97ac891 Fix redis server psks 2023-03-28 17:28:46 -07:00
IamTheFij 485bc22e78 Add TODO for using nomad api socket 2023-03-27 15:50:15 -07:00
IamTheFij 28564b6130 Bind nzbget to static port
This allows it to be referenced in sonarr by nzbget.nomad:6789
2023-03-27 15:23:55 -07:00
IamTheFij c38ba8589a Clean blocky config for latest version 2023-03-27 15:21:35 -07:00
IamTheFij c7f85bd985 Fix blocky redis stunnel lookup 2023-03-27 15:21:19 -07:00
IamTheFij f17dec7b57 Add nomad services to nomad zone using hosts in blocky 2023-03-27 15:20:50 -07:00
IamTheFij a748adbab0 Store blocky config in local task dir 2023-03-27 15:19:53 -07:00
IamTheFij 747d5ef0e7 Remove vault stanza from Grafana 2023-03-27 14:10:10 -07:00
IamTheFij 8e3fbcedb9 Fix backup conditionals 2023-03-24 23:34:23 -07:00
IamTheFij f1098d6448 Remove nomad-bridge because it's not used 2023-03-24 23:07:23 -07:00
IamTheFij 08d0e93638 Clean up and remove some consul and vault stuff 2023-03-24 22:58:44 -07:00
IamTheFij 74ce30c3c1 Get nomad client scraping working 2023-03-24 22:22:11 -07:00
IamTheFij 6f94b4ed67 Fix cluster setup 2023-03-24 21:12:02 -07:00
IamTheFij 98ea2a1ca0 A whole lot of incremental fixes for nomad variables and such
Also adds stunnel between redis and clients
2023-03-24 16:32:37 -07:00
IamTheFij 9204f3c7f0 Add consul back to terraform temporarily while I transition 2023-03-24 12:48:53 -07:00
IamTheFij d8307935f5 Refactor everything for nomad vars 2023-03-24 11:24:36 -07:00
IamTheFij 9f5752c66b Allow deleting of Nomad variables 2023-03-24 09:57:37 -07:00
IamTheFij 5fb0e0841e Blocky do not create read only user to reduce password exposure 2023-03-24 09:56:56 -07:00
IamTheFij 00697ebb02 Blocky use wgoverlay for api 2023-03-24 09:56:29 -07:00
IamTheFij f31569ad56 Update cloudflare variable names 2023-03-24 09:56:03 -07:00
IamTheFij 46dc44aca4 Simplify mysql for blocky 2023-03-24 08:55:27 -07:00
IamTheFij c9a892e377 Make levant template support nomad only services 2023-03-24 08:55:27 -07:00
IamTheFij 4430b3570e Fix blocky template 2023-03-24 08:55:27 -07:00
IamTheFij 8679cc1635 Fix Makefile by removing vault token 2023-03-24 08:55:27 -07:00
IamTheFij 65cb6afaf9 WIP: Moving vars and service discovery to Nomad
Starting with core
2023-03-24 08:55:23 -07:00
IamTheFij ee68310e58 Add Nomad provider and sample using Wesher 2023-03-24 08:50:16 -07:00
IamTheFij 73e7b7063f Fix wesher secrets 2023-03-24 08:50:01 -07:00
IamTheFij 6201aaa87e Upgrade Nomad to 1.5.2 2023-03-24 08:49:48 -07:00
IamTheFij f655488927 Add variables access to Nomad Admins 2023-03-24 08:47:02 -07:00
IamTheFij fda97b8d01 Add Wesher and Wesher overlay 2023-03-23 22:10:24 -07:00
IamTheFij 3ebb616219 Add nomad labels to docker logs 2023-03-17 11:47:40 -07:00
IamTheFij 3248f2817b Change min consul nodes to bootstrap to 2 2023-03-17 11:47:21 -07:00
IamTheFij 33fea63b5f Speed up consul recovery 2023-03-17 11:46:20 -07:00
IamTheFij bc354ba041 Increase sidecar resources for dvr 2023-03-14 22:43:51 -07:00
IamTheFij d501da4c90 Reduce redis memory
It's not used all that much right now
2023-03-12 10:23:26 -07:00
IamTheFij 437b5ce72e Update grafana 2023-03-12 10:22:47 -07:00
IamTheFij 2f9a4df668 Add sleep taget to add delay between some commands 2023-03-12 10:05:24 -07:00
IamTheFij 4f7c41a7a5 Add envoy metrics to redis 2023-03-02 11:07:07 -08:00
IamTheFij 24461d4c6f Add envoy metrics to ipdvr 2023-03-02 11:05:36 -08:00
IamTheFij e0fe3327f0 Move media library service to service template 2023-03-02 11:01:44 -08:00
IamTheFij a70ad9d5a6 Export envoy metrics for mysql service 2023-03-02 11:00:45 -08:00
IamTheFij 5228e7c7fb Export envoy metrics for services 2023-03-02 11:00:30 -08:00
IamTheFij 882fe7e29c Some cleanup of service template whitespace 2023-03-02 10:42:33 -08:00
IamTheFij f41bdb7dd0 Increase photoprism resources 2023-03-02 10:39:42 -08:00
IamTheFij 208f90e7bf Increase memory and max memory for connect proxy for services 2023-02-28 15:57:45 -08:00
IamTheFij 20bb6ba7aa Increase memory max for photoprism 2023-02-28 15:57:16 -08:00
IamTheFij de2dae3686 Increase retry count for nomad starting when deploying cluster 2023-02-28 12:17:45 -08:00
IamTheFij a7f9351728 Fix GC cleanup image_delay 2023-02-28 12:17:28 -08:00
IamTheFij 36d00300c3 Move nzbget and photoprism config to shared storage on NAS SSD 2023-02-28 12:16:49 -08:00
IamTheFij 19d5321731 Increase memory for promtail
n2 was getting OOM
2023-02-27 11:54:33 -08:00
IamTheFij 3a95fb46db Add more conditional checks to Blocky so it is more resiliant
Hopefully this will allow it to deploy if mysql or vault are down
2023-02-27 11:54:33 -08:00
IamTheFij f8555f0900 Add a 1d delay to cleaning up images from stopped tasks 2023-02-27 11:54:33 -08:00
IamTheFij 3a8cca53f3 Update photoprism and use new storage path 2023-02-27 11:54:33 -08:00
IamTheFij 9fe63d03d9 Add image_pull_timeout to service template 2023-02-27 11:54:33 -08:00
IamTheFij 5341cb1c8b Increase sonarr memory 2023-02-27 11:54:33 -08:00
IamTheFij f46cb72681 Add sabnzbd 2023-02-27 11:54:33 -08:00
IamTheFij 2f9d0533e0 Move nzbget to proxmox nfs share 2023-02-27 11:54:33 -08:00
IamTheFij bac0b28f68 Prompt for password for cluster deployment and recovery 2023-02-27 11:54:33 -08:00
IamTheFij 061c375652 Use new NAS paths 2023-02-27 11:54:32 -08:00
IamTheFij 15ea178e8e Update Plex url 2023-02-15 19:55:35 -08:00
IamTheFij 1811a851ab Tighten diun watch expressions 2023-02-14 12:28:41 -08:00
IamTheFij 6419f135a1 Remove prompt when unsealing vault 2023-02-14 12:28:19 -08:00
IamTheFij 0d9d2c7d21 Update promtail version and version checker 2023-01-13 15:47:48 -08:00
IamTheFij b0785b210f Switch default network interface to local network 2023-01-13 15:17:38 -08:00
IamTheFij bd35cb1265 Make sure there's a working DNS server when bootstrapping 2023-01-13 15:17:23 -08:00
IamTheFij fd92573c16 Add more flexible nfs mount definitions
Also commenting out NAS since it's down
2023-01-13 15:17:03 -08:00
IamTheFij 03fd68b4f7 Add diun for monitoring images 2023-01-12 12:11:16 -08:00
IamTheFij 0a798aa5a7 Add meta tags to service template 2023-01-11 15:40:42 -08:00
IamTheFij 19031834fb Quote monitor name to prevent shell issues 2023-01-07 14:10:42 -08:00
IamTheFij b92917329f Use a different ip address host for ddns 2023-01-07 14:10:20 -08:00
IamTheFij 1ef4b988ac Add recovery make targets 2023-01-07 14:09:38 -08:00
IamTheFij bbe5bfaba4 Decode nomad node-ids in recovery playbook 2023-01-07 14:09:19 -08:00
IamTheFij 290b8885b7 Fix vault hostnames for recover-consul playbook 2023-01-07 14:08:55 -08:00
IamTheFij acdccbc057 Fix consul recovery and decode node ids 2023-01-07 14:03:31 -08:00
IamTheFij e0c8d1f3c1 Exporters depend on prometheus 2023-01-06 23:07:33 -08:00
IamTheFij 976f8f9e4e Change ddclient ip url 2023-01-06 23:06:23 -08:00
IamTheFij 66db9fbd58 Update prune settings for backups 2023-01-06 16:08:19 -08:00
IamTheFij 54d479ee9f Rollback consul version
This error on 1.14 needs to be resolved https://github.com/hashicorp/consul/issues/15753
2023-01-06 16:07:55 -08:00
IamTheFij 91c2ff6345 Update blocklists 2022-12-22 15:13:31 -08:00
IamTheFij 7ec6f38c80 Update consul and vault: 2022-12-22 15:11:33 -08:00
IamTheFij fd731971d3 Try to stabilize DNS
Add all cluster nodes to each nodes resolv.conf and update blocky config
template to delay render on update to avoid unnecessary restarts
2022-11-27 22:46:25 -08:00
IamTheFij 7bed73b9a7 Disable consul autopilot 2022-11-27 22:46:05 -08:00
IamTheFij b9aec2a3c4 Update make targets
Add all and clean

Also removes prompts during make
2022-11-27 22:44:55 -08:00
IamTheFij fc86b974a7 Update versions 2022-11-27 22:44:26 -08:00
IamTheFij b0ea77a9f7 Update Cloudflare token variables 2022-11-21 14:25:01 -08:00
IamTheFij b046962b8c Disable consul autopilot
Was getting some instablity issues. Seeing if this helps
2022-11-21 14:22:49 -08:00
IamTheFij 33be46add4 Fix galaxy make targets 2022-11-20 17:26:33 -08:00
IamTheFij 4f5f4e0fe6 Increase priority of backup tasks 2022-11-18 08:58:38 -08:00
IamTheFij 162f567c85 Stop Nomad and Vault when recovering Consul 2022-11-18 08:58:20 -08:00
IamTheFij 3b9ad36ed0 New playbook to stop cluster gracefully 2022-11-18 08:57:41 -08:00
IamTheFij 049d9f0fe0 Make sure grafana points to port bound within it's task group 2022-11-18 08:57:06 -08:00
IamTheFij 3a828af690 No longer need to pin the envoy proxy 2022-11-18 08:56:29 -08:00
IamTheFij 080cea9637 Fix minitor
Healthcheck was failing due to wrong path and log alert was failing
due to the config file being templated by Nomad. Updated to use a
different delimiter.
2022-11-16 09:04:32 -08:00
IamTheFij f481e7b938 Update blocky dashboards 2022-11-16 08:42:36 -08:00
IamTheFij 35403d0219 Update nomad dashboard 2022-11-16 08:37:29 -08:00
IamTheFij 416676c9f9 Update minitor dashboard 2022-11-16 08:35:01 -08:00
IamTheFij 12b91e9566 Fix env location for lldap 2022-11-15 16:54:37 -08:00
IamTheFij 8a21dd7eb4 Bump traefik version 2022-11-15 15:57:23 -08:00
IamTheFij a1def1c69d Increase memory for lldap
Password hashing was causing OOM kills
2022-11-15 15:57:23 -08:00
IamTheFij c7d0fca6e7 Pin lldap verison 2022-11-15 15:57:23 -08:00
IamTheFij 86b472435c Use explicit lldap ports so that connect proxy can find them 2022-11-15 15:57:23 -08:00
IamTheFij 2db266bda7 Update blocky upstream dns to bootstrap better (hopefully) and forward to consul 2022-11-15 10:26:26 -08:00
IamTheFij cf2779c971 Update lldap to use dynamic ports 2022-11-15 09:43:13 -08:00
IamTheFij b098a325f8 Add backups for photoprism 2022-11-15 09:19:55 -08:00
IamTheFij c5135be4a0 Pin photoprism version to avoid pulling every time 2022-11-15 09:19:32 -08:00
IamTheFij 4a68894238 Add some minitor checks 2022-11-15 09:04:18 -08:00
IamTheFij 17ab7f637f Add metrics support to service template 2022-11-15 09:04:18 -08:00
IamTheFij e104cbccc4 Use new Photos share for Photoprism 2022-11-15 09:04:18 -08:00
IamTheFij 954a878915 Grafana update + renderer + new dashboards 2022-11-15 09:04:18 -08:00
IamTheFij bee9b641cc Fix local dns in docker 2022-11-15 09:04:18 -08:00
IamTheFij 19f1f8448d WIP: Add a new playbook for stopping cluster
Hopefully without data loss
2022-11-14 23:11:13 -08:00
IamTheFij bbec244f45 Fix typo in recovery playbooks 2022-11-14 23:10:16 -08:00
IamTheFij 0d47a1f8c8 Tune PhotoPrism 2022-11-11 16:21:47 -08:00
IamTheFij bb400a3f1c Add blocky metrics to grafana 2022-11-11 16:21:17 -08:00
IamTheFij da70aa74ca Add expose path for blocky /metrics
Not sure if it's needed
2022-11-11 13:47:27 -08:00
IamTheFij a30749f357 Switch services to module based Terraform template from levant
This ends up with a better experience in dealing with tfstate for some
services. Not sure why.
2022-11-11 13:47:26 -08:00
IamTheFij 49c8a73ac9 Store loki data on ephemeral disk 2022-11-11 13:24:54 -08:00
IamTheFij 3bfc1f61ac Update levant service template with some defaults 2022-11-11 13:24:28 -08:00
IamTheFij af32c9e2e5 Put grafana bootstrap secrets in secrets location 2022-11-10 13:39:12 -08:00
IamTheFij 3077e66e70 Limit all existing services to websecure entrypoint
This will be a bigger issue if exposing a public entrypoint.
2022-11-10 13:37:50 -08:00
IamTheFij 29110eaf47 Update Nomad UI links to Consul and Vault 2022-11-10 13:37:50 -08:00
IamTheFij 5b0f50140c Update playbook that clears nomad and consul data 2022-11-10 13:37:50 -08:00
IamTheFij 068dc73717 Add photoprism 2022-11-10 13:37:50 -08:00
IamTheFij e9f2fae609 Switch mysql to mariadb 2022-11-10 13:37:50 -08:00
IamTheFij 77c2fcc96b Add db bootstrap and more template options to services template 2022-11-10 10:20:53 -08:00
IamTheFij 2bde762902 Use journald for Nomad Docker logging so they can be ingested into Loki 2022-11-10 10:19:51 -08:00
IamTheFij 92fd139263 Update nomad version to 1.4.2 2022-11-10 10:19:22 -08:00
IamTheFij ae5f4d61fb Make consul raft v3 explicit 2022-11-10 10:19:07 -08:00
IamTheFij e707f2a790 Add pi4 host back 2022-11-10 10:18:37 -08:00
IamTheFij 539b9073cb Update permissions for mysql volume 2022-11-10 10:18:28 -08:00
IamTheFij 0e0d2bbcc6 Fix ansible_galaxy target references 2022-11-10 10:18:02 -08:00
IamTheFij 51a0b71bd8 Update recovery scripts 2022-11-10 10:15:30 -08:00
IamTheFij 1fad6b691c Update some metrics 2022-11-07 20:50:18 -08:00
IamTheFij f9d46faae9 Rename backup jobs to be lower case 2022-11-04 09:39:08 -07:00
IamTheFij 931e2b6e00 Make sure grafana mysql dump happens after dir is created 2022-11-03 22:18:04 -07:00
IamTheFij 9d56f6226c Try to reduce concurrent ftp 2022-11-03 22:17:48 -07:00
IamTheFij 39bd8aafaf Move backup jobs to local dir
Makes debugging easier
2022-11-03 22:17:26 -07:00
IamTheFij 779db10abc Add middleware support to service levant template 2022-11-03 15:10:51 -07:00
IamTheFij 5bbb6c494c Fix some formatting 2022-11-03 15:10:09 -07:00
IamTheFij 695567b8aa Add backup job for grafana 2022-11-02 20:30:04 -07:00
IamTheFij 2d63d7e74d Add sqlite backup for lldap 2022-11-02 20:14:25 -07:00
IamTheFij 37e80980bc Refactor ansible to clean root dir 2022-11-02 14:20:09 -07:00
IamTheFij 0996cfbf67 Update hooks 2022-11-02 12:59:32 -07:00
IamTheFij a203067400 Migrate pre-commits from parent repo up to this one 2022-11-02 11:26:52 -07:00
IamTheFij 1a1f243354 Remove query for nomad-clients on backup task
Not used anymore
2022-10-31 15:27:29 -07:00
IamTheFij 73b99f80f5 Go back to hard coded node names for `for_each`
For some reason this worked until it didn't
2022-10-31 15:24:14 -07:00
IamTheFij 25e1dc6566 Rename backup service fixing mysql access 2022-10-31 15:23:42 -07:00
IamTheFij dcfe0510a7 Fix sonarr dl folder 2022-10-31 11:32:09 -07:00
IamTheFij 9f241109c5 Refactor external service definitions 2022-10-28 12:42:28 -07:00
IamTheFij eae80ece7a Increase sonarr memory
Wow, this uses a lot of mem
2022-10-28 12:34:44 -07:00
IamTheFij 45c597b040 Big refactor to split core and services for better ordering 2022-10-27 14:28:34 -07:00
IamTheFij 253069439d Better first run bootstrap 2022-09-27 21:59:37 -07:00
IamTheFij 75bce82b0f Fix typo 2022-09-27 21:29:00 -07:00
IamTheFij b8cfc361fa New playbook to reset server data 2022-09-27 21:28:37 -07:00
IamTheFij 91e64f23eb Improve first run cluster setup 2022-09-27 21:28:02 -07:00
IamTheFij 16f7eaa844 Remove some hosts 2022-09-27 21:27:34 -07:00
IamTheFij 109113048f Add local loopback as dns in resolv.conf 2022-09-26 16:52:59 -07:00
IamTheFij 66bd276626 Increase priority of stateful jobs 2022-09-26 16:40:25 -07:00
IamTheFij 8ab6c2c3e0 Add playbook to recover consul using peers 2022-09-16 16:46:37 -07:00
IamTheFij 567e2d88e4 Add another pi 2022-09-16 16:46:10 -07:00
IamTheFij a80927e5b0 Enable preemption on nomad scheduler 2022-09-16 16:45:26 -07:00
IamTheFij 9d739eef7e Make Nomad media-read volume point to rw nfs 2022-09-16 16:44:40 -07:00
IamTheFij 12c273f440 Remove bootstrapping values from setup playbook
This will be done in another playbook
2022-09-16 16:43:45 -07:00
IamTheFij 5bd4e3716d Bootstrap with 3 servers 2022-09-16 16:42:54 -07:00
IamTheFij cb46743043 Get ddclient working again
It was failing due to oom and using cloudflare api tokens which aren't yet supported
2022-09-07 12:20:58 -07:00
IamTheFij a9073aafd4 Wait until Nomad is running before bootstrapping ACLs 2022-09-07 11:11:10 -07:00
IamTheFij 912ad58ff0 Clean up comments in setup cluster playbook 2022-09-07 11:09:33 -07:00
IamTheFij d742234a36 Explicitly set envoy version
The default v1.23.0 does not work on arm64
2022-09-07 11:06:26 -07:00
IamTheFij ad22ce6e2a Update vault and nomad versions 2022-09-07 11:05:54 -07:00
IamTheFij 141b53b809 Add playbook to unseal Vault 2022-09-07 11:05:27 -07:00
IamTheFij cc8e6faf36 Make vault load balancer sticky
Assets like css and js were not proxying correctly. I think it may be
because they were proxying to a different instance and that the paths
are dynamic. This should route subsequent requests for the session to
a single backend.
2022-09-06 17:17:14 -07:00
IamTheFij c8b9063b3e Add homeassistant external 2022-09-06 17:15:43 -07:00
IamTheFij f44e62fd1c add nomad login 2022-09-06 14:47:06 -07:00
IamTheFij 478f9c4c8f Update security todos and reference node IP for consul queries 2022-09-06 14:46:49 -07:00
IamTheFij ab1c9b41cd Move redis data to ephemeral disk 2022-09-06 11:31:15 -07:00
IamTheFij 712fd3958d Move prometheus tsdb data to emphemeral disk 2022-09-06 11:15:14 -07:00
IamTheFij 7296494141 Move acme certs to /local so they will persit between allocs 2022-09-06 09:45:04 -07:00
IamTheFij 8e827ef4ad Add splay to blocky template render
Avoid all instances going down at once when the template canges
2022-09-05 12:57:13 -07:00
IamTheFij 1be855e571 Fix syslog proxy
Apparently traefik only supports http proxy over connect.

https://github.com/traefik/traefik/issues/7803
2022-09-04 20:21:02 -07:00
IamTheFij 34a5ce8141 Use nomad as sole metrics exporter
Drops cadvisor and node_exporter since Nomad seems to export what I need.
2022-09-04 14:32:24 -07:00
IamTheFij bc4657463e Remove default volume read_only
It was always setting to true
2022-09-04 14:27:28 -07:00
IamTheFij aaae5d087e Update nfs volumes to try and fix permissions 2022-09-04 14:27:27 -07:00
IamTheFij 5c3b60329d Try to use default netowrk source for proxing syslogng 2022-09-04 14:27:27 -07:00
IamTheFij 9fe5393a40 Add Traefik proxy for Syslogng 2022-09-04 14:27:07 -07:00
IamTheFij 7f4995817b Use default arch maps where possible 2022-08-30 16:15:12 -07:00
IamTheFij 3bab881118 Update services template to support env and host volumes
Also adds sonarr as an example
2022-08-30 15:16:08 -07:00
IamTheFij 9ce1350b5f Use nomad token to look up policies 2022-08-30 15:15:29 -07:00
IamTheFij 226eb6cb05 Have nomad talk to vault over loopback 2022-08-30 15:15:10 -07:00
IamTheFij 3d3cc25951 Add new nfs volumes 2022-08-30 15:14:55 -07:00
IamTheFij 0ea02c2034 Multiarch install tweaks for arm64 2022-08-30 15:14:39 -07:00
IamTheFij 0ad777c76f Fix unsealing of single vault instance
Checking status of only one node meant that if that node was sealed
we would not try to unseal other nodes
2022-08-30 15:14:00 -07:00
IamTheFij 929501b72c Enable consul autopilot 2022-08-30 15:12:52 -07:00
IamTheFij 429854897f Update nomad, consul, vault versions 2022-08-30 15:12:35 -07:00
IamTheFij 551df5f0c5 Use newer cadvisor 2022-08-30 15:11:52 -07:00
IamTheFij f73a4b13ec Use updated ansible-nomad role
Has better support for multi-arch installs and fixes cni
2022-08-30 15:10:16 -07:00
IamTheFij f9a9a37f6d Add pi4 host 2022-08-30 15:09:48 -07:00
IamTheFij e96a7501dd Rename nomad anon policy file 2022-08-23 10:31:29 -07:00
IamTheFij c62a0118a5 WIP: Allow specifying https endpoints and fetching nomad token 2022-08-23 09:57:57 -07:00
IamTheFij 599dd02bdc Add mysql database storage to Grafana 2022-07-29 13:02:22 -07:00
IamTheFij afa6984001 Add Nomad dashboard to grafana 2022-07-29 13:01:59 -07:00
IamTheFij eb0b16abbe Don't deploy Nextcloud 2022-07-29 13:01:40 -07:00
IamTheFij c0afa52edc Stop duplicate nomad scraping
Already getting it from Client service
2022-07-29 13:01:22 -07:00
IamTheFij 0e0ff7bbac Increase promtail memory 2022-07-28 16:37:19 -07:00
IamTheFij 994c2f4743 Make traefik a service rather than a system job
Sets it up to support auto_revert and auto_promote
2022-07-28 15:11:59 -07:00
IamTheFij 795b683046 Traefik wildcard certs 2022-07-28 15:11:24 -07:00
IamTheFij 8af70181f3 Remove variable for consul_address for traefik
Now getting from Noamd environment
2022-07-28 15:10:39 -07:00
IamTheFij e3633f9961 Make lldap backup daily 2022-07-28 15:05:00 -07:00
IamTheFij c5538bb623 conditional dns lookups for router assigned domains 2022-07-27 22:04:46 -07:00
IamTheFij b9ef67b925 Working backup and restore 2022-07-27 22:04:22 -07:00
IamTheFij a5fd1942de Make traefik disk ephemeral and sticky 2022-07-27 17:30:35 -07:00
IamTheFij c0f64c9c8a Bump Traefik mem limit
We don't like this crashing
2022-07-27 17:26:13 -07:00
IamTheFij 7d27dbb7f9 Skip dump of lldap db 2022-07-27 17:25:41 -07:00
IamTheFij 73d193d0a5 Add lldap backup and templatize backup job
Now oneoff and system jobs are all using the same template
2022-07-27 17:02:29 -07:00
IamTheFij 0c6f82e93b Increase prometheus memory limit 2022-07-27 16:11:56 -07:00
IamTheFij 6c732800e6 Add lldap 2022-07-27 15:57:28 -07:00
IamTheFij eaa81ddc8a Remove set hostname because that's now done in bootstrap 2022-07-27 15:57:12 -07:00
IamTheFij c111427052 Extend ttl for nomad tokens 2022-07-27 15:56:40 -07:00
IamTheFij 5e1d1de521 Add ddclient 2022-07-27 14:45:08 -07:00
IamTheFij b996e745ec Clean up services template whitespace 2022-07-27 14:41:42 -07:00
IamTheFij 09f11dcd85 Add vault stanza to levant services 2022-07-27 14:41:13 -07:00
IamTheFij c17a3c950a Add further todos for Nomad Vault 2022-07-27 13:40:21 -07:00
IamTheFij 64a9302276 Update Nomad and Vault ACLs
Now nomad is read only and tokens can be retrieved from Vault
2022-07-27 13:13:11 -07:00
IamTheFij 5e4ca8efda Reduce memory for blocky sidecar 2022-07-27 11:22:02 -07:00
IamTheFij f762cb55f8 Hide blocky API from non-traefik route 2022-07-27 11:21:11 -07:00
IamTheFij a8e5be2162 Get letsencrypt certs working with Traefik 2022-07-27 11:12:08 -07:00
IamTheFij 5e1b679cbb Fix consul value bootstrap and hide secrets in log 2022-07-27 11:11:03 -07:00
IamTheFij 594609db64 Add basic auth to traefik 2022-07-26 21:48:16 -07:00
IamTheFij 7554509671 Make anonymous nomad read only 2022-07-26 20:20:43 -07:00
IamTheFij c21ed2fa3f Add userpass login to Vault 2022-07-26 20:09:52 -07:00
IamTheFij 7356b8d407 Make metrics more readable 2022-07-25 21:45:01 -07:00
IamTheFij 2625f6dcb1 Reduce task memory 2022-07-25 16:37:51 -07:00
IamTheFij aa6db53047 Fix mysql 2022-07-25 16:29:43 -07:00
IamTheFij 56b7ea8a9c WIP: Update oneoff backups 2022-07-25 16:29:35 -07:00
IamTheFij 7acca6d160 Fix consul backup 2022-07-25 16:29:06 -07:00
IamTheFij dcfe43f63d Move traefik connect intents to core 2022-07-25 15:54:23 -07:00
IamTheFij caa84a5340 Allow bypass of healthcheck 2022-07-25 15:52:47 -07:00
IamTheFij a8fe9bfff8 Get mysql root from vault 2022-07-25 15:52:47 -07:00
IamTheFij b300c220b6 Tweak memory requirements for tasks 2022-07-25 15:52:47 -07:00
IamTheFij 459481e8f7 Add test consul backup 2022-07-25 15:52:47 -07:00
IamTheFij 11e89de947 Clean up Grafana and Loki bootstraps 2022-07-25 15:52:47 -07:00
IamTheFij 349f7b930b Remove packer stuff 2022-07-25 15:49:07 -07:00
IamTheFij 2ed2056766 Update lockfile 2022-07-25 15:40:54 -07:00
IamTheFij 1142c0f53f Add new playbook and make target for bootstrapping values to Consul and Vault 2022-07-25 15:40:22 -07:00
IamTheFij 3a9ae20a6b Update playbook, move acls and comment for fixes
There are some items that I found are broken on first run and made some changes
2022-07-25 11:48:03 -07:00
IamTheFij b86c57d75d Make acls module stand alone 2022-07-25 11:48:03 -07:00
IamTheFij d5a0ec6828 Shorten pip installs 2022-07-25 11:48:03 -07:00
IamTheFij 18f7cebfc2 Add vault kv creation 2022-07-25 11:14:51 -07:00
IamTheFij 6988e19014 Add loki, promtail, and syslog-ng 2022-07-25 10:46:16 -07:00
IamTheFij 816d6b7097 Add sticky disk to service template 2022-07-25 10:44:37 -07:00
IamTheFij 1e35958044 Promethus: Use env for consul address rather than variable 2022-07-25 10:38:48 -07:00
IamTheFij 1c02e69225 Move core services to new tf file
Precursor to moving to a module so it can be applied separately
2022-07-25 10:37:32 -07:00
IamTheFij 2a77067bdc WIP: Write a consul backup job 2022-07-21 20:24:50 -07:00
IamTheFij 5b88413604 Add consul bootstrap and move vault to an example 2022-07-21 20:16:10 -07:00
IamTheFij 5165045ee9 Fix consul address in levant 2022-07-21 20:11:21 -07:00
IamTheFij 5583b2d38e Deploy Nomad, Consul, and Vault using apt repo 2022-07-21 19:04:44 -07:00
IamTheFij f460f890da Use vault for backups jobs 2022-07-21 19:03:40 -07:00
IamTheFij 29946a4df6 Major grafana refactor to include automatic loading of provisioning files 2022-07-21 15:54:05 -07:00
IamTheFij bde0b84d70 Go back to a single ingress node to simplify Traefik TLS
The open source version of Traefik doesn't natively support HA. Running
multiple instances means that the TLS certificates will have to be
managed outside of Traefik and distributed to running jobs via Vault and
Nomad. This is doable, but I've decided to reduce the scope for now to
simplify things and go to a single Ingress node so that Traefik cert
management can be used.
2022-07-21 15:50:13 -07:00
IamTheFij 52c7e3d326 More nextcloud config using Vault 2022-07-08 16:26:26 -07:00
IamTheFij 726b634092 Create levant tf module
Also a template service Nomad job that can be used for some straighforward services
2022-07-08 16:24:03 -07:00
IamTheFij 54f98e740f Ignore ansible_collections 2022-06-28 12:11:55 -07:00
IamTheFij b9736aba83 Add example secrets 2022-06-28 12:11:24 -07:00
IamTheFij 50dafc6b3e Fix secrets access from nomad tasks
Probably can be cleaned up and updated to follow least access
2022-06-28 12:11:07 -07:00
IamTheFij 723b5fab78 Improve vault bootstrap and nomad connection 2022-06-28 12:10:18 -07:00
IamTheFij 1dad4d22a1 Bootstrap vault secrets 2022-06-28 12:09:57 -07:00
IamTheFij ff4e473a89 Small improvement to consul kv role 2022-06-28 12:08:23 -07:00
IamTheFij 8434c22fd2 Add missing role requirements file
This uses updated fork of ansible-consul
2022-06-23 20:13:17 -07:00
IamTheFij 46ee046f6c Deploy traefik one at a time with autorevert 2022-06-23 20:12:30 -07:00
IamTheFij 609944df8e Install consul dns forwarding 2022-06-23 20:12:09 -07:00
IamTheFij ab58652932 Install consul from repo 2022-06-23 20:11:48 -07:00
IamTheFij b8b74e900b Make blocky config a bit more stable by removing templating based on whami 2022-06-23 20:11:28 -07:00
IamTheFij 7760d3387e Fix blocky upstream tcp for quad9 2022-06-23 20:11:09 -07:00
IamTheFij 0ea91e7ffc Auto revert broken blocky
Also enable traefik
2022-06-23 20:10:36 -07:00
IamTheFij eb129be95e Add Consul lookup for ads dns allowlist 2022-06-23 13:36:06 -07:00
IamTheFij 2f28748579 Add some more upstream dns options
Should pick one later
2022-06-23 13:34:08 -07:00
IamTheFij 710e901ab6 Increase priority of Traefik 2022-06-23 09:51:42 -07:00
IamTheFij 67631eb1a0 Update Nomad 2022-06-23 09:51:21 -07:00
IamTheFij dfa95ee454 Generate blocky host mapping from Consul kv 2022-06-23 09:51:09 -07:00
IamTheFij ca6e766a40 Update blocky one instance at a time
Avoids dns going down with all instances updating at once
2022-06-23 09:50:23 -07:00
IamTheFij d022fe9bc4 Deploy backup jobs to all hosts and dynamically determine jobs per node 2022-06-23 09:49:57 -07:00
IamTheFij 325a27a4ec Remove csi deployment 2022-06-23 09:49:03 -07:00
IamTheFij 37c4ab4c25 Move databases to a single module 2022-06-23 09:48:01 -07:00
IamTheFij 37c6fd4735 Make traefik a system service
For this to work, will need to put TLS certs in Vault
2022-06-17 15:20:43 -07:00
IamTheFij b6a9c80748 Add base hostname to consul in Playbook 2022-06-17 15:19:43 -07:00
IamTheFij 2f65105592 WIP: Add democratic-csi storage plugin 2022-06-17 15:19:19 -07:00
IamTheFij 18dbc89b2a Make nextcloud backup a non-sidecar task
Avoids restarting whole group when if it fails
2022-06-17 15:16:45 -07:00
IamTheFij 3cf69503ea Remove some unecessary traefik configs from tasks 2022-06-17 15:15:37 -07:00
IamTheFij 1f111bcd04 Make order of host configs match playbook order 2022-06-17 15:14:55 -07:00
IamTheFij e518288308 Use new host name in terraform consul address 2022-05-24 20:11:57 -07:00
IamTheFij 40e3562195 Use new token variable name after bootstrap 2022-05-24 20:11:41 -07:00
IamTheFij f544a54631 Add autopilot 2022-05-24 20:11:18 -07:00
IamTheFij e57fcfcfdb Add docker install 2022-05-24 20:11:07 -07:00
IamTheFij 423c8f23c5 Auto initialize vault 2022-05-24 20:10:47 -07:00
IamTheFij 2f95257325 Wait until mysql is deployed before continuing
Otherwise dependent jobs will fail and take up time restarting
2022-05-24 20:10:26 -07:00
IamTheFij c09af9936a Remove unused playbook 2022-05-24 20:09:45 -07:00
IamTheFij 321d60dc1f Switch to a 3 node cluster for better resiliance 2022-05-24 20:09:22 -07:00
IamTheFij a07f37ff1b Fix venv detection for ansible cluster target
This fixes the installation of the consul python library
2022-05-24 20:07:52 -07:00
IamTheFij faef7f3734 Make redis optional for blocky to help with resliliance to a single host failing 2022-05-19 16:54:16 -07:00
IamTheFij 8a606cbe05 Dynamically add dns routes to traefik instances to blocky 2022-05-19 16:53:56 -07:00
IamTheFij d39c82762e Add dedicated backup module and jobs
Possible alternative to backups deployed with each job
2022-05-18 14:23:46 -07:00
IamTheFij a3d9c40f46 Fix prom scraping 2022-05-18 14:22:52 -07:00
IamTheFij 18c5b006e8 Add smarttv block list to default on blocky 2022-05-18 14:22:35 -07:00
IamTheFij e71c534fcf Default nomad cluster to ansible 2022-05-18 14:22:21 -07:00
IamTheFij 9a360c91b2 nomad: Run block on all hosts 2022-05-18 11:29:00 -07:00
IamTheFij b6145a54a0 WIP: Vault db 2022-05-12 19:27:52 -07:00
IamTheFij 07ff8e57b8 Bind mysql to loopback 2022-05-09 21:45:08 -07:00
IamTheFij 7d587d59a1 Add prom ports to nextcloud backup 2022-05-09 21:44:26 -07:00
IamTheFij fc583abace Use consul http port in traefik 2022-04-15 12:25:15 -07:00
IamTheFij aeb662d799 Build traefik static config better when services aren't found 2022-04-15 12:13:00 -07:00
IamTheFij 456485aa5e no log for some more sensitive info 2022-04-15 12:12:28 -07:00
IamTheFij 126cd6743f WIP nomad vault db integration 2022-04-15 12:12:15 -07:00
IamTheFij 3c7df5fa54 Add nextcloud backup job 2022-04-15 12:11:41 -07:00
IamTheFij 5acb814abd Lint, format, lock 2022-04-13 14:02:42 -07:00
IamTheFij 036fe9c525 remove useless blank line 2022-04-05 09:44:40 -07:00
IamTheFij a1217250dc Create a lot more host volumes
Some are NFS volumes and present on all devices
2022-04-04 22:20:19 -07:00
IamTheFij 18635aad2d Fix nomad vault policies 2022-04-04 22:19:32 -07:00
IamTheFij f8f7cf2dc2 Add Nomad ACL bootstrap 2022-03-23 16:08:18 -07:00
156 changed files with 16849 additions and 2264 deletions

52
.gitignore vendored
View File

@ -1,3 +1,53 @@
roles/
# ---> Terraform
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sentitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
#
*.tfvars
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
#
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
# ---> Ansible
*.retry
ansible_galaxy/ansible_collections/
ansible_galaxy/roles/
# Repo specific
venv/
ca/
# Non-public bootstrap values
vault-keys.json
nomad_bootstrap.json
consul_values.yml
vault_hashi_vault_values.yml
vault_*.yml
ansible_playbooks/vars/nomad_vars.yml

34
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,34 @@
---
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.76.0
hooks:
- id: terraform_fmt
- id: terraform_validate
args:
- --tf-init-args=-lockfile=readonly
- id: terraform_tflint
args:
- --args=--config=__GIT_WORKING_DIR__/.tflint.hcl
- id: terraform_tfsec
# - id: terraform_providers_lock
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
hooks:
- id: check-added-large-files
- id: check-merge-conflict
- id: end-of-file-fixer
exclude: "^ansible_playbooks/vars/nomad_vars.sample.yml$"
- id: trailing-whitespace
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
hooks:
- id: detect-secrets
args: ['--baseline', '.secrets-baseline']
- repo: local
hooks:
- id: variable-sample
name: generate variable sample file
language: system
entry: bash -c 'venv/bin/python scripts/nomad_vars.py print > ./ansible_playbooks/vars/nomad_vars.sample.yml'
types: [file]

191
.secrets-baseline Normal file
View File

@ -0,0 +1,191 @@
{
"version": "1.4.0",
"plugins_used": [
{
"name": "ArtifactoryDetector"
},
{
"name": "AWSKeyDetector"
},
{
"name": "AzureStorageKeyDetector"
},
{
"name": "Base64HighEntropyString",
"limit": 4.5
},
{
"name": "BasicAuthDetector"
},
{
"name": "CloudantDetector"
},
{
"name": "DiscordBotTokenDetector"
},
{
"name": "GitHubTokenDetector"
},
{
"name": "HexHighEntropyString",
"limit": 3.0
},
{
"name": "IbmCloudIamDetector"
},
{
"name": "IbmCosHmacDetector"
},
{
"name": "JwtTokenDetector"
},
{
"name": "KeywordDetector",
"keyword_exclude": ""
},
{
"name": "MailchimpDetector"
},
{
"name": "NpmDetector"
},
{
"name": "PrivateKeyDetector"
},
{
"name": "SendGridDetector"
},
{
"name": "SlackDetector"
},
{
"name": "SoftlayerDetector"
},
{
"name": "SquareOAuthDetector"
},
{
"name": "StripeDetector"
},
{
"name": "TwilioKeyDetector"
}
],
"filters_used": [
{
"path": "detect_secrets.filters.allowlist.is_line_allowlisted"
},
{
"path": "detect_secrets.filters.common.is_baseline_file",
"filename": ".secrets-baseline"
},
{
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
"min_level": 2
},
{
"path": "detect_secrets.filters.heuristic.is_indirect_reference"
},
{
"path": "detect_secrets.filters.heuristic.is_likely_id_string"
},
{
"path": "detect_secrets.filters.heuristic.is_lock_file"
},
{
"path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
},
{
"path": "detect_secrets.filters.heuristic.is_potential_uuid"
},
{
"path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
},
{
"path": "detect_secrets.filters.heuristic.is_sequential_string"
},
{
"path": "detect_secrets.filters.heuristic.is_swagger_file"
},
{
"path": "detect_secrets.filters.heuristic.is_templated_secret"
},
{
"path": "detect_secrets.filters.regex.should_exclude_secret",
"pattern": [
"(\\${.*}|from_env|fake|!secret|VALUE)"
]
}
],
"results": {
"core/authelia.yml": [
{
"type": "Secret Keyword",
"filename": "core/authelia.yml",
"hashed_secret": "7cb6efb98ba5972a9b5090dc2e517fe14d12cb04",
"is_verified": false,
"line_number": 54,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "core/authelia.yml",
"hashed_secret": "a32b08d97b1615dc27f58b6b17f67624c04e2c4f",
"is_verified": false,
"line_number": 189,
"is_secret": false
}
],
"core/grafana/grafana.ini": [
{
"type": "Basic Auth Credentials",
"filename": "core/grafana/grafana.ini",
"hashed_secret": "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4",
"is_verified": false,
"line_number": 78,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "core/grafana/grafana.ini",
"hashed_secret": "55ebda65c08313526e7ba08ad733e5ebea9900bd",
"is_verified": false,
"line_number": 109,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "core/grafana/grafana.ini",
"hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997",
"is_verified": false,
"line_number": 151,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "core/grafana/grafana.ini",
"hashed_secret": "10bea62ff1e1a7540dc7a6bc10f5fa992349023f",
"is_verified": false,
"line_number": 154,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "core/grafana/grafana.ini",
"hashed_secret": "5718bce97710e6be87ea160b36eaefb5032857d3",
"is_verified": false,
"line_number": 239,
"is_secret": false
},
{
"type": "Secret Keyword",
"filename": "core/grafana/grafana.ini",
"hashed_secret": "10aed9d7ebef778a9b3033dba3f7813b639e0d50",
"is_verified": false,
"line_number": 252,
"is_secret": false
}
]
},
"generated_at": "2024-02-20T18:04:29Z"
}

View File

@ -1,38 +1,40 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/consul" {
version = "2.14.0"
provider "registry.terraform.io/hashicorp/nomad" {
version = "2.2.0"
hashes = [
"h1:xRwktNwLL3Vo43F7v73tfcgbcnjCE2KgCzcNrsQJ1cc=",
"zh:06dcca1f76b839af8f86c7b6f65b944003a7a35b30b865b3884f48e2c42f9aee",
"zh:16111df6a485e21cee6ca33cb863434baa1ca360c819c8e2af85e465c1361d2b",
"zh:26b59c82ac2861b2651c1fa31955c3e7790e3c2d5d097f22aa34d3c294da63cf",
"zh:70fd6853099126a602d5ac26caa80214a4a8a38f0cad8a5e3b7bef49923419d3",
"zh:7d4f0061d6fb86e0a5639ed02381063b868245082ec4e3a461bcda964ed00fcc",
"zh:a48cbf57d6511922362d5b0f76f449fba7a550c9d0702635fabb43b4f0a09fc0",
"zh:bb54994a53dd8e1ff84ca50742ce893863dc166fd41b91d951f4cb89fe6a6bc0",
"zh:bc61b19ee3c8d55a9915a3ad84203c87bfd0d57eca8eec788524b14e8b67f090",
"zh:cbe3238e756ada23c1e7c97c42a5c72bf810dc5bd1265c9f074c3e739d1090b0",
"zh:e30198054239eab46493e59956b9cd8c376c3bbd9515ac102a96d1fbd32e423f",
"zh:e74365dba529a0676107e413986d7be81c2125c197754ce69e3e89d8daa53153",
"h1:BAjqzVkuXxHtRKG+l9unaZJPk2kWZpSTCEcQPRcl2so=",
"zh:052f909d25121e93dc799290216292fca67943ccde12ba515068b838a6ff8c66",
"zh:20e29aeb9989f7a1e04bb4093817c7acc4e1e737bb21a3066f3ea46f2001feff",
"zh:2326d101ef427599b72cce30c0e0c1d18ae783f1a897c20f2319fbf54bab0a61",
"zh:3420cbe4fd19cdc96d715d0ae8e79c272608023a76033bbf582c30637f6d570f",
"zh:41ec570f87f578f1c57655e2e4fbdb9932d94cf92dc9cd11828cccedf36dd4a4",
"zh:5f90dcc58e3356ffead82ea211ecb4a2d7094d3c2fbd14ff85527c3652a595a2",
"zh:64aaa48609d2db868fcfd347490df0e12c6c3fcb8e4f12908c5d52b1a0adf73f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:86b4923e10e6ba407d1d2aab83740b702058e8b01460af4f5f0e4008f40e492c",
"zh:ae89dcba33097af33a306344d20e4e25181f15dcc1a860b42db5b7199a97c6a6",
"zh:ce56d68cdfba60891765e94f9c0bf69eddb985d44d97db9f91874bea027f08e2",
"zh:e993bcde5dbddaedf3331e3014ffab904f98ab0f5e8b5d6082b7ca5083e0a2f1",
]
}
provider "registry.terraform.io/hashicorp/nomad" {
version = "1.4.16"
provider "registry.terraform.io/hashicorp/random" {
version = "3.6.0"
hashes = [
"h1:tyfjD/maKzb0RxxD9KWgLnkJu9lnYziYsQgGw85Giz8=",
"zh:0d4fbb7030d9caac3b123e60afa44f50c83cc2a983e1866aec7f30414abe7b0e",
"zh:0db080228e07c72d6d8ca8c45249d6f97cd0189fce82a77abbdcd49a52e57572",
"zh:0df88393271078533a217654b96f0672c60eb59570d72e6aefcb839eea87a7a0",
"zh:2883b335bb6044b0db6a00e602d6926c047c7f330294a73a90d089f98b24d084",
"zh:390158d928009a041b3a182bdd82376b50530805ae92be2b84ed7c3b0fa902a0",
"zh:7169b8f8df4b8e9659c49043848fd5f7f8473d0471f67815e8b04980f827f5ef",
"zh:9417ee1383b1edd137024882d7035be4dca51fb4f725ca00ed87729086ec1755",
"zh:a22910b5a29eeab5610350700b4899267c1b09b66cf21f7e4d06afc61d425800",
"zh:a6185c9cd7aa458cd81861058ba568b6411fbac344373a20155e20256f4a7557",
"zh:b6260ca9f034df1b47905b4e2a9c33b67dbf77224a694d5b10fb09ae92ffad4c",
"zh:d87c12a6a7768f2b6c2a59495c7dc00f9ecc52b1b868331d4c284f791e278a1e",
"h1:R5Ucn26riKIEijcsiOMBR3uOAjuOMfI1x7XvH4P6B1w=",
"zh:03360ed3ecd31e8c5dac9c95fe0858be50f3e9a0d0c654b5e504109c2159287d",
"zh:1c67ac51254ba2a2bb53a25e8ae7e4d076103483f55f39b426ec55e47d1fe211",
"zh:24a17bba7f6d679538ff51b3a2f378cedadede97af8a1db7dad4fd8d6d50f829",
"zh:30ffb297ffd1633175d6545d37c2217e2cef9545a6e03946e514c59c0859b77d",
"zh:454ce4b3dbc73e6775f2f6605d45cee6e16c3872a2e66a2c97993d6e5cbd7055",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:91df0a9fab329aff2ff4cf26797592eb7a3a90b4a0c04d64ce186654e0cc6e17",
"zh:aa57384b85622a9f7bfb5d4512ca88e61f22a9cea9f30febaa4c98c68ff0dc21",
"zh:c4a3e329ba786ffb6f2b694e1fd41d413a7010f3a53c20b432325a94fa71e839",
"zh:e2699bc9116447f96c53d55f2a00570f982e6f9935038c3810603572693712d0",
"zh:e747c0fd5d7684e5bfad8aa0ca441903f15ae7a98a737ff6aca24ba223207e2c",
"zh:f1ca75f417ce490368f047b63ec09fd003711ae48487fba90b4aba2ccf71920e",
]
}

7
.tflint.hcl Normal file
View File

@ -0,0 +1,7 @@
rule "terraform_required_version" {
enabled = false
}
rule "terraform_required_providers" {
enabled = false
}

153
Makefile
View File

@ -1,80 +1,105 @@
SERVER ?= "192.168.2.41"
SSH_USER = iamthefij
SSH_KEY = ~/.ssh/id_ed25519
SLEEP_FOR ?= 10
VENV ?= venv
.PHONY: rm-nomad
rm-nomad:
hashi-up nomad uninstall \
--ssh-target-addr $(SERVER) \
--ssh-target-key $(SSH_KEY) \
--ssh-target-user $(SSH_USER) \
--ssh-target-sudo-pass $(SSH_TARGET_SUDO_PASS)
.PHONY: sleep
sleep:
sleep $(SLEEP_FOR)
.PHONY: nomad-up
nomad-up:
hashi-up nomad install \
--ssh-target-addr $(SERVER) \
--ssh-target-key $(SSH_KEY) \
--ssh-target-user $(SSH_USER) \
--ssh-target-sudo-pass $(SSH_TARGET_SUDO_PASS) \
--server --client
hashi-up nomad start \
--ssh-target-addr $(SERVER) \
--ssh-target-key $(SSH_KEY) \
--ssh-target-user $(SSH_USER) \
--ssh-target-sudo-pass $(SSH_TARGET_SUDO_PASS)
.PHONY: default
default: check
.PHONY: rm-consul
rm-consul:
hashi-up consul uninstall \
--ssh-target-addr $(SERVER) \
--ssh-target-key $(SSH_KEY) \
--ssh-target-user $(SSH_USER) \
--ssh-target-sudo-pass $(SSH_TARGET_SUDO_PASS)
.PHONY: consul-up
consul-up:
hashi-up consul install \
--ssh-target-addr $(SERVER) \
--ssh-target-key $(SSH_KEY) \
--ssh-target-user $(SSH_USER) \
--ssh-target-sudo-pass $(SSH_TARGET_SUDO_PASS) \
--advertise-addr $(SERVER) \
--client-addr 0.0.0.0 \
--http-addr 0.0.0.0 \
--connect \
--server
hashi-up consul start \
--ssh-target-addr $(SERVER) \
--ssh-target-key $(SSH_KEY) \
--ssh-target-user $(SSH_USER) \
--ssh-target-sudo-pass $(SSH_TARGET_SUDO_PASS)
.PHONY: all
all: cluster bootstrap-values apply
.PHONY: cluster
cluster: consul-up nomad-up
cluster: ansible-cluster
venv/bin/ansible:
python3 -m venv venv
./venv/bin/pip install ansible
./venv/bin/pip install python-consul
# Ensures virtualenv is present
$(VENV):
python3 -m venv $(VENV)
$(VENV)/bin/pip install -r requirements.txt
# Installs pre-commit hooks
.PHONY: install-hooks
install-hooks: $(VENV)
$(VENV)/bin/pre-commit install --install-hooks
# Checks files for encryption
.PHONY: check
check: $(VENV)
$(VENV)/bin/pre-commit run --all-files
# Creates a new secrets baseline
.secrets-baseline: $(VENV) Makefile
$(VENV)/bin/detect-secrets scan --exclude-secrets '(\$${.*}|from_env|fake|!secret|VALUE)' > .secrets-baseline
# Audits secrets against baseline
.PHONY: secrets-audit
secrets-audit: $(VENV) .secrets-baseline
$(VENV)/bin/detect-secrets audit .secrets-baseline
# Updates secrets baseline
.PHONY: secrets-update
secrets-update: $(VENV) .secrets-baseline
$(VENV)/bin/detect-secrets scan --baseline .secrets-baseline
.PHONY: ansible_galaxy
ansible_galaxy: ansible_galaxy/ansible_collections ansible_galaxy/roles
ansible_galaxy/ansible_collections: $(VENV) ./ansible_galaxy/requirements.yml
$(VENV)/bin/ansible-galaxy collection install -p ./ansible_galaxy -r ./ansible_galaxy/requirements.yml
ansible_galaxy/roles: $(VENV) ./ansible_galaxy/requirements.yml
$(VENV)/bin/ansible-galaxy install -p ./ansible_galaxy/roles -r ./ansible_galaxy/requirements.yml
.PHONY: ansible-cluster
ansible-cluster: venv/bin/ansible
./venv/bin/ansible-galaxy install -p roles -r roles/requirements.yml
./venv/bin/ansible-playbook -K -vv \
-e "@vault-keys.json" \
-i ansible_hosts.yml -M ./roles ./setup-cluster.yml
ansible-cluster: $(VENV) ansible_galaxy
env VIRTUAL_ENV=$(VENV) $(VENV)/bin/ansible-playbook -K -vv \
$(shell test -f vault-keys.json && echo '-e "@vault-keys.json"') \
./ansible_playbooks/setup-cluster.yml
.PHONY: bootstrap-values
bootstrap-values: $(VENV)
env NOMAD_ADDR=http://192.168.2.101:4646 \
NOMAD_TOKEN=$(shell jq -r .SecretID nomad_bootstrap.json) \
$(VENV)/bin/python ./scripts/nomad_vars.py
.PHONY: recover-nomad
recover-nomad: $(VENV)
$(VENV)/bin/ansible-playbook -K ./ansible_playbooks/recover-nomad.yaml
.PHONY: stop-cluster
stop-cluster: $(VENV)
$(VENV)/bin/ansible-playbook -K ./ansible_playbooks/stop-cluster.yml
.PHONY: init
init:
@terraform init
.PHONY: plan
plan:
terraform plan
@terraform plan \
-var "nomad_secret_id=$(shell jq -r .SecretID nomad_bootstrap.json)" \
.PHONY: apply
apply:
terraform apply
@terraform apply \
-auto-approve \
-var "nomad_secret_id=$(shell jq -r .SecretID nomad_bootstrap.json)" \
# Install CNI on hosts?
# curl -L -o cni-plugins.tgz "https://github.com/containernetworking/plugins/releases/download/v1.0.0/cni-plugins-linux-$( [ $(uname -m) = aarch64 ] && echo arm64 || echo amd64)"-v1.0.0.tgz
# sudo mkdir -p /opt/cni/bin
# sudo tar -C /opt/cni/bin -xzf cni-plugins.tgz
.PHONY: refresh
refresh:
@terraform refresh \
-var "nomad_secret_id=$(shell jq -r .SecretID nomad_bootstrap.json)" \
.PHONY: destroy
destroy:
@terraform destroy \
-var "nomad_secret_id=$(shell jq -r .SecretID nomad_bootstrap.json)" \
.PHONY: clean
clean:
env VIRTUAL_ENV=$(VENV) $(VENV)/bin/ansible-playbook -vv \
./ansible_playbooks/clear-data.yml
find -name "*.tfstate" -exec rm '{}' \;
rm -f ./vault-keys.json ./nomad_bootstrap.json

46
README.md Normal file
View File

@ -0,0 +1,46 @@
# Homelab Nomad
My configuration for creating my home Nomad cluster and deploying services to it.
This repo is not designed as general purpose templates, but rather to fit my specific needs. That said, I have made an effort for things to be as useful as possible for someone wanting to use or modify this.
## Running
make all
## Design
Both Ansible and Terraform are used as part of this configuration. All hosts must be reachable over SSH prior to running any of this configuration.
To begin, Ansible runs a playbook to setup the cluster. This includes installing Nomad, bootstrapping the cluster and ACLs, setting up NFS shares, creating Nomad Host Volumes, and setting up Wesher as a Wireguard mesh between hosts.
After this is complete, Nomad variables must be set for services to access and configure correctly. This depends on variables to be set based on the sample file.
Finally, the Terraform configuration can be applied setting up all services deployed on the cluster.
The configuration of new services is intended to be as templated as possible and to avoid requiring changes in multiple places. For example, most services are configured with a template that provides reverse proxy, DNS records, database tunnels, database bootstrapping, metrics scraping, and authentication. The only real exception is backups, which requires a distinct job file, for now.
## What does it do?
* Nomad cluster for scheduling and configuring all services
* Blocky DNS servers with integrated ad blocking. This also provides service discovery
* Prometheus with autodiscovery of service metrics
* Loki and Promtail aggregating logs
* Minitor for service availability checks
* Grafana providing dashboards, alerting, and log searching
* Photoprism for photo management
* Remote and shared volumes over NFS
* Authelia for OIDC and Proxy based authentication with 2FA
* Sonarr and Lidarr for multimedia management
* Automated block based backups using Restic
## Step by step
1. Update hosts in `ansible_playbooks/ansible_hosts.yml`
2. Update `ansible_playbook/setup-cluster.yml`
1. Update backup DNS server
2. Update NFS shares from NAS
3. Update volumes to make sure they are valid paths
3. Create `ansible_playbooks/vars/nomad_vars.yml` based on the sample file. TODO: This is quite specific and probably impossible without more documentation
4. Run `make all`
5. Update your network DNS settings to use the new servers IP addresses

21
acls/.terraform.lock.hcl Normal file
View File

@ -0,0 +1,21 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "1.4.20"
hashes = [
"h1:M/QVXHPfeySejJZI3I8mBYrL/J9VsbnyF/dKIMlUhXo=",
"zh:02989edcebe724fc0aa873b22176fd20074c4f46295e728010711a8fc5dfa72c",
"zh:089ba7d19bcf5c6bab3f8b8c5920eb6d78c52cf79bb0c5dfeb411c600e7efcba",
"zh:235865a2182ca372bcbf440201a8b8cc0715ad5dbc4de893d99b6f32b5be53ab",
"zh:67ea718764f3f344ecc6e027d20c1327b86353c8064aa90da3ec12cec4a88954",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:8c68c540f0df4980568bdd688c2adec86eda62eb2de154e3db215b16de0a7ae0",
"zh:911969c63a69a733be57b96d54c5966c9424e1abec8d5f20038c8cef3a504c65",
"zh:a673c92ddc9d47e8d53dcb9b376f1adcb4543488202fc83a3e7eab8677530684",
"zh:a94a73eae89fd8c8ebf872013079be41161d3f293f4026c92d45c4c5667dd613",
"zh:db6b89f8b696040c0344f00928e4cf6e0a75034421ba14cdcd8a4d23bc865dce",
"zh:e512c0b1239e3d66b60d22c2b4de19fea288e492cde90dff9277cc475fd9dbbf",
"zh:ef6eccecbdef3bb8ce629cabfb5550c1db5c3e952943dda1786ef6cb470a8c23",
]
}

View File

@ -0,0 +1,30 @@
namespace "*" {
policy = "write"
capabilities = ["alloc-node-exec"]
variables {
path "*" {
capabilities = ["write", "read", "destroy"]
}
}
}
agent {
policy = "write"
}
operator {
policy = "write"
}
quota {
policy = "write"
}
node {
policy = "write"
}
host_volume "*" {
policy = "write"
}

View File

@ -0,0 +1,23 @@
namespace "*" {
policy = "read"
}
agent {
policy = "read"
}
operator {
policy = "read"
}
quota {
policy = "read"
}
node {
policy = "read"
}
host_volume "*" {
policy = "read"
}

View File

@ -0,0 +1,4 @@
namespace "*" {
policy = "read"
capabilities = ["submit-job", "dispatch-job", "read-logs"]
}

18
acls/nomad_policies.tf Normal file
View File

@ -0,0 +1,18 @@
resource "nomad_acl_policy" "anon_policy" {
name = "anonymous"
description = "Anon RO"
rules_hcl = file("${path.module}/nomad-anon-policy.hcl")
}
resource "nomad_acl_policy" "admin" {
name = "admin"
description = "Admin RW for admins"
rules_hcl = file("${path.module}/nomad-admin-policy.hcl")
}
# TODO: (security) Limit this scope
resource "nomad_acl_policy" "deploy" {
name = "deploy"
description = "Write for job deployments"
rules_hcl = file("${path.module}/nomad-deploy-policy.hcl")
}

17
acls/nomad_roles.tf Normal file
View File

@ -0,0 +1,17 @@
resource "nomad_acl_role" "admin" {
name = "admin"
description = "Nomad administrators"
policy {
name = nomad_acl_policy.admin.name
}
}
resource "nomad_acl_role" "deploy" {
name = "deploy"
description = "Authorized to conduct deployments and view logs"
policy {
name = nomad_acl_policy.deploy.name
}
}

6
acls/providers.tf Normal file
View File

@ -0,0 +1,6 @@
# Configure the Nomad provider
provider "nomad" {
address = var.nomad_address
secret_id = var.nomad_secret_id
region = "global"
}

11
acls/vars.tf Normal file
View File

@ -0,0 +1,11 @@
variable "nomad_secret_id" {
type = string
description = "Secret ID for ACL bootstrapped Nomad"
sensitive = true
default = ""
}
variable "nomad_address" {
type = string
default = "http://n1.thefij:4646"
}

7
ansible.cfg Normal file
View File

@ -0,0 +1,7 @@
[defaults]
inventory=ansible_playbooks/ansible_hosts.yml
collections_paths=ansible_galaxy
roles_path=ansible_galaxy/roles
[inventory]
enable_plugins=yaml

View File

@ -0,0 +1,21 @@
---
collections:
- name: community.hashi_vault
version: 3.0.0
roles:
- src: https://github.com/IamTheFij/ansible-consul.git
name: ansible-consul
scm: git
version: my-main
- src: https://github.com/IamTheFij/ansible-nomad.git
name: ansible-nomad
scm: git
version: my-main
- src: https://github.com/ansible-community/ansible-vault.git
name: ansible-vault
scm: git
version: master
# - src: maxhoesel.smallstep
# version: 0.4.10
- src: geerlingguy.docker
version: 4.2.2

View File

@ -1,33 +0,0 @@
---
all:
children:
servers:
hosts:
nomad0.thefij:
# consul_node_role: bootstrap
nomad_node_role: both
nomad_host_volumes:
- name: mysql-data
path: /srv/volumes/mysql-data
owner: "root"
group: "bin"
mode: "0755"
read_only: false
# consul_auto_encrypt:
# enabled: true
# dns_san: ["services.thefij"]
# ip_san: ["192.168.2.41", "127.0.0.1"]
# motionpi.thefij: {}
nomad1.thefij:
nomad_node_class: ingress
nomad_node_role: both
consul_instances:
children:
servers: {}
nomad_instances:
children:
servers: {}
vault_instances:
children:
servers: {}

View File

@ -0,0 +1,72 @@
---
all:
hosts:
n1.thefij:
nomad_node_class: ingress
nomad_reserved_memory: 1024
# nomad_meta:
# hw_transcode.device: /dev/dri
# hw_transcode.type: intel
nfs_mounts:
- src: 10.50.250.2:/srv/volumes
path: /srv/volumes/moxy
opts: proto=tcp,rw
nomad_unique_host_volumes:
- name: mysql-data
path: /srv/volumes/mysql
owner: "999"
group: "100"
mode: "0755"
read_only: false
- name: postgres-data
path: /srv/volumes/postgres
owner: "999"
group: "999"
mode: "0755"
read_only: false
n2.thefij:
nomad_node_class: ingress
nomad_reserved_memory: 1024
nfs_mounts:
- src: 10.50.250.2:/srv/volumes
path: /srv/volumes/moxy
opts: proto=tcp,rw
nomad_unique_host_volumes:
- name: nextcloud-data
path: /srv/volumes/nextcloud
owner: "root"
group: "bin"
mode: "0755"
read_only: false
pi4:
nomad_node_class: ingress
nomad_reserved_memory: 512
nomad_meta:
hw_transcode.device: /dev/video11
hw_transcode.type: raspberry
qnomad.thefij:
ansible_host: 192.168.2.234
nomad_reserved_memory: 1024
# This VM uses a non-standard interface
nomad_network_interface: ens3
nomad_instances:
vars:
nomad_network_interface: eth0
children:
nomad_servers: {}
nomad_clients: {}
nomad_servers:
hosts:
nonopi.thefij:
ansible_host: 192.168.2.170
n1.thefij: {}
n2.thefij: {}
pi4: {}
qnomad.thefij: {}
nomad_clients:
hosts:
n1.thefij: {}
n2.thefij: {}
pi4: {}
qnomad.thefij: {}

View File

@ -0,0 +1,43 @@
# Stops Nomad and clears all data from its ata dirs
---
- name: Delete Nomad data
hosts: nomad_instances
tasks:
- name: Stop nomad
systemd:
name: nomad
state: stopped
become: true
- name: Kill nomad
shell:
cmd: systemctl kill nomad
become: true
- name: Stop all containers
shell:
cmd: docker ps -a | awk '/^[0-9abcdef]/{print $1}' | xargs -r docker stop
become: true
- name: Remove all containers
shell:
cmd: docker ps -a | awk '/^[0-9abcdef]/{print $1}' | xargs -r docker rm
become: true
- name: Unmount secrets
shell:
cmd: mount | awk '/nomad/ {print $3}' | xargs -n1 -r umount
become: true
- name: Remove data dir
file:
path: /var/nomad
state: absent
become: true
- name: Remove data dir
file:
path: /opt/nomad/data
state: absent
become: true

View File

@ -0,0 +1,27 @@
- name: Restart nomad and wesher
hosts: nomad_instances
tasks:
- name: Stop Nomad
systemd:
name: nomad
state: stopped
become: true
- name: Restart wesher
systemd:
name: wesher
state: restarted
become: true
- name: Start Docker
systemd:
name: docker
state: started
become: true
- name: Start Nomad
systemd:
name: nomad
state: started
become: true

View File

@ -0,0 +1,49 @@
---
- name: Recover Nomad
hosts: nomad_servers
any_errors_fatal: true
tasks:
- name: Stop Nomad
systemd:
name: nomad
state: stopped
become: true
- name: Remount all shares
command: mount -a
become: true
- name: Get node-id
slurp:
src: /var/nomad/server/node-id
register: nomad_node_id
become: true
- name: Node Info
debug:
msg: |
node_id: {{ nomad_node_id.content | b64decode }}
address: {{ ansible_default_ipv4.address }}
- name: Save
copy:
dest: /var/nomad/server/raft/peers.json
# I used to have reject('equalto', inventory_hostname) in the loop, but I'm not sure if I should
content: |
[
{% for host in ansible_play_hosts -%}
{
"id": "{{ hostvars[host].nomad_node_id.content | b64decode }}",
"address": "{{ hostvars[host].ansible_default_ipv4.address }}:4647",
"non_voter": false
}{% if not loop.last %},{% endif %}
{% endfor -%}
]
become: true
- name: Restart Nomad
systemd:
name: nomad
state: restarted
become: true

View File

@ -0,0 +1,364 @@
---
- name: Update DNS for bootstrapping with non-Nomad host
hosts: nomad_instances
become: true
gather_facts: false
vars:
non_nomad_dns: 192.168.2.170
tasks:
- name: Add non-nomad bootstrap DNS
lineinfile:
dest: /etc/resolv.conf
create: true
line: "nameserver {{ non_nomad_dns }}"
- name: Install Docker
hosts: nomad_clients
become: true
vars:
docker_architecture_map:
x86_64: amd64
armv7l: armhf
aarch64: arm64
docker_apt_arch: "{{ docker_architecture_map[ansible_architecture] }}"
docker_compose_arch: "{{ (ansible_architecture == 'armv7l') | ternary('armv7', ansible_architecture) }}"
roles:
- geerlingguy.docker
tasks:
- name: Remove snapd
package:
name: snapd
state: absent
# Not on Ubuntu 20.04
# - name: Install Podman
# hosts: nomad_instances
# become: true
#
# tasks:
# - name: Install Podman
# package:
# name: podman
# state: present
- name: Create NFS mounts
hosts: nomad_clients
become: true
vars:
shared_nfs_mounts:
- src: 192.168.2.10:/Media
path: /srv/volumes/media-read
opts: proto=tcp,port=2049,ro
- src: 192.168.2.10:/Media
path: /srv/volumes/media-write
opts: proto=tcp,port=2049,rw
- src: 192.168.2.10:/Photos
path: /srv/volumes/photos
opts: proto=tcp,port=2049,rw
- src: 192.168.2.10:/Container
path: /srv/volumes/nas-container
opts: proto=tcp,port=2049,rw
tasks:
- name: Install nfs
package:
name: nfs-common
state: present
- name: Mount NFS volumes
ansible.posix.mount:
src: "{{ item.src }}"
path: "{{ item.path }}"
opts: "{{ item.opts }}"
state: mounted
fstype: nfs4
loop: "{{ shared_nfs_mounts + (nfs_mounts | default([])) }}"
- import_playbook: wesher.yml
- name: Build Nomad cluster
hosts: nomad_instances
any_errors_fatal: true
become: true
vars:
shared_host_volumes:
- name: media-read
path: /srv/volumes/media-write
read_only: true
- name: media-write
path: /srv/volumes/media-write
owner: "root"
group: "root"
mode: "0755"
read_only: false
- name: media-downloads
path: /srv/volumes/media-write/Downloads
read_only: false
- name: sabnzbd-config
path: /srv/volumes/media-write/Downloads/sabnzbd
read_only: false
- name: photoprism-media
path: /srv/volumes/photos/Photoprism
read_only: false
- name: photoprism-storage
path: /srv/volumes/nas-container/photoprism
read_only: false
- name: nzbget-config
path: /srv/volumes/nas-container/nzbget
read_only: false
- name: sonarr-config
path: /srv/volumes/nas-container/sonarr
read_only: false
- name: lidarr-config
path: /srv/volumes/nas-container/lidarr
read_only: false
- name: radarr-config
path: /srv/volumes/nas-container/radarr
read_only: false
- name: bazarr-config
path: /srv/volumes/nas-container/bazarr
read_only: false
- name: gitea-data
path: /srv/volumes/nas-container/gitea
read_only: false
- name: all-volumes
path: /srv/volumes
owner: "root"
group: "root"
mode: "0755"
read_only: false
roles:
- name: ansible-nomad
vars:
nomad_version: "1.7.6-1"
nomad_install_upgrade: true
nomad_allow_purge_config: true
nomad_node_role: "{% if 'nomad_clients' in group_names %}{% if 'nomad_servers' in group_names %}both{% else %}client{% endif %}{% else %}server{% endif %}"
# Where nomad gets installed to
nomad_bin_dir: /usr/bin
nomad_install_from_repo: true
nomad_bootstrap_expect: "{{ [(play_hosts | length), 3] | min }}"
nomad_raft_protocol: 3
nomad_autopilot: true
nomad_encrypt_enable: true
# nomad_use_consul: true
# Metrics
nomad_telemetry: true
nomad_telemetry_prometheus_metrics: true
nomad_telemetry_publish_allocation_metrics: true
nomad_telemetry_publish_node_metrics: true
# Enable container plugins
nomad_cni_enable: true
nomad_cni_version: 1.0.1
nomad_docker_enable: true
nomad_docker_dmsetup: false
# nomad_podman_enable: true
# Merge shared host volumes with node volumes
nomad_host_volumes: "{{ shared_host_volumes + (nomad_unique_host_volumes | default([])) }}"
# Customize docker plugin
nomad_plugins:
docker:
config:
allow_privileged: true
gc:
image_delay: "24h"
volumes:
enabled: true
selinuxlabel: "z"
# Send logs to journald so we can scrape them for Loki
# logging:
# type: journald
extra_labels:
- "job_name"
- "job_id"
- "task_group_name"
- "task_name"
- "namespace"
- "node_name"
- "node_id"
# Bind nomad
nomad_bind_address: 0.0.0.0
# Default interface for binding tasks
# This is now set at the inventory level
# nomad_network_interface: eth0
# Create networks for binding task ports
nomad_host_networks:
- name: loopback
interface: lo
reserved_ports: "22"
- name: wesher
interface: wgoverlay
reserved_ports: "22"
# Enable ACLs
nomad_acl_enabled: true
nomad_config_custom:
ui:
enabled: true
- name: Bootstrap Nomad ACLs and scheduler
hosts: nomad_servers
tasks:
- name: Start Nomad
systemd:
state: started
name: nomad
- name: Nomad API reachable?
uri:
url: "http://127.0.0.1:4646/v1/status/leader"
method: GET
status_code: 200
register: nomad_check_result
retries: 8
until: nomad_check_result is succeeded
delay: 15
changed_when: false
run_once: true
- name: Bootstrap ACLs
command:
argv:
- "nomad"
- "acl"
- "bootstrap"
- "-json"
run_once: true
ignore_errors: true
register: bootstrap_result
changed_when: bootstrap_result is succeeded
- name: Save bootstrap result
copy:
content: "{{ bootstrap_result.stdout }}"
dest: "../nomad_bootstrap.json"
when: bootstrap_result is succeeded
delegate_to: localhost
run_once: true
- name: Read secret
command:
argv:
- jq
- -r
- .SecretID
- ../nomad_bootstrap.json
delegate_to: localhost
run_once: true
no_log: true
changed_when: false
register: read_secretid
- name: Look for policy
command:
argv:
- nomad
- acl
- policy
- list
environment:
NOMAD_TOKEN: "{{ read_secretid.stdout }}"
register: policies
run_once: true
changed_when: false
- name: Copy policy
copy:
src: ../acls/nomad-anon-policy.hcl
dest: /tmp/anonymous.policy.hcl
delegate_to: "{{ play_hosts[0] }}"
run_once: true
register: anon_policy
- name: Create anon-policy
command:
argv:
- nomad
- acl
- policy
- apply
- -description="Anon read only"
- anonymous
- /tmp/anonymous.policy.hcl
environment:
NOMAD_TOKEN: "{{ read_secretid.stdout }}"
when: policies.stdout == "No policies found" or anon_policy.changed
delegate_to: "{{ play_hosts[0] }}"
run_once: true
- name: Read scheduler config
command:
argv:
- nomad
- operator
- scheduler
- get-config
- -json
run_once: true
register: scheduler_config
changed_when: false
- name: Enable service scheduler preemption
command:
argv:
- nomad
- operator
- scheduler
- set-config
- -preempt-service-scheduler=true
environment:
NOMAD_TOKEN: "{{ read_secretid.stdout }}"
run_once: true
when: (scheduler_config.stdout | from_json)["SchedulerConfig"]["PreemptionConfig"]["ServiceSchedulerEnabled"] is false
- name: Enable system scheduler preemption
command:
argv:
- nomad
- operator
- scheduler
- set-config
- -preempt-system-scheduler=true
environment:
NOMAD_TOKEN: "{{ read_secretid.stdout }}"
run_once: true
when: (scheduler_config.stdout | from_json)["SchedulerConfig"]["PreemptionConfig"]["SystemSchedulerEnabled"] is false
# - name: Set up Nomad backend and roles in Vault
# community.general.terraform:
# project_path: ../acls
# force_init: true
# variables:
# consul_address: "{{ play_hosts[0] }}:8500"
# vault_token: "{{ root_token }}"
# nomad_secret_id: "{{ read_secretid.stdout }}"
# delegate_to: localhost
# run_once: true
# notify:
# - Restart Nomad
handlers:
- name: Restart Nomad
systemd:
state: restarted
name: nomad
retries: 6
delay: 5

View File

@ -0,0 +1,10 @@
---
- name: Stop Nomad
hosts: nomad_instances
tasks:
- name: Stop Nomad
systemd:
name: nomad
state: stopped
become: true

View File

@ -0,0 +1,159 @@
nomad/jobs:
base_hostname: VALUE
db_user_ro: VALUE
ldap_base_dn: VALUE
notify_email: VALUE
nomad/jobs/authelia:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
email_sender: VALUE
jwt_secret: VALUE
oidc_clients: VALUE
oidc_hmac_secret: VALUE
oidc_issuer_certificate_chain: VALUE
oidc_issuer_private_key: VALUE
session_secret: VALUE
storage_encryption_key: VALUE
nomad/jobs/authelia/authelia/stunnel:
redis_stunnel_psk: VALUE
nomad/jobs/backup:
backup_passphrase: VALUE
nas_ftp_host: VALUE
nas_ftp_pass: VALUE
nas_ftp_user: VALUE
nas_minio_access_key_id: VALUE
nas_minio_secret_access_key: VALUE
nomad/jobs/backup-oneoff-n1:
backup_passphrase: VALUE
nas_ftp_host: VALUE
nas_ftp_pass: VALUE
nas_ftp_user: VALUE
nas_minio_access_key_id: VALUE
nas_minio_secret_access_key: VALUE
nomad/jobs/backup-oneoff-n2:
backup_passphrase: VALUE
nas_ftp_host: VALUE
nas_ftp_pass: VALUE
nas_ftp_user: VALUE
nas_minio_access_key_id: VALUE
nas_minio_secret_access_key: VALUE
nomad/jobs/backup-oneoff-pi4:
backup_passphrase: VALUE
nas_ftp_host: VALUE
nas_ftp_pass: VALUE
nas_ftp_user: VALUE
nas_minio_access_key_id: VALUE
nas_minio_secret_access_key: VALUE
nomad/jobs/bazarr:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
nomad/jobs/blocky:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
mappings: VALUE
whitelists_ads: VALUE
nomad/jobs/blocky/blocky/stunnel:
redis_stunnel_psk: VALUE
nomad/jobs/ddclient:
domain: VALUE
domain_ddclient: VALUE
zone: VALUE
nomad/jobs/diun:
slack_hook_url: VALUE
nomad/jobs/git:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
oidc_secret: VALUE
secret_key: VALUE
smtp_sender: VALUE
nomad/jobs/grafana:
admin_pw: VALUE
alert_email_addresses: VALUE
db_name: VALUE
db_pass: VALUE
db_pass_ro: VALUE
db_user: VALUE
db_user_ro: VALUE
minio_access_key: VALUE
minio_secret_key: VALUE
oidc_secret: VALUE
slack_bot_token: VALUE
slack_bot_url: VALUE
slack_hook_url: VALUE
smtp_password: VALUE
smtp_user: VALUE
nomad/jobs/immich:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
nomad/jobs/lego:
acme_email: VALUE
domain_lego_dns: VALUE
usersfile: VALUE
nomad/jobs/lidarr:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
nomad/jobs/lldap:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
jwt_secret: VALUE
key_seed: VALUE
smtp_from: VALUE
smtp_reply_to: VALUE
nomad/jobs/minitor:
mailgun_api_key: VALUE
nomad/jobs/mysql-server:
mysql_root_password: VALUE
nomad/jobs/photoprism:
admin_password: VALUE
admin_user: VALUE
db_name: VALUE
db_pass: VALUE
db_user: VALUE
nomad/jobs/postgres-server:
superuser: VALUE
superuser_pass: VALUE
nomad/jobs/radarr:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
nomad/jobs/redis-authelia:
allowed_psks: VALUE
nomad/jobs/redis-blocky:
allowed_psks: VALUE
nomad/jobs/rediscommander:
redis_stunnel_psk: VALUE
nomad/jobs/sonarr:
db_name: VALUE
db_pass: VALUE
db_user: VALUE
nomad/jobs/traefik:
external: VALUE
usersfile: VALUE
nomad/jobs/unifi-traffic-route-ips:
unifi_password: VALUE
unifi_username: VALUE
nomad/oidc:
secret: VALUE
secrets/ldap:
admin_email: VALUE
admin_password: VALUE
admin_user: VALUE
secrets/mysql:
mysql_root_password: VALUE
secrets/postgres:
superuser: VALUE
superuser_pass: VALUE
secrets/smtp:
password: VALUE
port: VALUE
server: VALUE
tls: VALUE
user: VALUE

View File

@ -0,0 +1,2 @@
---
wesher_key: "{{ vault_wesher_key }}"

View File

@ -0,0 +1,53 @@
- name: Create overlay network
hosts: nomad_instances
become: true
vars_files:
- vars/wesher_vars.yml
- vars/vault_wesher_vars.yml
vars:
wesher_key: "{{ wesher_key }}"
wesher_version: v0.2.6
wesher_arch_map:
x86_64: amd64
armv7l: arm
aarch64: arm64
wesher_arch: "{{ wesher_arch_map[ansible_architecture] }}"
# wesher_sha256_map:
# x86_64: 8c551ca211d7809246444765b5552a8d1742420c64eff5677d1e27a34c72aeef
# armv7l: 97f5bbf2b00b8b11a4ca224540bf9c1affdb15432c3b6ad8da4c1a7b6175eb5d
# aarch64: 507c6397d67ea90bddb3e1c06ec9d8e38d4342ed6f0f6b47855fecc9f1d6fae0
# wesher_checksum: sha256:{{ wesher_sha256_map[ansible_architecture] }}
wesher_checksum: sha256:https://github.com/costela/wesher/releases/download/{{ wesher_version }}/wesher.sha256sums
tasks:
- name: Download wesher
get_url:
url: https://github.com/costela/wesher/releases/download/{{ wesher_version }}/wesher-{{ wesher_arch }}
dest: /usr/local/sbin/wesher
checksum: "{{ wesher_checksum }}"
owner: root
mode: "0755"
- name: Install systemd unit
get_url:
url: https://github.com/costela/wesher/raw/{{ wesher_version }}/dist/wesher.service
dest: /etc/systemd/system/wesher.service
- name: Write wesher config
lineinfile:
path: /etc/default/wesher
create: true
regexp: "^{{ item.split('=')[0] }}"
line: "{{ item }}"
owner: root
mode: "0600"
loop:
- WESHER_CLUSTER_KEY={{ wesher_key }}
- WESHER_JOIN={% for host in ansible_play_hosts %}{{ hostvars[host].ansible_default_ipv4.address }}{% if not loop.last %},{% endif %}{% endfor %}
- name: Start wesher
systemd:
name: wesher.service
daemon_reload: true
state: started
enabled: true

View File

@ -0,0 +1,40 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "2.0.0"
hashes = [
"h1:lIHIxA6ZmfyTGL3J9YIddhxlfit4ipSS09BLxkwo6L0=",
"zh:09b897d64db293f9a904a4a0849b11ec1e3fff5c638f734d82ae36d8dc044b72",
"zh:435cc106799290f64078ec24b6c59cb32b33784d609088638ed32c6d12121199",
"zh:7073444bd064e8c4ec115ca7d9d7f030cc56795c0a83c27f6668bba519e6849a",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:79d238c35d650d2d83a439716182da63f3b2767e72e4cbd0b69cb13d9b1aebfc",
"zh:7ef5f49344278fe0bbc5447424e6aa5425ff1821d010d944a444d7fa2c751acf",
"zh:92179091638c8ba03feef371c4361a790190f9955caea1fa59de2055c701a251",
"zh:a8a34398851761368eb8e7c171f24e55efa6e9fdbb5c455f6dec34dc17f631bc",
"zh:b38fd5338625ebace5a4a94cea1a28b11bd91995d834e318f47587cfaf6ec599",
"zh:b71b273a2aca7ad5f1e07c767b25b5a888881ba9ca93b30044ccc39c2937f03c",
"zh:cd14357e520e0f09fb25badfb4f2ee37d7741afdc3ed47c7bcf54c1683772543",
"zh:e05e025f4bb95138c3c8a75c636e97cd7cfd2fc1525b0c8bd097db8c5f02df6e",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
hashes = [
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
"zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831",
"zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3",
"zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b",
"zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2",
"zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865",
"zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03",
"zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602",
"zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014",
]
}

242
backups/backup.nomad Normal file
View File

@ -0,0 +1,242 @@
job "backup%{ if batch_node != null }-oneoff-${batch_node}%{ endif }" {
datacenters = ["dc1"]
priority = 90
%{ if batch_node == null ~}
type = "system"
%{ else ~}
type = "batch"
parameterized {
meta_required = ["job_name"]
meta_optional = ["task", "snapshot"]
}
meta {
task = "backup"
snapshot = "latest"
}
%{ endif ~}
%{ if batch_node != null ~}
constraint {
attribute = "$${node.unique.name}"
value = "${batch_node}"
}
%{ endif ~}
group "backup" {
network {
mode = "bridge"
port "metrics" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
to = 8080
}
}
volume "all-volumes" {
type = "host"
read_only = false
source = "all-volumes"
}
ephemeral_disk {
# Try to keep restic cache intact
sticky = true
}
service {
name = "backup"
provider = "nomad"
port = "metrics"
tags = [
"prometheus.scrape"
]
}
task "backup" {
driver = "docker"
shutdown_delay = "5m"
volume_mount {
volume = "all-volumes"
destination = "/data"
read_only = false
}
config {
image = "iamthefij/resticscheduler:0.4.0"
ports = ["metrics"]
args = [
%{ if batch_node != null ~}
"-once",
"-$${NOMAD_META_task}",
"$${NOMAD_META_job_name}",
"--snapshot",
"$${NOMAD_META_snapshot}",
"--push-gateway",
"http://pushgateway.nomad:9091",
%{ endif ~}
"$${NOMAD_TASK_DIR}/node-jobs.hcl",
]
}
action "unlockenv" {
command = "sh"
args = ["-c", "/bin/resticscheduler -once -unlock all $${NOMAD_TASK_DIR}/node-jobs.hcl"]
}
action "unlocktmpl" {
command = "/bin/resticscheduler"
args = ["-once", "-unlock", "all", "{{ env 'NOMAD_TASK_DIR' }}/node-jobs.hcl"]
}
action "unlockhc" {
command = "/bin/resticscheduler"
args = ["-once", "-unlock", "all", "/local/node-jobs.hcl"]
}
env = {
RCLONE_CHECKERS = "2"
RCLONE_TRANSFERS = "2"
RCLONE_FTP_CONCURRENCY = "5"
RESTIC_CACHE_DIR = "$${NOMAD_ALLOC_DIR}/data"
TZ = "America/Los_Angeles"
}
template {
data = <<EOF
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
{{ with nomadVar "secrets/mysql" }}
MYSQL_USER=root
MYSQL_PASSWORD={{ .mysql_root_password }}
{{ end -}}
{{ with nomadVar "secrets/postgres" }}
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER={{ .superuser }}
POSTGRES_PASSWORD={{ .superuser_password }}
{{ end -}}
{{ with nomadVar (print "nomad/jobs/" (index (env "NOMAD_JOB_ID" | split "/") 0)) -}}
BACKUP_PASSPHRASE={{ .backup_passphrase }}
RCLONE_FTP_HOST={{ .nas_ftp_host }}
RCLONE_FTP_USER={{ .nas_ftp_user }}
RCLONE_FTP_PASS={{ .nas_ftp_pass.Value | toJSON }}
RCLONE_FTP_EXPLICIT_TLS=true
RCLONE_FTP_NO_CHECK_CERTIFICATE=true
AWS_ACCESS_KEY_ID={{ .nas_minio_access_key_id }}
AWS_SECRET_ACCESS_KEY={{ .nas_minio_secret_access_key }}
{{ end -}}
EOF
destination = "secrets/db.env"
env = true
}
template {
# Build jobs based on node
data = <<EOF
# Current node is {{ env "node.unique.name" }} {{ env "node.unique.id" }}
%{ for job_file in fileset(module_path, "jobs/*.hcl") ~}
{{ range nomadService 1 "backups" "${trimsuffix(basename(job_file), ".hcl")}" -}}
# ${trimsuffix(basename(job_file), ".hcl")} .Node {{ .Node }}
{{ if eq .Node (env "node.unique.id") -}}
${file("${module_path}/${job_file}")}
{{ end -}}
{{ end -}}
%{ endfor ~}
# Dummy job to keep task healthy on node without any stateful services
job "Dummy" {
schedule = "@daily"
config {
repo = "/local/dummy-repo"
passphrase = env("BACKUP_PASSPHRASE")
}
backup {
paths = ["/local/node-jobs.hcl"]
}
forget {
KeepLast = 1
}
}
EOF
destination = "local/node-jobs.hcl"
}
resources {
cpu = 50
memory = 500
}
}
task "stunnel" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = true
}
config {
image = "iamthefij/stunnel:latest"
args = ["$${NOMAD_TASK_DIR}/stunnel.conf"]
}
resources {
cpu = 100
memory = 100
}
template {
data = <<EOF
syslog = no
foreground = yes
delay = yes
[mysql_client]
client = yes
accept = 127.0.0.1:3306
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "mysql-tls" }}
connect = {{ .Address }}:{{ .Port }}
{{ end }}
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/mysql_stunnel_psk.txt
[postgres_client]
client = yes
accept = 127.0.0.1:5432
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "postgres-tls" }}
connect = {{ .Address }}:{{ .Port }}
{{ end }}
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/postgres_stunnel_psk.txt
EOF
destination = "$${NOMAD_TASK_DIR}/stunnel.conf"
}
template {
data = <<EOF
{{- with nomadVar "secrets/mysql/allowed_psks/backups" }}{{ .psk }}{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/mysql_stunnel_psk.txt"
}
template {
data = <<EOF
{{- with nomadVar "secrets/postgres/allowed_psks/backups" }}{{ .psk }}{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/postgres_stunnel_psk.txt"
}
}
}
}

136
backups/backups.tf Normal file
View File

@ -0,0 +1,136 @@
resource "nomad_job" "backup" {
jobspec = templatefile("${path.module}/backup.nomad", {
module_path = path.module,
batch_node = null,
use_wesher = var.use_wesher
})
}
resource "nomad_job" "backup-oneoff" {
# TODO: Get list of nomad hosts dynamically
for_each = toset(["n1", "n2", "pi4"])
# for_each = toset([
# for node in data.consul_service.nomad.service :
# node.node_name
# ])
jobspec = templatefile("${path.module}/backup.nomad", {
module_path = path.module,
batch_node = each.key,
use_wesher = var.use_wesher
})
}
locals {
# NOTE: This can't be dynamic in first deploy since these values are not known
# all_job_ids = toset(flatten([[for job in resource.nomad_job.backup-oneoff : job.id], [resource.nomad_job.backup.id]]))
all_job_ids = toset(["backup", "backup-oneoff-n1", "backup-oneoff-n2", "backup-oneoff-pi4"])
}
resource "nomad_acl_policy" "secrets_mysql" {
for_each = local.all_job_ids
name = "${each.key}-secrets-mysql"
description = "Give access to MySQL secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = each.key
}
}
resource "random_password" "mysql_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "mysql_psk" {
path = "secrets/mysql/allowed_psks/backups"
items = {
psk = "backups:${resource.random_password.mysql_psk.result}"
}
}
resource "nomad_acl_policy" "mysql_psk" {
for_each = local.all_job_ids
name = "${each.key}-secrets-mysql-psk"
description = "Give access to MySQL PSK secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql/allowed_psks/backups" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = each.key
group = "backup"
task = "stunnel"
}
}
resource "nomad_acl_policy" "secrets_postgres" {
for_each = local.all_job_ids
name = "${each.key}-secrets-postgres"
description = "Give access to Postgres secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/postgres" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = each.key
}
}
resource "random_password" "postgres_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "postgres_psk" {
path = "secrets/postgres/allowed_psks/backups"
items = {
psk = "backups:${resource.random_password.postgres_psk.result}"
}
}
resource "nomad_acl_policy" "postgres_psk" {
for_each = local.all_job_ids
name = "${each.key}-secrets-postgres-psk"
description = "Give access to Postgres PSK secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/postgres/allowed_psks/backups" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = each.key
group = "backup"
task = "stunnel"
}
}

54
backups/jobs/authelia.hcl Normal file
View File

@ -0,0 +1,54 @@
job "authelia" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/authelia"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Create local authelia dir" {
pre_script {
on_backup = "mkdir -p /local/authelia"
}
}
task "Backup database" {
mysql "Backup database" {
hostname = env("MYSQL_HOST")
port = env("MYSQL_PORT")
database = "authelia"
username = env("MYSQL_USER")
password = env("MYSQL_PASSWORD")
no_tablespaces = true
dump_to = "/local/authelia/dump.sql"
}
}
backup {
paths = ["/local/authelia"]
backup_opts {
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepHourly = 24
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

57
backups/jobs/git.hcl Normal file
View File

@ -0,0 +1,57 @@
job "git" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/gitea"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Create local gitea dir" {
pre_script {
on_backup = "mkdir -p /local/gitea"
}
}
task "Backup database" {
mysql "Backup database" {
hostname = env("MYSQL_HOST")
port = env("MYSQL_PORT")
database = "gitea"
username = env("MYSQL_USER")
password = env("MYSQL_PASSWORD")
no_tablespaces = true
dump_to = "/local/gitea/dump.sql"
}
}
backup {
paths = [
"/local/gitea",
"/data/nas-container/gitea",
]
backup_opts {
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepHourly = 24
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

54
backups/jobs/grafana.hcl Normal file
View File

@ -0,0 +1,54 @@
job "grafana" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/grafana"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Create local grafana dir" {
pre_script {
on_backup = "mkdir -p /local/grafana"
}
}
task "Backup database" {
mysql "Backup database" {
hostname = env("MYSQL_HOST")
port = env("MYSQL_PORT")
database = "grafana"
username = env("MYSQL_USER")
password = env("MYSQL_PASSWORD")
no_tablespaces = true
dump_to = "/local/grafana/dump.sql"
}
}
backup {
paths = ["/local/grafana"]
backup_opts {
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepHourly = 24
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

64
backups/jobs/lidarr.hcl Normal file
View File

@ -0,0 +1,64 @@
job "lidarr" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/lidarr"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Backup main database" {
postgres "Backup database" {
hostname = env("POSTGRES_HOST")
port = env("POSTGRES_PORT")
username = env("POSTGRES_USER")
password = env("POSTGRES_PASSWORD")
database = "lidarr"
no_tablespaces = true
dump_to = "/data/nas-container/lidarr/Backups/dump-lidarr.sql"
}
}
task "Backup logs database" {
postgres "Backup database" {
hostname = env("POSTGRES_HOST")
port = env("POSTGRES_PORT")
username = env("POSTGRES_USER")
password = env("POSTGRES_PASSWORD")
database = "lidarr-logs"
no_tablespaces = true
dump_to = "/data/nas-container/lidarr/Backups/dump-lidarr-logs.sql"
}
}
backup {
paths = ["/data/nas-container/lidarr"]
backup_opts {
Exclude = [
"lidarr_backup_*.zip",
"/data/nas-container/lidarr/MediaCover",
"/data/nas-container/lidarr/logs",
]
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

53
backups/jobs/lldap.hcl Normal file
View File

@ -0,0 +1,53 @@
job "lldap" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/lldap"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Create local backup dir" {
pre_script {
on_backup = "mkdir -p /local/lldap"
}
}
task "Backup database" {
mysql "Backup database" {
hostname = env("MYSQL_HOST")
port = env("MYSQL_PORT")
username = env("MYSQL_USER")
password = env("MYSQL_PASSWORD")
database = "lldap"
no_tablespaces = true
dump_to = "/local/lldap/dump.sql"
}
}
backup {
paths = ["/local/lldap"]
backup_opts {
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

41
backups/jobs/nzbget.hcl Normal file
View File

@ -0,0 +1,41 @@
job "nzbget" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/nzbget"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
backup {
paths = [
# Configuration
"/data/nas-container/nzbget",
# Queued nzb files
"/data/media-write/Downloads/nzb",
]
backup_opts {
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepHourly = 24
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

View File

@ -0,0 +1,60 @@
job "photoprism" {
schedule = "10 * * * *"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/photoprism"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Create local photoprism dir" {
pre_script {
on_backup = "mkdir -p /local/photoprism"
}
}
task "Dump database" {
mysql "Dump database" {
hostname = env("MYSQL_HOST")
port = env("MYSQL_PORT")
database = "photoprism"
username = env("MYSQL_USER")
password = env("MYSQL_PASSWORD")
no_tablespaces = true
dump_to = "/local/photoprism/dump.sql"
}
}
backup {
paths = [
"/local/photoprism",
"/data/nas-container/photoprism",
]
backup_opts {
Host = "nomad"
Exclude = [
"/data/nas-container/photoprism/cache",
]
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepHourly = 24
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

64
backups/jobs/radarr.hcl Normal file
View File

@ -0,0 +1,64 @@
job "radarr" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/radarr"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Backup main database" {
postgres "Backup database" {
hostname = env("POSTGRES_HOST")
port = env("POSTGRES_PORT")
username = env("POSTGRES_USER")
password = env("POSTGRES_PASSWORD")
database = "radarr"
no_tablespaces = true
dump_to = "/data/nas-container/radarr/Backups/dump-radarr.sql"
}
}
task "Backup logs database" {
postgres "Backup database" {
hostname = env("POSTGRES_HOST")
port = env("POSTGRES_PORT")
username = env("POSTGRES_USER")
password = env("POSTGRES_PASSWORD")
database = "radarr-logs"
no_tablespaces = true
dump_to = "/data/nas-container/radarr/Backups/dump-radarr-logs.sql"
}
}
backup {
paths = ["/data/nas-container/radarr"]
backup_opts {
Exclude = [
"radarr_backup_*.zip",
"/data/nas-container/radarr/MediaCover",
"/data/nas-container/radarr/logs",
]
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

36
backups/jobs/sabnzbd.hcl Normal file
View File

@ -0,0 +1,36 @@
job "sabnzbd" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/sabnzbd"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
backup {
paths = ["/data/media-write/Downloads/sabnzbd"]
backup_opts {
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepHourly = 24
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

67
backups/jobs/sonarr.hcl Normal file
View File

@ -0,0 +1,67 @@
job "sonarr" {
schedule = "@daily"
config {
repo = "s3://backups-minio.agnosticfront.thefij:8443/nomad/sonarr"
passphrase = env("BACKUP_PASSPHRASE")
options {
InsecureTls = true
}
}
task "Backup main database" {
postgres "Backup database" {
hostname = env("POSTGRES_HOST")
port = env("POSTGRES_PORT")
username = env("POSTGRES_USER")
password = env("POSTGRES_PASSWORD")
database = "sonarr"
no_tablespaces = true
dump_to = "/data/nas-container/sonarr/Backups/dump-sonarr.sql"
}
}
task "Backup logs database" {
postgres "Backup database" {
hostname = env("POSTGRES_HOST")
port = env("POSTGRES_PORT")
username = env("POSTGRES_USER")
password = env("POSTGRES_PASSWORD")
database = "sonarr-logs"
no_tablespaces = true
dump_to = "/data/nas-container/sonarr/Backups/dump-sonarr-logs.sql"
}
}
backup {
paths = ["/data/nas-container/sonarr"]
backup_opts {
Exclude = [
"sonarr_backup_*.zip",
"/data/nas-container/sonarr/MediaCover",
"/data/nas-container/sonarr/logs",
"*.db",
"*.db-shm",
"*.db-wal",
]
Host = "nomad"
}
restore_opts {
Host = ["nomad"]
# Because path is absolute
Target = "/"
}
}
forget {
KeepLast = 2
KeepDaily = 30
KeepWeekly = 8
KeepMonthly = 6
KeepYearly = 2
Prune = true
}
}

5
backups/vars.tf Normal file
View File

@ -0,0 +1,5 @@
variable "use_wesher" {
type = bool
description = "Indicates whether or not services should expose themselves on the wesher network"
default = true
}

View File

@ -1,107 +0,0 @@
variable "config_data" {
type = string
description = "Plain text config file for blocky"
}
job "blocky" {
datacenters = ["dc1"]
type = "service"
group "blocky" {
count = 1
network {
mode = "bridge"
port "dns" {
static = "53"
}
port "api" {
to = "4000"
}
}
service {
name = "blocky-dns"
port = "dns"
}
service {
name = "blocky-api"
port = "api"
meta {
metrics_addr = "${NOMAD_ADDR_api}"
}
connect {
sidecar_service {
proxy {
local_service_port = 400
expose {
path {
path = "/metrics"
protocol = "http"
local_path_port = 4000
listener_port = "api"
}
}
upstreams {
destination_name = "redis"
local_bind_port = 6379
}
}
}
sidecar_task {
resources {
cpu = 50
memory = 50
}
}
}
check {
name = "api-health"
port = "api"
type = "http"
path = "/"
interval = "10s"
timeout = "3s"
}
tags = [
"traefik.enable=true",
"traefik.http.routers.blocky-api.entryPoints=websecure",
]
}
task "main" {
driver = "docker"
config {
image = "ghcr.io/0xerr0r/blocky"
ports = ["dns", "api"]
mount {
type = "bind"
target = "/app/config.yml"
source = "app/config.yml"
}
}
resources {
cpu = 50
memory = 100
}
template {
data = var.config_data
destination = "app/config.yml"
}
}
}
}

View File

@ -1,28 +0,0 @@
variable "base_hostname" {
type = string
description = "Base hostname to serve content from"
default = "dev.homelab"
}
locals {
config_data = templatefile(
"${path.module}/config.yml",
{
"base_hostname" = "${var.base_hostname}",
# Could get this from consul_service.traefik
# but not sure what happens if it doens't exist yet
"ingress_address" = "192.168.2.106",
}
)
}
resource "nomad_job" "blocky" {
hcl2 {
enabled = true
vars = {
"config_data" = "${local.config_data}",
}
}
jobspec = file("${path.module}/blocky.nomad")
}

View File

@ -1,46 +0,0 @@
upstream:
default:
- 1.1.1.1
- 1.0.0.1
blocking:
blackLists:
ads:
- https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
- http://sysctl.org/cameleon/hosts
- https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
- https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
- https://hosts-file.net/ad_servers.txt
smarttv:
- https://perflyst.github.io/PiHoleBlocklist/SmartTV.txt
- https://perflyst.github.io/PiHoleBlocklist/regex.list
malware:
- https://mirror1.malwaredomains.com/files/justdomains
clientGroupsBlock:
default:
- ads
- malware
customDNS:
customTTL: 1h
mapping:
${base_hostname}: ${ingress_address}
prometheus:
enable: true
redis:
address: {{ env "NOMAD_UPSTREAM_ADDR_redis" }}
# password: passwd
# database: 2
required: true
# connectionAttempts: 10
# connectionCooldown: 3s
# queryLog:
# type: mysql
# target: db_user:db_password@tcp(db_host_or_ip:3306)/db_user?charset=utf8mb4&parseTime=True&loc=Local
# logRetentionDays: 7
port: 53
httpPort: 4000

40
core/.terraform.lock.hcl Normal file
View File

@ -0,0 +1,40 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "2.1.1"
hashes = [
"h1:liQBgBXfQEYmwpoGZUfSsu0U0t/nhvuRZbMhaMz7wcQ=",
"zh:28bc6922e8a21334568410760150d9d413d7b190d60b5f0b4aab2f4ef52efeeb",
"zh:2d4283740e92ce1403875486cd5ff2c8acf9df28c190873ab4d769ce37db10c1",
"zh:457e16d70075eae714a7df249d3ba42c2176f19b6750650152c56f33959028d9",
"zh:49ee88371e355c00971eefee6b5001392431b47b3e760a5c649dda76f59fb8fa",
"zh:614ad3bf07155ed8a5ced41dafb09042afbd1868490a379654b3e970def8e33d",
"zh:75be7199d76987e7549e1f27439922973d1bf27353b44a593bfbbc2e3b9f698f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:888e14a24410d56b37212fbea373a3e0401d0ff8f8e4f4dd00ba8b29de9fed39",
"zh:aa261925e8b152636a0886b3a2149864707632d836e98a88dacea6cfb6302082",
"zh:ac10cefb4064b3bb63d4b0379624a416a45acf778eac0004466f726ead686196",
"zh:b1a3c8b4d5b2dc9b510eac5e9e02665582862c24eb819ab74f44d3d880246d4f",
"zh:c552e2fe5670b6d3ad9a5faf78e3a27197eeedbe2b13928d2c491fa509bc47c7",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.6.0"
hashes = [
"h1:R5Ucn26riKIEijcsiOMBR3uOAjuOMfI1x7XvH4P6B1w=",
"zh:03360ed3ecd31e8c5dac9c95fe0858be50f3e9a0d0c654b5e504109c2159287d",
"zh:1c67ac51254ba2a2bb53a25e8ae7e4d076103483f55f39b426ec55e47d1fe211",
"zh:24a17bba7f6d679538ff51b3a2f378cedadede97af8a1db7dad4fd8d6d50f829",
"zh:30ffb297ffd1633175d6545d37c2217e2cef9545a6e03946e514c59c0859b77d",
"zh:454ce4b3dbc73e6775f2f6605d45cee6e16c3872a2e66a2c97993d6e5cbd7055",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:91df0a9fab329aff2ff4cf26797592eb7a3a90b4a0c04d64ce186654e0cc6e17",
"zh:aa57384b85622a9f7bfb5d4512ca88e61f22a9cea9f30febaa4c98c68ff0dc21",
"zh:c4a3e329ba786ffb6f2b694e1fd41d413a7010f3a53c20b432325a94fa71e839",
"zh:e2699bc9116447f96c53d55f2a00570f982e6f9935038c3810603572693712d0",
"zh:e747c0fd5d7684e5bfad8aa0ca441903f15ae7a98a737ff6aca24ba223207e2c",
"zh:f1ca75f417ce490368f047b63ec09fd003711ae48487fba90b4aba2ccf71920e",
]
}

185
core/authelia.tf Normal file
View File

@ -0,0 +1,185 @@
module "authelia" {
source = "../services/service"
name = "authelia"
instance_count = 2
priority = 70
image = "authelia/authelia:4.37"
args = ["--config", "$${NOMAD_TASK_DIR}/authelia.yml"]
ingress = true
service_port = 9999
service_port_static = true
use_wesher = var.use_wesher
# metrics_port = 9959
env = {
AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE = "$${NOMAD_SECRETS_DIR}/ldap_password.txt"
AUTHELIA_JWT_SECRET_FILE = "$${NOMAD_SECRETS_DIR}/jwt_secret.txt"
AUTHELIA_SESSION_SECRET_FILE = "$${NOMAD_SECRETS_DIR}/session_secret.txt"
AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE = "$${NOMAD_SECRETS_DIR}/storage_encryption_key.txt"
AUTHELIA_STORAGE_MYSQL_PASSWORD_FILE = "$${NOMAD_SECRETS_DIR}/mysql_password.txt"
AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE = "$${NOMAD_SECRETS_DIR}/smtp_password.txt"
AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE = "$${NOMAD_SECRETS_DIR}/oidc_hmac_secret.txt"
AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE = "$${NOMAD_SECRETS_DIR}/oidc_issuer_private_key.txt"
# AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_CERTIFICATE_CHAIN_FILE = "$${NOMAD_SECRETS_DIR}/oidc_issuer_certificate_chain.txt"
}
use_mysql = true
use_ldap = true
use_redis = true
use_smtp = true
mysql_bootstrap = {
enabled = true
}
service_tags = [
# Configure traefik to add this middleware
"traefik.http.middlewares.authelia.forwardAuth.address=http://authelia.nomad:$${NOMAD_PORT_main}/api/verify?rd=https%3A%2F%2Fauthelia.${var.base_hostname}%2F",
"traefik.http.middlewares.authelia.forwardAuth.trustForwardHeader=true",
"traefik.http.middlewares.authelia.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email",
"traefik.http.middlewares.authelia-basic.forwardAuth.address=http://authelia.nomad:$${NOMAD_PORT_main}/api/verify?auth=basic",
"traefik.http.middlewares.authelia-basic.forwardAuth.trustForwardHeader=true",
"traefik.http.middlewares.authelia-basic.forwardAuth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email",
]
templates = [
{
data = file("${path.module}/authelia.yml")
dest = "authelia.yml"
mount = false
},
{
data = "{{ with nomadVar \"secrets/ldap\" }}{{ .admin_password }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "ldap_password.txt"
mount = false
},
{
data = "{{ with nomadVar \"nomad/jobs/authelia\" }}{{ .jwt_secret }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "jwt_secret.txt"
mount = false
},
{
data = "{{ with nomadVar \"nomad/jobs/authelia\" }}{{ .session_secret }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "session_secret.txt"
mount = false
},
{
data = "{{ with nomadVar \"nomad/jobs/authelia\" }}{{ .storage_encryption_key }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "storage_encryption_key.txt"
mount = false
},
{
data = "{{ with nomadVar \"nomad/jobs/authelia\" }}{{ .db_pass }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "mysql_password.txt"
mount = false
},
{
data = "{{ with nomadVar \"nomad/jobs/authelia\" }}{{ .oidc_hmac_secret }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "oidc_hmac_secret.txt"
mount = false
},
{
data = "{{ with nomadVar \"nomad/jobs/authelia\" }}{{ .oidc_issuer_private_key }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "oidc_issuer_private_key.txt"
mount = false
},
{
data = "{{ with nomadVar \"nomad/jobs/authelia\" }}{{ .oidc_issuer_certificate_chain }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "oidc_issuer_certificate_chain.txt"
mount = false
},
{
data = "{{ with nomadVar \"secrets/smtp\" }}{{ .password }}{{ end }}"
dest_prefix = "$${NOMAD_SECRETS_DIR}"
dest = "smtp_password.txt"
mount = false
},
]
}
resource "nomad_acl_policy" "authelia" {
name = "authelia"
description = "Give access to shared authelia variables"
rules_hcl = <<EOH
namespace "default" {
variables {
path "authelia/*" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = module.authelia.job_id
}
}
# Give access to ldap secrets
resource "nomad_acl_policy" "authelia_ldap_secrets" {
name = "authelia-secrets-ldap"
description = "Give access to LDAP secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/ldap" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = module.authelia.job_id
}
}
resource "nomad_acl_auth_method" "nomad_authelia" {
name = "authelia"
type = "OIDC"
token_locality = "global"
max_token_ttl = "1h0m0s"
default = true
config {
oidc_discovery_url = "https://authelia.${var.base_hostname}"
oidc_client_id = "nomad"
oidc_client_secret = yamldecode(file("${path.module}/../ansible_playbooks/vars/nomad_vars.yml"))["nomad/oidc"]["secret"]
bound_audiences = ["nomad"]
oidc_scopes = [
"groups",
"openid",
]
allowed_redirect_uris = [
"https://nomad.${var.base_hostname}/oidc/callback",
"https://nomad.${var.base_hostname}/ui/settings/tokens",
]
list_claim_mappings = {
"groups" : "roles"
}
}
}
resource "nomad_acl_binding_rule" "nomad_authelia_admin" {
description = "engineering rule"
auth_method = nomad_acl_auth_method.nomad_authelia.name
selector = "\"nomad-deploy\" in list.roles"
bind_type = "role"
bind_name = "admin" # acls.nomad_acl_role.admin.name
}
resource "nomad_acl_binding_rule" "nomad_authelia_deploy" {
description = "engineering rule"
auth_method = nomad_acl_auth_method.nomad_authelia.name
selector = "\"nomad-deploy\" in list.roles"
bind_type = "role"
bind_name = "deploy" # acls.nomad_acl_role.deploy.name
}

252
core/authelia.yml Normal file
View File

@ -0,0 +1,252 @@
theme: auto
# jwt_secret: <file>
{{ with nomadVar "nomad/jobs" }}
default_redirection_url: https://authelia.{{ .base_hostname }}/
{{ end }}
## Set the default 2FA method for new users and for when a user has a preferred method configured that has been
## disabled. This setting must be a method that is enabled.
## Options are totp, webauthn, mobile_push.
default_2fa_method: ""
server:
host: 0.0.0.0
port: {{ env "NOMAD_PORT_main" }}
disable_healthcheck: false
log:
## Level of verbosity for logs: info, debug, trace.
level: debug
format: json
telemetry:
metrics:
enabled: false
# address: '0.0.0.0:{{ env "NOMAD_PORT_metrics" }}'
totp:
disable: false
issuer: {{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}
digits: 6
## The TOTP algorithm to use.
## It is CRITICAL you read the documentation before changing this option:
## https://www.authelia.com/c/totp#algorithm
algorithm: sha1
webauthn:
disable: false
timeout: 60s
display_name: {{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}
user_verification: preferred
duo_api:
disable: true
# hostname:
# integration_key:
# secret_key:
# enable_self_enrollment: false
authentication_backend:
disable_reset_password: false
## Password Reset Options.
password_reset:
## External reset password url that redirects the user to an external reset portal. This disables the internal reset
## functionality.
# TODO: not sure if this is needed, probably not?
custom_url: ""
refresh_interval: 5m
ldap:
implementation: custom
# stunnel url
url: ldap://127.0.0.1:389
timeout: 5s
# TODO: Maybe use stunnel for this
start_tls: false
base_dn: {{ with nomadVar "nomad/jobs" }}{{ .ldap_base_dn }}{{ end }}
additional_users_dn: ou=people
additional_groups_dn: ou=groups
username_attribute: uid
group_name_attribute: cn
mail_attribute: mail
display_name_attribute: displayName
# To allow sign in both with username and email, one can use a filter like
# (&(|({username_attribute}={input})({mail_attribute}={input}))(objectClass=person))
users_filter: "(&({username_attribute}={input})(objectClass=person))"
# Only supported filter by lldap right now
groups_filter: (member={dn})
## The username and password of the admin user.
{{ with nomadVar "secrets/ldap" }}
user: uid={{ .admin_user }},ou=people,{{ with nomadVar "nomad/jobs" }}{{ .ldap_base_dn }}{{ end }}
{{ end }}
# password set using secrets file
# password: <secret>
password_policy:
standard:
enabled: false
min_length: 8
max_length: 0
require_uppercase: true
require_lowercase: true
require_number: true
require_special: true
zxcvbn:
enabled: false
min_score: 3
##
## Access Control Configuration
##
## Access control is a list of rules defining the authorizations applied for one resource to users or group of users.
##
## If 'access_control' is not defined, ACL rules are disabled and the 'bypass' rule is applied, i.e., access is allowed
## to anyone. Otherwise restrictions follow the rules defined.
##
## Note: One can use the wildcard * to match any subdomain.
## It must stand at the beginning of the pattern. (example: *.mydomain.com)
##
## Note: You must put patterns containing wildcards between simple quotes for the YAML to be syntactically correct.
##
## Definition: A 'rule' is an object with the following keys: 'domain', 'subject', 'policy' and 'resources'.
##
## - 'domain' defines which domain or set of domains the rule applies to.
##
## - 'subject' defines the subject to apply authorizations to. This parameter is optional and matching any user if not
## provided. If provided, the parameter represents either a user or a group. It should be of the form
## 'user:<username>' or 'group:<groupname>'.
##
## - 'policy' is the policy to apply to resources. It must be either 'bypass', 'one_factor', 'two_factor' or 'deny'.
##
## - 'resources' is a list of regular expressions that matches a set of resources to apply the policy to. This parameter
## is optional and matches any resource if not provided.
##
## Note: the order of the rules is important. The first policy matching (domain, resource, subject) applies.
access_control:
## Default policy can either be 'bypass', 'one_factor', 'two_factor' or 'deny'. It is the policy applied to any
## resource if there is no policy to be applied to the user.
default_policy: deny
networks:
- name: internal
networks:
- 192.168.1.0/24
- 192.168.2.0/24
- 192.168.10.0/24
- name: VPN
networks: 192.168.5.0/24
rules:
{{ range nomadVarList "authelia/access_control/service_rules" }}{{ with nomadVar .Path }}
- domain: '{{ .name }}.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}'
{{ .rule.Value | indent 6 }}
{{ end }}{{ end }}
## Rules applied to everyone
- domain: '*.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}'
networks:
- internal
policy: one_factor
- domain: '*.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}'
policy: two_factor
- domain:
# TODO: Drive these from Nomad variables
- 'secure.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}'
policy: two_factor
session:
## The name of the session cookie.
name: authelia_session
domain: {{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}
# Stored in a secrets file
# secret: <in file>
expiration: 1h
inactivity: 5m
remember_me_duration: 1M
redis:
host: 127.0.0.1
port: 6379
# username: authelia
# password: authelia
# database_index: 0
maximum_active_connections: 8
minimum_idle_connections: 0
regulation:
max_retries: 3
find_time: 2m
ban_time: 5m
##
## Storage Provider Configuration
##
## The available providers are: `local`, `mysql`, `postgres`. You must use one and only one of these providers.
storage:
# encryption_key: <in file>
##
## MySQL / MariaDB (Storage Provider)
##
mysql:
host: 127.0.0.1
port: 3306
{{ with nomadVar "nomad/jobs/authelia" }}
database: {{ .db_name }}
username: {{ .db_user }}
# password: <in_file>
{{- end }}
timeout: 5s
##
## Notification Provider
##
## Notifications are sent to users when they require a password reset, a Webauthn registration or a TOTP registration.
## The available providers are: filesystem, smtp. You must use only one of these providers.
notifier:
## You can disable the notifier startup check by setting this to true.
disable_startup_check: false
{{ with nomadVar "secrets/smtp" }}
smtp:
host: {{ .server }}
port: {{ .port }}
username: {{ .user }}
# password: <in file>
{{- end }}
{{ with nomadVar "nomad/jobs/authelia" }}
sender: "{{ .email_sender }}"
## Subject configuration of the emails sent. {title} is replaced by the text from the notifier.
subject: "[Authelia] {title}"
## This address is used during the startup check to verify the email configuration is correct.
## It's not important what it is except if your email server only allows local delivery.
startup_check_address: test@iamthefij.com
{{- end }}
identity_providers:
oidc:
# hmac_secret: <file>
# issuer_private_key: <file>
clients: {{ with nomadVar "nomad/jobs/authelia" }}{{ .oidc_clients.Value }}{{ end }}

View File

@ -0,0 +1,40 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "2.0.0"
hashes = [
"h1:lIHIxA6ZmfyTGL3J9YIddhxlfit4ipSS09BLxkwo6L0=",
"zh:09b897d64db293f9a904a4a0849b11ec1e3fff5c638f734d82ae36d8dc044b72",
"zh:435cc106799290f64078ec24b6c59cb32b33784d609088638ed32c6d12121199",
"zh:7073444bd064e8c4ec115ca7d9d7f030cc56795c0a83c27f6668bba519e6849a",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:79d238c35d650d2d83a439716182da63f3b2767e72e4cbd0b69cb13d9b1aebfc",
"zh:7ef5f49344278fe0bbc5447424e6aa5425ff1821d010d944a444d7fa2c751acf",
"zh:92179091638c8ba03feef371c4361a790190f9955caea1fa59de2055c701a251",
"zh:a8a34398851761368eb8e7c171f24e55efa6e9fdbb5c455f6dec34dc17f631bc",
"zh:b38fd5338625ebace5a4a94cea1a28b11bd91995d834e318f47587cfaf6ec599",
"zh:b71b273a2aca7ad5f1e07c767b25b5a888881ba9ca93b30044ccc39c2937f03c",
"zh:cd14357e520e0f09fb25badfb4f2ee37d7741afdc3ed47c7bcf54c1683772543",
"zh:e05e025f4bb95138c3c8a75c636e97cd7cfd2fc1525b0c8bd097db8c5f02df6e",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
hashes = [
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
"zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831",
"zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3",
"zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b",
"zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2",
"zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865",
"zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03",
"zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602",
"zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014",
]
}

229
core/blocky/blocky.nomad Normal file
View File

@ -0,0 +1,229 @@
variable "config_data" {
type = string
description = "Plain text config file for blocky"
}
job "blocky" {
datacenters = ["dc1"]
type = "system"
priority = 100
update {
max_parallel = 1
# TODO: maybe switch to service job from system so we can use canary and autorollback
# auto_revert = true
}
group "blocky" {
network {
mode = "bridge"
port "dns" {
static = "53"
}
port "api" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
to = "4000"
}
dns {
# Set expclicit DNS servers because tasks, by default, use this task
servers = [
"192.168.2.1",
]
}
}
service {
name = "blocky-dns"
provider = "nomad"
port = "dns"
}
service {
name = "blocky-api"
provider = "nomad"
port = "api"
tags = [
"prometheus.scrape",
"traefik.enable=true",
"traefik.http.routers.blocky-api.entryPoints=websecure",
]
check {
name = "api-health"
port = "api"
type = "http"
path = "/"
interval = "10s"
timeout = "3s"
}
}
task "blocky" {
driver = "docker"
config {
image = "ghcr.io/0xerr0r/blocky:v0.22"
args = ["-c", "$${NOMAD_TASK_DIR}/config.yml"]
ports = ["dns", "api"]
}
resources {
cpu = 50
memory = 75
memory_max = 150
}
template {
data = var.config_data
destination = "$${NOMAD_TASK_DIR}/config.yml"
splay = "1m"
wait {
min = "10s"
max = "20s"
}
}
template {
data = <<EOF
{{ range nomadServices }}
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") .Name -}}
{{ .Address }} {{ .Name }}.nomad
{{- end }}
{{- end }}
EOF
destination = "$${NOMAD_TASK_DIR}/nomad.hosts"
change_mode = "noop"
wait {
min = "10s"
max = "20s"
}
}
}
task "stunnel" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = true
}
config {
image = "iamthefij/stunnel:latest"
args = ["$${NOMAD_TASK_DIR}/stunnel.conf"]
ports = ["tls"]
}
resources {
cpu = 20
memory = 100
}
template {
data = <<EOF
syslog = no
foreground = yes
delay = yes
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "mysql-tls" -}}
[mysql_client]
client = yes
accept = 127.0.0.1:3306
connect = {{ .Address }}:{{ .Port }}
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/mysql_stunnel_psk.txt
{{- end }}
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "redis-blocky" -}}
[redis_client]
client = yes
accept = 127.0.0.1:6379
connect = {{ .Address }}:{{ .Port }}
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/stunnel_psk.txt
{{- end }}
EOF
destination = "$${NOMAD_TASK_DIR}/stunnel.conf"
}
template {
data = <<EOF
{{- with nomadVar "secrets/mysql/allowed_psks/blocky" }}{{ .psk }}{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/mysql_stunnel_psk.txt"
}
template {
data = <<EOF
{{- with nomadVar "nomad/jobs/blocky/blocky/stunnel" -}}{{ .redis_stunnel_psk }}{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/stunnel_psk.txt"
}
}
task "mysql-bootstrap" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = false
}
config {
image = "mariadb:10"
args = [
"/usr/bin/timeout",
"2m",
"/bin/bash",
"-c",
"until /usr/bin/mysql --defaults-extra-file=$${NOMAD_SECRETS_DIR}/my.cnf < $${NOMAD_SECRETS_DIR}/bootstrap.sql; do sleep 10; done",
]
}
template {
data = <<EOF
[client]
host=127.0.0.1
port=3306
user=root
{{ with nomadVar "secrets/mysql" }}
password={{ .mysql_root_password }}
{{ end }}
EOF
destination = "$${NOMAD_SECRETS_DIR}/my.cnf"
}
template {
data = <<EOF
{{ with nomadVar "nomad/jobs/blocky" }}{{ if .db_name -}}
{{ $db_name := .db_name }}
CREATE DATABASE IF NOT EXISTS `{{ $db_name }}`;
CREATE USER IF NOT EXISTS '{{ .db_user }}'@'%' IDENTIFIED BY '{{ .db_pass }}';
GRANT ALL ON `{{ $db_name }}`.* to '{{ .db_user }}'@'%';
{{ with nomadService "grafana" }}{{ with nomadVar "nomad/jobs" -}}
-- Grant grafana read_only user access to db
GRANT SELECT ON `{{ $db_name }}`.* to '{{ .db_user_ro }}'@'%';
{{ end }}{{ end -}}
{{ else -}}
SELECT 'NOOP';
{{ end -}}{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/bootstrap.sql"
}
resources {
cpu = 50
memory = 50
}
}
}
}

68
core/blocky/blocky.tf Normal file
View File

@ -0,0 +1,68 @@
locals {
config_data = file("${path.module}/config.yml")
}
resource "nomad_job" "blocky" {
hcl2 {
vars = {
"config_data" = local.config_data,
}
}
jobspec = templatefile("${path.module}/blocky.nomad", {
use_wesher = var.use_wesher,
})
}
# Generate secrets and policies for access to MySQL
resource "nomad_acl_policy" "blocky_mysql_bootstrap_secrets" {
name = "blocky-secrets-mysql"
description = "Give access to MySQL secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = "blocky"
group = "blocky"
task = "mysql-bootstrap"
}
}
resource "random_password" "blocky_mysql_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "blocky_mysql_psk" {
path = "secrets/mysql/allowed_psks/blocky"
items = {
psk = "blocky:${resource.random_password.blocky_mysql_psk.result}"
}
}
resource "nomad_acl_policy" "blocky_mysql_psk" {
name = "blocky-secrets-mysql-psk"
description = "Give access to MySQL PSK secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql/allowed_psks/blocky" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = "blocky"
group = "blocky"
task = "stunnel"
}
}

148
core/blocky/config.yml Normal file
View File

@ -0,0 +1,148 @@
ports:
dns: 53
http: 4000
bootstrapDns:
- upstream: 1.1.1.1
- upstream: 1.0.0.1
- upstream: 9.9.9.9
- upstream: 149.112.112.112
upstreams:
groups:
default:
- https://dns.quad9.net/dns-query
- tcp-tls:dns.quad9.net
- https://one.one.one.one/dns-query
- tcp-tls:one.one.one.one
cloudflare:
- 1.1.1.1
- 1.0.0.1
- 2606:4700:4700::1111
- 2606:4700:4700::1001
- https://one.one.one.one/dns-query
- tcp-tls:one.one.one.one
quad9:
- 9.9.9.9
- 149.112.112.112
- 2620:fe::fe
- 2620:fe::9
- https://dns.quad9.net/dns-query
- tcp-tls:dns.quad9.net
quad9-secured:
- 9.9.9.11
- 149.112.112.11
- 2620:fe::11
- 2620:fe::fe:11
- https://dns11.quad9.net/dns-query
- tcp-tls:dns11.quad9.net
quad9-unsecured:
- 9.9.9.10
- 149.112.112.10
- 2620:fe::10
- 2620:fe::fe:10
- https://dns10.quad9.net/dns-query
- tcp-tls:dns10.quad9.net
conditional:
fallbackUpstream: false
mapping:
home.arpa: 192.168.2.1
in-addr.arpa: 192.168.2.1
iot: 192.168.2.1
local: 192.168.2.1
thefij: 192.168.2.1
.: 192.168.2.1
hostsFile:
sources:
- {{ env "NOMAD_TASK_DIR" }}/nomad.hosts
hostsTTL: 30s
loading:
refreshPeriod: 30s
clientLookup:
upstream: 192.168.2.1
blocking:
blackLists:
ads:
- https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
- http://sysctl.org/cameleon/hosts
- https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt
- https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt
# - https://hosts-file.net/ad_servers.txt
smarttv:
- https://perflyst.github.io/PiHoleBlocklist/SmartTV.txt
# - https://perflyst.github.io/PiHoleBlocklist/regex.list
wemo:
- |
# Remote commands
api.xbcs.net
# Firmware updates
fw.xbcs.net
# TURN service
nat.wemo2.com
# Connectivity checks
heartbeat.xwemo.com
antisocial:
- |
facebook.com
instagram.com
reddit.com
twitter.com
youtube.com
custom:
- https://git.thefij.rocks/iamthefij/blocklists/raw/branch/main/block
whiteLists:
ads:
{{ with nomadVar "nomad/jobs/blocky" -}}
{{ .whitelists_ads.Value | indent 6 }}
{{- end }}
custom:
- https://git.thefij.rocks/iamthefij/blocklists/raw/branch/main/allow
clientGroupsBlock:
default:
- ads
- custom
- smarttv
- wemo
customDNS:
customTTL: 1h
mapping:
{{ with nomadVar "nomad/jobs/blocky" }}{{ .mappings.Value | indent 4 }}{{ end }}
# Catch all at top domain to traefik
{{ with nomadService "traefik" -}}
{{- $last := len . | subtract 1 -}}
{{- $services := . -}}
{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}: {{ range $i := loop $last -}}
{{- with index $services $i }}{{ .Address }},{{ end -}}
{{- end -}}
{{- with index . $last }}{{ .Address }}{{ end -}}
{{- end }}
prometheus:
enable: true
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "redis-blocky" -}}
redis:
address: 127.0.0.1:6379
# password: ""
# database: 0
connectionAttempts: 10
connectionCooldown: 3s
{{ end -}}
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "mysql-tls" -}}
{{ with nomadVar "nomad/jobs/blocky" -}}
queryLog:
type: mysql
target: {{ .db_user }}:{{ .db_pass }}@tcp(127.0.0.1:3306)/{{ .db_name }}?charset=utf8mb4&parseTime=True&loc=Local
logRetentionDays: 14
{{ end -}}
{{ end -}}

5
core/blocky/vars.tf Normal file
View File

@ -0,0 +1,5 @@
variable "use_wesher" {
type = bool
description = "Indicates whether or not services should expose themselves on the wesher network"
default = true
}

48
core/ddclient.nomad Normal file
View File

@ -0,0 +1,48 @@
job "ddclient" {
datacenters = ["dc1"]
type = "service"
group "ddclient" {
task "ddclient" {
driver = "docker"
config {
image = "ghcr.io/linuxserver/ddclient:v3.10.0-ls104"
mount {
type = "bind"
source = "secrets/ddclient.conf"
target = "/config/ddclient.conf"
}
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/ddclient" -}}
daemon=900
ssl=yes
use=web
web=api.myip.com
protocol=cloudflare,
zone={{ .zone }},
ttl=1,
login=token,
password={{ .domain_ddclient }}
{{ .domain }}
{{- end }}
EOH
destination = "secrets/ddclient.conf"
change_mode = "restart"
}
resources {
cpu = 50
memory = 50
memory_max = 100
}
}
}
}

141
core/exporters.nomad Normal file
View File

@ -0,0 +1,141 @@
job "exporters" {
datacenters = ["dc1"]
type = "system"
group "promtail" {
network {
mode = "bridge"
port "promtail" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
to = 9080
}
}
service {
name = "promtail"
provider = "nomad"
port = "promtail"
meta {
nomad_dc = "$${NOMAD_DC}"
nomad_node_name = "$${node.unique.name}"
}
tags = [
"prometheus.scrape",
]
}
task "promtail" {
driver = "docker"
meta = {
"diun.sort_tags" = "semver"
"diun.watch_repo" = true
"diun.include_tags" = "^[0-9]+\\.[0-9]+\\.[0-9]+$"
}
config {
image = "grafana/promtail:2.9.1"
args = ["-config.file=$${NOMAD_TASK_DIR}/promtail.yml"]
ports = ["promtail"]
# Bind mount host machine-id and log directories
mount {
type = "bind"
source = "/etc/machine-id"
target = "/etc/machine-id"
readonly = true
}
mount {
type = "bind"
source = "/var/log/journal/"
target = "/var/log/journal/"
readonly = true
}
mount {
type = "bind"
source = "/run/log/journal/"
target = "/run/log/journal/"
readonly = true
}
# mount {
# type = "bind"
# source = "/var/log/audit"
# target = "/var/log/audit"
# readonly = true
# }
}
template {
data = <<EOF
---
server:
http_listen_address: 0.0.0.0
http_listen_port: 9080
clients:
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "loki" -}}
- url: http://{{ .Address }}:{{ .Port }}/loki/api/v1/push
{{- end }}
scrape_configs:
- job_name: journal
journal:
json: false
max_age: 12h
path: /var/log/journal
labels:
job: systemd-journal
relabel_configs:
- source_labels: ['__journal__systemd_unit']
target_label: unit
- source_labels: ['__journal__hostname']
target_label: hostname
- source_labels: ['__journal__transport']
target_label: journal_transport
# Docker log labels
- source_labels: ['__journal_syslog_identifier']
target_label: syslog_identifier
- source_labels: ['__journal_image_name']
target_label: docker_image_name
- source_labels: ['__journal_container_name']
target_label: docker_container_name
- source_labels: ['__journal_container_id']
target_label: docker_container_id
- source_labels: ['__journal_com_docker_compose_project']
target_label: docker_compose_project
- source_labels: ['__journal_com_docker_compose_service']
target_label: docker_compose_service
- source_labels: ['__journal_com_hashicorp_nomad_alloc_id']
target_label: nomad_alloc_id
- source_labels: ['__journal_com_hashicorp_nomad_job_id']
target_label: nomad_job_id
- source_labels: ['__journal_com_hashicorp_nomad_job_name']
target_label: nomad_job_name
- source_labels: ['__journal_com_hashicorp_nomad_node_name']
target_label: nomad_node_name
- source_labels: ['__journal_com_hashicorp_nomad_group_name']
target_label: nomad_group_name
- source_labels: ['__journal_com_hashicorp_nomad_task_name']
target_label: nomad_task_name
EOF
destination = "$${NOMAD_TASK_DIR}/promtail.yml"
}
resources {
cpu = 50
memory = 100
}
}
}
}

293
core/grafana.nomad Normal file
View File

@ -0,0 +1,293 @@
job "grafana" {
datacenters = ["dc1"]
group "grafana" {
count = 1
network {
mode = "bridge"
port "web" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
to = 3000
}
}
ephemeral_disk {
migrate = true
sticky = true
}
service {
name = "grafana"
provider = "nomad"
port = "web"
tags = [
"traefik.enable=true",
"traefik.http.routers.grafana.entryPoints=websecure",
]
}
task "stunnel" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = true
}
config {
image = "iamthefij/stunnel:latest"
args = ["$${NOMAD_TASK_DIR}/stunnel.conf"]
}
resources {
cpu = 100
memory = 100
}
template {
data = <<EOF
syslog = no
foreground = yes
delay = yes
[mysql_client]
client = yes
accept = 127.0.0.1:3306
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "mysql-tls" -}}
connect = {{ .Address }}:{{ .Port }}
{{- end }}
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/mysql_stunnel_psk.txt
EOF
destination = "$${NOMAD_TASK_DIR}/stunnel.conf"
}
template {
data = <<EOF
{{- with nomadVar "secrets/mysql/allowed_psks/grafana" }}{{ .psk }}{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/mysql_stunnel_psk.txt"
}
}
task "mysql-bootstrap" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = false
}
config {
image = "mariadb:10"
args = [
"/usr/bin/timeout",
"2m",
"/bin/bash",
"-c",
"until /usr/bin/mysql --defaults-extra-file=$${NOMAD_SECRETS_DIR}/my.cnf < $${NOMAD_SECRETS_DIR}/bootstrap.sql; do sleep 10; done",
]
}
template {
data = <<EOF
[client]
host=127.0.0.1
port=3306
user=root
{{ with nomadVar "secrets/mysql" -}}
password={{ .mysql_root_password }}
{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/my.cnf"
}
template {
data = <<EOF
{{ with nomadVar "nomad/jobs/grafana" -}}
{{ if .db_name -}}
CREATE DATABASE IF NOT EXISTS `{{ .db_name }}`;
CREATE USER IF NOT EXISTS '{{ .db_user }}'@'%' IDENTIFIED BY '{{ .db_pass }}';
GRANT ALL ON `{{ .db_name }}`.* to '{{ .db_user }}'@'%';
-- Create Read Only user
CREATE USER IF NOT EXISTS '{{ .db_user_ro }}'@'%' IDENTIFIED BY '{{ .db_pass_ro }}';
{{ else -}}
SELECT 'NOOP';
{{ end -}}
{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/bootstrap.sql"
}
resources {
cpu = 50
memory = 50
}
}
task "grafana" {
driver = "docker"
config {
image = "grafana/grafana:10.0.10"
args = ["--config", "$${NOMAD_ALLOC_DIR}/config/grafana.ini"]
ports = ["web"]
}
env = {
"GF_INSTALL_PLUGINS" = "grafana-clock-panel,grafana-piechart-panel,grafana-polystat-panel,natel-discrete-panel",
"GF_PATHS_CONFIG" = "$${NOMAD_ALLOC_DIR}/config/grafana.ini",
"GF_PATHS_PROVISIONING" = "$${NOMAD_ALLOC_DIR}/config/provisioning",
}
template {
data = <<EOF
{{ with nomadVar "secrets/smtp" -}}
GF_SMTP_USER={{ .user }}
GF_SMTP_PASSWORD={{ .password }}
{{ end -}}
{{ with nomadVar "nomad/jobs/grafana" -}}
GF_SECURITY_ADMIN_PASSWORD={{ .admin_pw }}
GF_EXTERNAL_IMAGE_STORAGE_S3_ACCESS_KEY={{ .minio_access_key }}
GF_EXTERNAL_IMAGE_STORAGE_S3_SECRET_KEY={{ .minio_secret_key }}
GRAFANA_ALERT_EMAIL_ADDRESSES={{ .alert_email_addresses }}
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET={{ .oidc_secret }}
{{ if .db_name -}}
# Database storage
GF_DATABASE_TYPE=mysql
GF_DATABASE_HOST=127.0.0.1:3306
GF_DATABASE_NAME={{ .db_name }}
GF_DATABASE_USER={{ .db_user }}
GF_DATABASE_PASSWORD={{ .db_pass }}
{{ end -}}
SLACK_BOT_URL={{ .slack_bot_url }}
SLACK_BOT_TOKEN={{ .slack_bot_token }}
SLACK_HOOK_URL={{ .slack_hook_url }}
{{ end -}}
EOF
env = true
destination = "secrets/conf.env"
}
resources {
cpu = 100
memory = 200
}
}
task "grafana-reprovisioner" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = true
}
config {
image = "alpine:3.17"
args = ["$${NOMAD_TASK_DIR}/startup.sh"]
}
resources {
cpu = 100
memory = 100
}
env = {
LOG_FILE = "/var/log/grafana_reloader.log"
}
template {
data = <<EOF
#! /bin/sh
apk add curl
touch "$LOG_FILE"
exec tail -f "$LOG_FILE"
EOF
perms = "777"
destination = "$${NOMAD_TASK_DIR}/startup.sh"
}
template {
data = <<EOF
{{ with nomadVar "nomad/jobs/grafana" -}}
GF_SECURITY_ADMIN_PASSWORD={{ .admin_pw }}
{{ end -}}
EOF
env = true
destination = "$${NOMAD_SECRETS_DIR}/conf.env"
}
template {
data = <<EOF
#! /bin/sh
exec > "$LOG_FILE"
exec 2>&1
GRAFANA_URL=http://127.0.0.1:3000
echo "Reload dashboards"
curl -s -S --user admin:$GF_SECURITY_ADMIN_PASSWORD --request POST $GRAFANA_URL/api/admin/provisioning/dashboards/reload
echo "Reload datasources"
curl -s -S --user admin:$GF_SECURITY_ADMIN_PASSWORD --request POST $GRAFANA_URL/api/admin/provisioning/datasources/reload
echo "Reload plugins"
curl -s -S --user admin:$GF_SECURITY_ADMIN_PASSWORD --request POST $GRAFANA_URL/api/admin/provisioning/plugins/reload
echo "Reload notifications"
curl -s -S --user admin:$GF_SECURITY_ADMIN_PASSWORD --request POST $GRAFANA_URL/api/admin/provisioning/notifications/reload
echo "Reload access-control"
curl -s -S --user admin:$GF_SECURITY_ADMIN_PASSWORD --request POST $GRAFANA_URL/api/admin/provisioning/access-control/reload
echo "Reload alerting"
curl -s -S --user admin:$GF_SECURITY_ADMIN_PASSWORD --request POST $GRAFANA_URL/api/admin/provisioning/alerting/reload
EOF
change_mode = "noop"
perms = "777"
destination = "$${NOMAD_TASK_DIR}/reload_config.sh"
}
%{ for config_file in fileset(join("/", [module_path, "grafana"]), "**") ~}
template {
data = <<EOF
${file(join("/", [module_path, "grafana", config_file]))}
EOF
destination = "$${NOMAD_ALLOC_DIR}/config/${config_file}"
perms = 777
# Set owner to grafana uid
# uid = 472
# Change template delimeter for dashboard files that use json and have double curly braces and square braces
%{ if endswith(config_file, ".json") ~}
left_delimiter = "<<<<"
right_delimiter = ">>>>"
%{ endif }
change_mode = "script"
change_script {
command = "/local/reload_config.sh"
}
}
%{ endfor }
}
task "grafana-image-renderer" {
driver = "docker"
constraint {
attribute = "$${attr.cpu.arch}"
value = "amd64"
}
config {
image = "grafana/grafana-image-renderer:3.6.1"
ports = ["renderer"]
}
env = {
"RENDERING_MODE" = "clustered"
"RENDERING_CLUSTERING_MODE" = "browser"
"RENDERING_CLUSTERING_MAX_CONCURRENCY" = 5
"RENDERING_CLUSTERING_TIMEOUT" = 30
}
}
}
}

455
core/grafana/grafana.ini Normal file
View File

@ -0,0 +1,455 @@
##################### Grafana Configuration Example #####################
#
# Everything has defaults so you only need to uncomment things you want to
# change
# possible values : production, development
;app_mode = production
# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty
;instance_name = ${HOSTNAME}
#################################### Paths ####################################
[paths]
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
data = /var/lib/grafana
# Directory where grafana can store logs
;logs = /var/log/grafana
# Directory where grafana will automatically scan and look for plugins
;plugins = /var/lib/grafana/plugins
# folder that contains PROVISIONING config files that grafana will apply on startup and while running.
provisioning = from_env
#################################### Server ####################################
[server]
# Protocol (http, https, socket)
;protocol = http
# The ip address to bind to, empty will bind to all interfaces
;http_addr =
# The http port to use
;http_port = 3000
# The public facing domain name used to access grafana from a browser
;domain = localhost
# Redirect to correct domain if host header does not match domain
# Prevents DNS rebinding attacks
;enforce_domain = false
# The full public facing url you use in browser, used for redirects and emails
# If you use reverse proxy and sub path specify full url (with sub path)
root_url = https://grafana.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}
# Log web requests
;router_logging = false
# the path relative working path
;static_root_path = public
# enable gzip
;enable_gzip = false
# https certs & key file
;cert_file =
;cert_key =
# Unix socket path
;socket =
#################################### Database ####################################
[database]
# You can configure the database connection by specifying type, host, name, user and password
# as separate properties or as on string using the url properties.
# Either "mysql", "postgres" or "sqlite3", it's your choice
;type = sqlite3
;host = 127.0.0.1:3306
;name = grafana
;user = root
# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;"""
;password =
# Use either URL or the previous fields to configure the database
# Example: mysql://user:secret@host:port/database
;url =
# For "postgres" only, either "disable", "require" or "verify-full"
;ssl_mode = disable
# For "sqlite3" only, path relative to data_path setting
;path = grafana.db
# Max idle conn setting default is 2
;max_idle_conn = 2
# Max conn setting default is 0 (mean not set)
;max_open_conn =
# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours)
;conn_max_lifetime = 14400
# Set to true to log the sql calls and execution times.
log_queries =
#################################### Session ####################################
[session]
# Either "memory", "file", "redis", "mysql", "postgres", default is "file"
;provider = file
# Provider config options
# memory: not have any config yet
# file: session dir path, is relative to grafana data_path
# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=grafana`
# mysql: go-sql-driver/mysql dsn config string, e.g. `user:password@tcp(127.0.0.1:3306)/database_name`
# postgres: user=a password=b host=localhost port=5432 dbname=c sslmode=disable
;provider_config = sessions
# Session cookie name
;cookie_name = grafana_sess
# If you use session in https only, default is false
;cookie_secure = false
# Session life time, default is 86400
;session_life_time = 86400
#################################### Data proxy ###########################
[dataproxy]
# This enables data proxy logging, default is false
;logging = false
#################################### Analytics ####################################
[analytics]
# Server reporting, sends usage counters to stats.grafana.org every 24 hours.
# No ip addresses are being tracked, only simple counters to track
# running instances, dashboard and error counts. It is very helpful to us.
# Change this option to false to disable reporting.
;reporting_enabled = true
# Set to false to disable all checks to https://grafana.net
# for new vesions (grafana itself and plugins), check is used
# in some UI views to notify that grafana or plugin update exists
# This option does not cause any auto updates, nor send any information
# only a GET request to http://grafana.com to get latest versions
;check_for_updates = true
# Google Analytics universal tracking code, only enabled if you specify an id here
;google_analytics_ua_id =
#################################### Security ####################################
[security]
# default admin user, created on startup
;admin_user = admin
# default admin password, can be changed before first start of grafana, or in profile settings
;admin_password = admin
# used for signing
;secret_key = SW2YcwTIb9zpOOhoPsMm
# Auto-login remember days
;login_remember_days = 7
;cookie_username = grafana_user
;cookie_remember_name = grafana_remember
# disable gravatar profile images
;disable_gravatar = false
# data source proxy whitelist (ip_or_domain:port separated by spaces)
;data_source_proxy_whitelist =
# disable protection against brute force login attempts
;disable_brute_force_login_protection = false
#################################### Snapshots ###########################
[snapshots]
# snapshot sharing options
;external_enabled = true
;external_snapshot_url = https://snapshots-origin.raintank.io
;external_snapshot_name = Publish to snapshot.raintank.io
# remove expired snapshot
;snapshot_remove_expired = true
#################################### Dashboards History ##################
[dashboards]
# Number dashboard versions to keep (per dashboard). Default: 20, Minimum: 1
;versions_to_keep = 20
#################################### Users ###############################
[users]
# disable user signup / registration
;allow_sign_up = true
# Allow non admin users to create organizations
;allow_org_create = true
# Set to true to automatically assign new users to the default organization (id 1)
;auto_assign_org = true
# Default role new users will be automatically assigned (if disabled above is set to true)
;auto_assign_org_role = Viewer
# Background text for the user field on the login page
;login_hint = email or username
# Default UI theme ("dark" or "light")
;default_theme = dark
# External user management, these options affect the organization users view
;external_manage_link_url =
;external_manage_link_name =
;external_manage_info =
# Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard.
;viewers_can_edit = false
[auth]
# Set to true to disable (hide) the login form, useful if you use OAuth, defaults to false
;disable_login_form = false
# Set to true to disable the signout link in the side menu. useful if you use auth.proxy, defaults to false
;disable_signout_menu = false
# URL to redirect the user to after sign out
;signout_redirect_url =
#################################### Anonymous Auth ##########################
[auth.anonymous]
# enable anonymous access
;enabled = false
# specify organization name that should be used for unauthenticated users
;org_name = Main Org.
# specify role for unauthenticated users
;org_role = Viewer
#################################### Github Auth ##########################
[auth.github]
;enabled = false
;allow_sign_up = true
;client_id = some_id
;client_secret = some_secret
;scopes = user:email,read:org
;auth_url = https://github.com/login/oauth/authorize
;token_url = https://github.com/login/oauth/access_token
;api_url = https://api.github.com/user
;team_ids =
;allowed_organizations =
#################################### Google Auth ##########################
[auth.google]
;enabled = false
;allow_sign_up = true
;client_id = some_client_id
;client_secret = some_client_secret
;scopes = https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
;auth_url = https://accounts.google.com/o/oauth2/auth
;token_url = https://accounts.google.com/o/oauth2/token
;api_url = https://www.googleapis.com/oauth2/v1/userinfo
;allowed_domains =
#################################### Generic OAuth ##########################
[auth.generic_oauth]
enabled = true
name = Authelia
;allow_sign_up = true
client_id = grafana
client_secret = from_env
scopes = openid profile email groups
auth_url = https://authelia.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}/api/oidc/authorization
token_url = https://authelia.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}/api/oidc/token
api_url = https://authelia.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}/api/oidc/userinfo
login_attribute_path = preferred_username
groups_attribute_path = groups
name_attribute_path = name
# Role attribute path is not working
role_attribute_path = contains(groups[*], 'admin') && 'Admin' || contains(groups[*], 'grafana-admin') && 'Admin' || contains(groups[*], 'grafana-editor') && 'Editor' || contains(groups[*], 'developer') && 'Editor'
allow_assign_grafana_admin = true
skip_org_role_sync = true
use_pkce = true
;team_ids =
;allowed_organizations =
#################################### Grafana.com Auth ####################
[auth.grafana_com]
;enabled = false
;allow_sign_up = true
;client_id = some_id
;client_secret = some_secret
;scopes = user:email
;allowed_organizations =
#################################### Auth Proxy ##########################
[auth.proxy]
{{ with nomadService "traefik" -}}
enabled = false
header_name = Remote-User
header_property = username
auto_sign_up = true
{{- $last := len . | subtract 1 -}}
{{- $services := . -}}
whitelist = {{ range $i := loop $last -}}
{{- with index $services $i }}{{ .Address }},{{ end -}}
{{- end -}}
{{- with index . $last }}{{ .Address }}{{ end -}}
{{- end }}
#################################### Basic Auth ##########################
[auth.basic]
;enabled = true
#################################### Auth LDAP ##########################
[auth.ldap]
;enabled = false
;config_file = /etc/grafana/ldap.toml
;allow_sign_up = true
#################################### SMTP / Emailing ##########################
[smtp]
enabled = true
host = smtp.mailgun.org:587
user = from_env
# If the password contains # or ; you have to wrap it with trippel quotes. Ex """#password;"""
password = from_env
;cert_file =
;key_file =
;skip_verify = false
from_address = grafana@iamthefij.com
from_name = Grafana
# EHLO identity in SMTP dialog (defaults to instance_name)
;ehlo_identity = dashboard.example.com
[emails]
;welcome_email_on_sign_up = false
#################################### Logging ##########################
[log]
# Either "console", "file", "syslog". Default is console and file
# Use space to separate multiple modes, e.g. "console file"
;mode = console file
# Either "debug", "info", "warn", "error", "critical", default is "info"
;level = info
# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
;filters =
# For "console" mode only
[log.console]
;level =
# log line format, valid options are text, console and json
;format = console
# For "file" mode only
[log.file]
;level =
# log line format, valid options are text, console and json
;format = text
# This enables automated log rotate(switch of following options), default is true
;log_rotate = true
# Max line number of single file, default is 1000000
;max_lines = 1000000
# Segment log daily, default is true
;daily_rotate = true
# Expired days of log file(delete after max days), default is 7
;max_days = 7
[log.syslog]
;level =
# log line format, valid options are text, console and json
;format = text
# Syslog network type and address. This can be udp, tcp, or unix. If left blank, the default unix endpoints will be used.
;network =
;address =
# Syslog facility. user, daemon and local0 through local7 are valid.
;facility =
# Syslog tag. By default, the process' argv[0] is used.
;tag =
#################################### Alerting ############################
[alerting]
# Disable alerting engine & UI features
;enabled = true
# Makes it possible to turn off alert rule execution but alerting UI is visible
;execute_alerts = true
#################################### Explore #############################
[explore]
# Enable the Explore section
;enabled = false
#################################### Internal Grafana Metrics ##########################
# Metrics available at HTTP API Url /metrics
[metrics]
# Disable / Enable internal metrics
enabled = true
# Publish interval
;interval_seconds = 10
# Send internal metrics to Graphite
[metrics.graphite]
# Enable by setting the address setting (ex localhost:2003)
;address =
;prefix = prod.grafana.%(instance_name)s.
#################################### Distributed tracing ############
[tracing.jaeger]
# Enable by setting the address sending traces to jaeger (ex localhost:6831)
;address = localhost:6831
# Tag that will always be included in when creating new spans. ex (tag1:value1,tag2:value2)
;always_included_tag = tag1:value1
# Type specifies the type of the sampler: const, probabilistic, rateLimiting, or remote
;sampler_type = const
# jaeger samplerconfig param
# for "const" sampler, 0 or 1 for always false/true respectively
# for "probabilistic" sampler, a probability between 0 and 1
# for "rateLimiting" sampler, the number of spans per second
# for "remote" sampler, param is the same as for "probabilistic"
# and indicates the initial sampling rate before the actual one
# is received from the mothership
;sampler_param = 1
#################################### Grafana.com integration ##########################
# Url used to to import dashboards directly from Grafana.com
[grafana_com]
;url = https://grafana.com
#################################### External image storage ##########################
[external_image_storage]
# Used for uploading images to public servers so they can be included in slack/email messages.
# you can choose between (s3, webdav, gcs, azure_blob, local)
provider = s3
[external_image_storage.s3]
endpoint = https://minio.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}
bucket = grafana-images
region = us-east-1
path_style_access = true
[external_image_storage.local]
# does not require any configuration
[rendering]
# Since they are inside the same group, we can reference their bound ports
server_url = http://127.0.0.1:8081/render
callback_url = http://127.0.0.1:3000/

View File

@ -0,0 +1,882 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"description": "Query report for blocky (MySQL)",
"editable": true,
"fiscalYearStartMonth": 0,
"gnetId": 14980,
"graphTooltip": 0,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 0,
"y": 0
},
"id": 14,
"links": [],
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true,
"values": [
"value"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "8.1.2",
"repeatDirection": "v",
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT t.response_type, t.request_Ts as time, count(*) as cnt from log_entries t \n WHERE $__timeFilter(t.request_Ts) and \n t.response_type in ($response_type) and \n t.client_name in ($client_name) and \n (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0)\n group by t.response_type\n order by t.request_Ts",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Query count by response type",
"transformations": [],
"type": "piechart"
},
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 6,
"y": 0
},
"id": 16,
"links": [],
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"showLegend": true,
"values": [
"value"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT t.request_ts AS time,\n case when t.reason like 'BLOCKED%' then SUBSTRING_INDEX(SUBSTRING_INDEX(t.reason,'(',-1), ')',1) else '' end AS metric,\n count(t.reason) AS cnt\nFROM log_entries t\nWHERE t.response_type ='BLOCKED'\n AND $__timeFilter(t.request_Ts)\n AND t.client_name in ($client_name)\n AND (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0)\nGROUP BY 2\nORDER BY time",
"refId": "A",
"select": [
[
{
"params": [
"duration_ms"
],
"type": "column"
}
]
],
"table": "log_entries",
"timeColumn": "request_ts",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Blocked by Blacklist",
"type": "piechart"
},
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"displayName": "$__cell_1",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 13,
"links": [],
"options": {
"displayMode": "gradient",
"minVizHeight": 10,
"minVizWidth": 0,
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": true
},
"showUnfilled": true,
"text": {}
},
"pluginVersion": "9.2.4",
"repeatDirection": "v",
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "table",
"group": [],
"metricColumn": "f",
"rawQuery": true,
"rawSql": "SELECT t.request_Ts as time, t.client_name as metric, count(*) as cnt from log_entries t \n WHERE $__timeFilter(t.request_Ts) and \n t.response_type in ($response_type) and \n t.client_name in ($client_name) and \n (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0)\n group by t.client_name\n order by 3 desc",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Query count by client",
"transformations": [],
"type": "bargauge"
},
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"description": "Top 20 effective top level domain plus one more label",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"displayName": "$__cell_0",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 8
},
"id": 11,
"options": {
"displayMode": "gradient",
"minVizHeight": 10,
"minVizWidth": 0,
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": true
},
"showUnfilled": true
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "question_name",
"rawQuery": true,
"rawSql": "SELECT t.effective_tldp as metric, count(*) as value from log_entries t \nWHERE $__timeFilter(t.request_Ts) \n and t.response_type in ($response_type) \n and t.client_name in ($client_name) \n and (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0) \n group by t.effective_tldp order by count(*) desc limit 20",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "log_entries",
"timeColumn": "request_ts",
"where": []
}
],
"title": "Top 20 effective TLD+1",
"type": "bargauge"
},
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"displayName": "$__cell_0",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 8
},
"id": 8,
"options": {
"displayMode": "gradient",
"minVizHeight": 10,
"minVizWidth": 0,
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": true
},
"showUnfilled": true
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "question_name",
"rawQuery": true,
"rawSql": "SELECT t.question_name as metric, count(*) as value from log_entries t \n WHERE $__timeFilter(t.request_Ts) and \n t.response_type in ($response_type) and \n t.client_name in ($client_name) and \n (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0) \n group by t.question_name order by count(*) desc limit 20",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"table": "log_entries",
"timeColumn": "request_ts",
"where": []
}
],
"title": "Top 20 queried domains",
"type": "bargauge"
},
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 24,
"x": 0,
"y": 16
},
"id": 12,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroupAlias(t.request_Ts, '30m'),\n t.client_name as metric,\n count(*) as c\nFROM log_entries t\nWHERE\n $__timeFilter(t.request_Ts) and \n t.response_type in ($response_type) and \n t.client_name in ($client_name) and \n (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0)\nGROUP BY 1,2\nORDER BY 1",
"refId": "A",
"select": [
[
{
"params": [
"duration_ms"
],
"type": "column"
}
]
],
"table": "log_entries",
"timeColumn": "request_ts",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Queries number per client (30m)",
"type": "timeseries"
},
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "dtdurationms"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 23
},
"id": 10,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "multi",
"sort": "none"
}
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n UNIX_TIMESTAMP(t.request_Ts) as time,\n t.duration_ms\nFROM log_entries t\nWHERE\n $__timeFilter(t.request_Ts) and \n t.response_type in ($response_type) and \n t.client_name in ($client_name) and \n (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0)\nORDER BY request_ts",
"refId": "A",
"select": [
[
{
"params": [
"duration_ms"
],
"type": "column"
}
]
],
"table": "log_entries",
"timeColumn": "request_ts",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Query duration",
"type": "timeseries"
},
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"description": "Last 100 queries, newest on top",
"fieldConfig": {
"defaults": {
"custom": {
"displayMode": "auto",
"filterable": false,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "time"
},
"properties": [
{
"id": "unit",
"value": "dateTimeAsIsoNoDateIfToday"
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 31
},
"id": 4,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT UNIX_TIMESTAMP(t.request_Ts) as \"time\", \n t.client_ip as \"client IP\", \n t.client_name as \"client name\", \n t.duration_ms as \"duration in ms\", \n t.response_type as \"response type\", \n t.question_type as \"question type\", \n t.question_name as \"question name\", \n t.effective_tldp as \"effective TLD+1\", \n t.answer as \"answer\" from log_entries t \n WHERE $__timeFilter(t.request_Ts) and \n t.response_type in ($response_type) and \n t.client_name in ($client_name) and \n (length('$question') = 0 or INSTR(t.question_name, lower('$question')) > 0) \n order by t.request_Ts desc limit 100",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Last queries",
"type": "table"
}
],
"refresh": "",
"schemaVersion": 37,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"allValue": "",
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"definition": "select distinct client_name from log_entries",
"hide": 0,
"includeAll": true,
"label": "Client name",
"multi": true,
"name": "client_name",
"options": [],
"query": "select distinct client_name from log_entries",
"refresh": 2,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "mysql",
"uid": "DN2DNsD4z"
},
"definition": "select distinct response_type from log_entries",
"hide": 0,
"includeAll": true,
"label": "Response type",
"multi": true,
"name": "response_type",
"options": [],
"query": "select distinct response_type from log_entries",
"refresh": 2,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": false,
"text": "",
"value": ""
},
"hide": 0,
"label": "Domain (contains)",
"name": "question",
"options": [
{
"selected": true,
"text": "",
"value": ""
}
],
"query": "",
"skipUrlSync": false,
"type": "textbox"
}
]
},
"time": {
"from": "now-24h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Blocky query",
"uid": "AVmWSVWgz",
"version": 1,
"weekStart": ""
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,411 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "rgb(31, 120, 193)",
"mode": "fixed"
},
"mappings": [
{
"options": {
"match": "null",
"result": {
"text": "N/A"
}
},
"type": "special"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "#d44a3a",
"value": null
},
{
"color": "rgba(237, 129, 40, 0.89)",
"value": 0
},
{
"color": "#299c46",
"value": 1
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 4,
"x": 0,
"y": 0
},
"id": 2,
"links": [],
"maxDataPoints": 100,
"options": {
"colorMode": "none",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"expr": "count(minitor_monitor_up_count)",
"format": "time_series",
"instant": true,
"intervalFactor": 2,
"refId": "A"
}
],
"title": "Total Monitors",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"decimals": 0,
"mappings": [
{
"options": {
"match": "null",
"result": {
"text": "N/A"
}
},
"type": "special"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "#299c46",
"value": null
},
{
"color": "rgba(237, 129, 40, 0.89)",
"value": 1
},
{
"color": "#d44a3a",
"value": 2
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 4,
"x": 4,
"y": 0
},
"id": 6,
"links": [],
"maxDataPoints": 100,
"options": {
"colorMode": "background",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"textMode": "auto"
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"expr": "count(minitor_monitor_up_count)-count(minitor_monitor_up_count>=1)",
"format": "time_series",
"instant": false,
"intervalFactor": 1,
"refId": "A"
}
],
"title": "Total Down Services",
"type": "stat"
},
{
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"fillOpacity": 70,
"lineWidth": 0,
"spanNulls": 900000
},
"mappings": [
{
"options": {
"0": {
"index": 1,
"text": "Down"
},
"1": {
"index": 0,
"text": "Up"
}
},
"type": "value"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "red",
"value": null
},
{
"color": "red",
"value": 0
},
{
"color": "green",
"value": 1
}
]
},
"unit": "bool_on_off"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 14,
"x": 8,
"y": 0
},
"id": 4,
"links": [],
"options": {
"alignValue": "left",
"legend": {
"displayMode": "list",
"placement": "bottom",
"showLegend": false
},
"mergeValues": true,
"rowHeight": 0.9,
"showValue": "never",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "9.2.4",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"editorMode": "code",
"exemplar": false,
"expr": "sum(minitor_monitor_up_count) by (monitor)",
"format": "time_series",
"instant": false,
"intervalFactor": 1,
"legendFormat": "{{monitor}}",
"range": true,
"refId": "A"
}
],
"title": "Service Status",
"type": "state-timeline"
},
{
"columns": [],
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"fontSize": "100%",
"gridPos": {
"h": 11,
"w": 22,
"x": 0,
"y": 7
},
"id": 8,
"links": [],
"scroll": true,
"showHeader": true,
"sort": {
"col": 0,
"desc": true
},
"styles": [
{
"alias": "Time",
"align": "auto",
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"pattern": "Time",
"type": "date"
},
{
"alias": "Status",
"align": "auto",
"colorMode": "cell",
"colors": [
"#F2495C",
"#F2495C",
"rgba(50, 172, 45, 0.97)"
],
"decimals": 0,
"link": false,
"mappingType": 1,
"pattern": "Value",
"thresholds": [
"0",
"1"
],
"type": "string",
"unit": "short",
"valueMaps": [
{
"text": "Ok",
"value": "1"
},
{
"text": "Not Ok",
"value": "0"
}
]
}
],
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "rS2OIfv4z"
},
"expr": "minitor_monitor_up_count",
"format": "time_series",
"instant": true,
"intervalFactor": 1,
"legendFormat": "{{monitor}}",
"refId": "A"
}
],
"title": "Monitor status",
"transform": "timeseries_to_rows",
"type": "table-old"
}
],
"refresh": "30s",
"schemaVersion": 37,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Minitor Monitor",
"uid": "qoE5Hrxiz",
"version": 1,
"weekStart": ""
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
apiVersion: 1
providers:
- name: default
folder: 'Provisioned'
type: file
disableDeletion: false
options:
path: {{ env "NOMAD_ALLOC_DIR" }}/config/provisioning/dashboards/default

View File

@ -0,0 +1,19 @@
---
apiVersion: 1
datasources:
- name: HASS Metrics
url: "http://192.168.2.75:8086"
type: influxdb
access: proxy
database: hass
jsonData:
dbName: hass
- name: Proxmox Metrics
url: "http://192.168.2.75:8086"
type: influxdb
access: proxy
database: proxmox
jsonData:
dbName: proxmox

View File

@ -0,0 +1,12 @@
---
apiVersion: 1
datasources:
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "loki" }}
- name: Loki
url: "http://{{ .Address }}:{{ .Port }}"
type: loki
access: proxy
isDefault: false
version: 1
{{ end }}

View File

@ -0,0 +1,16 @@
---
apiVersion: 1
datasources:
- name: Blocky logs
url: 127.0.0.1:3306
# TODO: Looking for an acl friendly way to expose this since it's a variable in blocky setup
database: blocky
type: mysql
isDefault: false
version: 1
{{ with nomadVar "nomad/jobs/grafana" -}}
user: {{ .db_user_ro }}
secureJsonData:
password: {{ .db_pass_ro }}
{{- end }}

View File

@ -0,0 +1,12 @@
---
apiVersion: 1
datasources:
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "prometheus" }}
- name: Prometheus
url: "http://{{ .Address }}:{{ .Port }}"
type: prometheus
access: proxy
isDefault: true
version: 1
{{ end }}

View File

@ -0,0 +1,11 @@
---
{{ with nomadVar "nomad/jobs/grafana" -}}
notifiers:
- name: Personal email
type: email
uid: email-1
org_id: 1
is_default: false
settings:
addresses: "{{ .alert_email_addresses }}"
{{ end -}}

View File

@ -0,0 +1,27 @@
---
{{ with nomadVar "nomad/jobs/grafana" -}}
notifiers:
- name: Slack Bot
type: slack
uid: slack-1
org_id: 1
is_default: false
settings:
url: "{{ .slack_bot_url }}"
recipient: "#site-notifications"
username: Grafana Alerts
icon_url: https://grafana.iamthefij.com/public/img/grafana_icon.svg
token: "{{ .slack_bot_token }}"
uploadImage: true
mentionChannel: channel
- name: Slack Hook
type: slack
uid: slack-2
org_id: 1
is_default: true
settings:
url: "{{ .slack_hook_url }}"
icon_url: https://grafana.iamthefij.com/public/img/grafana_icon.svg
uploadImage: true
mentionChannel: channel
{{ end -}}

97
core/lego.nomad Normal file
View File

@ -0,0 +1,97 @@
variable "lego_version" {
default = "4.14.2"
type = string
}
variable "nomad_var_dirsync_version" {
default = "0.0.2"
type = string
}
job "lego" {
type = "batch"
periodic {
cron = "@weekly"
prohibit_overlap = true
}
group "main" {
network {
dns {
servers = ["1.1.1.1", "1.0.0.1"]
}
}
task "main" {
driver = "exec"
config {
# image = "alpine:3"
command = "/bin/bash"
args = ["${NOMAD_TASK_DIR}/start.sh"]
}
artifact {
source = "https://github.com/go-acme/lego/releases/download/v${var.lego_version}/lego_v${var.lego_version}_linux_${attr.cpu.arch}.tar.gz"
}
artifact {
source = "https://git.iamthefij.com/iamthefij/nomad-var-dirsync/releases/download/v${var.nomad_var_dirsync_version}/nomad-var-dirsync-linux-${attr.cpu.arch}.tar.gz"
}
template {
data = <<EOH
#! /bin/sh
set -ex
cd ${NOMAD_TASK_DIR}
echo "Read certs from nomad vars"
${NOMAD_TASK_DIR}/nomad-var-dirsync-linux-{{ env "attr.cpu.arch" }} -root-var=secrets/certs read .
action=run
if [ -f /.lego/certificates/_.thefij.rocks.crt ]; then
action=renew
fi
echo "Attempt to $action certificates"
${NOMAD_TASK_DIR}/lego \
--accept-tos --pem \
--email=iamthefij@gmail.com \
--domains="*.thefij.rocks" \
--dns="cloudflare" \
$action \
--$action-hook="${NOMAD_TASK_DIR}/nomad-var-dirsync-linux-{{ env "attr.cpu.arch" }} -root-var=secrets/certs write .lego" \
EOH
destination = "${NOMAD_TASK_DIR}/start.sh"
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/lego" -}}
CF_DNS_API_TOKEN={{ .domain_lego_dns }}
CF_ZONE_API_TOKEN={{ .domain_lego_dns }}
{{- end }}
EOH
destination = "secrets/cloudflare.env"
env = true
}
env = {
NOMAD_ADDR = "unix:///secrets/api.sock"
}
identity {
env = true
}
resources {
cpu = 50
memory = 100
}
}
}
}

23
core/lego.tf Normal file
View File

@ -0,0 +1,23 @@
resource "nomad_job" "lego" {
jobspec = file("${path.module}/lego.nomad")
}
resource "nomad_acl_policy" "secrets_certs_write" {
name = "secrets-certs-write"
description = "Write certs to secrets store"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/certs/*" {
capabilities = ["write", "read"]
}
path "secrets/certs" {
capabilities = ["write", "read"]
}
}
}
EOH
job_acl {
job_id = "lego/*"
}
}

41
core/loki-config.yml Normal file
View File

@ -0,0 +1,41 @@
auth_enabled: false
server:
http_listen_port: 3100
common:
ring:
instance_addr: 127.0.0.1
kvstore:
store: inmemory
replication_factor: 1
path_prefix: /tmp/loki
schema_config:
configs:
- from: 2020-05-15
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: {{ env "NOMAD_TASK_DIR" }}/index
filesystem:
directory: {{ env "NOMAD_TASK_DIR" }}/chunks
limits_config:
enforce_metric_name: false
reject_old_samples: true
reject_old_samples_max_age: 168h
chunk_store_config:
max_look_back_period: 168h
table_manager:
retention_deletes_enabled: true
retention_period: 168h

24
core/loki.tf Normal file
View File

@ -0,0 +1,24 @@
module "loki" {
source = "../services/service"
detach = false
name = "loki"
image = "grafana/loki:2.8.7"
args = ["--config.file=$${NOMAD_TASK_DIR}/loki-config.yml"]
service_port = 3100
ingress = true
use_wesher = var.use_wesher
service_check = {
path = "/ready"
}
sticky_disk = true
templates = [
{
data = file("${path.module}/loki-config.yml")
dest = "loki-config.yml"
mount = false
}
]
}

27
core/main.tf Normal file
View File

@ -0,0 +1,27 @@
module "blocky" {
source = "./blocky"
use_wesher = var.use_wesher
# Not in this module
# depends_on = [module.databases]
}
module "traefik" {
source = "./traefik"
}
resource "nomad_job" "nomad-client-stalker" {
# Stalker used to allow using Nomad service registry to identify nomad client hosts
jobspec = file("${path.module}/nomad-client-stalker.nomad")
}
resource "nomad_job" "syslog-ng" {
jobspec = file("${path.module}/syslogng.nomad")
depends_on = [module.loki]
}
resource "nomad_job" "ddclient" {
jobspec = file("${path.module}/ddclient.nomad")
}

95
core/metrics.tf Normal file
View File

@ -0,0 +1,95 @@
resource "nomad_job" "exporters" {
jobspec = templatefile("${path.module}/exporters.nomad", {
use_wesher = var.use_wesher,
})
}
resource "nomad_job" "prometheus" {
jobspec = templatefile("${path.module}/prometheus.nomad", {
use_wesher = var.use_wesher,
})
detach = false
}
resource "nomad_job" "grafana" {
jobspec = templatefile("${path.module}/grafana.nomad", {
module_path = path.module
use_wesher = var.use_wesher
})
depends_on = [nomad_job.prometheus]
}
resource "nomad_acl_policy" "grafana_smtp_secrets" {
name = "grafana-secrets-smtp"
description = "Give access to MySQL secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/smtp" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = "grafana"
group = "grafana"
task = "grafana"
}
}
# Generate secrets and policies for access to MySQL
resource "nomad_acl_policy" "grafana_mysql_bootstrap_secrets" {
name = "grafana-secrets-mysql"
description = "Give access to MySQL secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = "grafana"
group = "grafana"
task = "mysql-bootstrap"
}
}
resource "random_password" "grafana_mysql_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "grafana_mysql_psk" {
path = "secrets/mysql/allowed_psks/grafana"
items = {
psk = "grafana:${resource.random_password.grafana_mysql_psk.result}"
}
}
resource "nomad_acl_policy" "grafana_mysql_psk" {
name = "grafana-secrets-mysql-psk"
description = "Give access to MySQL PSK secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql/allowed_psks/grafana" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = "grafana"
group = "grafana"
task = "stunnel"
}
}

View File

@ -0,0 +1,32 @@
job "nomad-client-stalker" {
type = "system"
group "main" {
network {
mode = "host"
port "main" {}
}
service {
name = "nomad-client-stalker"
provider = "nomad"
port = "main"
}
task "main" {
driver = "docker"
config {
image = "busybox"
args = ["tail", "-f", "/dev/null"]
}
resources {
cpu = 10
memory = 15
memory_max = 30
}
}
}
}

145
core/prometheus.nomad Normal file
View File

@ -0,0 +1,145 @@
job "prometheus" {
datacenters = ["dc1"]
group "prometheus" {
count = 1
network {
mode = "bridge"
port "web" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
to = 9090
}
port "pushgateway" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
static = 9091
}
}
ephemeral_disk {
migrate = true
sticky = true
}
service {
name = "prometheus"
provider = "nomad"
port = "web"
// TODO: Remove traefik tags
tags = [
"traefik.enable=true",
"traefik.http.routers.prometheus.entryPoints=websecure",
]
}
service {
name = "pushgateway"
provider = "nomad"
port = "pushgateway"
}
task "prometheus" {
driver = "docker"
config {
image = "prom/prometheus:v2.43.0"
ports = ["web"]
args = [
"--config.file=$${NOMAD_TASK_DIR}/prometheus.yml",
"--storage.tsdb.path=$${NOMAD_ALLOC_DIR}/data/tsdb",
"--web.listen-address=0.0.0.0:9090",
"--web.console.libraries=/usr/share/prometheus/console_libraries",
"--web.console.templates=/usr/share/prometheus/consoles",
]
}
template {
data = <<EOF
---
global:
scrape_interval: 30s
evaluation_interval: 3s
scrape_configs:
- job_name: prometheus
static_configs:
- targets:
- 127.0.0.1:9090
- job_name: "pushgateway"
honor_labels: true
static_configs:
- targets:
- 127.0.0.1:9091
- job_name: "nomad_client"
metrics_path: "/v1/metrics"
params:
format:
- "prometheus"
nomad_sd_configs:
# TODO: Use NOMAD_SECRETS_DIR/api.sock and workload idenity when
# workload acls can be set using terraform
- server: "http://{{env "attr.unique.network.ip-address"}}:4646"
relabel_configs:
- source_labels: [__meta_nomad_service]
regex: nomad-client-stalker
action: keep
- source_labels: [__meta_nomad_address]
replacement: "$1:4646"
target_label: __address__
- job_name: "nomad_services"
metrics_path: "/metrics"
nomad_sd_configs:
- server: "http://{{env "attr.unique.network.ip-address"}}:4646"
relabel_configs:
- source_labels: [__meta_nomad_tags]
regex: .*(prometheus.scrape).*
action: keep
- source_labels: [__meta_nomad_service_address,__meta_nomad_service_port]
separator: ":"
target_label: __address__
- source_labels: [__meta_nomad_service]
target_label: nomad_service
- source_labels: [__meta_nomad_dc]
target_label: nomad_dc
- source_labels: [__meta_nomad_node_id]
target_label: nomad_node_id
EOF
change_mode = "signal"
change_signal = "SIGHUP"
destination = "$${NOMAD_TASK_DIR}/prometheus.yml"
}
resources {
cpu = 100
memory = 300
}
}
task "pushgateway" {
driver = "docker"
config {
image = "prom/pushgateway"
ports = ["pushgateway"]
args = [
"--persistence.file=$${NOMAD_ALLOC_DIR}/pushgateway-persistence",
]
}
resources {
cpu = 50
memory = 50
}
}
}
}

141
core/syslogng.nomad Normal file
View File

@ -0,0 +1,141 @@
job "syslogng" {
datacenters = ["dc1"]
type = "service"
group "promtail" {
count = 1
network {
mode = "bridge"
port "main" {
to = 1514
}
port "metrics" {
to = 9080
}
}
service {
name = "syslogng-promtail"
provider = "nomad"
port = "main"
}
task "promtail" {
driver = "docker"
meta = {
"diun.watch_repo" = true
}
config {
image = "grafana/promtail:2.9.1"
ports = ["main", "metrics"]
args = ["--config.file=/etc/promtail/promtail.yml"]
mount {
type = "bind"
target = "/etc/promtail/promtail.yml"
source = "local/promtail.yml"
}
}
template {
data = <<EOF
---
server:
http_listen_address: 0.0.0.0
http_listen_port: 9080
clients:
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "loki" -}}
- url: http://{{ .Address }}:{{ .Port }}/loki/api/v1/push
{{- end }}
scrape_configs:
# TCP syslog receiver
- job_name: syslog
syslog:
listen_address: 0.0.0.0:{{ env "NOMAD_PORT_main" }}
labels:
job: syslog
relabel_configs:
- source_labels: ['__syslog_message_hostname']
target_label: hostname
EOF
destination = "local/promtail.yml"
}
resources {
cpu = 50
memory = 20
}
}
}
group "syslogng" {
count = 1
network {
mode = "bridge"
port "main" {
to = 514
}
}
service {
name = "syslogng"
provider = "nomad"
port = "main"
}
task "syslogng" {
driver = "docker"
config {
image = "balabit/syslog-ng:3.37.1"
ports = ["main"]
args = ["--no-caps"]
mount {
type = "bind"
target = "/etc/syslog-ng/syslog-ng.conf"
source = "local/syslog-ng.conf"
}
}
template {
data = <<EOF
@version: 3.37
@include "scl.conf"
source s_network {
default-network-drivers(
);
};
source s_internal {
internal();
};
destination d_loki {
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "syslogng-promtail" -}}
syslog("{{ .Address }}" transport("tcp") port({{ .Port }}));
{{- end }}
};
log { source(s_internal); destination(d_loki); };
log { source(s_network); destination(d_loki); };
EOF
destination = "local/syslog-ng.conf"
}
resources {
cpu = 50
memory = 10
}
}
}
}

View File

@ -0,0 +1,21 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "2.1.0"
hashes = [
"h1:ek0L7fA+4R1/BXhbutSRqlQPzSZ5aY/I2YfVehuYeEU=",
"zh:39ba4d4fc9557d4d2c1e4bf866cf63973359b73e908cce237c54384512bdb454",
"zh:40d2b66e3f3675e6b88000c145977c1d5288510c76b702c6c131d9168546c605",
"zh:40fbe575d85a083f96d4703c6b7334e9fc3e08e4f1d441de2b9513215184ebcc",
"zh:42ce6db79e2f94557fae516ee3f22e5271f0b556638eb45d5fbad02c99fc7af3",
"zh:4acf63dfb92f879b3767529e75764fef68886521b7effa13dd0323c38133ce88",
"zh:72cf35a13c2fb542cd3c8528826e2390db9b8f6f79ccb41532e009ad140a3269",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:8b8bcc136c05916234cb0c3bcc3d48fda7ca551a091ad8461ea4ab16fb6960a3",
"zh:8e1c2f924eae88afe7ac83775f000ae8fd71a04e06228edf7eddce4df2421169",
"zh:abc6e725531fc06a8e02e84946aaabc3453ecafbc1b7a442ea175db14fd9c86a",
"zh:b735fcd1fb20971df3e92f81bb6d73eef845dcc9d3d98e908faa3f40013f0f69",
"zh:ce59797282505d872903789db8f092861036da6ec3e73f6507dac725458a5ec9",
]
}

314
core/traefik/traefik.nomad Normal file
View File

@ -0,0 +1,314 @@
job "traefik" {
datacenters = ["dc1"]
type = "service"
priority = 100
constraint {
attribute = "${node.class}"
value = "ingress"
}
constraint {
distinct_hosts = true
}
update {
max_parallel = 1
# canary = 1
# auto_promote = true
auto_revert = true
}
group "traefik" {
count = 2
network {
port "web" {
static = 80
}
port "websecure" {
static = 443
}
port "syslog" {
static = 514
}
port "gitssh" {
static = 2222
}
port "metrics" {}
dns {
servers = [
"192.168.2.101",
"192.168.2.102",
"192.168.2.30",
]
}
}
ephemeral_disk {
migrate = true
sticky = true
}
task "traefik" {
driver = "docker"
service {
name = "traefik"
provider = "nomad"
port = "web"
check {
type = "http"
path = "/ping"
interval = "10s"
timeout = "2s"
}
tags = [
"traefik.enable=true",
"traefik.http.routers.traefik.entryPoints=websecure",
"traefik.http.routers.traefik.service=api@internal",
]
}
service {
name = "traefik-metrics"
provider = "nomad"
port = "metrics"
tags = [
"prometheus.scrape",
]
}
config {
image = "traefik:2.10"
ports = ["web", "websecure", "syslog", "gitssh", "metrics"]
network_mode = "host"
mount {
type = "bind"
target = "/etc/traefik"
source = "local/config"
}
mount {
type = "bind"
target = "/etc/traefik/usersfile"
source = "secrets/usersfile"
}
mount {
type = "bind"
target = "/etc/traefik/certs"
source = "secrets/certs"
}
}
template {
# Avoid conflict with TOML lists [[ ]] and Go templates {{ }}
left_delimiter = "<<"
right_delimiter = ">>"
data = <<EOH
[log]
level = "DEBUG"
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.web.http]
[entryPoints.web.http.redirections]
[entryPoints.web.http.redirections.entrypoint]
to = "websecure"
scheme = "https"
[entryPoints.websecure]
address = ":443"
[entryPoints.websecure.http.tls]
[entryPoints.metrics]
address = ":<< env "NOMAD_PORT_metrics" >>"
[entryPoints.syslogtcp]
address = ":514"
[entryPoints.syslogudp]
address = ":514/udp"
[entryPoints.gitssh]
address = ":2222"
[api]
dashboard = true
[ping]
entrypoint = "web"
[metrics]
[metrics.prometheus]
entrypoint = "metrics"
# manualRouting = true
[providers.file]
directory = "/etc/traefik/conf"
watch = true
[providers.nomad]
exposedByDefault = false
defaultRule = "Host(`{{normalize .Name}}.<< with nomadVar "nomad/jobs" >><< .base_hostname >><< end >>`)"
[providers.nomad.endpoint]
address = "http://127.0.0.1:4646"
EOH
destination = "${NOMAD_TASK_DIR}/config/traefik.toml"
}
template {
data = <<EOH
[http]
[http.routers]
[http.routers.nomad]
entryPoints = ["websecure"]
service = "nomad"
rule = "Host(`nomad.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}`)"
{{ with nomadVar "nomad/jobs/traefik" }}{{ with .external }}{{ with .Value | parseYAML -}}
{{ range $service, $url := . }}
[http.routers.{{ $service }}]
entryPoints = ["websecure"]
service = "{{ $service }}"
rule = "Host(`{{ $service }}.{{ with nomadVar "nomad/jobs" }}{{ .base_hostname }}{{ end }}`)"
{{ end }}
{{- end }}{{ end }}{{ end }}
[http.services]
[http.services.nomad]
[http.services.nomad.loadBalancer]
[[http.services.nomad.loadBalancer.servers]]
url = "http://127.0.0.1:4646"
{{ with nomadVar "nomad/jobs/traefik" }}{{ with .external }}{{ with .Value | parseYAML -}}
{{ range $service, $url := . }}
[http.services.{{ $service }}]
[http.services.{{ $service }}.loadBalancer]
[[http.services.{{ $service }}.loadBalancer.servers]]
url = "{{ $url }}"
{{ end }}
{{- end }}{{ end }}{{ end }}
EOH
destination = "${NOMAD_TASK_DIR}/config/conf/route-hashi.toml"
change_mode = "noop"
splay = "1m"
wait {
min = "10s"
max = "20s"
}
}
template {
data = <<EOH
{{ with nomadService "syslogng" -}}
[tcp.routers]
[tcp.routers.syslogtcp]
entryPoints = ["syslogtcp"]
service = "syslogngtcp"
rule = "HostSNI(`*`)"
[tcp.services]
[tcp.services.syslogngtcp]
[tcp.services.syslogngtcp.loadBalancer]
{{ range . -}}
[[tcp.services.syslogngtcp.loadBalancer.servers]]
address = "{{ .Address }}:{{ .Port }}"
{{ end -}}
{{- end }}
{{ with nomadService "syslogng" -}}
[udp.routers]
[udp.routers.syslogudp]
entryPoints = ["syslogudp"]
service = "syslogngudp"
[udp.services]
[udp.services.syslogngudp]
[udp.services.syslogngudp.loadBalancer]
{{ range . -}}
[[udp.services.syslogngudp.loadBalancer.servers]]
address = "{{ .Address }}:{{ .Port }}"
{{ end -}}
{{- end }}
EOH
destination = "${NOMAD_TASK_DIR}/config/conf/route-syslog-ng.toml"
change_mode = "noop"
splay = "1m"
wait {
min = "10s"
max = "20s"
}
}
template {
data = <<EOF
{{- with nomadVar "secrets/certs/_lego/certificates/__thefij_rocks_crt" }}{{ .contents }}{{ end -}}"
EOF
destination = "${NOMAD_SECRETS_DIR}/certs/_.thefij.rocks.crt"
change_mode = "noop"
}
template {
data = <<EOF
{{- with nomadVar "secrets/certs/_lego/certificates/__thefij_rocks_key" }}{{ .contents }}{{ end -}}"
EOF
destination = "${NOMAD_SECRETS_DIR}/certs/_.thefij.rocks.key"
change_mode = "noop"
}
template {
data = <<EOH
[[tls.certificates]]
certFile = "/etc/traefik/certs/_.thefij.rocks.crt"
keyFile = "/etc/traefik/certs/_.thefij.rocks.key"
EOH
destination = "${NOMAD_TASK_DIR}/config/conf/dynamic-tls.toml"
change_mode = "noop"
}
template {
data = <<EOH
[http.middlewares]
{{ with nomadVar "nomad/jobs/traefik" }}
{{ if .usersfile }}
[http.middlewares.basic-auth.basicAuth]
usersFile = "/etc/traefik/usersfile"
{{- end }}
{{- end }}
EOH
destination = "${NOMAD_TASK_DIR}/config/conf/middlewares.toml"
change_mode = "noop"
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/traefik" -}}
{{ .usersfile }}
{{- end }}
EOH
destination = "${NOMAD_SECRETS_DIR}/usersfile"
change_mode = "noop"
}
resources {
cpu = 100
memory = 150
}
}
}
}

23
core/traefik/traefik.tf Normal file
View File

@ -0,0 +1,23 @@
resource "nomad_job" "traefik" {
jobspec = file("${path.module}/traefik.nomad")
}
resource "nomad_acl_policy" "treafik_secrets_certs_read" {
name = "traefik-secrets-certs-read"
description = "Read certs to secrets store"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/certs/*" {
capabilities = ["read"]
}
path "secrets/certs" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
job_id = resource.nomad_job.traefik.id
}
}

11
core/vars.tf Normal file
View File

@ -0,0 +1,11 @@
variable "base_hostname" {
type = string
description = "Base hostname to serve content from"
default = "dev.homelab"
}
variable "use_wesher" {
type = bool
description = "Indicates whether or not services should expose themselves on the wesher network"
default = true
}

View File

@ -0,0 +1,40 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "2.0.0"
hashes = [
"h1:lIHIxA6ZmfyTGL3J9YIddhxlfit4ipSS09BLxkwo6L0=",
"zh:09b897d64db293f9a904a4a0849b11ec1e3fff5c638f734d82ae36d8dc044b72",
"zh:435cc106799290f64078ec24b6c59cb32b33784d609088638ed32c6d12121199",
"zh:7073444bd064e8c4ec115ca7d9d7f030cc56795c0a83c27f6668bba519e6849a",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:79d238c35d650d2d83a439716182da63f3b2767e72e4cbd0b69cb13d9b1aebfc",
"zh:7ef5f49344278fe0bbc5447424e6aa5425ff1821d010d944a444d7fa2c751acf",
"zh:92179091638c8ba03feef371c4361a790190f9955caea1fa59de2055c701a251",
"zh:a8a34398851761368eb8e7c171f24e55efa6e9fdbb5c455f6dec34dc17f631bc",
"zh:b38fd5338625ebace5a4a94cea1a28b11bd91995d834e318f47587cfaf6ec599",
"zh:b71b273a2aca7ad5f1e07c767b25b5a888881ba9ca93b30044ccc39c2937f03c",
"zh:cd14357e520e0f09fb25badfb4f2ee37d7741afdc3ed47c7bcf54c1683772543",
"zh:e05e025f4bb95138c3c8a75c636e97cd7cfd2fc1525b0c8bd097db8c5f02df6e",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
hashes = [
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
"zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831",
"zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3",
"zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b",
"zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2",
"zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865",
"zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03",
"zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602",
"zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014",
]
}

249
databases/lldap.nomad Normal file
View File

@ -0,0 +1,249 @@
job "lldap" {
datacenters = ["dc1"]
type = "service"
priority = 80
group "lldap" {
network {
mode = "bridge"
port "web" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
}
port "ldap" {
%{~ if use_wesher ~}
host_network = "wesher"
%{~ endif ~}
}
port "tls" {}
}
service {
name = "lldap"
provider = "nomad"
port = "ldap"
}
service {
name = "lldap-tls"
provider = "nomad"
port = "tls"
}
service {
name = "ldap-admin"
provider = "nomad"
port = "web"
tags = [
"traefik.enable=true",
"traefik.http.routers.ldap-admin.entryPoints=websecure",
]
}
task "lldap" {
driver = "docker"
config {
image = "ghcr.io/lldap/lldap:v0.5"
ports = ["ldap", "web"]
args = ["run", "--config-file", "$${NOMAD_TASK_DIR}/lldap_config.toml"]
}
env = {
"LLDAP_VERBOSE" = "true"
"LLDAP_LDAP_PORT" = "$${NOMAD_PORT_ldap}"
"LLDAP_HTTP_PORT" = "$${NOMAD_PORT_web}"
"LLDAP_DATABASE_URL_FILE" = "$${NOMAD_SECRETS_DIR}/database_url.txt"
"LLDAP_KEY_SEED_FILE" = "$${NOMAD_SECRETS_DIR}/key_seed.txt"
"LLDAP_JWT_SECRET_FILE" = "$${NOMAD_SECRETS_DIR}/jwt_secret.txt"
"LLDAP_USER_PASS_FILE" = "$${NOMAD_SECRETS_DIR}/user_pass.txt"
"LLDAP_SMTP_OPTIONS__PASSWORD_FILE" = "$${NOMAD_SECRETS_DIR}/smtp_password.txt"
}
template {
data = <<EOH
ldap_base_dn = "{{ with nomadVar "nomad/jobs" }}{{ .ldap_base_dn }}{{ end }}"
{{ with nomadVar "secrets/ldap" -}}
ldap_user_dn = "{{ .admin_user }}"
ldap_user_email = "{{ .admin_email }}"
{{ end -}}
{{ with nomadVar "nomad/jobs/lldap" -}}
[smtp_options]
from = "{{ .smtp_from }}"
reply_to = "{{ .smtp_reply_to }}"
enable_password_reset = true
{{ end -}}
{{ with nomadVar "secrets/smtp" -}}
server = "{{ .server }}"
port = {{ .port }}
tls_required = {{ .tls.Value | toLower }}
user = "{{ .user }}"
{{ end -}}
EOH
destination = "$${NOMAD_TASK_DIR}/lldap_config.toml"
change_mode = "restart"
}
template {
data = "{{ with nomadVar \"nomad/jobs/lldap\" }}mysql://{{ .db_user }}:{{ .db_pass }}@127.0.0.1:3306/{{ .db_name }}{{ end }}"
destination = "$${NOMAD_SECRETS_DIR}/database_url.txt"
change_mode = "restart"
}
template {
data = "{{ with nomadVar \"nomad/jobs/lldap\" }}{{ .key_seed }}{{ end }}"
destination = "$${NOMAD_SECRETS_DIR}/key_seed.txt"
change_mode = "restart"
}
template {
data = "{{ with nomadVar \"nomad/jobs/lldap\" }}{{ .jwt_secret }}{{ end }}"
destination = "$${NOMAD_SECRETS_DIR}/jwt_secret.txt"
change_mode = "restart"
}
template {
data = "{{ with nomadVar \"secrets/ldap\" }}{{ .admin_password }}{{ end }}"
destination = "$${NOMAD_SECRETS_DIR}/user_pass.txt"
change_mode = "restart"
}
template {
data = "{{ with nomadVar \"secrets/smtp\" }}{{ .password }}{{ end }}"
destination = "$${NOMAD_SECRETS_DIR}/smtp_password.txt"
change_mode = "restart"
}
resources {
cpu = 10
memory = 200
memory_max = 200
}
}
task "bootstrap" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = false
}
config {
image = "mariadb:10"
args = [
"/usr/bin/timeout",
"2m",
"/bin/bash",
"-c",
"until /usr/bin/mysql --defaults-extra-file=$${NOMAD_SECRETS_DIR}/my.cnf < $${NOMAD_SECRETS_DIR}/bootstrap.sql; do sleep 10; done",
]
}
template {
data = <<EOF
[client]
host=127.0.0.1
port=3306
user=root
{{ with nomadVar "secrets/mysql" -}}
password={{ .mysql_root_password }}
{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/my.cnf"
}
template {
data = <<EOF
{{ with nomadVar "nomad/jobs/lldap" -}}
{{ $db_name := .db_name }}
CREATE DATABASE IF NOT EXISTS `{{ .db_name }}`
CHARACTER SET = 'utf8mb4'
COLLATE = 'utf8mb4_unicode_ci';
DROP USER IF EXISTS '{{ .db_user }}'@'%';
CREATE USER '{{ .db_user }}'@'%'
IDENTIFIED BY '{{ .db_pass }}';
GRANT ALL ON `{{ .db_name }}`.*
TO '{{ .db_user }}'@'%';
{{ else -}}
SELECT 'NOOP';
{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/bootstrap.sql"
}
resources {
cpu = 50
memory = 50
}
}
task "stunnel" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = true
}
config {
image = "iamthefij/stunnel:latest"
args = ["$${NOMAD_TASK_DIR}/stunnel.conf"]
ports = ["tls"]
}
resources {
cpu = 100
memory = 100
}
template {
data = <<EOF
syslog = no
foreground = yes
delay = yes
[ldap_server]
accept = {{ env "NOMAD_PORT_tls" }}
connect = 127.0.0.1:{{ env "NOMAD_PORT_ldap" }}
ciphers = PSK
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/stunnel_psk.txt
[mysql_client]
client = yes
accept = 127.0.0.1:3306
{{ range nomadService 1 (env "NOMAD_ALLOC_ID") "mysql-tls" -}}
connect = {{ .Address }}:{{ .Port }}
{{- end }}
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/mysql_stunnel_psk.txt
EOF
destination = "$${NOMAD_TASK_DIR}/stunnel.conf"
}
template {
data = <<EOF
{{ range nomadVarList "secrets/ldap/allowed_psks" -}}
{{ with nomadVar .Path }}{{ .psk }}{{ end }}
{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/stunnel_psk.txt"
}
template {
data = <<EOF
{{- with nomadVar "secrets/mysql/allowed_psks/lldap" }}{{ .psk }}{{ end -}}
EOF
destination = "$${NOMAD_SECRETS_DIR}/mysql_stunnel_psk.txt"
}
}
}
}

123
databases/lldap.tf Normal file
View File

@ -0,0 +1,123 @@
resource "nomad_job" "lldap" {
jobspec = templatefile("${path.module}/lldap.nomad", {
use_wesher = var.use_wesher,
})
depends_on = [resource.nomad_job.mysql-server]
# Block until deployed as there are servics dependent on this one
detach = false
}
# Give access to ldap secrets
resource "nomad_acl_policy" "lldap_ldap_secrets" {
name = "lldap-secrets-ldap"
description = "Give access to LDAP secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/ldap/*" {
capabilities = ["read"]
}
path "secrets/ldap" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
# job_id = resource.nomad_job.lldap.id
job_id = "lldap"
}
}
# Create self-scoped psk so that config is valid at first start
resource "random_password" "lldap_ldap_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "lldap_ldap_psk" {
path = "secrets/ldap/allowed_psks/ldap"
items = {
psk = "lldap:${resource.random_password.lldap_ldap_psk.result}"
}
}
# Give access to smtp secrets
resource "nomad_acl_policy" "lldap_smtp_secrets" {
name = "lldap-secrets-smtp"
description = "Give access to SMTP secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/smtp" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
# job_id = resource.nomad_job.lldap.id
job_id = "lldap"
group = "lldap"
task = "lldap"
}
}
# Generate secrets and policies for access to MySQL
resource "nomad_acl_policy" "lldap_mysql_bootstrap_secrets" {
name = "lldap-secrets-mysql"
description = "Give access to MySQL secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
# job_id = resource.nomad_job.lldap.id
job_id = "lldap"
group = "lldap"
task = "bootstrap"
}
}
resource "random_password" "lldap_mysql_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "lldap_mysql_psk" {
path = "secrets/mysql/allowed_psks/lldap"
items = {
psk = "lldap:${resource.random_password.lldap_mysql_psk.result}"
}
}
resource "nomad_acl_policy" "lldap_mysql_psk" {
name = "lldap-secrets-mysql-psk"
description = "Give access to MySQL PSK secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql/allowed_psks/lldap" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
# job_id = resource.nomad_job.lldap.id
job_id = "lldap"
group = "lldap"
task = "stunnel"
}
}

119
databases/mysql.nomad Normal file
View File

@ -0,0 +1,119 @@
job "mysql-server" {
datacenters = ["dc1"]
type = "service"
priority = 80
group "mysql-server" {
count = 1
restart {
attempts = 10
interval = "5m"
delay = "25s"
mode = "delay"
}
network {
mode = "bridge"
port "db" {
static = 3306
}
port "tls" {}
}
volume "mysql-data" {
type = "host"
read_only = false
source = "mysql-data"
}
service {
name = "mysql-server"
provider = "nomad"
port = "db"
}
service {
name = "mysql-tls"
provider = "nomad"
port = "tls"
}
task "mysql-server" {
driver = "docker"
config {
image = "mariadb:10"
ports = ["db"]
args = ["--innodb-buffer-pool-size=1G"]
}
volume_mount {
volume = "mysql-data"
destination = "/var/lib/mysql"
read_only = false
}
env = {
# Allow connections from any host
"MYSQL_ROOT_HOST" = "%"
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/mysql-server" }}
MYSQL_ROOT_PASSWORD={{ .mysql_root_password }}
{{ end }}
EOH
destination = "${NOMAD_SECRETS_DIR}/db.env"
env = true
}
resources {
cpu = 300
memory = 1536
}
}
task "stunnel" {
driver = "docker"
config {
image = "iamthefij/stunnel:latest"
args = ["${NOMAD_TASK_DIR}/stunnel.conf"]
ports = ["tls"]
}
resources {
cpu = 100
memory = 100
}
template {
data = <<EOF
syslog = no
foreground = yes
delay = yes
[mysql_server]
accept = {{ env "NOMAD_PORT_tls" }}
connect = 127.0.0.1:3306
ciphers = PSK
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/stunnel_psk.txt
EOF
destination = "${NOMAD_TASK_DIR}/stunnel.conf"
}
template {
data = <<EOF
{{ range nomadVarList "secrets/mysql/allowed_psks" -}}
{{ with nomadVar .Path }}{{ .psk }}{{ end }}
{{ end -}}
EOF
destination = "${NOMAD_SECRETS_DIR}/stunnel_psk.txt"
}
}
}
}

41
databases/mysql.tf Normal file
View File

@ -0,0 +1,41 @@
resource "nomad_job" "mysql-server" {
jobspec = file("${path.module}/mysql.nomad")
# Block until deployed as there are servics dependent on this one
detach = false
}
resource "nomad_acl_policy" "secrets_mysql" {
name = "secrets-mysql"
description = "Give access to MySQL secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/mysql" {
capabilities = ["read"]
}
path "secrets/mysql/*" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
# job_id = resource.nomad_job.mysql-server.id
job_id = "mysql-server"
}
}
# Create self-scoped psk so that config is valid at first start
resource "random_password" "mysql_mysql_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "mysql_mysql_psk" {
path = "secrets/mysql/allowed_psks/mysql"
items = {
psk = "mysql:${resource.random_password.mysql_mysql_psk.result}"
}
}

120
databases/postgres.nomad Normal file
View File

@ -0,0 +1,120 @@
job "postgres-server" {
datacenters = ["dc1"]
type = "service"
priority = 80
group "postgres-server" {
count = 1
restart {
attempts = 10
interval = "5m"
delay = "25s"
mode = "delay"
}
network {
mode = "bridge"
port "db" {
static = 5432
}
port "tls" {}
}
volume "postgres-data" {
type = "host"
read_only = false
source = "postgres-data"
}
service {
name = "postgres-server"
provider = "nomad"
port = "db"
}
service {
name = "postgres-tls"
provider = "nomad"
port = "tls"
}
task "postgres-server" {
driver = "docker"
config {
image = "postgres:14"
ports = ["db"]
}
volume_mount {
volume = "postgres-data"
destination = "/var/lib/postgresql/data"
read_only = false
}
env = {
# Allow connections from any host
"MYSQL_ROOT_HOST" = "%"
}
template {
data = <<EOH
{{ with nomadVar "nomad/jobs/postgres-server" }}
POSTGRES_USER={{ .superuser }}
POSTGRES_PASSWORD={{ .superuser_pass }}
{{ end }}
EOH
destination = "secrets/db.env"
env = true
}
resources {
cpu = 500
memory = 700
memory_max = 1200
}
}
task "stunnel" {
driver = "docker"
config {
image = "iamthefij/stunnel:latest"
args = ["${NOMAD_TASK_DIR}/stunnel.conf"]
ports = ["tls"]
}
resources {
cpu = 100
memory = 100
}
template {
data = <<EOF
syslog = no
foreground = yes
delay = yes
[postgres_server]
accept = {{ env "NOMAD_PORT_tls" }}
connect = 127.0.0.1:5432
ciphers = PSK
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/stunnel_psk.txt
EOF
destination = "${NOMAD_TASK_DIR}/stunnel.conf"
}
template {
data = <<EOF
{{ range nomadVarList "secrets/postgres/allowed_psks" -}}
{{ with nomadVar .Path }}{{ .psk }}{{ end }}
{{ end -}}
EOF
destination = "${NOMAD_SECRETS_DIR}/stunnel_psk.txt"
}
}
}
}

41
databases/postgres.tf Normal file
View File

@ -0,0 +1,41 @@
resource "nomad_job" "postgres-server" {
jobspec = file("${path.module}/postgres.nomad")
# Block until deployed as there are servics dependent on this one
detach = false
}
resource "nomad_acl_policy" "secrets_postgres" {
name = "secrets-postgres"
description = "Give access to Postgres secrets"
rules_hcl = <<EOH
namespace "default" {
variables {
path "secrets/postgres" {
capabilities = ["read"]
}
path "secrets/postgres/*" {
capabilities = ["read"]
}
}
}
EOH
job_acl {
# job_id = resource.nomad_job.postgres-server.id
job_id = "postgres-server"
}
}
# Create self-scoped psk so that config is valid at first start
resource "random_password" "postgres_postgres_psk" {
length = 32
override_special = "!@#%&*-_="
}
resource "nomad_variable" "postgres_postgres_psk" {
path = "secrets/postgres/allowed_psks/postgres"
items = {
psk = "postgres:${resource.random_password.postgres_postgres_psk.result}"
}
}

83
databases/redis.nomad Normal file
View File

@ -0,0 +1,83 @@
job "redis-${name}" {
datacenters = ["dc1"]
type = "service"
priority = 80
group "cache" {
count = 1
ephemeral_disk {
migrate = true
sticky = true
size = 300
}
network {
mode = "bridge"
port "tls" {}
}
service {
name = "redis-${name}"
provider = "nomad"
port = "tls"
}
task "redis" {
driver = "docker"
config {
image = "redis:6"
args = ["redis-server", "--save", "60", "1", "--loglevel", "warning", "--dir", "$${NOMAD_ALLOC_DIR}/data"]
ports = ["main"]
}
resources {
cpu = 100
memory = 64
memory_max = 512
}
}
task "stunnel" {
driver = "docker"
config {
image = "iamthefij/stunnel:latest"
args = ["$${NOMAD_TASK_DIR}/stunnel.conf"]
ports = ["tls"]
}
resources {
cpu = 50
memory = 15
}
template {
data = <<EOF
syslog = no
foreground = yes
delay = yes
[redis_server]
accept = {{ env "NOMAD_PORT_tls" }}
connect = 127.0.0.1:6379
ciphers = PSK
PSKsecrets = {{ env "NOMAD_SECRETS_DIR" }}/stunnel_psk.txt
EOF
destination = "$${NOMAD_TASK_DIR}/stunnel.conf"
}
template {
data = <<EOF
{{ with nomadVar "nomad/jobs/redis-${name}" -}}
{{ .allowed_psks }}
{{- end }}
EOF
destination = "$${NOMAD_SECRETS_DIR}/stunnel_psk.txt"
}
}
}
}

12
databases/redis.tf Normal file
View File

@ -0,0 +1,12 @@
resource "nomad_job" "redis" {
for_each = toset(["blocky", "authelia"])
jobspec = templatefile("${path.module}/redis.nomad",
{
name = each.key,
}
)
# Block until deployed as there are servics dependent on this one
detach = false
}

5
databases/vars.tf Normal file
View File

@ -0,0 +1,5 @@
variable "use_wesher" {
type = bool
description = "Indicates whether or not services should expose themselves on the wesher network"
default = true
}

View File

@ -1,47 +0,0 @@
---
- name: Setup Docker on host
hosts: docker_hosts
vars:
user_name: "{{ create_user | default(ansible_user) }}"
docker_config_path: >-
{% if ansible_facts.os_family == 'Darwin' -%}
~/.docker/daemon.json
{%- else -%}
/etc/docker/daemon.json
{%- endif %}
ansible_python_interpreter: python3
roles:
- docker_install
tasks:
- name: Add to Docker group
user:
name: "{{ user_name }}"
groups: docker
append: true
become: true
- name: Use journald logging driver # noqa 207
json_merge:
path: "{{ docker_config_path }}"
allow_create: true
format_indent: true
update_json: {
"log-driver": "journald",
"log-opts": {
"tag": "{{ '{{ .Name }}/{{ .ImageName }}/{{ .ID }}' }}",
"labels": "com.docker.compose.project,com.docker.compose.service"
}
}
become: true
notify: Restart Docker daemon
handlers:
- name: Restart Docker daemon
service:
name: docker
state: restarted
become: true
when: ansible_facts['os_family'] != "Darwin"

View File

@ -1,144 +0,0 @@
#!/usr/bin/env bash
export VERIFY_CHECKSUM=0
export ALIAS_NAME=
export OWNER=jsiebens
export REPO=hashi-up
export SUCCESS_CMD="$REPO version"
export BINLOCATION="~/bin"
###############################
# Content common across repos #
###############################
version=$(curl -sI https://github.com/$OWNER/$REPO/releases/latest | grep -i location: | awk -F"/" '{ printf "%s", $NF }' | tr -d '\r')
if [ ! $version ]; then
echo "Failed while attempting to install $REPO. Please manually install:"
echo ""
echo "1. Open your web browser and go to https://github.com/$OWNER/$REPO/releases"
echo "2. Download the latest release for your platform. Call it '$REPO'."
echo "3. chmod +x ./$REPO"
echo "4. mv ./$REPO $BINLOCATION"
if [ -n "$ALIAS_NAME" ]; then
echo "5. ln -sf $BINLOCATION/$REPO /usr/local/bin/$ALIAS_NAME"
fi
exit 1
fi
getPackage() {
uname=$(uname)
userid=$(id -u)
suffix=""
case $uname in
"Darwin")
suffix="-darwin"
;;
"MINGW"*)
suffix=".exe"
BINLOCATION="$HOME/bin"
mkdir -p $BINLOCATION
;;
"Linux")
arch=$(uname -m)
case $arch in
"aarch64")
suffix="-arm64"
;;
esac
case $arch in
"armv6l" | "armv7l")
suffix="-armhf"
;;
esac
;;
esac
targetFile="/tmp/$REPO$suffix"
if [ "$userid" != "0" ]; then
targetFile="$(pwd)/$REPO$suffix"
fi
if [ -e "$targetFile" ]; then
rm "$targetFile"
fi
url=https://github.com/$OWNER/$REPO/releases/download/$version/$REPO$suffix
echo "Downloading package $url as $targetFile"
curl -sSL $url --output "$targetFile"
if [ "$?" = "0" ]; then
if [ "$VERIFY_CHECKSUM" = "1" ]; then
checkHash
fi
chmod +x "$targetFile"
echo "Download complete."
if [ ! -w "$BINLOCATION" ]; then
echo
echo "============================================================"
echo " The script was run as a user who is unable to write"
echo " to $BINLOCATION. To complete the installation the"
echo " following commands may need to be run manually."
echo "============================================================"
echo
echo " sudo cp $REPO$suffix $BINLOCATION/$REPO"
if [ -n "$ALIAS_NAME" ]; then
echo " sudo ln -sf $BINLOCATION/$REPO $BINLOCATION/$ALIAS_NAME"
fi
echo
else
echo
echo "Running with sufficient permissions to attempt to move $REPO to $BINLOCATION"
if [ ! -w "$BINLOCATION/$REPO" ] && [ -f "$BINLOCATION/$REPO" ]; then
echo
echo "================================================================"
echo " $BINLOCATION/$REPO already exists and is not writeable"
echo " by the current user. Please adjust the binary ownership"
echo " or run sh/bash with sudo."
echo "================================================================"
echo
exit 1
fi
mv "$targetFile" $BINLOCATION/$REPO
if [ "$?" = "0" ]; then
echo "New version of $REPO installed to $BINLOCATION"
fi
if [ -e "$targetFile" ]; then
rm "$targetFile"
fi
if [ $(which $ALIAS_NAME) ]; then
echo "There is already a command '$ALIAS_NAME' in the path, NOT creating alias"
else
if [ -n "$ALIAS_NAME" ]; then
if [ ! -L $BINLOCATION/$ALIAS_NAME ]; then
ln -s $BINLOCATION/$REPO $BINLOCATION/$ALIAS_NAME
echo "Creating alias '$ALIAS_NAME' for '$REPO'."
fi
fi
fi
${SUCCESS_CMD}
fi
fi
}
getPackage

34
main.tf Normal file
View File

@ -0,0 +1,34 @@
module "databases" {
source = "./databases"
use_wesher = var.use_wesher
}
module "core" {
source = "./core"
base_hostname = var.base_hostname
use_wesher = var.use_wesher
# Metrics and Blocky depend on databases
depends_on = [module.databases]
}
module "services" {
source = "./services"
base_hostname = var.base_hostname
use_wesher = var.use_wesher
# NOTE: It may be possible to flip this and core so core templates don't
# need to be rerendered every time a service goes up or down.
depends_on = [module.databases, module.core]
}
module "backups" {
source = "./backups"
use_wesher = var.use_wesher
depends_on = [module.databases, module.services, module.core]
}

View File

@ -1,20 +0,0 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/nomad" {
version = "1.4.16"
hashes = [
"h1:tyfjD/maKzb0RxxD9KWgLnkJu9lnYziYsQgGw85Giz8=",
"zh:0d4fbb7030d9caac3b123e60afa44f50c83cc2a983e1866aec7f30414abe7b0e",
"zh:0db080228e07c72d6d8ca8c45249d6f97cd0189fce82a77abbdcd49a52e57572",
"zh:0df88393271078533a217654b96f0672c60eb59570d72e6aefcb839eea87a7a0",
"zh:2883b335bb6044b0db6a00e602d6926c047c7f330294a73a90d089f98b24d084",
"zh:390158d928009a041b3a182bdd82376b50530805ae92be2b84ed7c3b0fa902a0",
"zh:7169b8f8df4b8e9659c49043848fd5f7f8473d0471f67815e8b04980f827f5ef",
"zh:9417ee1383b1edd137024882d7035be4dca51fb4f725ca00ed87729086ec1755",
"zh:a22910b5a29eeab5610350700b4899267c1b09b66cf21f7e4d06afc61d425800",
"zh:a6185c9cd7aa458cd81861058ba568b6411fbac344373a20155e20256f4a7557",
"zh:b6260ca9f034df1b47905b4e2a9c33b67dbf77224a694d5b10fb09ae92ffad4c",
"zh:d87c12a6a7768f2b6c2a59495c7dc00f9ecc52b1b868331d4c284f791e278a1e",
]
}

View File

@ -1,225 +0,0 @@
job "metrics" {
datacenters = ["dc1"]
type = "system"
group "exporters" {
network {
mode = "bridge"
port "cadvisor" {
host_network = "nomad-bridge"
to = 8080
}
port "node_exporter" {
host_network = "nomad-bridge"
to = 9100
}
port "promtail" {
host_network = "nomad-bridge"
to = 9080
}
port "expose" {
host_network = "nomad-bridge"
}
port "cadvisor_envoy_metrics" {
host_network = "nomad-bridge"
to = 9102
}
}
service {
name = "cadvisor"
port = "cadvisor"
meta {
metrics_addr = "${NOMAD_ADDR_expose}"
envoy_metrics_addr = "${NOMAD_ADDR_cadvisor_envoy_metrics}"
nomad_dc = "${NOMAD_DC}"
nomad_node_name = "${node.unique.name}"
}
connect {
sidecar_service {
proxy {
local_service_port = 8080
expose {
path {
path = "/metrics"
protocol = "http"
local_path_port = 8080
listener_port = "expose"
}
}
config {
envoy_prometheus_bind_addr = "0.0.0.0:9102"
}
}
}
sidecar_task {
resources {
cpu = 50
memory = 50
}
}
}
check {
type = "http"
path = "/metrics"
port = "cadvisor"
interval = "10s"
timeout = "10s"
}
// TODO: Remove traefik tags
tags = [
"traefik.enable=true",
"traefik.http.routers.cadvisor.entrypoints=web,websecure",
"traefik.http.routers.cadvisor.rule=Host(`cadvisor.dev.homelab`)",
"traefik.http.routers.cadvisor.tls=true",
]
}
task "cadvisor" {
driver = "docker"
config {
# image = "iamthefij/cadvisor:0.37.5"
image = "gcr.io/cadvisor/cadvisor:v0.39.3"
args = ["--docker_only=true"]
ports = ["cadvisor"]
# volumes = [
# "/:/rootfs:ro",
# "/var/run:/var/run:rw",
# "/sys:/sys:ro",
# "/var/lib/docker/:/var/lib/docker:ro",
# "/cgroup:/cgroup:ro",
# "/etc/machine-id:/etc/machine-id:ro",
# ]
mount {
type = "bind"
source = "/"
target = "/rootfs"
readonly = true
}
mount {
type = "bind"
source = "/var/run"
target = "/var/run"
readonly = false
}
mount {
type = "bind"
source = "/sys"
target = "/sys"
readonly = true
}
mount {
type = "bind"
source = "/var/lib/docker"
target = "/var/lib/docker"
readonly = true
}
# mount {
# type = "bind"
# source = "/cgroup"
# target = "/cgroup"
# readonly = true
# }
mount {
type = "bind"
source = "/etc/machine-id"
target = "/etc/machine-id"
readonly = true
}
}
resources {
cpu = 50
memory = 100
}
}
service {
name = "nodeexporter"
port = "node_exporter"
meta {
metrics_addr = "${NOMAD_ADDR_node_exporter}"
nomad_dc = "${NOMAD_DC}"
nomad_node_name = "${node.unique.name}"
}
connect {
sidecar_service {
proxy {
local_service_port = 9100
}
}
sidecar_task {
resources {
cpu = 50
memory = 50
}
}
}
check {
type = "http"
path = "/metrics"
port = "node_exporter"
interval = "10s"
timeout = "10s"
}
// TODO: Remove traefik tags
tags = [
"traefik.enable=true",
"traefik.http.routers.node_exporter.entrypoints=web,websecure",
"traefik.http.routers.node_exporter.rule=Host(`node_exporter.dev.homelab`)",
"traefik.http.routers.node_exporter.tls=true",
]
}
task "node_exporter" {
driver = "docker"
config {
image = "prom/node-exporter:v1.0.1"
args = ["--path.rootfs", "/host"]
ports = ["node_exporter"]
mount {
type = "bind"
source = "/"
target = "/host"
readonly = true
}
}
resources {
cpu = 50
memory = 50
}
}
}
}

View File

@ -1,127 +0,0 @@
job "grafana" {
datacenters = ["dc1"]
group "grafana" {
count = 1
network {
mode = "bridge"
port "web" {
host_network = "loopback"
to = 3000
}
}
ephemeral_disk {
migrate = true
sticky = true
}
service {
name = "grafana"
port = "web"
connect {
sidecar_service {
proxy {
local_service_port = 3000
upstreams {
destination_name = "prometheus"
local_bind_port = 9090
}
}
}
sidecar_task {
resources {
cpu = 50
memory = 50
}
}
}
check {
type = "http"
path = "/"
port = "web"
interval = "10s"
timeout = "10s"
}
tags = [
"traefik.enable=true",
"traefik.http.routers.grafana.entryPoints=websecure",
]
}
task "grafana" {
driver = "docker"
config {
image = "grafana/grafana:7.3.6"
ports = ["web"]
mount {
type = "bind"
target = "/etc/grafana/grafana.ini"
source = "local/config/grafana.ini"
}
mount {
type = "bind"
target = "/etc/grafana/provisioning"
source = "local/config/provisioning"
}
}
env = {
"GF_SECURITY_ADMIN_PASSWORD" = "password",
"GF_INSTALL_PLUGINS" = "grafana-clock-panel,grafana-piechart-panel,grafana-polystat-panel",
}
template {
data = <<EOF
[paths]
# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used)
data = /var/lib/grafana
# folder that contains provisioning config files that grafana will apply on startup and while running.
provisioning = /etc/grafana/provisioning
[server]
# Protocol (http, https, socket)
protocol = http
http_port = 3000
EOF
change_mode = "signal"
change_signal = "SIGHUP"
destination = "local/config/grafana.ini"
}
template {
data = <<EOF
---
apiVersion: 1
datasources:
- name: Prometheus
url: http://${NOMAD_UPSTREAM_ADDR_prometheus}
type: prometheus
access: proxy
isDefault: true
version: 1
EOF
change_mode = "signal"
change_signal = "SIGHUP"
destination = "local/config/provisioning/datasources/prometheus.yml"
}
resources {
cpu = 100
memory = 200
}
}
}
}

View File

@ -1,64 +0,0 @@
variable "consul_address" {
type = string
description = "address of consul server for dynamic scraping"
}
# resource "nomad_job" "exporters" {
# hcl2 {
# enabled = true
# }
#
# jobspec = file("${path.module}/exporters.nomad")
# }
data "consul_nodes" "all-nodes" {
query_options {
datacenter = "dc1"
}
}
resource "nomad_job" "prometheus" {
hcl2 {
enabled = true
vars = {
# "consul_address" = "${var.consul_address}",
# TODO: Should this be a list?
"consul_address" = "http://${data.consul_nodes.all-nodes.nodes[0].address}:8500",
}
}
jobspec = file("${path.module}/prometheus.nomad")
}
resource "nomad_job" "grafana" {
hcl2 {
enabled = true
}
jobspec = file("${path.module}/grafana.nomad")
}
resource "consul_config_entry" "prometheus_intent" {
name = "prometheus"
kind = "service-intentions"
config_json = jsonencode({
Sources = [
{
Action = "allow"
Name = "grafana"
Precedence = 9
Type = "consul"
},
]
})
}
# resource "consul_config_entry" "envoy_prometheus_bind" {
# name = "global"
# kind = "proxy-defaults"
#
# config_json = jsonencode({
# "envoy_prometheus_bind_addr" = "0.0.0.0:9102"
# })
# }

View File

@ -1,187 +0,0 @@
variable "consul_address" {
type = string
description = "Full address of Consul instance to get catalog from"
default = "http://127.0.0.1:5400"
}
job "prometheus" {
datacenters = ["dc1"]
group "prometheus" {
count = 1
network {
mode = "bridge"
port "web" {
host_network = "loopback"
to = 9090
}
}
ephemeral_disk {
migrate = true
sticky = true
}
service {
name = "prometheus"
port = "web"
connect {
sidecar_service {
proxy {
local_service_port = 9090
}
}
sidecar_task {
resources {
cpu = 50
memory = 50
}
}
}
check {
type = "http"
path = "/"
port = "web"
interval = "10s"
timeout = "10s"
}
// TODO: Remove traefik tags
tags = [
"traefik.enable=true",
"traefik.http.routers.prometheus.entryPoints=websecure",
]
}
task "prometheus" {
driver = "docker"
config {
image = "prom/prometheus:v2.30.2"
ports = ["web"]
args = [
"--config.file=/etc/prometheus/config/prometheus.yml",
"--storage.tsdb.path=/prometheus",
"--web.listen-address=0.0.0.0:9090",
"--web.console.libraries=/usr/share/prometheus/console_libraries",
"--web.console.templates=/usr/share/prometheus/consoles",
]
mount {
type = "bind"
target = "/etc/prometheus/config"
source = "local/config"
}
}
template {
data = <<EOF
---
global:
scrape_interval: 30s
evaluation_interval: 3s
scrape_configs:
- job_name: prometheus
static_configs:
- targets:
- 0.0.0.0:9090
- job_name: "nomad_server"
metrics_path: "/v1/metrics"
params:
format:
- "prometheus"
consul_sd_configs:
- server: "${var.consul_address}"
services:
- "nomad"
tags:
- "http"
- job_name: "nomad_client"
metrics_path: "/v1/metrics"
params:
format:
- "prometheus"
consul_sd_configs:
- server: "${var.consul_address}"
services:
- "nomad-client"
- job_name: "consul"
metrics_path: "/v1/agent/metrics"
params:
format:
- "prometheus"
consul_sd_configs:
- server: "${var.consul_address}"
services:
- "consul"
relabel_configs:
- source_labels: [__meta_consul_address]
replacement: $1:8500
target_label: __address__
- job_name: "exporters"
metrics_path: "/metrics"
consul_sd_configs:
- server: "${var.consul_address}"
services:
- "cadvisor"
- "nodeexporter"
- "blocky-api"
relabel_configs:
- source_labels: [__meta_consul_service_metadata_metrics_addr]
action: keep
regex: (.+)
- source_labels: [__meta_consul_service_metadata_metrics_addr]
target_label: __address__
- source_labels: [__meta_consul_service]
target_label: consul_service
- source_labels: [__meta_consul_node]
target_label: consul_node
- source_labels: [__meta_consul_service_nomad_dc]
target_label: nomad_dc
- source_labels: [__meta_consul_service_nomad_node_name]
target_label: nomad_node_name
- job_name: "envoy"
metrics_path: "/metrics"
consul_sd_configs:
- server: "${var.consul_address}"
relabel_configs:
- source_labels: [__meta_consul_service]
action: drop
regex: (.+)-sidecar-proxy
- source_labels: [__meta_consul_service_metadata_envoy_metrics_addr]
action: keep
regex: (.+)
- source_labels: [__meta_consul_service_metadata_envoy_metrics_addr]
target_label: __address__
- source_labels: [__meta_consul_service]
target_label: consul_service
- source_labels: [__meta_consul_node]
target_label: consul_node
- source_labels: [__meta_consul_service_nomad_dc]
target_label: nomad_dc
- source_labels: [__meta_consul_service_nomad_node_name]
target_label: nomad_node_name
EOF
change_mode = "signal"
change_signal = "SIGHUP"
destination = "local/config/prometheus.yml"
}
resources {
cpu = 100
memory = 200
}
}
}
}

View File

@ -1,70 +0,0 @@
job "adminer" {
datacenters = ["dc1"]
type = "service"
group "adminer" {
count = 1
# Some affinity to stateful hosts?
network {
mode = "bridge"
port "adminer" {
host_network = "loopback"
to = 8080
}
}
service {
name = "adminer"
port = "adminer"
connect {
sidecar_service {
proxy {
local_service_port = 8080
upstreams {
destination_name = "mysql-server"
# TODO: how do I get these to not bind to the host eth0 address
local_bind_port = 4040
}
config {
protocol = "tcp"
}
}
}
sidecar_task {
resources {
cpu = 50
memory = 25
}
}
}
tags = [
"traefik.enable=true",
"traefik.http.routers.adminer.entryPoints=websecure",
]
}
task "adminer" {
driver = "docker"
config {
image = "adminer"
ports = ["adminer"]
}
env = {
"ADMINER_DEFAULT_SERVER" = "${NOMAD_UPSTREAM_ADDR_mysql_server}"
}
resources {
cpu = 50
memory = 50
}
}
}
}

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