diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66f378d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.passwords diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..06ef654 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "william-voyek.vscode-nginx" + ] +} \ No newline at end of file diff --git a/ansible.cfg b/ansible.cfg index 72e7995..6797fd3 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -1,4 +1,4 @@ [defaults] inventory = inventory remote_user = ansible -# Will use yubikey for ssh authentication \ No newline at end of file +# Will use yubikey or bitwarden for ssh authentication \ No newline at end of file diff --git a/decrypt-vault.sh b/decrypt-vault.sh new file mode 100755 index 0000000..21a6603 --- /dev/null +++ b/decrypt-vault.sh @@ -0,0 +1,8 @@ +#!/bin/bash +./helpers/get-passwords.sh + +ansible-vault decrypt vars/bootstrap.yaml --vault-password-file ./.passwords/vault +ansible-vault decrypt vars/bigboi.yaml --vault-password-file ./.passwords/vault +ansible-vault decrypt vars/smolboi.yaml --vault-password-file ./.passwords/vault + +./helpers/delete-passwords.sh diff --git a/encrypt-vault.sh b/encrypt-vault.sh new file mode 100755 index 0000000..4cd0214 --- /dev/null +++ b/encrypt-vault.sh @@ -0,0 +1,8 @@ +#!/bin/bash +./helpers/get-passwords.sh + +ansible-vault encrypt vars/bootstrap.yaml --vault-password-file ./.passwords/vault +ansible-vault encrypt vars/bigboi.yaml --vault-password-file ./.passwords/vault +ansible-vault encrypt vars/smolboi.yaml --vault-password-file ./.passwords/vault + +./helpers/delete-passwords.sh diff --git a/git-hooks/commit-msg b/git-hooks/commit-msg new file mode 100755 index 0000000..e7a69ee --- /dev/null +++ b/git-hooks/commit-msg @@ -0,0 +1,16 @@ +#!/bin/bash +# TODO: Clean this up so it iterates over the vars files +if grep -qE "vault_decrypted: true" "./vars/bigboi.yaml";then + echo "The vault isn't encrypted, run './encrypt-vault.sh' before committing." + exit 1 +fi + +if grep -qE "vault_decrypted: true" "./vars/bootstrap.yaml";then + echo "The vault isn't encrypted, run './encrypt-vault.sh' before committing." + exit 1 +fi + +if grep -qE "vault_decrypted: true" "./vars/smolboi.yaml";then + echo "The vault isn't encrypted, run './encrypt-vault.sh' before committing." + exit 1 +fi \ No newline at end of file diff --git a/helpers/delete-passwords.sh b/helpers/delete-passwords.sh new file mode 100755 index 0000000..4fc51c5 --- /dev/null +++ b/helpers/delete-passwords.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +if [[ -d "./.passwords" ]] +then + rm -rf "./.passwords" + echo "Deleted passwords" +else + echo "There were no passwords to delete" +fi \ No newline at end of file diff --git a/helpers/generate-password.sh b/helpers/generate-password.sh new file mode 100755 index 0000000..da87635 --- /dev/null +++ b/helpers/generate-password.sh @@ -0,0 +1,2 @@ +#!/bin/sh +mkpasswd --method=sha-512 \ No newline at end of file diff --git a/helpers/get-passwords.sh b/helpers/get-passwords.sh new file mode 100755 index 0000000..34156c7 --- /dev/null +++ b/helpers/get-passwords.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +if ! [[ -d "./.passwords" ]] +then + mkdir "./.passwords" + echo "Created passwords directory" +fi + +bw get password "ansible vault encryption" > ./.passwords/vault +echo "Fetched vault password" +bw get password "linux user ansible@bigboi" > ./.passwords/become +echo "Fetched ansible user password" +bw get password "linux user wholteza@bigboi" > ./.passwords/bootstrap_become +echo "Fetched bootstrap user password" \ No newline at end of file diff --git a/install-requirements.sh b/install-requirements.sh new file mode 100755 index 0000000..52296a9 --- /dev/null +++ b/install-requirements.sh @@ -0,0 +1,8 @@ +#!/bin/sh +sudo apt update \ + && sudo apt install whois ansible \ + && ansible-galaxy collection install community.general \ + && ansible-galaxy collection install community.libvirt \ + && ansible-galaxy collection install ansible.posix + +cp ./git-hooks/commit-msg ./.git/hooks/commit-msg \ No newline at end of file diff --git a/inventory b/inventory index ea99993..d04a7d3 100644 --- a/inventory +++ b/inventory @@ -1,5 +1,5 @@ -[nas] -temp.zacke.xyz +[production_main] +bigboi.zacke.xyz -[reverse-proxy] -temp.zacke.xyz \ No newline at end of file +[production_first_backup] +smolboi.zacke.xyz diff --git a/main.yaml b/main.yaml index e593a56..c38a683 100644 --- a/main.yaml +++ b/main.yaml @@ -1,11 +1,46 @@ --- +# - hosts: all +# become: true +# remote_user: wholteza +# roles: +# - role: shared/bootstrap +# vars_files: +# - vars/bootstrap.yaml -- hosts: all - become: true - roles: - - role: base - -- hosts: nas +- hosts: production_main become: true roles: - - role: nas \ No newline at end of file + # - role: shared/update + # - role: bigboi/users + # - role: bigboi/network + # - role: shared/avahi + # - role: bigboi/nginx + # - role: shared/ssmtp + # - role: bigboi/zfs + # - role: shared/zfs-zed + # - role: bigboi/samba + # - role: bigboi/sanoid + # - role: shared/powertop + # - role: bigboi/apcupsd + # - role: bigboi/libvirt + # - role: bigboi/kvm-homeassistant + - role: bigboi/docker + # - role: artis3n.tailscale + # - role: shared/various-cli-tools + vars_files: + - vars/bigboi.yaml + +- hosts: production_first_backup + become: true + roles: + # - role: shared/update + # - role: shared/powertop + # - role: shared/avahi + # - role: shared/ssmtp + # - role: shared/zfs-install + # - role: smolboi/zfs + # - role: shared/zfs-zed + # - role: smolboi/sanoid + # - role: shared/various-cli-tools + vars_files: + - vars/smolboi.yaml diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..d080a60 --- /dev/null +++ b/readme.md @@ -0,0 +1,27 @@ +## Installing and running + +Prior to following the steps below you need to set up the ssh agent to work with the gpg key from your yubikey and also the `pass` password store. + +1. Install the required packages with `install-requirements.sh` +2. Run the playbook `run-playbook.sh` + - The script automatically fetches the required passwords from the password store + +The playbook requires the user `ansible` to be present on the target systems with the yubikey ssh key as authorized key and member of the sudoers group. + +The required password to be set on the ansible user can be found in `pass wholteza/network/lilleback/ansible/ansible`. + +While running fetched passwords will be placed in the `.temp` directory. Any script that creates that directory must delete it afterwards. + +## Making changes to the vault + +The vault encryption is managed by `ansible-vault` + passwords from `pass`. + +Use `./decrypt-vault.sh` and `./encrypt-vault.sh` to make the file into clear text and the other way around. + +There is a pre-commit git hook that will prevent you from committing if the file is clear text. + +Be careful because even if you have a non-encrypted version of the vault staged and the unstaged file is encrypted you will be able to commit atm. + +## Generating passwords for the vault file + +1. Use `generate-password.sh` to generate a hash of your password. diff --git a/roles/bigboi/apcupsd/files/apcupsd.conf b/roles/bigboi/apcupsd/files/apcupsd.conf new file mode 100644 index 0000000..e15f966 --- /dev/null +++ b/roles/bigboi/apcupsd/files/apcupsd.conf @@ -0,0 +1,343 @@ +## apcupsd.conf v1.1 ## +# +# "apcupsd" POSIX config file + +# +# Note that the apcupsd daemon must be restarted in order for changes to +# this configuration file to become active. +# + +# +# ========= General configuration parameters ============ +# + +# UPSNAME xxx +# Use this to give your UPS a name in log files and such. This +# is particulary useful if you have multiple UPSes. This does not +# set the EEPROM. It should be 8 characters or less. +#UPSNAME + +# UPSCABLE +# Defines the type of cable connecting the UPS to your computer. +# +# Possible generic choices for are: +# simple, smart, ether, usb +# +# Or a specific cable model number may be used: +# 940-0119A, 940-0127A, 940-0128A, 940-0020B, +# 940-0020C, 940-0023A, 940-0024B, 940-0024C, +# 940-1524C, 940-0024G, 940-0095A, 940-0095B, +# 940-0095C, 940-0625A, M-04-02-2000 +# +UPSCABLE usb + +# To get apcupsd to work, in addition to defining the cable +# above, you must also define a UPSTYPE, which corresponds to +# the type of UPS you have (see the Description for more details). +# You must also specify a DEVICE, sometimes referred to as a port. +# For USB UPSes, please leave the DEVICE directive blank. For +# other UPS types, you must specify an appropriate port or address. +# +# UPSTYPE DEVICE Description +# apcsmart /dev/tty** Newer serial character device, appropriate for +# SmartUPS models using a serial cable (not USB). +# +# usb Most new UPSes are USB. A blank DEVICE +# setting enables autodetection, which is +# the best choice for most installations. +# +# net hostname:port Network link to a master apcupsd through apcupsd's +# Network Information Server. This is used if the +# UPS powering your computer is connected to a +# different computer for monitoring. +# +# snmp hostname:port:vendor:community +# SNMP network link to an SNMP-enabled UPS device. +# Hostname is the ip address or hostname of the UPS +# on the network. Vendor can be can be "APC" or +# "APC_NOTRAP". "APC_NOTRAP" will disable SNMP trap +# catching; you usually want "APC". Port is usually +# 161. Community is usually "private". +# +# netsnmp hostname:port:vendor:community +# OBSOLETE +# Same as SNMP above but requires use of the +# net-snmp library. Unless you have a specific need +# for this old driver, you should use 'snmp' instead. +# +# dumb /dev/tty** Old serial character device for use with +# simple-signaling UPSes. +# +# pcnet ipaddr:username:passphrase:port +# PowerChute Network Shutdown protocol which can be +# used as an alternative to SNMP with the AP9617 +# family of smart slot cards. ipaddr is the IP +# address of the UPS management card. username and +# passphrase are the credentials for which the card +# has been configured. port is the port number on +# which to listen for messages from the UPS, normally +# 3052. If this parameter is empty or missing, the +# default of 3052 will be used. +# +# modbus /dev/tty** Serial device for use with newest SmartUPS models +# supporting the MODBUS protocol. +# modbus Leave the DEVICE setting blank for MODBUS over USB +# or set to the serial number of the UPS to ensure +# that apcupsd binds to that particular unit +# (helpful if you have more than one USB UPS). +# +UPSTYPE usb +#DEVICE /dev/ttyS0 + +# POLLTIME +# Interval (in seconds) at which apcupsd polls the UPS for status. This +# setting applies both to directly-attached UPSes (UPSTYPE apcsmart, usb, +# dumb) and networked UPSes (UPSTYPE net, snmp). Lowering this setting +# will improve apcupsd's responsiveness to certain events at the cost of +# higher CPU utilization. The default of 60 is appropriate for most +# situations. +#POLLTIME 60 + +# LOCKFILE +# Path for device lock file for UPSes connected via USB or +# serial port. This is the directory into which the lock file +# will be written. The directory must already exist; apcupsd will not create +# it. The actual name of the lock file is computed from DEVICE. +# Not used on Win32. +LOCKFILE /var/lock + +# SCRIPTDIR +# Directory in which apccontrol and event scripts are located. +SCRIPTDIR /etc/apcupsd + +# PWRFAILDIR +# Directory in which to write the powerfail flag file. This file +# is created when apcupsd initiates a system shutdown and is +# checked in the OS halt scripts to determine if a killpower +# (turning off UPS output power) is required. +PWRFAILDIR /etc/apcupsd + +# NOLOGINDIR +# Directory in which to write the nologin file. The existence +# of this flag file tells the OS to disallow new logins. +NOLOGINDIR /etc + + +# +# ======== Configuration parameters used during power failures ========== +# + +# The ONBATTERYDELAY is the time in seconds from when a power failure +# is detected until we react to it with an onbattery event. +# +# This means that, apccontrol will be called with the powerout argument +# immediately when a power failure is detected. However, the +# onbattery argument is passed to apccontrol only after the +# ONBATTERYDELAY time. If you don't want to be annoyed by short +# powerfailures, make sure that apccontrol powerout does nothing +# i.e. comment out the wall. +ONBATTERYDELAY 6 + +# +# Note: BATTERYLEVEL, MINUTES, and TIMEOUT work in conjunction, so +# the first that occurs will cause the initation of a shutdown. +# + +# If during a power failure, the remaining battery percentage +# (as reported by the UPS) is below or equal to BATTERYLEVEL, +# apcupsd will initiate a system shutdown. +BATTERYLEVEL 10 + +# If during a power failure, the remaining runtime in minutes +# (as calculated internally by the UPS) is below or equal to MINUTES, +# apcupsd, will initiate a system shutdown. +MINUTES 5 + +# If during a power failure, the UPS has run on batteries for TIMEOUT +# many seconds or longer, apcupsd will initiate a system shutdown. +# A value of 0 disables this timer. +# +# Note, if you have a Smart UPS, you will most likely want to disable +# this timer by setting it to zero. That way, you UPS will continue +# on batteries until either the % charge remaing drops to or below BATTERYLEVEL, +# or the remaining battery runtime drops to or below MINUTES. Of course, +# if you are testing, setting this to 60 causes a quick system shutdown +# if you pull the power plug. +# If you have an older dumb UPS, you will want to set this to less than +# the time you know you can run on batteries. +TIMEOUT 0 + +# Time in seconds between annoying users to signoff prior to +# system shutdown. 0 disables. +ANNOY 300 + +# Initial delay after power failure before warning users to get +# off the system. +ANNOYDELAY 60 + +# The condition which determines when users are prevented from +# logging in during a power failure. +# NOLOGON [ disable | timeout | percent | minutes | always ] +NOLOGON disable + +# If KILLDELAY is non-zero, apcupsd will continue running after a +# shutdown has been requested, and after the specified time in +# seconds attempt to kill the power. This is for use on systems +# where apcupsd cannot regain control after a shutdown. +# KILLDELAY 0 disables +KILLDELAY 0 + +# +# ==== Configuration statements for Network Information Server ==== +# + +# NETSERVER [ on | off ] on enables, off disables the network +# information server. If netstatus is on, a network information +# server process will be started for serving the STATUS and +# EVENT data over the network (used by CGI programs). +NETSERVER on + +# NISIP +# IP address on which NIS server will listen for incoming connections. +# This is useful if your server is multi-homed (has more than one +# network interface and IP address). Default value is 0.0.0.0 which +# means any incoming request will be serviced. Alternatively, you can +# configure this setting to any specific IP address of your server and +# NIS will listen for connections only on that interface. Use the +# loopback address (127.0.0.1) to accept connections only from the +# local machine. +NISIP 127.0.0.1 + +# NISPORT default is 3551 as registered with the IANA +# port to use for sending STATUS and EVENTS data over the network. +# It is not used unless NETSERVER is on. If you change this port, +# you will need to change the corresponding value in the cgi directory +# and rebuild the cgi programs. +NISPORT 3551 + +# If you want the last few EVENTS to be available over the network +# by the network information server, you must define an EVENTSFILE. +EVENTSFILE /var/log/apcupsd.events + +# EVENTSFILEMAX +# By default, the size of the EVENTSFILE will be not be allowed to exceed +# 10 kilobytes. When the file grows beyond this limit, older EVENTS will +# be removed from the beginning of the file (first in first out). The +# parameter EVENTSFILEMAX can be set to a different kilobyte value, or set +# to zero to allow the EVENTSFILE to grow without limit. +EVENTSFILEMAX 10 + +# +# ========== Configuration statements used if sharing ============= +# a UPS with more than one machine + +# +# Remaining items are for ShareUPS (APC expansion card) ONLY +# + +# UPSCLASS [ standalone | shareslave | sharemaster ] +# Normally standalone unless you share an UPS using an APC ShareUPS +# card. +UPSCLASS standalone + +# UPSMODE [ disable | share ] +# Normally disable unless you share an UPS using an APC ShareUPS card. +UPSMODE disable + +# +# ===== Configuration statements to control apcupsd system logging ======== +# + +# Time interval in seconds between writing the STATUS file; 0 disables +STATTIME 0 + +# Location of STATUS file (written to only if STATTIME is non-zero) +STATFILE /var/log/apcupsd.status + +# LOGSTATS [ on | off ] on enables, off disables +# Note! This generates a lot of output, so if +# you turn this on, be sure that the +# file defined in syslog.conf for LOG_NOTICE is a named pipe. +# You probably do not want this on. +LOGSTATS off + +# Time interval in seconds between writing the DATA records to +# the log file. 0 disables. +DATATIME 0 + +# FACILITY defines the logging facility (class) for logging to syslog. +# If not specified, it defaults to "daemon". This is useful +# if you want to separate the data logged by apcupsd from other +# programs. +#FACILITY DAEMON + +# +# ========== Configuration statements used in updating the UPS EPROM ========= +# + +# +# These statements are used only by apctest when choosing "Set EEPROM with conf +# file values" from the EEPROM menu. THESE STATEMENTS HAVE NO EFFECT ON APCUPSD. +# + +# UPS name, max 8 characters +#UPSNAME UPS_IDEN + +# Battery date - 8 characters +#BATTDATE mm/dd/yy + +# Sensitivity to line voltage quality (H cause faster transfer to batteries) +# SENSITIVITY H M L (default = H) +#SENSITIVITY H + +# UPS delay after power return (seconds) +# WAKEUP 000 060 180 300 (default = 0) +#WAKEUP 60 + +# UPS Grace period after request to power off (seconds) +# SLEEP 020 180 300 600 (default = 20) +#SLEEP 180 + +# Low line voltage causing transfer to batteries +# The permitted values depend on your model as defined by last letter +# of FIRMWARE or APCMODEL. Some representative values are: +# D 106 103 100 097 +# M 177 172 168 182 +# A 092 090 088 086 +# I 208 204 200 196 (default = 0 => not valid) +#LOTRANSFER 208 + +# High line voltage causing transfer to batteries +# The permitted values depend on your model as defined by last letter +# of FIRMWARE or APCMODEL. Some representative values are: +# D 127 130 133 136 +# M 229 234 239 224 +# A 108 110 112 114 +# I 253 257 261 265 (default = 0 => not valid) +#HITRANSFER 253 + +# Battery charge needed to restore power +# RETURNCHARGE 00 15 50 90 (default = 15) +#RETURNCHARGE 15 + +# Alarm delay +# 0 = zero delay after pwr fail, T = power fail + 30 sec, L = low battery, N = never +# BEEPSTATE 0 T L N (default = 0) +#BEEPSTATE T + +# Low battery warning delay in minutes +# LOWBATT 02 05 07 10 (default = 02) +#LOWBATT 2 + +# UPS Output voltage when running on batteries +# The permitted values depend on your model as defined by last letter +# of FIRMWARE or APCMODEL. Some representative values are: +# D 115 +# M 208 +# A 100 +# I 230 240 220 225 (default = 0 => not valid) +#OUTPUTVOLTS 230 + +# Self test interval in hours 336=2 weeks, 168=1 week, ON=at power on +# SELFTEST 336 168 ON OFF (default = 336) +#SELFTEST 336 diff --git a/roles/bigboi/apcupsd/tasks/main.yaml b/roles/bigboi/apcupsd/tasks/main.yaml new file mode 100644 index 0000000..08922de --- /dev/null +++ b/roles/bigboi/apcupsd/tasks/main.yaml @@ -0,0 +1,22 @@ +--- + +- name: Install apcupsd + apt: + name: apcupsd + state: present + +- name: Copy config + copy: + src: apcupsd.conf + dest: /etc/apcupsd/apcupsd.conf + mode: 0644 + owner: root + group: root + register: apcupsd_config + +- name: Restart service + systemd: + name: apcupsd + state: restarted + enabled: yes + when: apcupsd_config.changed \ No newline at end of file diff --git a/roles/bigboi/docker/tasks/main.yaml b/roles/bigboi/docker/tasks/main.yaml new file mode 100644 index 0000000..355a5d4 --- /dev/null +++ b/roles/bigboi/docker/tasks/main.yaml @@ -0,0 +1,134 @@ +# Frigate directories +- name: Ensure frigate root dir + file: + path: "{{docker.frigate.root_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure frigate config dir + file: + path: "{{docker.frigate.config_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure frigate media dir + file: + path: "{{docker.frigate.media_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure frigate config file + template: + src: frigate-config.yml + dest: "{{docker.frigate.config_volume}}/config.yml" + owner: docker + group: root + register: frigate_config_file + +# Grafana directory +- name: Ensure grafana config dir + file: + path: "{{docker.grafana.data_volume}}" + state: directory + owner: docker + group: root + +# InfluxDB directory +- name: Ensure influxdb config dir + file: + path: "{{docker.influxdb.data_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure photoprism config dir + file: + path: "{{docker.photoprism.data_volume}}" + state: directory + owner: photoprism + group: root + +- name: Ensure mariadb config dir + file: + path: "{{docker.mariadb.data_volume}}" + state: directory + owner: photoprism + group: root + +- name: Ensure vaultwarden config dir + file: + path: "{{docker.vaultwarden.data_volume}}" + state: directory + owner: docker + group: root + +- name: Ensure letsencrypt config dir + file: + path: "{{docker.letsencrypt.data_volume}}" + state: directory + owner: docker + group: root + +# Docker service setup +- name: Install requirements + apt: + name: + - docker.io + - docker-compose + - python3-pip + state: present + +- name: install requirements + ansible.builtin.pip: + name: docker + +- name: Enable docker + systemd: + name: docker + enabled: yes + +- name: Ensure docker project dir + file: + path: "{{docker.project_path}}" + state: directory + owner: docker + group: docker + +- name: Ensure docker-compose file + template: + src: docker-compose.yml + dest: "{{docker.project_path}}" + owner: ansible + group: ansible + register: docker_compose_file + +- name: Tear down existing project + community.docker.docker_compose: + project_src: "{{ docker.project_path }}" + state: absent + remove_orphans: yes + when: docker_compose_file.changed + +- name: Set up project + register: docker_project + community.docker.docker_compose: + project_src: "{{docker.project_path}}" + state: present + remove_orphans: yes + when: docker_compose_file.changed + +- name: debug + ansible.builtin.debug: + var: docker_project + when: docker_compose_file.changed + +# Restart frigate if needed +- name: Restart frigate + when: frigate_config_file.changed + community.docker.docker_container: + name: frigate + restart: true + state: started diff --git a/roles/bigboi/docker/templates/docker-compose.yml b/roles/bigboi/docker/templates/docker-compose.yml new file mode 100644 index 0000000..2b89084 --- /dev/null +++ b/roles/bigboi/docker/templates/docker-compose.yml @@ -0,0 +1,256 @@ +version: "3.1" +services: + unifi-controller: + image: lscr.io/linuxserver/unifi-controller:7.2.92 + environment: + - PUID=1400 + - MEM_LIMIT=1024 + - MEM_STARTUP=1024 + volumes: + - "{{docker.volumes_path}}/unifi_controller:/config" + ports: + - 8443:8443 + - 3478:3478/udp + - 10001:10001/udp + - 8080:8080 + - 1900:1900/udp + - 8843:8843 + - 8880:8880 + - 6789:6789 + - 5514:5514/udp + restart: always + plex: + image: lscr.io/linuxserver/plex:latest + container_name: plex + environment: + - PUID=1400 # docker + - PGID=1202 # warez + - TZ=Etc/UTC + - VERSION=docker + volumes: + - "{{docker.plex.config_volume}}/plex:/config" + - "{{docker.plex.tv_volume}}:/tv" + - "{{docker.plex.movies_volume}}:/movies" + restart: unless-stopped + # This makes the instance claiming a lot easier since you can access the host on the same subnet. + network_mode: host + frigate: + container_name: frigate + privileged: true # this may not be necessary for all setups + restart: unless-stopped + image: ghcr.io/blakeblackshear/frigate:0.14.1 + shm_size: "150mb" # update for your cameras based on calculation in docs + devices: + #- /dev/bus/usb:/dev/bus/usb # passes the USB Coral, needs to be modified for other versions + # /dev/apex_0:/dev/apex_0 # passes a PCIe Coral, follow driver instructions here https://coral.ai/docs/m2/get-started/#2a-on-linux + - /dev/dri/renderD128 # for intel hwaccel, needs to be updated for your hardware + volumes: + - /etc/localtime:/etc/localtime:ro + - "{{docker.frigate.config_volume}}/config.yml:/config/config.yml" + - "{{docker.frigate.media_volume}}:/media/frigate" + - type: tmpfs # Optional: 2GB of memory, reduces SSD/SD Card wear + target: /tmp/cache + tmpfs: + size: 2000000000 + ports: + - "5000:5000" + - "8554:8554" # RTSP feeds + - "8555:8555/tcp" # WebRTC over tcp + - "8555:8555/udp" # WebRTC over udp + environment: + FRIGATE_RTSP_PASSWORD: "{{docker.frigate.rtsp_password}}" + influxdb: + image: influxdb:1.8-alpine + container_name: influxdb + restart: always + environment: + - INFLUXDB_DB=influx + - INFLUXDB_ADMIN_USER=admin + - "INFLUXDB_ADMIN_PASSWORD={{docker.influxdb.admin_password}}" + ports: + - "8086:8086" + volumes: + - "{{docker.influxdb.data_volume}}:/var/lib/influxdb" + grafana: + image: grafana/grafana + container_name: grafana + restart: always + depends_on: + - influxdb + environment: + - GF_SECURITY_ADMIN_USER=admin + - "GF_SECURITY_ADMIN_PASSWORD={{docker.grafana.admin_password}}" + - GF_INSTALL_PLUGINS= + links: + - influxdb + ports: + - "3000:3000" + volumes: + - "{{docker.grafana.data_volume}}:/var/lib/grafana" + user: "1400" # docker + photoprism: + container_name: photoprism + user: "1401:1201" # photoprism + image: photoprism/photoprism:latest + # restart: unless-stopped + stop_grace_period: 10s + depends_on: + - mariadb + security_opt: + - seccomp:unconfined + - apparmor:unconfined + ports: + - "2342:2342" # HTTP port (host:container) + environment: + PHOTOPRISM_ADMIN_USER: "wholteza" # admin login username + PHOTOPRISM_ADMIN_PASSWORD: "{{docker.photoprism.admin_password}}" # initial admin password (8-72 characters) + PHOTOPRISM_AUTH_MODE: "password" # authentication mode (public, password) + PHOTOPRISM_SITE_URL: "http://bigboi.zacke.xyz:2342/" # server URL in the format "http(s)://domain.name(:port)/(path)" + PHOTOPRISM_DISABLE_TLS: "false" # disables HTTPS/TLS even if the site URL starts with https:// and a certificate is available + PHOTOPRISM_DEFAULT_TLS: "true" # defaults to a self-signed HTTPS/TLS certificate if no other certificate is available + PHOTOPRISM_ORIGINALS_LIMIT: 5000 # file size limit for originals in MB (increase for high-res video) + PHOTOPRISM_HTTP_COMPRESSION: "gzip" # improves transfer speed and bandwidth utilization (none or gzip) + PHOTOPRISM_LOG_LEVEL: "warning" # log level: trace, debug, info, warning, error, fatal, or panic + PHOTOPRISM_READONLY: "false" # do not modify originals directory (reduced functionality) + PHOTOPRISM_EXPERIMENTAL: "false" # enables experimental features + PHOTOPRISM_DISABLE_CHOWN: "false" # disables updating storage permissions via chmod and chown on startup + PHOTOPRISM_DISABLE_WEBDAV: "false" # disables built-in WebDAV server + PHOTOPRISM_DISABLE_SETTINGS: "false" # disables settings UI and API + PHOTOPRISM_DISABLE_TENSORFLOW: "false" # disables all features depending on TensorFlow + PHOTOPRISM_DISABLE_FACES: "false" # disables face detection and recognition (requires TensorFlow) + PHOTOPRISM_DISABLE_CLASSIFICATION: "false" # disables image classification (requires TensorFlow) + PHOTOPRISM_DISABLE_VECTORS: "false" # disables vector graphics support + PHOTOPRISM_DISABLE_RAW: "false" # disables indexing and conversion of RAW images + PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW images (reduces performance) + PHOTOPRISM_JPEG_QUALITY: 85 # a higher value increases the quality and file size of JPEG images and thumbnails (25-100) + PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow) + PHOTOPRISM_UPLOAD_NSFW: "true" # allows uploads that MAY be offensive (no effect without TensorFlow) + # PHOTOPRISM_DATABASE_DRIVER: "sqlite" # SQLite is an embedded database that doesn't require a server + PHOTOPRISM_DATABASE_DRIVER: "mysql" # use MariaDB 10.5+ or MySQL 8+ instead of SQLite for improved performance + PHOTOPRISM_DATABASE_SERVER: "mariadb:3306" # MariaDB or MySQL database server (hostname:port) + PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB or MySQL database schema name + PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB or MySQL database user name + PHOTOPRISM_DATABASE_PASSWORD: "{{docker.mariadb.database_password}}" # MariaDB or MySQL database user password + PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App" + PHOTOPRISM_SITE_DESCRIPTION: "" # meta site description + PHOTOPRISM_SITE_AUTHOR: "" # meta site author + ## Video Transcoding (https://docs.photoprism.app/getting-started/advanced/transcoding/): + # PHOTOPRISM_FFMPEG_ENCODER: "software" # H.264/AVC encoder (software, intel, nvidia, apple, raspberry, or vaapi) + # PHOTOPRISM_FFMPEG_SIZE: "1920" # video size limit in pixels (720-7680) (default: 3840) + # PHOTOPRISM_FFMPEG_BITRATE: "32" # video bitrate limit in Mbit/s (default: 50) + ## Run/install on first startup (options: update https gpu tensorflow davfs clitools clean): + # PHOTOPRISM_INIT: "https gpu tensorflow" + ## Run as a non-root user after initialization (supported: 0, 33, 50-99, 500-600, and 900-1200): + # PHOTOPRISM_UID: 1000 + # PHOTOPRISM_GID: 1000 + # PHOTOPRISM_UMASK: 0000 + ## Start as non-root user before initialization (supported: 0, 33, 50-99, 500-600, and 900-1200): + # user: "1000:1000" + ## Share hardware devices with FFmpeg and TensorFlow (optional): + # devices: + # - "/dev/dri:/dev/dri" # Intel QSV + # - "/dev/nvidia0:/dev/nvidia0" # Nvidia CUDA + # - "/dev/nvidiactl:/dev/nvidiactl" + # - "/dev/nvidia-modeset:/dev/nvidia-modeset" + # - "/dev/nvidia-nvswitchctl:/dev/nvidia-nvswitchctl" + # - "/dev/nvidia-uvm:/dev/nvidia-uvm" + # - "/dev/nvidia-uvm-tools:/dev/nvidia-uvm-tools" + # - "/dev/video11:/dev/video11" # Video4Linux Video Encode Device (h264_v4l2m2m) + working_dir: "/photoprism" # do not change or remove + ## Storage Folders: "~" is a shortcut for your home directory, "." for the current directory + volumes: + # "/host/folder:/photoprism/folder" # Example + - "{{docker.photoprism.image_volumes.lilleback}}:/photoprism/originals" # Original media files (DO NOT REMOVE) + - "{{docker.photoprism.image_volumes.ellen}}:/photoprism/originals/ellen" # Original media files (DO NOT REMOVE) + # - "/example/family:/photoprism/originals/family" # *Additional* media folders can be mounted like this + # - "~/Import:/photoprism/import" # *Optional* base folder from which files can be imported to originals + - "{{docker.photoprism.data_volume}}:/photoprism/storage" # *Writable* storage folder for cache, database, and sidecar files (DO NOT REMOVE) + + mariadb: + container_name: mariadb + user: "1401" # photoprism + image: mariadb:11 + restart: unless-stopped + stop_grace_period: 5s + security_opt: # see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239 + - seccomp:unconfined + - apparmor:unconfined + command: --innodb-buffer-pool-size=512M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120 + volumes: + - "{{docker.mariadb.data_volume}}:/var/lib/mysql" + environment: + MARIADB_AUTO_UPGRADE: "1" + MARIADB_INITDB_SKIP_TZINFO: "1" + MARIADB_DATABASE: "photoprism" + MARIADB_USER: "photoprism" + MARIADB_PASSWORD: "{{docker.mariadb.database_password}}" + MARIADB_ROOT_PASSWORD: "{{docker.mariadb.database_password}}" + + vaultwarden: + image: vaultwarden/server:1.32.7 + container_name: vaultwarden + restart: unless-stopped + environment: + DOMAIN: "https://vaultwarden.zacke.xyz" + EXPERIMENTAL_CLIENT_FEATURE_FLAGS: "autofill-v2,ssh-key-vault-item,ssh-agent" + volumes: + - "{{docker.vaultwarden.data_volume}}:/data" + ports: + - 8081:80 + labels: + traefik.enable: true + # redirect scheme + traefik.http.middlewares.vaultwarden-redirect.redirectscheme.scheme: https + traefik.http.middlewares.vaultwarden-redirect.redirectscheme.permanent: true + # http + traefik.http.routers.vaultwarden-web.rule: Host(`vaultwarden.zacke.xyz`) + traefik.http.routers.vaultwarden-web.entrypoints: web + traefik.http.routers.vaultwarden-web.middlewares: vaultwarden-redirect + # https + traefik.http.routers.vaultwarden-websecure.rule: Host(`vaultwarden.zacke.xyz`) + traefik.http.routers.vaultwarden-websecure.entrypoints: websecure + traefik.http.routers.vaultwarden-websecure.tls.certresolver: myresolver + traefik.http.routers.vaultwarden-websecure.middlewares: vaultwarden-redirect + # services + traefik.http.services.vaultwarden-websecure.loadbalancer.server.port: 80 + + reverse-proxy: + container_name: "reverse-proxy" + image: traefik:v3.2 + command: + - --api.insecure=true + - --providers.docker=true + - --providers.docker.exposedbydefault=false + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --certificatesresolvers.myresolver.acme.dnschallenge=true + - --certificatesresolvers.myresolver.acme.dnschallenge.provider=linodev4 + #- --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory + - --certificatesresolvers.myresolver.acme.email=hosting@montell.com + - --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json + ports: + # The Web UI (enabled by --api.insecure=true) + - "8082:8080" + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - "{{docker.letsencrypt.data_volume}}:/letsencrypt" + environment: + LINODE_TOKEN: "{{docker.letsencrypt.linode_token}}" + labels: + traefik.enable: true + # redirect scheme + traefik.http.middlewares.traefik-redirect.redirectscheme.scheme: https + traefik.http.middlewares.traefik-redirect.redirectscheme.permanent: true + # http + traefik.http.routers.traefik-web.rule: Host(`traefik.zacke.xyz`) + traefik.http.routers.traefik-web.entrypoints: web + traefik.http.routers.traefik-web.middlewares: traefik-redirect + # https + traefik.http.routers.traefik-websecure.rule: Host(`traefik.zacke.xyz`) + traefik.http.routers.traefik-websecure.entrypoints: websecure + traefik.http.routers.traefik-websecure.tls.certresolver: myresolver + traefik.http.routers.traefik-websecure.middlewares: traefik-redirect + # services + traefik.http.services.traefik-websecure.loadbalancer.server.port: 8080 diff --git a/roles/bigboi/docker/templates/frigate-config.yml b/roles/bigboi/docker/templates/frigate-config.yml new file mode 100644 index 0000000..420ef7a --- /dev/null +++ b/roles/bigboi/docker/templates/frigate-config.yml @@ -0,0 +1,87 @@ +mqtt: + enabled: False +detectors: + cpu1: + type: cpu + num_threads: 2 +cameras: + house_camera: + objects: + filters: + car: + mask: + - 1065,47,998,316,658,266,694,0 + + # Optional: list of objects to track from labelmap.txt (default: shown below) + track: + - person + - bicycle + - car + - motorcycle + - bus + - car + - dog + - horse + - sheep + - cow + - knife + ffmpeg: + inputs: + - path: "{{docker.frigate.house_camera.rtsp_mrl}}/Streaming/Channels/101" + roles: + - record + - path: "{{docker.frigate.house_camera.rtsp_mrl}}/Streaming/Channels/102" + roles: + - detect + detect: + enabled: True # <---- disable detection until you have a working camera feed + width: 2560 # <---- update for your camera's resolution + height: 1440 # <---- update for your camera's resolution + snapshots: + enabled: true + record: + enabled: true + events: + retain: + default: 10 + + garage_camera: # <------ Name the camera + objects: + filters: + car: + mask: + - 1616,193,1602,331,1297,322,1309,199 # Stallet + - 393,472,783,402,769,202,359,234 # Car porten + # Optional: list of objects to track from labelmap.txt (default: shown below) + track: + - person + - bicycle + - car + - motorcycle + - bus + - car + - dog + - horse + - sheep + - cow + - knife + ffmpeg: + inputs: + - path: "{{docker.frigate.garage_camera.rtsp_mrl}}/Streaming/Channels/101" # <----- The stream you want to use for detection + roles: + - record + - path: "{{docker.frigate.garage_camera.rtsp_mrl}}/Streaming/Channels/102" # <----- The stream you want to use for detection + roles: + - detect + detect: + enabled: True # <---- disable detection until you have a working camera feed + width: 2560 # <---- update for your camera's resolution + height: 1440 # <---- update for your camera's resolution + snapshots: + enabled: true + record: + enabled: true + events: + retain: + default: 10 +### IVMS4200 linux build diff --git a/roles/bigboi/kvm-homeassistant/tasks/main.yaml b/roles/bigboi/kvm-homeassistant/tasks/main.yaml new file mode 100644 index 0000000..97ace18 --- /dev/null +++ b/roles/bigboi/kvm-homeassistant/tasks/main.yaml @@ -0,0 +1,80 @@ +- name: install requirements + apt: + name: xz-utils + state: present + +- name: retrieve list of all vms + community.libvirt.virt: + command: list_vms + register: all_vms + +- name: print list of vms + debug: + var: all_vms + +- name: ensure homeassistant directory + file: + state: directory + path: "{{homeassistant.disk_image_path_root}}" + owner: libvirt-qemu + group: kvm + +- name: check if the homeassistant disk image exists + stat: + path: "{{homeassistant.disk_image_path_decompressed}}" + register: homeassistant_disk_file + +- name: create helper variables + set_fact: + hass_disk_image_does_not_exist: "{{homeassistant_disk_file.stat.exists is false}}" + +- name: download homeassistant disk image + when: hass_disk_image_does_not_exist + shell: "curl {{homeassistant.disk_image_download_url}} -o {{homeassistant.disk_image_path_compressed}} -L" + args: + executable: "/bin/bash" + +- name: decompress disk image + when: hass_disk_image_does_not_exist + shell: "xz --decompress {{homeassistant.disk_image_path_compressed}}" + args: + executable: "/bin/bash" + +- name: set permissions on file + when: hass_disk_image_does_not_exist + file: + path: "{{homeassistant.disk_image_path_decompressed}}" + state: file + owner: libvirt-qemu + group: kvm + +- name: create helper variables + set_fact: + hass_kvm_does_not_exist: "{{'hass' not in all_vms.list_vms}}" + +- name: define vm + when: hass_kvm_does_not_exist + community.libvirt.virt: + command: define + xml: "{{ lookup('template', 'templates/hass-kvm.xml.j2') }}" + autostart: yes + +- name: start vm + community.libvirt.virt: + name: hass + state: running + +- name: List all VMs + community.libvirt.virt: + command: list_vms + register: all_vms + +- name: print vms + debug: + var: all_vms + failed_when: "'hass' not in all_vms.list_vms" + +- name: ensure compressed source image is removed + file: + path: "{{homeassistant.disk_image_path_compressed}}" + state: absent \ No newline at end of file diff --git a/roles/bigboi/kvm-homeassistant/templates/hass-kvm.xml.j2 b/roles/bigboi/kvm-homeassistant/templates/hass-kvm.xml.j2 new file mode 100644 index 0000000..178061f --- /dev/null +++ b/roles/bigboi/kvm-homeassistant/templates/hass-kvm.xml.j2 @@ -0,0 +1,109 @@ + + hass + 959513bb-d405-44a9-8090-c6d0bf6bbf1c + 2097152 + 2097152 + 2 + + hvm + /usr/share/OVMF/OVMF_CODE_4M.fd + /var/lib/libvirt/qemu/nvram/vm1_VARS.fd + + + + + + + + + + + + + + destroy + restart + destroy + + + + + + /usr/bin/qemu-system-x86_64 + + + + +
+ + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + + + +
+ + + + + + + + + + + +
+ + + +
+ + +
+ + + + + + + + +
+ +