moved all to subfolder nixos

This commit is contained in:
Ingolf Wagner 2021-09-25 20:28:25 +02:00
commit 15c6866362
No known key found for this signature in database
GPG key ID: 76BF5F1928B9618B
263 changed files with 638 additions and 762 deletions

105
nixos/assets/jack.sh Executable file
View file

@ -0,0 +1,105 @@
set -e
defaultDevice=PCH
start_jack(){
internal_device_number=-1
komplete_device_number=$(aplay -l | grep Vestax | cut -d":" -f1 | cut -d" " -f2)
babyface_device_number=$(aplay -l | grep Babyface | cut -d":" -f1 | cut -d" " -f2)
cmedia_device_number=$(aplay -l | grep C-Media | cut -d":" -f1 | cut -d" " -f2)
h2n_device_number=$(aplay -l | grep H2n | cut -d":" -f1 | cut -d" " -f2)
# this should be more readable some day
if [[ $babyface_device_number == "" ]]; then
if [[ $komplete_device_number == "" ]]; then
if [[ $cmedia_device_number == "" ]]; then
if [[ $h2n_device_number == "" ]]; then
echo "use : default device"
device_number=$internal_device_number
else
echo "use : h2n"
device_number=$h2n_device_number
fi
else
echo "use : c-media"
device_number=$cmedia_device_number
fi
else
echo "use : komplete"
device_number=$komplete_device_number
fi
else
echo "use : babyface"
device_number=$babyface_device_number
fi
# device parameter configuration
# ==============================
#
# to find configuration options do
# jack_control dp
if [[ $device_number -eq -1 ]]
then
# we use alsa in reality, but pulse opens up all the pulse
# sink and source stuff
# jack_control ds pulse # not working for some reason
jack_control ds alsa
jack_control dps device hw:$defaultDevice
else
jack_control ds alsa
jack_control dps device hw:$device_number # use usb card
fi
jack_control dps duplex True # record and playback ports
jack_control dps hwmon False # no hardware monitoring
jack_control dps rate 48000 # use cd sample rate
# nperiods are the splitup of the
# sound-ring-buffer. 2 are ok for internal cards
# but for usb you should use 3 because
# you can have to write in junks to the card
# so there is one backup slice in the middle
if [[ $internal_device_number -ne -1 ]]
then
jack_control dps nperiods 3
fi
# engine parameter configuration
# ==============================
#
# to find configuration options do
# jack_control ep
jack_control eps sync True
# realtime kernel
# set True for using a realtime kernel
jack_control eps realtime False
# set priority if realtime kernel is set True
# jack_control eps realtime-priority 10
jack_control start
}
stop_jack(){
jack_control exit
}
status_jack() {
jack_control dp
jack_control ep
jack_control status
}
case $1 in
start) start_jack
;;
stop) stop_jack
;;
restart) stop_jack ; start_jack
;;
*) status_jack
;;
esac

View file

@ -0,0 +1,50 @@
function stop_program(){
echo "stop $1"
sudo systemctl stop $1
}
function start_program(){
echo "start $1"
sudo systemctl stop $1
}
function start(){
echo "starting programs again"
echo "-----------------------"
echo
start_program backup.on-porani.insecure.timer
start_program backup.on-workhorse.insecure.timer
start_program backup.on-workout.insecure.timer
start_program syncthing.service
start_program tlp.service
start_program tor.service
}
function stop(){
echo "stopping programs"
echo "-----------------"
echo
stop_program backup.on-porani.insecure.timer
stop_program backup.on-workhorse.insecure.timer
stop_program backup.on-workout.insecure.timer
stop_program syncthing.service
stop_program tlp.service
stop_program tor.service
}
# ----
# main
# ----
stop
echo
echo -n "wait to start again -> "
read
echo
start

View file

@ -0,0 +1,39 @@
#!/usr/bin/env nix-shell
#!nix-shell -p nginx-config-formatter python3 -i python3
import os
import re
import shutil
import subprocess
import sys
from tempfile import TemporaryDirectory
def nginx_config() -> str:
reload_config ="/etc/nginx/nginx.conf"
if os.path.exists(reload_config):
return reload_config
out = subprocess.check_output(["systemctl", "cat", "nginx"])
match = re.search(r"-c '(\S+-nginx\.conf)", out.decode("utf-8"))
if not match:
print("Could not find nginx.conf in nginx.service", file=sys.stderr)
sys.exit(1)
return match.group(1)
def main():
config_path = nginx_config()
with TemporaryDirectory() as temp_dir:
temp_path = os.path.join(temp_dir, "nginx.conf")
with open(temp_path, "wb+") as temp_file, \
open(config_path, "rb") as config_file:
shutil.copyfileobj(config_file, temp_file)
temp_file.flush()
subprocess.check_call(["nginxfmt", temp_file.name])
editor = os.environ.get("EDITOR", "cat")
subprocess.check_call([editor, temp_file.name] + sys.argv[1:])
if __name__ == "__main__":
main()

57
nixos/assets/shrink_exports Executable file
View file

@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -e
PROJECTS_FOLDER=~/music-projects/Rendered-Projects
GARBADGE_FOLDER=/share
# file separator (needed to handle files with spaces)
IFS="
"
function info_log(){
echo -n ">>> [ "
echo -n $@
echo " ]"
}
function run_ffmpeg(){
local input=$1
local output=$2
info_log "ffmpeg : ${output}"
ffmpeg \
-i "${input}" \
-f mp3 \
-codec:a libmp3lame \
-qscale:a 5 \
-ar 44100 \
-loglevel error \
-af loudnorm \
"${output}"
}
function delete(){
local input=$1
if [[ -e "${input}" ]]
then
info_log "move : ${input} to ${GARBADGE_FOLDER}"
mv "${input}" "${GARBADGE_FOLDER}/$(basename ${input})"
#info_log "delete : ${input}"
#rm "${input}"
fi
}
for file in `ls ${PROJECTS_FOLDER} | grep wav$`
do
filename=`basename ${file} .wav`
mp3="${filename}.mp3"
info_log ${filename}
delete "${PROJECTS_FOLDER}/${mp3}"
run_ffmpeg "${PROJECTS_FOLDER}/${file}" "${PROJECTS_FOLDER}/${mp3}"
delete "${PROJECTS_FOLDER}/${file}"
done

View file

@ -0,0 +1,3 @@
"Fernsehen ist für mich der Elektro-Jude" - Dr Axel Stoll
"Der Zellkern ist gleich pures Licht" - Dr Axel Stoll
"Die Sonne ist Kalt!" - Dr Axel Stoll

18
nixos/assets/sprueche-siw Normal file
View file

@ -0,0 +1,18 @@
"Also Attracktiv oder net, ich seh hier immer noch am Besten aus" - Reiker
"Hör mal auf dich selber zu bemitleiden, sonst baft das aber echt jetzt! Dummer Lutscher" - Käpten Pika
"Komm Junge is besser du gibs dat jetzt zu, dann haste wenigsten noch ein paar Jahre wat von deinen Zähnen" - Käpten Pika
"Ich sauf doch net mit euch Pennern, echt Ey" - Käpten Pika
"Ja toll dann hab ich ja schon wieder verloren" - Deiter
"Komisch! Mein Vater heißt auch Vater, aber sieht ganz anders aus!" - Reiker
"Sag noch einmal Junge, Junge!" - Käpten Pika
"Häää? Worte, was sind Worte?" - Reiker
"Ich hau dir gleich die Zähne aus deiner fiesen Schnauze!" - Käpten Pika
"Ihr glaubt wohl ich kann keine schlauen Sachen sagen, wa? Schlaue Sachen!!" - Reiker
"Jetzt... also ich raff das jetzt echt net. Ist das so kompliziert?" - Reiker
"Dat is hier die Molekularsymplexion über trivial-komplexive Plasmakonvergenzen, wie? Wat?" - Jordi
"Äh, äh, Junge? Ey, Junge, echt jetz! Weißte?" - Käpten Pika
"Deine Dummheit unterscheidet meinen Datentransfer" - Computer
"Ähh, komisch mein Vadder sieht ganz anders aus, obwohl der auch Vadder heißt." - Reiker
"Aaaaaah, ich kann nimmer" - Reiker
"Also ich hab keine Gummipuppe. Nur Gummibärchen." - Reiker
"Richtig heisser, kochend heisser Kaffee" - Käpten Pika

View file

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC/WCoJ9zSL85R1mNJHUGRofyigeg2+g4+bwWVysHxWroPMIpP2hJrMRPoP13SAOaLOjl6X112jjoQ2wpJ/qptjtojrsF8bpFgoMqCQFsQDD4zuG4V/AaIt0nAF4B5tDBGFN3Hj6vpbwVAidv+8Kr41r5JOG/8Z/UiJDGrIMab3kDwyOklrMPLWr7IBYC0O8Jwyz3lAl18ukMSEZvoPaJhPJyyqRhSagX59U7AQiNrnq18kzi7Pszy3e1d7x3vWSXemJGZaUJ+cFbl1LrvFHwUa55sSUVUVBRxgABc906YoiUcr31aw98zUX4W+2+AqDzIIquV5frIc/+nnfsmDrsnMl81cLglxuRxqib0AuSYqkNQimWrR61M7TaLvGZomMk8Vheew/QlxvHvhbHwnu7/tgNll2i+Zi1T7VZ5Hcy4quYDZQA7NDrvu0dEm+dTlOfuJJZdMLWws20ao8xtv8IxxCN31CBCbCSETpsSuvT7joHKGpJoOf3eilLLqOKjrbo5E6s6S1w1WRoZ6LuXQo2l5uvMVSzUZ+4CG+FX+Q73bpQ5SWUvz2o5HovX8RbcneuG7mfZMe80F5IyaqSmi0r+kFOqK4NKz/InHhSJjrFYJWl2PP+30MfsHx5NMOVhfKdRZje5oTds6L2o9+3vhiE7CmgZVR+RqMHMUtRrERODwPQ==

View file

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC6uza62+Go9sBFs3XZE2OkugBv9PJ7Yv8ebCskE5WYPcahMZIKkQw+zkGI8EGzOPJhQEv2xk+XBf2VOzj0Fto4nh8X5+Llb1nM+YxQPk1SVlwbNAlhh24L1w2vKtBtMy277MF4EP+caGceYP6gki5+DzlPUSdFSAEFFWgN1WPkiyUii15Xi3QuCMR8F18dbwVUYbT11vwNhdiAXWphrQG+yPguALBGR+21JM6fffOln3BhoDUp2poVc5Qe2EBuUbRUV3/fOU4HwWVKZ7KCFvLZBSVFutXCj5HuNWJ5T3RuuxJSmY5lYuFZx9gD+n+DAEJt30iXWcaJlmUqQB5awcB1S2d9pJ141V4vjiCMKUJHIdspFrI23rFNYD9k2ZXDA8VOnQE33BzmgF9xOVh6qr4G0oEpsNqJoKybVTUeSyl4+ifzdQANouvySgLJV/pcqaxX1srSDIUlcM2vDMWAs3ryCa0aAlmAVZIHgRhh6wa+IXW8gIYt+5biPWUuihJ4zGBEwkyVXXf2xsecMWCAGPWPDL0/fBfY9krNfC5M2sqxey2ShFIq+R/wMdaI7yVjUCF2QIUNiIdFbJL6bDrDyHnEXJJN+rAo23jUoTZZRv7Jq3DB/A5H7a73VCcblZyUmwMSlpg3wos7pdw5Ctta3zQPoxoAKGS1uZ+yTeZbPMmdbw== cardno:000611343142

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = 94CccmfAuNtQzopd5NiVYjTjZvSgabMh66BI/iyVmnJ
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA8m9cBRv+9K8ywH19CZKDidwmzEa+2j3rkFjek+uPLVCHX5FlVQv+
flX5fY06DuaPzWKf4MoXHxmVa9T/WOcKZJUmhSJC2AVorhuPihOx0FNrQr69bamy
x03fiH0pHmDXumNdGMUcNf+06Zu2Nr9yze8rE1B97zb0RPBf+XC1uHw4E4PrWC/F
swibj9U45bp07wFvJrkAsngw4c6+TFERW6TK5DPKDQs7KfgdsqFGLvg2cY5phwC1
08HBC7eTf2xG6paaS7gEbhDMQ/K47Lbhbv2srnYfaBw5iyc8f29ZwEuNfE4V15B3
foz/kGAhceTuBKNCVvKvqSIL2yEsibFVyl7zlgGp3EKWuR5ETQAspJViGILwiyq6
iRYQ1AxxyroqS146CUAB8/68w0PwroKt8lXMEtx58S7/OAW0KnXGxwqSfocH+iE4
qry9pPuSs7RR6lXBB0nvSfTbaZDMUXtiyV24+pyZgl5Q31kDgUWgFpzGRBc/CTO2
h8OmUcvEyLxh3bruu0SQGXa35G1Igsumuh/uLifgHB/odLYY00PhEdpp52BswgXe
yz88nfXMOyvm7ROEyA7r2qruM1kEHDSQ8IRuxhd8YebyI7k6mYVE8CR5T89QfVl3
mrNk+f6Q/cpFiNBxr7+UBCiHix3/GDAD4NEgvu5nfqinTA34FuscTS8CAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = LnE+w6ZfNCky4Kad3TBxpFKRJ2PJshkSpW6mC3pcsPI
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAmAyz71GoQq2Mn4XeUVcN9yfgxeWT57li7i6Te9lq7OVAXQ+CBtD3
puTMrW3/LXOIS678E2iMYPmdQzMZLmADi8+ZrXOqX98uceNv5bPrTJF0z/RA9Tif
kfh78GcJCGHmZz+GGWu1ExtSa5ekBdamEtehW6vAGbrPM6Umu9B2UCn8zaSx+RGe
Y7Z81wO21+ywUorMPTbHeuPYZW+Z8L+QKHO9NdYhzZ9zMPeVMi0x/mwIZqXJ57Wz
57nx0rrPh+e+5cj3Jh+i4HC76mxPGCyCdvf+60d7W87UZxPqRiTLt2SwgltEKf56
jBsVeOb5Fjzb6LcNGWfF8zNh0w6rAQsG4W7l93VlerTd46GtG2XW42JkGhuKb8JJ
L1olPUmbcDbxlQGGUNaI7thAzubszAzinqyat3oU8NjgDJJIueHLmo752RW+yHUY
giyRSBYtDRM9cE3s848WsToO5BtjXLkg/rC4WIWX2MNJFsAZXzfHWDmae+ajpoVy
Gl6tGYbLhjd8KtSWB9kB0OWsV56f4KmWeRxHwTgylMO30l6v+XRdnoRUAp9wj8dV
c6HJHnn5b2q4dk+qwWOYgwvpRFnSixbCCT4PoedEU9xVOzLmzxRtGmkzPsOXEOj5
6r4Jvk0jw2LTkhEVX1CPblTrGpms9NO02SXNHkF/Akw7PGuJu+w3HZUCAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = 9JI8y56NWiKMRS6g/k2H3VgTEw0q+8UEDDJdiCjOl8O
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA4Ff6XRvf83XSuWUkb70Yz+cWo1/dq4LBh5ZG7SJypdIXYnWQpQJc
sLRfAS6nJZ6VixNADx7A03c8TdADVaAgl591rLd4CSzM22EgaOFstU2VO/MfMKHf
v+WUQsrTE6CQ48SW+MDbSZZ7M7FRa/A6hwqZc5qygxdG/tgTei9pmTfqW+ZdQBWl
IeCCINiNSA/fD+FjWXslZIFRZ5sQ7AYZJgL2nFAueY+cKtRZy3tcDL1v6mhDdIrE
h8JjEUiayQDGnWmBlflLqE3ODqEsEKoL6W7epqK6PcwvZQxSNwrZe/wzH3oTC43m
Yg7TQGr0v3SnSziXv3cJvcHfwr9+huo37wTbUJNmozGpI8nLszfUTEIfhbu2ODQv
R2iM7FJcE4wV48y9aybEnESKA0vsjgI23RIQfxkN0oii7L6NAZVHgl/JJBOtCMXf
V5uXAdOtkv9UvfofrrV0uahncvbz5efPTSPF8fS5EiwzWfDUW6KHrp/9+gDcnirn
H8HvmmNVeOGWA1xlrKgi8kiBHv5BxCXfurD0aD6ZIlxdLjJCvGfnLnJZ6gr//GAf
1BJJVei98uZzihNe4VbRF6Iaphns1KezsdygMsEV9gDIJw3IIqTukcUK7AcBXhb4
IJ792j2iRwUOyiAcUYLeVYzAt3xFN6wPNcC/Opdo6TVbdMZu2uS1ZN8CAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = kc1SACqsoYjk5GimZfP+eszfJmUzZkMQhWeW42UKjfL
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA2ACttoosnRZ99o+OyMrxBdUWPqsT5btzSIQ5dU1XWqGjO4nRchCE
8tO0b/4jqVgJVTRZVIUJQESZRlSmclsCAjdM8tsGj74CJrm7tBvgbBn2IObSs5+4
oJWe57VsQaeHPuI2JZuGqv8Z3Esw+B07bQS5VTaC1ISo7vnLG/q5XLCbKHB9JZc/
ztYbk4bEQHwbulfoPjD9FY3heLnTzqPw9Xr3ixao5gbAXfWNJM+iCluMq+Q2g1BD
ozSnyYvaGLQ6h4yksDp+xuK8YCqiRj174EkXySI8Jee1CBMuI8ciX/5Q7yzvzscQ
ZQ/MLVdx3MRW+VeT0ctaRzoA9E09ILqPe+56DjpsKzt4Ne8qeMG5HdpzO9UdNzTu
MuibsCL7CJy5Ytl38PK+LAXHQr3Os1Z4OHjeTZ38vTAZcOUJZEkl6w9nO1XjcyBL
rIaG+20Nx0ZU79MlJZFiG7ovlUiDfIEKNygng8v/yoTMaqMYLxQZ/leQwLMNLujo
sku8+oV4Jvx4SyUjuAS6jgG9CnejLCnHP/yyDGdaMQSzmlzYXacLMfnPZE3r7bj1
EjA6yQbkPixm7xLCyMm5u2leWtqtbg1oRA6Mw3UyYkNy3hiTU+jTvztEI3SCliDH
yjGlESH4/edryKjLNjmYP77VFbM9ZSQ+QGlbMGPvjcn6XCdJGdxm3PUCAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = ZK9iznseTpMqjaMgDJ7MdjYaq62QlEOFquLfVxlLpFK
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAzBU8x9aB7F3sPJlcg9avJiSrsAoTHsMkfk6uRKFVjUjuNJgb3rjW
gyQ7krftLAyxLkTYJzpD+4D+qWiudEgju7W+BU65/hudMIvBmbRYqXmcQlD9B9Pv
0bVAazHJ80wN8GJD060Wq6XTtkrtAJhPmQSyMt0xU4WmWw/39QBX9rWtOTy75813
qrfuv1I11YcVQ3jegPLUIzlZqz6LeouCXiP7IRIa+WUXIwAdAYtO/RJC+tty6zyI
BXNd0Mkvpf0Qaw5joQJRXkdb1sWHOZYh75JW1QWqFMWCclkGG7/Dve4KzuO9N5XZ
ZMs/MCtDkJQpweNDT3aaiqZa8Oj29OXs4HR4FFrvYkY+qqmKCUqS70FYLo45uNx1
sRb7GKX8/dsPyOGHfXDuFTSXsKLh9gNLMlF/kuTQ2yJMfeMKdC5jDClL145Fm0ux
akH/PWSS9DENxSu0GH1sTQnLyhc4mVzOehu1XfR9EALjYY0BNBUir7aAaiLTCbq9
LKwMaF/D467W3j3Zp5xEAsf8xYC2CyMl1Df43zxcxLY+3K8/kUM2rkU7ocl2VT3o
7yNC+JqQz41n4SDOXBZc6cfxUXj2MqqEw9Ywgs+aXZiSCaVOulhyXj0TSE1mX1NI
woDHEzyx7q4AryQOWQsLq5JimI0v2/xN2yz+cNXoetDypjEWnws4e/ECAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = Z567IKl00Kw5JFBNwMvjL33QYe2hRoNtQcNIDFRPReB
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA1vhCFsFK0QSYDlXSS6ngpZbilplYtaPBzbxWYGAxa5vNtwoaO2Tz
BZ4ptvE26TR2+Jygvlk5xdoNYAL/yhNI9p86vs/pA+sJmBlsYAWOA5qAnXoIL2u4
1CBB9t+uMnQKhyPoBoDq6QXmM1HlFhxtkKVlLyEHxARxu7g/inFtghPqYD/HyjVJ
V6h9OdKEgY+wcn6GGLXGjrSMAsIZP2w8fPQfS45UAtjK+cFODFKElxGZrjqgJP1w
/Jw6nB03yKMGsMHNkiwC2BJbK3+pT92JfyqXRg3REw0hVMZghcsoNtWfBoNYLvFY
qwk+bvf5bVdLxLMEv33+B3F8SScXuwMUpBwCeMi58ltt+OuOVhh8PLA9ncA6tGa9
tzyUo7i8qjGTremSilWIdRYqOexriPKCdnYcJcw/L9Vl2H3QbIj7uVxbszQbqDGS
KM43U5cXgpMIYI9CwxnWB8np7n/IXZFG5E+9afd4kYTLShzaObzu2I1yom0O4Ks7
HsdvlsBgv4iT6ctquHtU6IFsa/Wfm4ntDiAcczoQEXs0F2v839FH62TPTY70xzyd
wQhAdCegb6MYVmVmQL9jv8QDfxwUxtsohbW3ncBDYrdy2rmeiZDGaRJVEbyf6MKI
OfaobRGXY0NnOURX9/fkXSydDKd3rIhOMubfUq0+Smm3YrsHWeZVvNMCAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = Hm+YwSe6XiRNQD4HfJPgTB8UFVMyVi0vy+3ofMnW6jD
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEAu7Ajx9+mEaDK/ASZ5hoVj3X3IkWl+8MAhmj7dwnhqc4YrPrvwKE4
cOnVcEUp4K4pyIHUG8zhsesstfpu/0owLQaz8Cekr4CyQWsjDfi7K/QiAN+v2O2m
DQOjrYzDvRyBa20A2MnO1kZU/aFHE9qcIHefZhQUZyv97j+QcsE/FDuIH/RAua6/
p+br2tfecePGH6f0fMk8dp+YbxcjjVyhJkjyaYF2r+n+YflDl5y3ngxUFJ0UnNE0
RfYJf2NE1wzt4rIdnYobFP3vifDIeYj6M0LGHnURPsT6zP+zStZ81MYZKrNlTJ37
sbZhorVmO6x46xEWaDUd7UqcKJBpb7u8iSAE4S3tHLFRxBs60dPS+3UEraiTvTHr
FvWTq1Q+t/FivTxXEkVt74N5auOKbT5AAkztak21Izx6enspdx6da2aLuJD5I0OU
3F4kd8lW5PqEZubkYziDwcVoNsx88hQzHi5l2aRdzY57o82+ltWw4xXmAFR2o605
SwVJ4AUmORHuIoDYSR+UgbtKHguxVaTLVggdfvHzlDQ1VERwEU58awMwPLU1k+jP
3QW7ehPLKRN+StB6LBlnmRD1ltkaPY5iy+NMXj17hJx0trpz3qoCuv+5TRvsGvQ7
Je/G7c6suIGd4HbA9TvCinW6/JLbJQlDiG7MD2oCOPS1pdayUuB9Jw0CAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = sPs48jzCdtTv0Viy2Of3HlXipfxH5Y8bA+KYVkOrSiK
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA01HJ49zxmnixWC9YMP0c3UFxZc4Hl5UK9nJvhMRBOuxm75kpzZsz
3v6mSy1YrVE9rrGXYjZ76wKrRhchMpvrMKKD8/DRjVqTkuFwtGgUEigzpSFoSLtC
u2Wis7Z6GW3nLgAS79NU9IUUEoeevND1zzglDb0HdERuiImiZVg3I+VXLyA31X3L
Z/B7T4QLmZGIRvFw0y1TawMjFMJZmDBtzMqfO7behkms2O1ORAciGhGxmZ9gd7yk
n/NKCpSSzeC6sJ28i33LRrWF3hRUXAEJFgq8YRxm6mjRoPLsJVsw2S98DvTcxmjN
eyVnqPVQi7JuKrOQsewQvwV2KiqI9ibEYH1zZNXwy+l05b3QSaAcyRtDpwRW7FCY
H4B3S0vjte75D4bEuYTFgT3wCzlAjdB7fPZ4jyZXdrP8G3IfbMmgsdECz5uIMwam
UaSZISlHkSJv+erA8TMJLBnqAO7ERKYI7PRIDdIun0VtX2QjRJpWIdVpxEcL4fZU
w6gzX8lOQe5NnoH/MFUfU0LyBuUH1k6WX7xdwrynUVS087vwaQN+H/VTp0QSX6PQ
oCLYPCGKS2B/St954uaPanzeG7QZQpWbvttaFVmUSkilx78xqqu3zDm9pSofFKCX
08TGlluy8JAwUqAxekQVKey2PdLmKjlMCcoUeNYbJybGplc9gv2hYhsCAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,14 @@
Ed25519PublicKey = r6mRDc814z2YtyG9ev/XXV2SgquqWR8n53V13xNXb7O
-----BEGIN RSA PUBLIC KEY-----
MIICCgKCAgEA7/bur2JIXzNrsgjQ7kfoaLUVCC9S7HNNdDrlnSdum0sWvN9urdxS
1OfzqG+kjDhQ0sS4fEeYyLMU8W3/aHkSbMjfKBiZS70bg5yHRepUEPZNqDqR3+rO
LTAGWMi/IQQQmnfcN5SjaNY/ZyXoaPd1emlpV2UXBvXo/bQTl+pmOt7AIAh7Z7M6
X5KAwU23kUwrfn/7zFCw98euNEPcCKpdF5oD4+G+S0PGfFvBmE6Xoi2blM1rcjJ4
39IGVCsKAlW1Vg48yj7FypSSjaFvIW+kyRcNNTEZ4V5p50Vm7DfylfW96NqAOeuz
2aSVaLhvmu8fU9z+g95MdGZOJYd57jFt76GbkwcLCF8KBCP9NhMfOQu0i1glk+AP
CcJcDa/Oj7lLQVB2+holJhw5fkHH2Yi+L+UsjIF0iLiOSTjGJp4yRT9Al9pgMCj2
O1JUMYxQ490mSFHBomNv1fq+f5VJnytEwAkJH6AgH+RIcAC5/r+sowfLv+Gy0ga8
jKG6t9d/x6lRNv0x5sUhYkiUD9Naq0NncaZz1GtkBAyu+hUZx2+zg3r8He4XoiXx
zWAQEgcW3X1/9VC7IBvaK9cdLG5pbeGCBaDv8S0Ue332mM0XNDlffjdC7Sg9f/TG
YV8MHpR3RwwUqdi6WFPQqVz5Hv1pE02v/Uw6tby1UgAnzskrufPh+m8CAwEAAQ==
-----END RSA PUBLIC KEY-----

View file

@ -0,0 +1,61 @@
{ lib, config, pkgs, ... }: {
imports = [
<modules>
<system/server/initssh.nix>
<system/all/sshd.nix>
./hardware-configuration.nix
];
# enable initrd ssh
configuration.init-ssh = {
enable = "enabled";
kernelModules = [ "virtio_pci" ];
hostECDSAKey = <secrets/init-ssh/host_ecdsa_key>;
};
networking.hostName = "dummy";
# font
# ----
programs.custom.urxvt.fontSize = 17;
programs.custom.xterm.fontSize = 17;
system.custom.fonts.dpi = 140;
# allow un-free
# -------------
nixpkgs.config.allowUnfree = true;
environment.variables.NIXPKGS_ALLOW_UNFREE = "1";
# some system stuff
# -----------------
time.timeZone = lib.mkDefault "Europe/Berlin";
#time.timeZone = lib.mkDefault "Pacific/Auckland";
#time.timeZone = lib.mkDefault "Asia/Singapore";
#time.timeZone = lib.mkDefault "Asia/Makassar";
i18n.defaultLocale = "en_US.UTF-8";
console.font = "Lat2-Terminus16";
console.keyMap = "us";
# swappiness
# ----------
# 0 = only when running out of RAM
# 100 = always swapp
boot.kernel.sysctl."vm.swappiness" = 0;
# rewire NIX_PATH
# ---------------
environment.variables.NIX_PATH = lib.mkForce "/var/src";
environment.systemPackages = [ pkgs.git ];
boot.initrd.network.postCommands = lib.mkAfter ''
echo "sleep engineering"
sleep 120
'';
}

View file

@ -0,0 +1,9 @@
{ ... }: {
imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ];
boot.loader.grub.device = "/dev/sda";
fileSystems."/" = {
device = "/dev/sda1";
fsType = "ext4";
};
}

View file

@ -0,0 +1,32 @@
{ config, pkgs, lib, ... }: {
imports = [
<system/desktop>
./hardware-configuration.nix
./tinc.nix
];
system.custom.wifi.interfaces = [ ];
networking.hostName = "mobi";
security.wrappers = {
pmount.source = "${pkgs.pmount}/bin/pmount";
pumount.source = "${pkgs.pmount}/bin/pumount";
};
# fonts
# -----
programs.custom.urxvt.fontSize = 12;
programs.custom.xterm.fontSize = 12;
system.custom.fonts.dpi = 100;
configuration.desktop = {
width = 1366;
height = 768;
};
}

View file

@ -0,0 +1,58 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports = [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix> ];
boot.initrd.availableKernelModules =
[ "ehci_pci" "ahci" "xhci_pci" "usb_storage" "sd_mod" "sdhci_pci" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# efi boot loader configuration using grub
boot.loader.efi.canTouchEfiVariables = false;
boot.loader.grub = {
device = "nodev";
efiInstallAsRemovable = true;
efiSupport = true;
enable = true;
version = 2;
};
fileSystems."/share/" = {
device = "/dev/ram1";
fsType = "tmpfs";
};
# NTFS support
# ------------
environment.systemPackages = [ pkgs.ntfs3g ];
# lvm volume group
# ----------------
boot.initrd.luks.devices = {
mobi = {
device = "/dev/disk/by-uuid/e138095f-c703-4dea-bb1c-bf888b8e1b81";
preLVM = true;
};
};
# root
# ----
fileSystems."/" = {
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/mobi/root";
fsType = "ext4";
};
# boot
# ----
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/064D-3144";
fsType = "vfat";
};
}

View file

@ -0,0 +1,14 @@
{ config, lib, pkgs, ... }:
with lib;
{
module.cluster.services.tinc = {
"private" = {
enable = true;
openPort = true;
connectTo = [ "sputnik" ];
};
};
}

View file

@ -0,0 +1,32 @@
{ lib, config, pkgs, ... }: {
# provide borg backup repository
services.borgbackup.repos = {
default = {
quota = "100G";
allowSubRepos = true;
authorizedKeys = [
# todo rename
(lib.fileContents ../../assets/ssh/borg_access.pub)
(lib.fileContents ../../assets/ssh/card_rsa.pub)
];
};
};
# mirror backup folder to /media
systemd.services.borg-mirror-to-media = {
enable = true;
script = ''
${pkgs.rsync}/bin/rsync -a \
/var/lib/borgbackup/ \
/media/borg-backup-mirror \
--delete-after
'';
};
systemd.timers.borg-mirror-to-media = {
enable = true;
timerConfig.OnCalendar = "daily";
wantedBy = [ "multi-user.target" ];
};
}

View file

@ -0,0 +1,58 @@
{ config, pkgs, lib, ... }: {
imports = [
<system/server>
./hardware-configuration.nix
#./home-assistant.nix
#./kodi.nix
./syncthing.nix
./tinc.nix
#./wifi-access-point.nix
#./lan.nix
./dms.nix
./borg.nix
];
nixpkgs.config.permittedInsecurePackages = [ "homeassistant-0.114.4" ];
networking.hostName = "pepe";
# fonts
# -----
programs.custom.urxvt.fontSize = 12;
programs.custom.xterm.fontSize = 12;
system.custom.fonts.dpi = 100;
# networking
# ----------
#system.custom.wifi = {
#enable = false;
#interfaces = [ "wlp3s0" ];
#configurationFile = <secrets/wpa_supplicant>;
#};
configuration.init-ssh = {
enable = "enabled";
kernelModules = [ "e1000e" ];
};
# just enable lan
networking.dhcpcd.allowInterfaces = [ "enp0s25" ];
# nix-shell -p speedtest_cli --run speedtest
configuration.fireqos = {
enable = true;
interface = "wlp3s0";
input = 5000;
output = 1200;
balance = false;
};
services.printing.enable = false;
services.smartd.enable = true;
}

View file

@ -0,0 +1,95 @@
{ lib, pkgs, config, ... }:
# a very simple dms setup which.
# I have a brother ADS-1600W scanner, which is configured to send all
# PDFs to this machine in /home/ftp-upload/input
# from there the dms.py scans them and makes them searchable.
let
dms = pkgs.fetchgit {
url = "https://github.com/mrVanDalo/dms.git";
rev = "2f5c44f017bdfd8abfe908d419ef26bac300f809";
sha256 = "0dxhk1ah6wwbsxyk4hd32rz7886w7r5gfy16485gjbvky1qsi8gd";
};
in {
# setup ftp
services.vsftpd = {
enable = true;
userlist = [ "ftp-upload" ];
userlistEnable = true;
localUsers = true;
writeEnable = true;
extraConfig = ''
# additional ports for directory list and stuff
pasv_min_port=4242
pasv_max_port=4243
'';
};
networking.firewall.allowedTCPPortRanges = [
{
# ftp: additional ports for directory list and stuff
from = 4242;
to = 4243;
}
{
# ftp
from = 20;
to = 21;
}
];
# create user
users.users.ftp-upload = {
passwordFile = toString <secrets/ftp/password>;
isNormalUser = true;
};
# create dms service
systemd.services.dms = {
enable = true;
wantedBy = [ "multi-user.target" ];
path = [
(pkgs.python3.withPackages (ps: with ps; [ flask ]))
pkgs.imagemagickBig
(pkgs.pdfsandwich.override { imagemagick = pkgs.imagemagickBig; })
pkgs.poppler_utils
pkgs.which
pkgs.netpbm
pkgs.gawk
pkgs.bash
];
serviceConfig = { User = "ftp-upload"; };
preStart = ''
if [[ ! -L /home/ftp-upload/db/SOURCE_DIR ]]
then
rm -rf /home/ftp-upload/db/SOURCE_DIR
mkdir -p /home/ftp-upload/db
mkdir -p /home/ftp-upload/input
ln -s /home/ftp-upload/input /home/ftp-upload/db/SOURCE_DIR
fi
'';
script = ''
DMSDATA=/home/ftp-upload/db \
FLASK_APP=${dms}/dms.py \
flask run --host 0.0.0.0 \
"$@"
'';
};
# host nginx setup
services.nginx = {
enable = true;
virtualHosts = {
"dms.pepe.private" = {
serverAliases =
[ "pdf.pepe.private" "docs.pepe.private" "dms.pepe.lan" ];
locations."/" = { proxyPass = "http://localhost:5000"; };
};
};
};
# add documents to backup
backup.dirs = [ "/home/ftp-upload/db" ];
}

View file

@ -0,0 +1,86 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports = [
<nixpkgs/nixos/modules/installer/scan/not-detected.nix>
(let mediaUUID = "29ebe5ba-7599-4dd3-99a3-37b9bf8e4d61";
in {
fileSystems."/media" = {
device = "/dev/disk/by-uuid/${mediaUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${mediaUUID}";
where = "/media";
}];
})
];
boot.initrd.availableKernelModules =
[ "ehci_pci" "ahci" "usb_storage" "sd_mod" "sdhci_pci" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
boot.loader.grub = {
enable = true;
version = 2;
device = "/dev/sda";
};
zramSwap = {
enable = true;
numDevices = 2;
swapDevices = 1;
memoryPercent = 50;
};
fileSystems."/share" = {
#device = "/dev/ram1";
device = "none";
fsType = "tmpfs";
};
nix.maxJobs = lib.mkDefault 2;
# lvm volume group
# ----------------
boot.initrd.luks.devices = {
"secure_vg" = {
device = "/dev/sda2";
preLVM = true;
};
};
# NTFS support
# ------------
environment.systemPackages = [ pkgs.ntfs3g ];
# root
# ----
fileSystems."/" = {
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/secure_vg/root";
fsType = "ext4";
};
# boot
# ----
fileSystems."/boot" = {
device = "/dev/sda1";
fsType = "ext4";
};
}

View file

@ -0,0 +1,302 @@
{ pkgs, config, lib, ... }: {
imports = [
#./home-assistant/mpd.nix
#./home-assistant/timer.nix
./home-assistant/light-control.nix
./home-assistant/iot-control.nix
./home-assistant/chaospott.nix
./home-assistant/kodi.nix
./home-assistant/mqtt.nix
./home-assistant/sonoff.nix
./home-assistant/stocks.nix
./home-assistant/weather.nix
./home-assistant/workday.nix
./home-assistant/zigbee2mqtt.nix
];
networking.firewall.allowedTCPPorts = [ 8123 ];
networking.firewall.allowedUDPPorts = [ 8123 ];
services.homeAssistantConfig = {
# turn on to edit GUI
lovelace = { };
homeassistant = {
name = "Home";
latitude = 51.444847;
longitude = 6.967006;
elevation = 116;
unit_system = "metric";
time_zone = config.time.timeZone;
auth_providers = [{
type = "trusted_networks";
trusted_networks = [
config.module.cluster.services.tinc."private".networkSubnet
"192.168.178.0/24"
"127.0.0.1"
];
}];
};
# needed, don't know why
default_config = { };
config = { };
# to grab metrics via prometheus
prometheus.namespace = "hass";
# manual state
# ------------
# all scenes
input_select.scene = {
icon = "mdi:brightness-auto";
options = [ "up-bright" "up-dark" "half" "down" "night" "outside" ];
};
# scenes controlled by buttons
input_select.scene_button = {
icon = "mdi:brightness-auto";
options = [ "up-dark" "night" ];
};
input_boolean.situation_toggle.icon = "mdi:toggle-switch";
input_boolean.printer_toggle.icon = "mdi:toggle-switch";
input_boolean.windows_up.icon = "mdi:toggle-switch";
automation = [
{
alias = "Hold Button -> Toggle Printer";
trigger = {
platform = "state";
entity_id = "input_boolean.printer_toggle";
};
action = {
service = "switch.toggle";
data.entity_id = "switch.pal02";
};
}
{
alias = "set light-control scene";
trigger = {
platform = "state";
entity_id = "input_select.scene";
};
action = {
service = "mqtt.publish";
data_template = {
topic = "control/lights/set";
payload_template =
''{"scene":"{{ states('input_select.scene')}}"}'';
};
};
}
# control situation with buttons
{
alias = "Button -> Change Scene";
trigger = {
platform = "state";
entity_id = "input_boolean.situation_toggle";
};
action = {
service = "input_select.select_next";
data.entity_id = "input_select.scene_button";
};
}
{
alias = "Button Scene -> Scene";
trigger = {
platform = "state";
entity_id = "input_select.scene_button";
};
action = {
service = "input_select.select_option";
data_template = {
entity_id = "input_select.scene";
option = "{{ states('input_select.scene_button') }}";
};
};
}
# outside and home
{
alias = "when opening door I'm outside";
trigger = [
{
platform = "state";
# todo : groups are not working right now
entity_id = "binary_sensor.door_sensor_4";
from = "off";
to = "on";
}
{
platform = "state";
# todo : groups are not working right now
entity_id = "binary_sensor.door_sensor_4";
from = "on";
to = "off";
}
];
action = [{
service = "input_select.select_option";
data = {
entity_id = "input_select.scene";
option = "outside";
};
}];
}
{
alias = "reset everything when back home";
trigger = map (entity_id: {
platform = "state";
entity_id = entity_id;
from = "off";
to = "on";
}) [
"binary_sensor.motion_sensor_1"
"binary_sensor.motion_sensor_2"
"binary_sensor.motion_sensor_3"
"binary_sensor.motion_sensor_4"
"binary_sensor.motion_sensor_5"
"binary_sensor.motion_sensor_6"
"binary_sensor.motion_sensor_7"
"binary_sensor.motion_sensor_8"
];
condition = {
condition = "template";
value_template =
''{{ states.input_select.scene.state == "outside" }}'';
};
action = [{
service = "input_select.select_option";
data = {
entity_id = "input_select.scene";
option = "up-dark";
};
}];
}
];
group = let
create_room = { name, description }: {
"${name}" = {
name = "${description}";
entities = [ ];
};
};
create_rooms = rooms:
lib.foldr (a: b: a // b) { } (map create_room rooms);
# rooms
# -----
in (create_rooms [
{
name = "floor_room";
description = "Flur";
}
{
name = "bed_room";
description = "Schlafzimmer";
}
{
name = "living_room";
description = "Wohnzimmer";
}
{
name = "kitchen_room";
description = "Küche";
}
{
name = "bath_room";
description = "Klo";
}
]) // {
# overview
# --------
all_sensors = { name = "Alle Sensoren"; };
today = {
name = "Today";
entities = [ "input_select.scene" ];
};
# other stuff
# -----------
tv = { name = "TV"; };
all_lights = { name = "Alle Lampen"; };
unknown = {
name = "Not Used";
entities = [ ];
};
};
};
services.home-assistant = {
enable = true;
package = pkgs.unstable.home-assistant;
#package = pkgs.unstable.home-assistant.override {
# python3 = pkgs.unstable.python37;
# extraPackages = python: [
# # todo : check which is still needed
# python.netdisco
# python.xmltodict
# python.mpd2
# # for mqtt
# python.hbmqtt
# python.paho-mqtt
# # needed for platform workday
# #(python.buildPythonPackage rec {
# # pname = "holidays";
# # version = "0.9.10";
# # src = python.fetchPypi {
# # inherit pname version;
# # sha256 =
# # "9f06d143eb708e8732230260636938f2f57114e94defd8fa2082408e0d422d6f";
# # };
# # doCheck = false;
# # buildInputs = [ pkgs.dateutils ];
# # propagatedBuildInputs = [ python."python-dateutil" python."six" ];
# # meta = with pkgs.stdenv.lib; {
# # homepage = "https://github.com/dr-prodigy/python-holidays";
# # license = licenses.mit;
# # description = "Generate and work with holidays in Python";
# # maintainers = with maintainers; [ mrVanDalo ];
# # };
# #})
# ];
#};
};
# host nginx setup
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"iot.pepe.private" = {
serverAliases = [ "hass.pepe.private" "home.pepe.private" ];
extraConfig = ''
proxy_buffering off;
'';
locations."/" = {
proxyPass = "http://127.0.0.1:8123";
proxyWebsockets = true;
extraConfig = ''
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
'';
};
};
};
};
}

View file

@ -0,0 +1,79 @@
{ config, pkgs, ... }:
let
name = "chaospott";
folderPath = config.services.home-assistant.configDir;
filePath = "${folderPath}/${name}.json";
in {
services.homeAssistantConfig = {
sensor = [
{
platform = "file";
name = "${name}_aerie";
file_path = filePath;
value_template = "{{ value_json.aerie }}";
}
{
platform = "file";
name = "${name}_cellar";
file_path = filePath;
value_template = "{{ value_json.cellar }}";
}
];
homeassistant = {
whitelist_external_dirs = [ folderPath ];
customize = {
"sensor.${name}_aerie" = {
icon = "mdi:store";
entity_picture = "https://chaospott.de/images/logo.png";
friendly_name = "ChaosPott Oben";
};
"sensor.${name}_cellar" = {
icon = "mdi:store";
entity_picture = "https://chaospott.de/images/logo.png";
friendly_name = "ChaosPott Unten";
};
};
};
group = {
"${name}" = {
name = "ChaosPott (Essen)";
entities = [ "sensor.${name}_aerie" "sensor.${name}_cellar" ];
};
};
};
systemd.services."${name}" = {
enable = true;
before = [ "home-assistant.service" ];
wantedBy = [ "home-assistant.service" ];
serviceConfig = {
User = "hass";
Type = "oneshot";
};
description = "set ${name} for homeassistant";
script = ''
${pkgs.curl}/bin/curl --location --silent https://status.chaospott.de/api \
| ${pkgs.jq}/bin/jq --compact-output \
'.sensors.door_locked |
[.[] | { "\(.location)" : (if .value then "closed" else "open" end) }] |
reduce .[] as $item ({}; . + $item) ' \
>> ${filePath}
'';
};
systemd.timers."${name}" = {
enable = true;
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnCalendar = "hourly";
Persistent = "true";
};
};
}

View file

@ -0,0 +1,30 @@
{ lib, pkgs, config, ... }: {
users.users.heater-control = { };
#systemd.services.heater-control = {
# enable = true;
# wantedBy = [ "multi-user.target" ];
# serviceConfig = { User = "heater-control"; };
# script =
# let myPython = pkgs.python3.withPackages (ps: with ps; [ paho-mqtt ]);
# in ''
# cd ${<mqtt>}
# ${myPython}/bin/python heater.py
# '';
#};
users.users.fyrtur-control = { };
systemd.services.fyrtur-control = {
enable = true;
wantedBy = [ "multi-user.target" ];
serviceConfig = { User = "fyrtur-control"; };
script =
let myPython = pkgs.python3.withPackages (ps: with ps; [ paho-mqtt ]);
in ''
cd ${<mqtt>}
${myPython}/bin/python fyrtur.py
'';
};
}

View file

@ -0,0 +1,45 @@
{ pkgs, config, lib, ... }: {
services.homeAssistantConfig = {
media_player = [{
platform = "kodi";
host = "127.0.0.1";
}];
shell_command = {
start_kodi = "sudo ${pkgs.systemd}/bin/systemctl start display-manager";
stop_kodi = "sudo ${pkgs.systemd}/bin/systemctl stop display-manager";
};
automation = [
{
alias = "turn kodi on once the tv is turned on";
trigger = {
platform = "state";
entity_id = "group.tv";
from = "off";
to = "on";
for.seconds = 60;
};
action = [{ service = "shell_command.start_kodi"; }];
}
{
alias = "turn off kodi on once the tv is turned off";
trigger = {
platform = "state";
entity_id = "group.tv";
from = "on";
to = "off";
};
action = [{ service = "shell_command.stop_kodi"; }];
}
];
};
security.sudo.extraConfig = ''
hass ALL= (root) NOPASSWD: ${pkgs.systemd}/bin/systemctl start display-manager
hass ALL= (root) NOPASSWD: ${pkgs.systemd}/bin/systemctl stop display-manager
'';
}

View file

@ -0,0 +1,228 @@
{ pkgs, lib, config, ... }: {
services.mqtt.light-control.enable = true;
services.mqtt.light-control.loglevel = "debug";
services.mqtt.light-control.config = {
credentials = {
host = "tcp://localhost:1883";
user = "homeassistant";
password = "hallo";
};
scenes = [
{
name = "up-dark";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "half";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
disabled_switches = [
"zigbee2mqtt/led_1"
"zigbee2mqtt/led_2"
"zigbee2mqtt/light_2"
"zigbee2mqtt/light_4"
"zigbee2mqtt/light_5"
"zigbee2mqtt/light_7"
];
}
{
name = "down";
ignored_sensors = [
"zigbee2mqtt/door_sensor_1"
"zigbee2mqtt/door_sensor_4"
"zigbee2mqtt/door_sensor_5"
];
}
{
name = "up-bright";
disabled_switches = [
"zigbee2mqtt/led_1"
"zigbee2mqtt/led_2"
"zigbee2mqtt/light_2"
"zigbee2mqtt/light_4"
"zigbee2mqtt/light_5"
"zigbee2mqtt/light_7"
];
ignored_sensors = [ "zigbee2mqtt/door_sensor_4" ];
}
{
name = "outside";
room_tracking_enabled = false;
ignored_sensors = [ "zigbee2mqtt/door_sensor_4" ];
}
{
name = "night";
room_tracking_enabled = false;
brightness = 25;
ignored_sensors =
[ "zigbee2mqtt/motion_sensor_7" "zigbee2mqtt/door_sensor_4" ];
}
];
sensors = let
door = { topic, room }: {
topic = topic;
key = "contact";
room = room;
invert_state = true;
delay = 90;
};
motion = { topic, room }: {
topic = topic;
key = "occupancy";
room = room;
delay = 60;
};
in [
(motion {
topic = "zigbee2mqtt/motion_sensor_1";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_2";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_6";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_8";
room = "office_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_7";
room = "sleeping_room";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_5";
room = "kitchen";
})
(motion {
topic = "zigbee2mqtt/motion_sensor_4";
room = "storage_room";
})
(door {
topic = "zigbee2mqtt/door_sensor_1";
room = "storage_room";
})
(door {
topic = "zigbee2mqtt/door_sensor_5";
room = "sleeping_room";
})
(door {
# house door
topic = "zigbee2mqtt/door_sensor_4";
room = "floor";
})
];
switches = let
sonoff = { id, rooms, delay ? 0 }: {
topic = "stat/${id}/RESULT";
key = "POWER";
rooms = rooms;
delay = delay;
command = {
command = "{{state}}";
init_command = "(null)";
topic = "cmnd/${id}/POWER";
on = "ON";
off = "OFF";
};
};
light = { topic, rooms, delay ? 0 }: {
topic = topic;
key = "state";
rooms = rooms;
delay = delay;
command = {
command = ''{"state":"{{state}}","brightness":{{brightness}}}'';
topic = "${topic}/set";
on = "ON";
off = "OFF";
};
};
led = { topic, rooms, delay ? 0 }: {
topic = topic;
key = "state";
rooms = rooms;
delay = delay;
command = {
# Configure it once to the color you like
# {"state":"{{state}}","brightness":{{brightness}},"color":{"hex":"#FFFFFF},"color_temp":255","transition":0}
command = ''
{"state":"{{state}}","brightness":{{brightness}},"transition":0}'';
topic = "${topic}/set";
on = "ON";
off = "OFF";
};
};
in [
(light {
topic = "zigbee2mqtt/light_2";
rooms = [ "office_room" ];
})
(light {
topic = "zigbee2mqtt/light_4";
rooms = [ "office_room" ];
})
(light {
topic = "zigbee2mqtt/light_5";
rooms = [ "storage_room" ];
})
(light {
topic = "zigbee2mqtt/light_7";
rooms = [ "sleeping_room" ];
})
(led {
topic = "zigbee2mqtt/led_1";
rooms = [ "office_room" ];
})
(led {
topic = "zigbee2mqtt/led_2";
rooms = [ "kitchen" ];
})
#(sonoff {
# id = "PAL01";
# rooms = [ "bed_room" ];
#})
#(sonoff {
# id = "PAL03";
# rooms = [ "living_room" ];
#})
#(sonoff {
# id = "PAL04";
# rooms = [ "bed_room" ];
#})
#(sonoff {
# id = "PAL06";
# rooms = [ "kitchen" ];
#})
## monitor and speakers
#(sonoff {
# id = "PAL07";
# rooms = [ "bed_room" ];
# delay = 180;
#})
#(sonoff {
# id = "PAL08";
# rooms = [ "bed_room" ];
# delay = 180;
#})
];
};
}

View file

@ -0,0 +1,13 @@
{ lib, ... }:
{
services.homeAssistantConfig = {
media_player = [{
platform = "mpd";
host = "localhost";
}];
};
}

View file

@ -0,0 +1,31 @@
{ lib, ... }: {
services.homeAssistantConfig.mqtt = {
# discovery = false;
# for mosquitto
broker = "127.0.0.1";
username = lib.fileContents <secrets/home-assistant/mqtt-user>;
password = lib.fileContents <secrets/home-assistant/mqtt-password>;
};
services.mosquitto = {
enable = true;
host = "0.0.0.0";
users = {
homeassistant = {
password = lib.fileContents <secrets/mosquitto/password>;
acl = [ "topic readwrite #" ];
};
zigbee = {
password = lib.fileContents <secrets/zigbee/password>;
acl = [ "topic readwrite #" ];
};
lightcontrol = {
password = "password";
acl = [ "topic readwrite #" ];
};
};
};
}

View file

@ -0,0 +1,104 @@
{ pkgs, config, lib, ... }:
let
sonoffSwitches = {
#"pal01" = {
# label = "Schlafzimmer Lampe Links";
# icon = "mdi:lightbulb";
# groups = [ "bed_room" ];
#};
"pal02" = {
label = "Drucker / Scanner";
icon = "mdi:printer";
groups = [ "bed_room" "today" ];
};
#"pal03" = {
# label = "Wohnzimmer Lampe";
# icon = "mdi:lightbulb";
# groups = [ "living_room" ];
#};
#"pal04" = {
# label = "Schlafzimmer Lampe Rechts";
# icon = "mdi:lightbulb";
# groups = [ "bed_room" ];
#};
"pal05" = {
label = "TV";
icon = "mdi:television";
groups = [ "tv" ];
device = "tv";
};
#"pal06" = {
# label = "Küchen Lampe";
# icon = "mdi:lightbulb";
# groups = ["kitchen_room"];
#};
"pal07" = { label = "Nummer 7"; };
"pal08" = { label = "Nummer 8"; };
};
toSwitch = name: "switch.${name}";
in {
imports = [ ./mqtt.nix ];
services.homeAssistantConfig = {
# nicer names
# -----------
homeassistant.customize = lib.mapAttrs' (entity:
{ label, icon ? "mdi:power-plug-off", ... }: {
name = toSwitch entity;
value = {
friendly_name = label;
icon = icon;
};
}) sonoffSwitches;
# define switches
# ---------------
switch = lib.mapAttrsToList (name:
{ ... }: {
name = name;
platform = "mqtt";
command_topic = "cmnd/${lib.toUpper name}/POWER";
state_topic = "stat/${lib.toUpper name}/POWER";
payload_on = "ON";
payload_off = "OFF";
state_on = "ON";
state_off = "OFF";
}) sonoffSwitches;
# discover state on init
# ----------------------
automation = [{
alias = "Sonoff initial Power state";
trigger = {
platform = "homeassistant";
event = "start";
};
action = lib.mapAttrsToList (name:
{ ... }: {
service = "mqtt.publish";
data = {
topic = "cmnd/${lib.toUpper name}/power";
payload = "";
};
}) sonoffSwitches;
}];
# append to groups
# ----------------
group = let
# sort lights into given groups.
sortedInGroups = let
groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
{ groups ? [ ], ... }:
map (groupName: { "${groupName}" = "switch.${name}"; }) groups)
sonoffSwitches));
in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
in sortedInGroups;
};
}

View file

@ -0,0 +1,173 @@
{ lib, config, pkgs, ... }:
let
folderPath = config.services.home-assistant.configDir;
# find symbols with
# https://www.alphavantage.co/query?function=SYMBOL_SEARCH&keywords=<keywords>&apikey=<api_key>
# as described here : https://www.alphavantage.co/documentation/#symbolsearch
#
# example:
# --------
# stocks = [
# {
# symbol = "GOOGL";
# name = "google";
# friendly_name = "Google";
# currency = "$";
# # I own 50 and bought at a price of 1000
# own = {
# pieces = 50;
# price = 1000;
# };
# }
# ];
stocks = import <secrets/home-assistant/stocks>;
filePath = name: "${folderPath}/stock_${name}.json";
cleanup_list = list: lib.filter (entry: entry != { }) (lib.flatten list);
in {
services.homeAssistantConfig = {
sensor = cleanup_list (map ({ name, currency, own ? { }, ... }: [
{
platform = "file";
name = "stock_${name}";
file_path = filePath name;
value_template = "{{ value_json.price}} ${currency}";
}
{
platform = "file";
name = "stock_${name}_change";
file_path = filePath name;
value_template = "{{ value_json.change}} ${currency}";
}
{
platform = "file";
name = "stock_${name}_change_percent";
file_path = filePath name;
value_template = "{{ value_json.change_percent}} %";
}
(lib.optionalAttrs (own != { }) {
platform = "file";
name = "stock_${name}_profit";
file_path = filePath name;
value_template = ''
{{ "{:,.2f}".format( value_json.price * ${toString own.pieces} - ${
toString (own.pieces * own.price)
} ) }} ${currency}'';
})
]) stocks);
homeassistant = {
whitelist_external_dirs = [ folderPath ];
customize = builtins.listToAttrs (cleanup_list (map
({ name, own ? { }, ... }: [
{
name = "sensor.stock_${name}";
value = {
icon = "mdi:cash-usd-outline";
friendly_name = "Price";
};
}
{
name = "sensor.stock_${name}_change";
value = {
icon = "mdi:radar";
friendly_name = "Difference";
};
}
{
name = "sensor.stock_${name}_change_percent";
value = {
icon = "mdi:radar";
friendly_name = "Percent";
};
}
(lib.optionalAttrs (own != { }) {
name = "sensor.stock_${name}_profit";
value = {
icon = "mdi:radar";
friendly_name = "Profit";
};
})
]) stocks));
};
group = (builtins.listToAttrs (map
({ name, friendly_name, own ? { }, ... }: {
name = "stock_${name}";
value = {
name = "${friendly_name} Aktie";
entities = [
"sensor.stock_${name}"
"sensor.stock_${name}_change"
"sensor.stock_${name}_change_percent"
] ++ (lib.optional (own != { }) "sensor.stock_${name}_profit");
};
}) stocks));
};
systemd.services = let
pullService = { name, symbol, currency, ... }: {
name = "pull_stock_${name}";
value = {
enable = true;
before = [ "home-assistant.service" ];
wantedBy = [ "home-assistant.service" ];
serviceConfig = {
User = "hass";
Type = "oneshot";
};
description = "pull stock_${name} for homeassistant";
script = ''
SYMBOL="${symbol}"
CURRENCY="${currency}"
APIKEY=${
lib.fileContents <secrets/home-assistant/alphavantage/apikey>
}
${pkgs.curl}/bin/curl --location --silent \
"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=$SYMBOL&apikey=$APIKEY" \
| ${pkgs.jq}/bin/jq --compact-output \
'.["Global Quote"] |
{
price: .["05. price"] | tonumber,
currency: "'$CURRENCY'",
change_percent: .["10. change percent"] | .[0:-1] | tonumber,
change: .["09. change"] | tonumber,
last_date: .["07. latest trading day"],
}' \
>> ${filePath name}
# old and stupid
#${pkgs.curl}/bin/curl --location --silent \
#"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=$SYMBOL&interval=5min&apikey=$APIKEY" \
#| ${pkgs.jq}/bin/jq --compact-output \
# '.["Time Series (5min)"] | to_entries | [ .[]
# | { date : .key , value : .value["4. close"], currency: "'$CURRENCY'" } ]
# | sort_by(.date) | reverse | .[0]' \
'';
};
};
in builtins.listToAttrs (map pullService stocks);
systemd.timers = let
pullTimer = { name, ... }: {
name = "pull_stock_${name}";
value = {
enable = true;
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnCalendar = "hourly";
Persistent = "true";
};
};
};
in builtins.listToAttrs (map pullTimer stocks);
}

View file

@ -0,0 +1,225 @@
{ config, ... }: {
imports = [ ./mpd.nix ];
services.homeAssistantConfig = {
sensor = [{
platform = "time_date";
display_options = [ "time" "date" ];
}];
input_datetime = {
wakeup = {
name = "Arbeitswecker";
has_time = true;
has_date = false;
icon = "mdi:alarm";
};
leave = {
name = "Turn off Time";
has_time = true;
has_date = false;
icon = "mdi:alarm";
};
return = {
name = "Return home";
has_time = true;
has_date = false;
icon = "mdi:alarm";
};
sleep = {
name = "Turn off Time";
has_time = true;
has_date = false;
icon = "mdi:alarm";
};
};
input_boolean = {
wakeup = {
name = "enable";
icon = "mdi:toggle-switch";
};
leave = {
name = "enable";
icon = "mdi:toggle-switch";
};
return = {
name = "enable";
icon = "mdi:toggle-switch";
};
sleep = {
name = "enable";
icon = "mdi:toggle-switch";
};
};
input_select = {
wakeup = {
name = "Playlist";
icon = "mdi:library-music";
options = [ "wakeup" "wakeup1" "wakeup2" ];
};
};
binary_sensor = [
{
platform = "tod";
name = "night";
after = "sunset";
before = "sunrise";
}
{
platform = "tod";
name = "daytime";
after = "sunrise";
before = "sunset";
}
];
group = {
timer_wakeup = {
name = "Arbeits Aufwachen";
entities = [
"input_boolean.wakeup"
"input_datetime.wakeup"
"input_select.wakeup"
];
};
timer_leave = {
name = "Leave Time";
entities = [ "input_boolean.leave" "input_datetime.leave" ];
};
timer_return = {
name = "Nach Hause kommen";
entities = [ "input_boolean.sleep" "input_datetime.sleep" ];
};
timers.entities = [
"group.timer_wakeup"
"group.timer_leave"
"group.timer_return"
"group.timer_sleep"
"binary_sensor.night"
"binary_sensor.daytime"
];
today.entities = [ "sensor.date" "sensor.time" ];
};
automation = [
{
alias = "Wecker Arbeiten";
trigger = {
platform = "template";
value_template =
"{{ states('sensor.time') == (states.input_datetime.wakeup.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}";
};
condition = {
condition = "and";
conditions = [
{
condition = "state";
entity_id = "input_boolean.wakeup";
state = "on";
}
{
condition = "state";
entity_id = "binary_sensor.workday";
state = "on";
}
];
};
action = [
{
alias = "Play wakeup list";
service = "media_player.play_media";
data_template = {
entity_id = "media_player.mpd";
media_content_type = "playlist";
media_content_id = "{{ states('input_select.wakeup') }}";
};
}
{
alias = "turn all on";
service = "script.turn_on";
entity_id = "script.turn_all_on";
}
];
}
{
alias = "Leave Turn all off Timer";
trigger = {
platform = "template";
value_template =
"{{ states('sensor.time') == (states.input_datetime.leave.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}";
};
condition = {
condition = "and";
conditions = [{
condition = "state";
entity_id = "input_boolean.leave";
state = "on";
}];
};
action = [{
alias = "turn all off";
service = "script.turn_on";
entity_id = "script.turn_all_off";
}];
}
{
alias = "Return to Home";
trigger = {
platform = "template";
value_template =
"{{ states('sensor.time') == (states.input_datetime.return.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}";
};
condition = {
condition = "and";
conditions = [{
condition = "state";
entity_id = "input_boolean.return";
state = "on";
}];
};
action = [{
alias = "turn all on";
service = "script.turn_on";
entity_id = "script.turn_all_on";
}];
}
{
alias = "Sleep Turn all off Timer";
trigger = {
platform = "template";
value_template =
"{{ states('sensor.time') == (states.input_datetime.sleep.attributes.timestamp | int | timestamp_custom('%H:%M', False)) }}";
};
condition = {
condition = "and";
conditions = [{
condition = "state";
entity_id = "input_boolean.sleep";
state = "on";
}];
};
action = [{
alias = "turn all off";
service = "script.turn_on";
entity_id = "script.turn_all_off";
}];
}
];
};
}

View file

@ -0,0 +1,18 @@
{ config, ... }: {
services.homeAssistantConfig = {
sensor = [
# Weather prediction
{
platform = "zamg";
name = "weather";
}
];
sun = { };
group.today.entities = [ "sensor.weather_temperature" "sun.sun" ];
};
}

View file

@ -0,0 +1,55 @@
{ config, lib, ... }:
let
holiday-range = month: dayA: dayB:
map (day: "${month}-${toString day}")
(map (lib.fixedWidthNumber 2) (lib.range dayA dayB));
privateHolidays = import <secrets/home-assistant/holidays>;
# for example :
# holidays = lib.flatten [
# (holiday-range "2020-06" 7 24)
# (holiday-range "2020-10" 10 16)
#];
holidays = lib.flatten (privateHolidays holiday-range);
in {
services.homeAssistantConfig = {
binary_sensor = [
{
name = "before_workday";
platform = "workday";
country = "DE";
province = "NW";
workdays = [ "mon" "tue" "wed" "thu" "fri" ];
days_offset = 1;
add_holidays = holidays;
}
{
name = "workday";
platform = "workday";
country = "DE";
province = "NW";
workdays = [ "mon" "tue" "wed" "thu" "fri" ];
add_holidays = holidays;
}
];
homeassistant.customize = {
"binary_sensor.workday" = {
icon = "mdi:calendar";
friendly_name = "Workday?";
};
"binary_sensor.before_workday" = {
icon = "mdi:calendar";
friendly_name = "Workday Tomorrow?";
};
};
group = {
today.entities =
[ "binary_sensor.before_workday" "binary_sensor.workday" ];
};
};
}

View file

@ -0,0 +1,100 @@
{ pkgs, lib, config, ... }:
# no need to set ZIGBEE2MQTT_DATA anymore
assert lib.versionOlder lib.version "21.03";
{
imports = [
./mqtt.nix
./zigbee2mqtt/service.nix
./zigbee2mqtt/buttons.nix
./zigbee2mqtt/configurationHelper.nix
./zigbee2mqtt/doors.nix
./zigbee2mqtt/fyrtur.nix
./zigbee2mqtt/heater.nix
./zigbee2mqtt/leds.nix
./zigbee2mqtt/lights.nix
./zigbee2mqtt/motion.nix
./zigbee2mqtt/repeater.nix
./zigbee2mqtt/temperatur.nix
];
custom.services.zigbee2mqtt = {
enable = true;
#package = pkgs.own_zigbee2mqtt;
#package = unstable.zigbee2mqtt;
package = pkgs.unstable.zigbee2mqtt.overrideAttrs (old: rec {
version = "1.18.1";
src = pkgs.fetchFromGitHub {
owner = "Koenkk";
repo = "zigbee2mqtt";
rev = version;
sha256 = "1x73k346ayik5hv5axa3nvmd82mgwyrpxqv3dxnffi8aa1r8pf8x";
};
});
config = {
# Home Assistant integration (MQTT discovery)
homeassistant = false;
# allow new devices to join
permit_join = false;
# MQTT settings
mqtt = {
# MQTT base topic for zigbee2mqtt MQTT messages
base_topic = "zigbee2mqtt";
# MQTT server URL
server = "mqtt://127.0.0.1:1883";
# MQTT server authentication, uncomment if required:
user = "zigbee";
password = lib.fileContents <secrets/zigbee/password>;
};
# Serial settings
serial = {
#port = "/dev/ttyACM0";
port = "/dev/ttyUSB0";
# disable LED of CC2531 USB sniffer
#disable_led = true;
};
# you own network key,
# 16 numbers between 0 and 255
# see https://www.zigbee2mqtt.io/how_tos/how_to_secure_network.html
advanced.network_key = import <secrets/home-assistant/zigbee/networkKey>;
advanced.log_output = [ "console" ];
advanced.pan_id = 1337;
# add last seen information
advanced.last_seen = "ISO_8601_local";
# configure web ui
frontend.port = 9666;
frontend.host = "0.0.0.0";
experimental.new_api = true;
};
};
#systemd.services.zigbee2mqtt.environment = {
# ZIGBEE2MQTT_DATA = "/var/lib/zigbee2mqtt";
#};
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"zigbee2mqtt.pepe.private" = {
serverAliases = [ "zigbee.pepe.private" ];
locations."/" = {
proxyPass = "http://localhost:${
toString config.custom.services.zigbee2mqtt.config.frontend.port
}";
proxyWebsockets = true;
};
};
};
};
}

View file

@ -0,0 +1,226 @@
{ pkgs, lib, ... }:
let
# we create 3 input_boolean which get toggled by the 3 types of buttons pressed.
# input_boolean.single_${name} : single click
# input_boolean.double_${name} : double click
# input_boolean.hold_${name} : hold
# if you override these input (via states) you have to create the input yourself
# https://www.zigbee2mqtt.io/devices/WXKG12LM.html
allDevices = {
"button_a1" = {
id = "0x00158d0002b04f65";
#groups = [ "living_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
"button_a2" = {
id = "0x00158d0002b04f09";
#groups = [ "bed_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
"button_a3" = {
id = "0x00158d0002b00e04";
#groups = [ "bed_room" ];
states.single = "input_boolean.situation_toggle";
states.hold = "input_boolean.printer_toggle";
states.double = "input_boolean.windows_up";
};
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
}) allDevices;
services.homeAssistantConfig = {
# define input_boolean
# --------------------
# which get toggled by the buttons
input_boolean = let stripEmpty = lib.filter (a: a != { });
in builtins.listToAttrs (stripEmpty (lib.flatten (lib.mapAttrsToList (name:
{ states ? { }, ... }: [
(lib.optionalAttrs (!lib.hasAttr "single" states) {
name = "single_${name}";
value = { icon = "mdi:toggle-switch"; };
})
(lib.optionalAttrs (!lib.hasAttr "double" states) {
name = "double_${name}";
value = { icon = "mdi:toggle-switch"; };
})
(lib.optionalAttrs (!lib.hasAttr "hold" states) {
name = "hold_${name}";
value = { icon = "mdi:toggle-switch"; };
})
]) allDevices)));
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
platform = "mqtt";
name = name;
icon = "mdi:toggle-switch";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
value_template = "{{ value_json.click }}";
}
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
binary_sensor = lib.mapAttrsToList (name:
{ ... }: {
name = name;
platform = "mqtt";
device_class = "motion";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
payload_on = true;
payload_off = false;
value_template = "{{ value_json.occupancy }}";
}) allDevices;
# create groups
# -------------
#group = let
# # to have nice panels for every device
# sensorGroups = lib.mapAttrs (name:
# { states ? { }, ... }:
# let
# entityIds = { single ? "input_boolean.single_${name}"
# , double ? "input_boolean.double_${name}"
# , hold ? "input_boolean.hold_${name}", ... }: [
# single
# double
# hold
# ];
# in {
# entities = [ "sensor.${name}" ] ++ (entityIds states)
# ++ [ "sensor.battery_${name}" "sensor.link_${name}" ];
# }) allDevices;
# # sort lights into given groups.
# sortedInGroups = let
# groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
# { groups ? [ ], states ? { }, ... }:
# map (groupName: {
# "${groupName}" = if (lib.hasAttr "single" states) then
# states.single
# else
# "input_boolean.single_${name}";
# }) groups) allDevices));
# in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
#in sortedInGroups // sensorGroups // {
# all_sensors.entities =
# lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices;
#};
# create automation
# -----------------
automation = let
# single click
toggle_single_button_input = lib.mapAttrsToList (name:
{ states ? { }, ... }:
let
entityId = if (lib.hasAttr "single" states) then
states.single
else
"input_boolean.single_${name}";
in {
alias = "toggle single click ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "single" == trigger.payload_json.click}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
}) allDevices;
# double click
toggle_double_button_input = lib.mapAttrsToList (name:
{ states ? { }, ... }:
let
entityId = if (lib.hasAttr "double" states) then
states.double
else
"input_boolean.double_${name}";
in {
alias = "toggle double click ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "double" == trigger.payload_json.click}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
}) allDevices;
# hold
toggle_hold_button_input = lib.mapAttrsToList (name:
{ states ? { }, ... }:
let
entityId = if (lib.hasAttr "hold" states) then
states.hold
else
"input_boolean.hold_${name}";
in {
alias = "toggle hold ${name}";
trigger = {
platform = "mqtt";
topic = "zigbee2mqtt/${name}";
};
condition = {
condition = "template";
value_template = ''{{ "hold" == trigger.payload_json.action}}'';
};
action = {
service = "input_boolean.toggle";
data.entity_id = entityId;
};
}) allDevices;
in lib.flatten (toggle_single_button_input ++ toggle_double_button_input
++ toggle_hold_button_input);
};
}

View file

@ -0,0 +1,12 @@
{ config, lib, pkgs, ... }:
with lib;
let cfg = config.services.zigbee2mqttConfiguration;
in {
options.services.zigbee2mqttConfiguration = mkOption {
type = with types; attrs;
description = ''
device definitions
'';
};
config = { services.zigbee2mqtt.config.devices = cfg; };
}

View file

@ -0,0 +1,87 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/MCCGQ11LM.html
allDevices = {
"door_sensor_1" = { id = "0x00158d000312dc52"; };
"door_sensor_2" = { id = "0x00158d000316d5bf"; };
"door_sensor_3" = { id = "0x00158d0002f9516f"; };
"door_sensor_4" = { id = "0x00158d00031383b9"; };
"door_sensor_5" = { id = "0x00158d0003120d3e"; };
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
}) allDevices;
services.homeAssistantConfig = {
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
binary_sensor = lib.mapAttrsToList (name:
{ ... }: {
name = name;
platform = "mqtt";
device_class = "door";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
payload_on = false;
payload_off = true;
value_template = "{{ value_json.contact}}";
}) allDevices;
# create groups
# -------------
group = let
# to have nice panels for every device
sensorGroups = lib.mapAttrs (name:
{ ... }: {
entities = [
"binary_sensor.${name}"
"sensor.battery_${name}"
"sensor.link_${name}"
];
}) allDevices;
# sort lights into given groups.
sortedInGroups = let
groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
{ groups ? [ ], ... }:
map (groupName: { "${groupName}" = "binary_sensor.${name}"; }) groups)
allDevices));
in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
in sortedInGroups // sensorGroups // {
all_sensors.entities =
lib.mapAttrsToList (name: { ... }: "binary_sensor.${name}") allDevices;
};
};
}

View file

@ -0,0 +1,52 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/E1757.html
allDevices = {
"office_fyrtur_1" = { id = "0x680ae2fffe64fa40"; };
"office_fyrtur_2" = { id = "0x680ae2fffe91d234"; };
"bedroom_fyrtur_1" = { id = "0x680ae2fffe6e9f41"; };
"broken_fyrtur_1" = { id = "0x680ae2fffe8f6411"; };
};
# -t "zigbee2mqtt/fyrtur1/set" -m '{"position":100}'
# -t "zigbee2mqtt/fyrtur1/set" -m '{"position":15}'
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
transition = 0.1;
};
}) allDevices;
services.homeAssistantConfig = {
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
};
}

View file

@ -0,0 +1,80 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/SPZB0001.html
allDevices = {
"office_heater_1" = { id = "0x00158d00032f5ee4"; }; # office
"office_heater_2" = { id = "0x00158d00032f5f9f"; }; # office (kitchen)
"bedroom_heater_1" = { id = "0x00158d00032f6d1e"; }; # bed room
"storage_heater_1" = { id = "0x00158d00032f604d"; }; # abstell raum
};
# -t "zigbee2mqtt/heater3/set" -m '{"system_mode":"auto","current_heating_setpoint":23}'
# -t "zigbee2mqtt/heater3/set" -m '{"system_mode":"off"}'
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
legacy = false;
retain = false;
friendly_name = name;
transition = 1;
debounce = 0.5;
filtered_attributes = [
"battery_low"
"eurotronic_host_flags"
"eurotronic_system_mode"
#"occupied_heating_setpoint"
#"pi_heating_demand"
#"unoccupied_heating_setpoint"
];
};
}) allDevices;
services.homeAssistantConfig = {
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
{
platform = "mqtt";
name = "temperature_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "°C";
device_class = "temperature";
value_template = "{{ value_json.local_temperature }}";
}
{
platform = "mqtt";
name = "pi_heating_demand_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
value_template = "{{ value_json.pi_heating_demand }}";
}
]) allDevices);
};
}

View file

@ -0,0 +1,43 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/GL-C-007-1ID.html
allDevices = {
"led_1" = { id = "0x00124b001f7a5be9"; };
"led_2" = { id = "0x00124b001ee958b3"; };
};
# -t "zigbee2mqtt/led_1/set" -m '{"state":"ON","transition":0, "color_temp":255}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"OFF","transition":0, "color_temp":255}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"ON","brightness":255,"color":{"hex":"#00FFFF"}}'
# -t "zigbee2mqtt/led_1/set" -m '{"state":"OFF"}'
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
transition = 1;
};
}) allDevices;
services.homeAssistantConfig = {
light = lib.mapAttrsToList (name:
{ ... }: {
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
command_topic = "zigbee2mqtt/${name}/set";
value_template = "{{ value_json.click }}";
brightness = true;
color_temp = true;
schema = "json";
}) allDevices;
};
}

View file

@ -0,0 +1,94 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/AC10787.html
allDevices = {
"light_1" = { id = "0x7cb03eaa0a0347b5"; };
"light_2" = { id = "0x7cb03eaa0a0387b9"; };
"light_3" = { id = "0x7cb03eaa0a033a86"; };
"light_4" = { id = "0x7cb03eaa0a04aabf"; };
"light_5" = { id = "0x7cb03eaa0a0346e4"; };
"light_6" = { id = "0x7cb03eaa0a034b46"; };
"light_7" = { id = "0x7cb03eaa0a033b4f"; };
"light_8" = { id = "0x7cb03eaa0a0384d3"; };
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
osram_set_transition = 2; # time in seconds (integer or float)
};
}) allDevices;
services.homeAssistantConfig = {
light = lib.mapAttrsToList (name:
{ ... }: {
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
command_topic = "zigbee2mqtt/${name}/set";
value_template = "{{ value_json.click }}";
brightness = true;
color_temp = true;
schema = "json";
}) allDevices;
# sensor = with lib;
# mapAttrsToList (name:
# { ... }: {
# name = "link_${name}";
# platform = "mqtt";
# state_topic = "zigbee2mqtt/${name}";
# availability_topic = "zigbee2mqtt/bridge/state";
# icon = "mdi:signal";
# value_template = "{{ value_json.linkquality}}";
# }) allDevices;
# binary_sensor = lib.mapAttrsToList (name:
# { ... }: {
# name = "update_${name}";
# platform = "mqtt";
# state_topic = "zigbee2mqtt/${name}";
# availability_topic = "zigbee2mqtt/bridge/state";
# payload_on = true;
# payload_off = false;
# value_template = "{{ value_json.update_available }}";
# }) allDevices;
# # create groups
# # -------------
# group = let
# # to have nice panels for every device
# lightGroups = lib.mapAttrs (name:
# { ... }: {
# entities = [
# "light.${name}"
# "sensor.link_${name}"
# "binary_sensor.update_${name}"
# ];
# }) allDevices;
# # sort lights into given groups.
# sortedInGroups = let
# groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
# { groups ? [ ], ... }:
# map (groupName: { "${groupName}" = "light.${name}"; }) groups)
# allDevices));
# in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
# in sortedInGroups // lightGroups // {
# all_lights.entities =
# lib.mapAttrsToList (name: { ... }: "light.${name}") allDevices;
# };
};
}

View file

@ -0,0 +1,69 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/RTCGQ01LM.html
allDevices = {
"motion_sensor_1" = { id = "0x00158d0002fbd451"; };
"motion_sensor_2" = { id = "0x00158d0002f9a6b8"; };
"motion_sensor_3" = { id = "0x00158d0002f04522"; };
"motion_sensor_4" = { id = "0x00158d0002f9a558"; };
"motion_sensor_5" = { id = "0x00158d0002f9a56f"; };
"motion_sensor_6" = { id = "0x00158d0002f9a5cb"; };
"motion_sensor_7" = { id = "0x00158d0002f9a6aa"; };
"motion_sensor_8" = { id = "0x00158d0002f04637"; };
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, timeout ? 65, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
# should not be set below 60 seconds
occupancy_timeout = timeout;
};
}) allDevices;
services.homeAssistantConfig = {
# define meta information sensors
binary_sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [{
name = "${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
value_template = "{{ value_json.occupancy }}";
#icon = "mdi:battery-10";
payload_on = true;
payload_off = false;
device_class = "motion";
}]) allDevices);
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
};
}

View file

@ -0,0 +1,18 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/SPZB0001.html
allDevices = {
"repeater1" = { id = "0x680ae2fffe6e7dc1"; };
"repeater2" = { id = "0x680ae2fffe8e240d"; };
"repeater3" = { id = "0x680ae2fffe404f60"; };
"repeater4" = { id = "0x680ae2fffe8e2e71"; };
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = { friendly_name = name; };
}) allDevices;
}

View file

@ -0,0 +1,118 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.custom.services.zigbee2mqtt;
configJSON = pkgs.writeText "configuration.json" (builtins.toJSON
(recursiveUpdate cfg.config config.services.zigbee2mqtt.config));
configFile =
pkgs.runCommand "configuration.yaml" { preferLocalBuild = true; } ''
${pkgs.remarshal}/bin/json2yaml -i ${configJSON} -o $out
'';
# the default config contains all required settings,
# so the service starts up without crashing.
defaultConfig = {
homeassistant = false;
permit_join = false;
mqtt = {
base_topic = "zigbee2mqtt";
server = "mqtt://localhost:1883";
};
serial.port = "/dev/ttyACM0";
# put device configuration into separate file because configuration.yaml
# is copied from the store on startup
devices = "devices.yaml";
};
in {
options.custom.services.zigbee2mqtt = {
enable = mkEnableOption "enable zigbee2mqtt service";
package = mkOption {
description = "ignored";
default = pkgs.zigbee2mqtt.override { dataDir = cfg.dataDir; };
defaultText = "pkgs.zigbee2mqtt";
type = types.package;
};
dataDir = mkOption {
description = "Zigbee2mqtt data directory";
default = "/var/lib/zigbee2mqtt";
type = types.path;
};
config = mkOption {
default = { };
type = with types; nullOr attrs;
example = literalExample ''
{
homeassistant = config.services.home-assistant.enable;
permit_join = true;
serial = {
port = "/dev/ttyACM1";
};
}
'';
description = ''
Your <filename>configuration.yaml</filename> as a Nix attribute set.
'';
};
};
config = mkIf (cfg.enable) {
virtualisation.oci-containers.containers.zigbee2mqtt = {
image = "koenkk/zigbee2mqtt:1.18.1";
volumes = [
"${cfg.dataDir}:/app/data"
#"/run/udev:/run/udev:ro"
];
extraOptions = [
"--device=${cfg.config.serial.port}" # /dev/ttyUSB0
"--network=host"
#"--privileged=true"
];
environment = { TZ = "Europe/Amsterdam"; };
#ports = [ "127.0.0.1:${toString frontPort}:80" ];
};
# create config before staring container
systemd.services.docker-zigbee2mqtt = {
preStart = ''
cp --no-preserve=mode ${configFile} "${cfg.dataDir}/configuration.yaml"
'';
};
#systemd.services.zigbee2mqtt = {
# description = "Zigbee2mqtt Service";
# wantedBy = [ "multi-user.target" ];
# after = [ "network.target" ];
# environment.ZIGBEE2MQTT_DATA = cfg.dataDir;
# serviceConfig = {
# ExecStart = "${cfg.package}/bin/zigbee2mqtt";
# User = "zigbee2mqtt";
# WorkingDirectory = cfg.dataDir;
# Restart = "on-failure";
# ProtectSystem = "strict";
# ReadWritePaths = cfg.dataDir;
# PrivateTmp = true;
# RemoveIPC = true;
# };
# preStart = ''
# cp --no-preserve=mode ${configFile} "${cfg.dataDir}/configuration.yaml"
# '';
#};
#users.users.zigbee2mqtt = {
# home = cfg.dataDir;
# createHome = true;
# group = "zigbee2mqtt";
# extraGroups = [ "dialout" ];
# uid = config.ids.uids.zigbee2mqtt;
#};
#users.groups.zigbee2mqtt.gid = config.ids.gids.zigbee2mqtt;
};
}

View file

@ -0,0 +1,107 @@
{ pkgs, lib, ... }:
let
# https://www.zigbee2mqtt.io/devices/WSDCGQ11LM.html
allDevices = {
"temperature_sensor_1" = {
id = "0x00158d0002d79220";
groups = [ "living_room" ];
};
"temperature_sensor_2" = {
id = "0x00158d0002d7913d";
groups = [ "living_room" ];
};
};
in {
services.zigbee2mqttConfiguration = lib.mapAttrs' (name:
{ id, ... }: {
name = id;
value = {
retain = false;
friendly_name = name;
};
}) allDevices;
services.homeAssistantConfig = {
# define meta information sensors
sensor = lib.flatten (lib.mapAttrsToList (name:
{ ... }: [
{
platform = "mqtt";
name = name;
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "°C";
device_class = "temperature";
value_template = "{{ value_json.temperature }}";
}
{
platform = "mqtt";
name = "humidity_${name}";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
device_class = "humidity";
value_template = "{{ value_json.humidity }}";
}
#{
# platform = "mqtt";
# name = "pressure_${name}";
# state_topic = "zigbee2mqtt/${name}";
# availability_topic = "zigbee2mqtt/bridge/state";
# unit_of_measurement = "hPa";
# device_class = "pressure";
# value_template = "{{ value_json.pressure }}";
#}
{
name = "battery_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
unit_of_measurement = "%";
icon = "mdi:battery-10";
value_template = "{{ value_json.battery }}";
}
{
name = "link_${name}";
platform = "mqtt";
state_topic = "zigbee2mqtt/${name}";
availability_topic = "zigbee2mqtt/bridge/state";
icon = "mdi:signal";
unit_of_measurement = "lqi";
value_template = "{{ value_json.linkquality }}";
}
]) allDevices);
# create groups
# -------------
#group = let
# # to have nice panels for every device
# sensorGroups = lib.mapAttrs (name:
# { ... }: {
# entities = [
# "sensor.${name}"
# "sensor.humidity_${name}"
# #"sensor.pressure_${name}"
# "sensor.battery_${name}"
# "sensor.link_${name}"
# ];
# }) allDevices;
# # sort lights into given groups.
# sortedInGroups = let
# groupEntries = lib.zipAttrs (lib.flatten (lib.mapAttrsToList (name:
# { groups ? [ ], ... }:
# map (groupName: { "${groupName}" = "sensor.${name}"; }) groups)
# allDevices));
# in lib.mapAttrs (name: entities: { inherit entities; }) groupEntries;
#in sortedInGroups // sensorGroups // {
# all_sensors.entities =
# lib.mapAttrsToList (name: { ... }: "sensor.${name}") allDevices;
#};
};
}

View file

@ -0,0 +1,44 @@
{ config, lib, pkgs, ... }: {
services.xserver = {
enable = false;
autorun = false;
videoDrivers = [ "intel" ];
#deviceSection = ''
# Option "DRI" "2"
# Option "TearFree" "true"
#'';
desktopManager = {
kodi.enable = true;
default = "kodi";
xterm.enable = false;
};
displayManager = {
sddm = {
enable = true;
autoLogin = {
enable = true;
relogin = true;
user = config.users.users.kodi.name;
};
};
};
};
users = {
# mutableUsers = true;
users.kodi = {
isNormalUser = true;
name = "kodi";
uid = 1338;
initialPassword = lib.fileContents <secrets/kodi/password>;
};
};
# allow everybody in the net to access the wifi
networking.firewall = {
allowedTCPPorts = [ 8080 ];
allowedUDPPorts = [ 8080 ];
};
}

View file

@ -0,0 +1,26 @@
{ lib, pkgs, ... }:
let
interface = "enp0s25";
ipAddress = "10.1.0.2";
prefixLength = 24;
in {
networking.extraHosts = ''
10.1.0.1 workout.lan
10.1.0.2 pepe.lan
'';
# todo only open needed ports
networking.firewall.trustedInterfaces = [ interface ];
networking.networkmanager.unmanaged = [ interface ];
networking.dhcpcd.denyInterfaces = [ interface ];
networking.interfaces."${interface}".ipv4.addresses = [{
address = ipAddress;
prefixLength = prefixLength;
}];
}

View file

@ -0,0 +1,136 @@
{ config, pkgs, lib, ... }: {
users.groups."syncthing".members = [ "mpd" "syncthing" "kodi" ];
custom.samba-share = {
enable = true;
folders = {
movies = config.services.syncthing.declarative.folders.movies.path;
music = config.services.syncthing.declarative.folders.music-library.path;
samples = config.services.syncthing.declarative.folders.samples.path;
series = config.services.syncthing.declarative.folders.series.path;
books = config.services.syncthing.declarative.folders.books.path;
};
#private.test = {
# folder = ;
# users = "kodi";
#};
};
services.syncthing = {
enable = true;
openDefaultPorts = true;
declarative = {
cert = toString <secrets/syncthing/cert.pem>;
key = toString <secrets/syncthing/key.pem>;
overrideFolders = true;
folders = {
# on encrypted drive
# ------------------
private = {
enable = true;
path = "/home/syncthing/private";
};
desktop = {
enable = true;
path = "/home/syncthing/desktop";
};
finance = {
enable = true;
path = "/home/syncthing/finance";
};
lost-fotos = {
enable = true;
path = "/home/syncthing/lost-fotos.ct";
};
fotos = {
enable = true;
path = "/home/syncthing/fotos";
};
zettlr = {
enable = true;
path = "/home/syncthing/zettlr";
};
# no need to be encrypted
# -----------------------
borg-mirror = {
enable = true;
path = "/media/syncthing/borg";
rescanInterval = 36 * 3600;
type = "receiveonly";
};
books = {
enable = true;
path = "/media/syncthing/books";
rescanInterval = 8 * 3600;
};
movies = {
enable = true;
path = "/media/syncthing/movies";
rescanInterval = 8 * 3600;
};
music-projects = {
enable = true;
path = "/media/syncthing/music-projects";
};
music-library = {
enable = true;
path = "/media/syncthing/music-library";
rescanInterval = 8 * 3600;
};
music-library-free = {
enable = true;
path = "/media/syncthing/music-library-free";
rescanInterval = 8 * 3600;
};
samples = {
enable = true;
path = "/media/syncthing/samples";
rescanInterval = 8 * 3600;
};
series = {
enable = true;
path = "/media/syncthing/series";
rescanInterval = 8 * 3600;
};
smartphone-folder = {
enable = true;
path = "/media/syncthing/smartphone-folder";
rescanInterval = 8 * 3600;
};
video-material = {
enable = true;
path = "/media/syncthing/video-material";
rescanInterval = 8 * 3600;
};
processing = {
enable = true;
path = "/media/syncthing/sketchbook";
rescanInterval = 8 * 3600;
};
};
};
};
system.permown."/home/syncthing" = {
owner = "syncthing";
group = "syncthing";
};
system.permown."/media/syncthing" = {
owner = "syncthing";
group = "syncthing";
};
systemd.services."permown._media_syncthing" = {
bindsTo = [ "media.mount" ];
after = [ "media.mount" ];
};
systemd.services."syncthing" = {
bindsTo = [ "media.mount" ];
after = [ "media.mount" ];
};
}

View file

@ -0,0 +1,18 @@
{ config, lib, pkgs, ... }:
with lib;
{
module.cluster.services.tinc = {
"private" = {
enable = true;
openPort = true;
connectTo = [ "sputnik" ];
};
"retiolum" = {
enable = true;
openPort = true;
};
};
}

View file

@ -0,0 +1,78 @@
{ lib, pkgs, ... }:
let
# you find this device using `ifconfig -a` or `ip link`
wifi = "wlp0s26u1u2";
#wifi = "wlp3s0-ifb";
ipAddress = "10.23.45.1";
prefixLength = 24;
servedAddressRange = "10.23.45.2,10.23.45.150,12h";
ssid = "palosiot";
wifiPassword = lib.fileContents <secrets/iot_wifi>;
in {
# todo only open needed ports
networking.firewall.trustedInterfaces = [ wifi ];
networking.networkmanager.unmanaged = [ wifi ];
networking.dhcpcd.denyInterfaces = [ wifi ];
networking.interfaces."${wifi}".ipv4.addresses = [{
address = ipAddress;
prefixLength = prefixLength;
}];
systemd.services.hostapd = {
description = "hostapd wireless AP";
path = [ pkgs.hostapd ];
wantedBy = [ "network.target" ];
after = [
"${wifi}-cfg.service"
"nat.service"
"bind.service"
"dhcpd.service"
"sys-subsystem-net-devices-${wifi}.device"
];
serviceConfig = {
ExecStart = "${pkgs.hostapd}/bin/hostapd ${
pkgs.writeText "hostapd.conf" ''
interface=${wifi}
hw_mode=g
channel=10
ieee80211d=1
country_code=DE
ieee80211n=1
wmm_enabled=1
ssid=${ssid}
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=${wifiPassword}
''
}";
Restart = "always";
};
};
services.dnsmasq = {
enable = true;
extraConfig = ''
# Only listen to routers' LAN NIC. Doing so opens up tcp/udp port 53 to
# localhost and udp port 67 to world:
interface=${wifi}
# Explicitly specify the address to listen on
listen-address=${ipAddress}
# Dynamic range of IPs to make available to LAN PC and the lease time.
# Ideally set the lease time to 5m only at first to test everything works okay before you set long-lasting records.
dhcp-range=${servedAddressRange}
'';
};
}

View file

@ -0,0 +1,56 @@
{ pkgs, lib, config, ... }: {
imports = [
<system/server>
./hardware-configuration.nix
./tinc.nix
./syncthing.nix
#./kodi.nix
];
networking.hostName = "porani";
# not needed because not encrypted
# enable initrd ssh
#configuration.init-ssh = {
# enable = "enabled";
# kernelModules = [ "e1000e" ];
# hostECDSAKey = <secrets/init-ssh/host_ecdsa_key>;
#};
# programs
programs.custom.vim.enable = true;
environment.systemPackages = [ pkgs.mosh ];
# wifi setup
system.custom.wifi = {
enable = true;
configurationFile = <secrets/wpa_supplicant>;
interfaces = [ "wlp3s0" ];
};
# nix-shell -p speedtest_cli --run speedtest
configuration.fireqos = {
enable = true;
interface = "wlp3s0";
input = 2500;
output = 1200;
balance = false;
};
# nix-shell -p speedtest_cli --run speedtest
#configuration.fireqos = {
# enable = true;
# interface = "eth0";
# input = 2500;
# output = 1200;
# balance = false;
#};
# new Zealand overwrites
# services.netdata.enable = lib.mkForce false ;
services.SystemdJournal2Gelf.enable = lib.mkForce false;
}

View file

@ -0,0 +1,79 @@
{ config, lib, pkgs, ... }: {
imports = [
<nixpkgs/nixos/modules/installer/scan/not-detected.nix>
# boot loader
# -----------
{
# Use the systemd-boot EFI boot loader, not grub
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
#boot.loader.grub = {
# device = "/dev/sda";
# enable = true;
# version = 2;
#};
}
# kernel
# ------
{
boot.initrd.availableKernelModules =
[ "ehci_pci" "ahci" "usb_storage" "sd_mod" "sdhci_pci" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
}
# automount
# ---------
(let mediaUUID = "3d106f56-89e5-400d-9d6b-1dd957919548";
in {
fileSystems."/media" = {
device = "/dev/disk/by-uuid/${mediaUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${mediaUUID}";
where = "/media";
}];
})
];
# NTFS support
# ------------
environment.systemPackages = [ pkgs.ntfs3g ];
# partitions
# ----------
fileSystems."/" = {
device = "/dev/disk/by-uuid/77a3e839-5a80-4777-93c3-31be7f0cb99d";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-uuid/FBFB-8DA5";
fsType = "vfat";
};
fileSystems."/home" = {
device = "/dev/disk/by-uuid/192a8bd6-e5f7-4e66-b69e-f3da701da343";
fsType = "ext4";
};
fileSystems."/backup" = {
device = "/dev/disk/by-uuid/ca895f0e-f932-4a9e-b2ff-a1a488b0953d";
fsType = "ext4";
};
swapDevices = [ ];
nix.maxJobs = lib.mkDefault 4;
}

View file

@ -0,0 +1,38 @@
{ config, lib, pkgs, ... }: {
services.xserver = {
enable = true;
autorun = true;
desktopManager = {
kodi.enable = true;
default = "kodi";
xterm.enable = false;
};
displayManager = {
sddm = {
enable = true;
autoLogin = {
enable = true;
relogin = true;
user = config.users.users.kodi.name;
};
};
};
};
users = {
# mutableUsers = true;
users.kodi = {
isNormalUser = true;
name = "kodi";
uid = 1338;
initialPassword = lib.fileContents <secrets/kodi/password>;
};
};
# allow everybody in the net to access the wifi
networking.firewall = {
allowedTCPPorts = [ 8080 ];
allowedUDPPorts = [ 8080 ];
};
}

View file

@ -0,0 +1,11 @@
{ config, lib, ... }: {
sound.enable = true;
services.mpd = {
enable = true;
network.listenAddress = "any";
musicDirectory = "/media/music-library";
};
}

View file

@ -0,0 +1,5 @@
{ pkgs, ... }: {
environment.systemPackages = [ ];
}

View file

@ -0,0 +1,62 @@
{ config, pkgs, lib, ... }: {
networking.firewall.interfaces."tinc.secure".allowedTCPPorts = [ 8384 ];
custom.samba-share = {
enable = true;
folders = {
movies = config.services.syncthing.declarative.folders.movies.path;
series = config.services.syncthing.declarative.folders.series.path;
music = config.services.syncthing.declarative.folders.music-library.path;
};
};
services.syncthing = {
guiAddress = lib.mkForce "${config.networking.hostName}.secret:8384";
enable = true;
openDefaultPorts = true;
declarative = {
cert = toString <secrets/syncthing/cert.pem>;
key = toString <secrets/syncthing/key.pem>;
overrideFolders = true;
folders = {
# on media hard drive (not encrypted)
# -----------------------------------
movies = {
enable = true;
path = "/media/movies";
rescanInterval = 8 * 3600;
};
music-library = {
enable = true;
path = "/media/music-library";
rescanInterval = 8 * 3600;
};
series = {
enable = true;
path = "/media/series";
rescanInterval = 8 * 3600;
};
};
};
};
system.permown."/media" = {
owner = "syncthing";
group = "syncthing";
};
systemd.services."permown._media" = {
bindsTo = [ "media.mount" ];
after = [ "media.mount" ];
};
systemd.services."syncthing" = {
bindsTo = [ "media.mount" ];
after = [ "media.mount" ];
};
users.groups."syncthing".members = [ "mpd" "syncthing" "kodi" "palo" ];
backup.dirs = [ "/var/lib/syncthing/finance" ];
}

View file

@ -0,0 +1,16 @@
{ config, lib, pkgs, ... }: {
module.cluster.services.tinc = {
#"private" = {
# enable = true;
# openPort = true;
# connectTo = [ "sputnik" ];
#};
"secure" = {
enable = true;
openPort = true;
connectTo = [ "sputnik" ];
};
};
}

View file

@ -0,0 +1,75 @@
{ lib, pkgs, ... }:
let
wifi = "wlp0s29u1u2";
ipAddress = "10.23.45.1";
prefixLength = 24;
servedAddressRange = "10.23.45.2,10.23.45.150,12h";
ssid = "palosiot";
wifiPassword = lib.fileContents <secrets/iot_wifi>;
in {
# todo only open needed ports
networking.firewall.trustedInterfaces = [ wifi ];
networking.networkmanager.unmanaged = [ wifi ];
networking.dhcpcd.denyInterfaces = [ wifi ];
networking.interfaces."${wifi}".ipv4.addresses = [{
address = ipAddress;
prefixLength = prefixLength;
}];
systemd.services.hostapd = {
description = "hostapd wireless AP";
path = [ pkgs.hostapd ];
wantedBy = [ "network.target" ];
after = [
"${wifi}-cfg.service"
"nat.service"
"bind.service"
"dhcpd.service"
"sys-subsystem-net-devices-${wifi}.device"
];
serviceConfig = {
ExecStart = "${pkgs.hostapd}/bin/hostapd ${
pkgs.writeText "hostapd.conf" ''
interface=${wifi}
hw_mode=g
channel=10
ieee80211d=1
country_code=DE
ieee80211n=1
wmm_enabled=1
ssid=${ssid}
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=${wifiPassword}
''
}";
Restart = "always";
};
};
services.dnsmasq = {
enable = true;
extraConfig = ''
# Only listen to routers' LAN NIC. Doing so opens up tcp/udp port 53 to
# localhost and udp port 67 to world:
interface=${wifi}
# Explicitly specify the address to listen on
listen-address=${ipAddress}
# Dynamic range of IPs to make available to LAN PC and the lease time.
# Ideally set the lease time to 5m only at first to test everything works okay before you set long-lasting records.
dhcp-range=${servedAddressRange}
'';
};
}

View file

@ -0,0 +1,4 @@
{ config, lib, ... }:
{
}

View file

@ -0,0 +1,12 @@
{ config, pkgs, lib, ... }: {
services.bitwarden_rs = {
enable = true;
config = {
domain = "https://bitwarden.ingolf-wagner.de";
signupsAllowed = false;
rocketPort = 8222;
rocketLog = "critical";
};
};
backup.dirs = [ "/var/lib/bitwarden_rs" ];
}

View file

@ -0,0 +1,41 @@
{ config, lib, pkgs, ... }: {
services.nginx.enable = true;
services.nginx.virtualHosts.hedgedoc = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
enableACME = true;
addSSL = true;
serverName = "md.ingolf-wagner.de";
locations."/".extraConfig = ''
client_max_body_size 4G;
proxy_set_header Host $host;
proxy_pass http://localhost:3091;
'';
};
services.hedgedoc = {
enable = true;
configuration = {
allowFreeURL = true;
db = {
dialect = "sqlite";
storage = "/var/lib/hedgedoc/db.sqlite";
useCDN = false;
};
port = 3091;
};
};
}

View file

@ -0,0 +1,54 @@
{ config, pkgs, lib, ... }: {
imports = [
<system/proxy>
./hardware-configuration.nix
<system/server/packages.nix>
./nginx.nix
./tinc.nix
./codimd.nix
./bitwarden.nix
#./syncplay.nix
];
networking.hostName = "sputnik";
networking.useDHCP = true;
boot.kernelParams = [ "net.ifnames=0" ];
boot.loader.grub = {
enable = true;
version = 2;
device = "/dev/sda";
};
# nix-shell -p speedtest_cli --run speedtest
configuration.fireqos = {
enable = true;
interface = "eth0";
input = 55000;
output = 4000;
balance = false;
};
services.custom.ssh.sshd.rootKeyFiles =
[ (toString <secrets/ssh/jenkins_rsa.pub>) ];
# make sure ssh is only available trough the tinc
networking.firewall.extraCommands = ''
iptables -t nat -A PREROUTING ! -i tinc.private -p tcp -m tcp --dport 22 -j REDIRECT --to-ports 0
'';
# enable all subdomains to be reached to make nginx rules easier
services.dnsmasq = with lib; {
enable = true;
extraConfig = ''
${concatStringsSep "\n"
(flip mapAttrsToList config.module.cluster.services.tinc."private".hosts
(name: attrs: "address=/.${name}.private/${attrs.tincIp}"))}
'';
};
}

View file

@ -0,0 +1,22 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports = [ <nixpkgs/nixos/modules/profiles/qemu-guest.nix> ];
boot.initrd.availableKernelModules =
[ "ata_piix" "uhci_hcd" "virtio_pci" "sd_mod" "sr_mod" ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
fileSystems."/" = {
device = "/dev/disk/by-uuid/8f2986a3-d2b0-4735-be98-9ec081b87984";
fsType = "ext4";
};
swapDevices = [ ];
nix.maxJobs = lib.mkDefault 1;
}

View file

@ -0,0 +1,19 @@
{ pkgs, config, ... }:
let
domain = "io.ingolf-wagner.de";
publicIp = "195.201.134.247";
pw = import <secrets/iodinepw.nix>;
in {
services.iodine.server = {
enable = true;
domain = domain;
ip = "172.16.10.1/24";
extraConfig = "-c -P ${pw} -l ${publicIp}";
};
#krebs.iptables.tables.filter.INPUT.rules = [
#{ predicate = "-p udp --dport 53"; target = "ACCEPT";}
#];
}

View file

@ -0,0 +1,470 @@
{ config, lib, pkgs, ... }:
let
errorPages = pkgs.fetchgit {
url = "https://git.ingolf-wagner.de/palo/http-errors.git";
rev = "74b8e4c1d9bbba3db6ad858b888e1867318af1f0";
sha256 = "0czdzafx4k76q773lyf3vsjm74g1995iz542dhw15kpy5xbivsrg";
};
error = {
extraConfig = ''
error_page 400 /errors/400.html;
error_page 401 /errors/401.html;
error_page 402 /errors/402.html;
error_page 403 /errors/403.html;
error_page 404 /errors/404.html;
error_page 405 /errors/405.html;
error_page 406 /errors/406.html;
error_page 500 /errors/500.html;
error_page 501 /errors/501.html;
error_page 502 /errors/502.html;
error_page 503 /errors/503.html;
error_page 504 /errors/504.html;
'';
locations."^~ /errors/" = {
extraConfig = "internal;";
root = "${errorPages}/";
};
};
in {
networking.firewall.allowedTCPPorts =
[ 80 443 4443 config.services.taskserver.listenPort ];
networking.firewall.allowedUDPPorts =
[ 80 443 4443 config.services.taskserver.listenPort ];
services.nginx = {
enable = true;
recommendedProxySettings = true;
virtualHosts = {
"bitwarden.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
locations."/" = {
proxyPass = "http://localhost:${
toString config.services.bitwarden_rs.config.rocketPort
}";
};
};
"git.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
proxyPass = "http://workhorse.private:3000";
extraConfig = ''
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
#
# Custom headers and headers various browsers *should* be OK with but aren't
#
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
#
# Tell client that this pre-flight info is valid for 20 days
#
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
if ($request_method = 'POST') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
if ($request_method = 'GET') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
}
'';
};
} // error.locations;
};
"paste.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = { proxyPass = "http://workhorse.private:8000"; };
} // error.locations;
};
"travel.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
root = "/srv/www/travel";
extraConfig = ''
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$request_uri/ permanent;
}
'';
};
} // error.locations;
};
"tech.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
root = "/srv/www/tech";
extraConfig = ''
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$request_uri/ permanent;
}
'';
};
} // error.locations;
};
"preview.tech.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
basicAuth.moderator = "IwantitIwantitIwantit";
root = "/srv/www/tech_preview";
extraConfig = ''
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$request_uri/ permanent;
}
'';
};
} // error.locations;
};
"terranix.org" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
root = "/srv/www/terranix";
extraConfig = ''
if (-d $request_filename) {
rewrite [^/]$ $scheme://$http_host$request_uri/ permanent;
}
'';
};
} // error.locations;
};
#"chat.ingolf-wagner.de" = {
# listen = [
# {
# addr = "0.0.0.0";
# port = 4443;
# ssl = true;
# }
# {
# addr = "0.0.0.0";
# port = 80;
# ssl = false;
# }
# ];
# forceSSL = true;
# enableACME = true;
# extraConfig = error.extraConfig;
# locations = {
# "/" = {
# proxyPass = "http://chat.workhorse.private";
# proxyWebsockets = true;
# extraConfig = ''
# sub_filter "http://chat.ingolf-wagner.de" "https://chat.ingolf-wagner.de";
# sub_filter "chat.workhorse.private" "chat.ingolf-wagner.de";
# '';
# };
# } // error.locations;
#};
"nextcloud.ingolf-wagner.de" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = {
proxyPass = "http://nextcloud.workhorse.private";
extraConfig = ''
sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de";
sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de";
# used for view/edit office file via Office Online Server
client_max_body_size 0;
'';
};
"= /.well-known/carddav" = {
priority = 210;
extraConfig = "return 301 $scheme://$host/remote.php/dav;";
};
"= /.well-known/caldav" = {
priority = 210;
extraConfig = "return 301 $scheme://$host/remote.php/dav;";
};
"~ .(?:css|js|svg|gif)$" = {
proxyPass = "http://nextcloud.workhorse.private$request_uri";
extraConfig = ''
expires 6M; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de";
sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de";
# used for view/edit office file via Office Online Server
client_max_body_size 0;
'';
};
"~ .woff2?$" = {
proxyPass = "http://nextcloud.workhorse.private$request_uri";
extraConfig = ''
expires 7d; # Cache-Control policy borrowed from `.htaccess`
access_log off; # Optional: Don't log access to assets
sub_filter "http://nextcloud.ingolf-wagner.de" "https://nextcloud.ingolf-wagner.de";
sub_filter "nextcloud.workhorse.private" "nextcloud.ingolf-wagner.de";
# used for view/edit office file via Office Online Server
client_max_body_size 0;
'';
};
} // error.locations;
};
"gaykraft.com" = {
listen = [
{
addr = "0.0.0.0";
port = 4443;
ssl = true;
}
{
addr = "0.0.0.0";
port = 80;
ssl = false;
}
];
forceSSL = true;
enableACME = true;
extraConfig = error.extraConfig;
locations = {
"/" = { root = "/srv/www/gaykraft"; };
} // error.locations;
};
#"home.ingolf-wagner.de" = {
# listen = [
# {
# addr = "0.0.0.0";
# port = 4443;
# ssl = true;
# }
# {
# addr = "0.0.0.0";
# port = 80;
# ssl = false;
# }
# ];
# extraConfig = ''
# proxy_buffering off;
# # client certificate
# ssl_client_certificate ${<secrets/client-cert/ca.crt>};
# # make verification optional, so we can display a 403 message to those
# # who fail authentication
# ssl_verify_client optional;
# '';
# forceSSL = true;
# enableACME = true;
# locations."/" = {
# proxyPass = "http://pepe.private:8123";
# proxyWebsockets = true;
# extraConfig = ''
# # if the client-side certificate failed to authenticate, show a 403
# # message to the client
# if ($ssl_client_verify != SUCCESS) {
# return 403;
# }
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# proxy_set_header Upgrade $http_upgrade;
# proxy_set_header Connection $connection_upgrade;
# '';
# };
#};
};
};
services.sslh = {
enable = true;
listenAddresses = [ "0.0.0.0" ];
port = 443;
verbose = true;
transparent = true;
# List of protocols
#
# Each protocol entry consists of:
# name: name of the probe. These are listed on the command line (ssh -?), plus 'regex' and 'timeout'.
# service: (optional) libwrap service name (see hosts_access(5))
# host, port: where to connect when this probe succeeds
# log_level: 0 to turn off logging
# 1 to log each incoming connection
# keepalive: Should TCP keepalive be on or off for that
# connection (default is off)
# fork: Should a new process be forked for this protocol?
# (only useful for sslh-select)
# tfo_ok: Set to true if the server supports TCP FAST OPEN
#
# Probe-specific options:
# (sslh will try each probe in order they are declared, and
# connect to the first that matches.)
#
# tls:
# sni_hostnames: list of FQDN for that target
# alpn_protocols: list of ALPN protocols for that target, see:
# https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
#
# if both sni_hostnames AND alpn_protocols are specified, both must match
#
# if neither are set, it is just checked whether this is the TLS protocol or not
#
# Obviously set the most specific probes
# first, and if you use TLS with no ALPN/SNI
# set it as the last TLS probe
# regex:
# regex_patterns: list of patterns to match for
# that target.
#
# You can specify several of 'regex' and 'tls'.
appendConfig = ''
protocols:
(
{ name: "ssh"; service: "ssh"; host: "localhost"; port: "2222";},
{ name: "tls"; host: "localhost"; port: "4443";},
{ name: "tinc"; host: "localhost"; port: "655"; }
);
'';
};
systemd.services."socat-proxy" = {
wantedBy = [ "sslh.service" "multi-user.target" ];
after = [ "sslh.service" ];
script = ''
${pkgs.socat}/bin/socat TCP-LISTEN:2222,fork TCP:workhorse.private:2222
'';
};
systemd.services."socat-taskd" = {
wantedBy = [ "multi-user.target" ];
script = let port = toString config.services.taskserver.listenPort;
in ''
${pkgs.socat}/bin/socat TCP-LISTEN:${port},fork TCP:workhorse.private:${port}
'';
};
}

View file

@ -0,0 +1,5 @@
{ config, ... }: {
services.syncplay.enable = true;
networking.firewall.allowedTCPPorts = [ config.services.syncplay.port ];
networking.firewall.allowedUDPPorts = [ config.services.syncplay.port ];
}

View file

@ -0,0 +1,13 @@
{ config, lib, pkgs, ... }: {
module.cluster.services.tinc = {
"private" = {
enable = true;
openPort = true;
};
"secret" = {
enable = true;
openPort = true;
};
};
}

View file

@ -0,0 +1,104 @@
{ config, pkgs, lib, ... }: {
imports = [
<system/desktop>
./hardware-configuration.nix
./packages.nix
./syncthing.nix
./tinc.nix
];
networking.hostName = "sternchen";
system.custom.mainUser.userName = "tina";
system.custom.wifi.interfaces = [ "wlp3s0" ];
security.wrappers = {
pmount.source = "${pkgs.pmount}/bin/pmount";
pumount.source = "${pkgs.pmount}/bin/pumount";
};
programs.custom.steam.enable = false;
programs.custom.video.enable = true;
# keyboard fiddling
i18n.defaultLocale = "de_DE.UTF-8";
console.keyMap = "de";
services.xserver.layout = "de,us";
system.custom.suspend.enable = false;
services.printing.enable = true;
# fonts
# -----
programs.custom.urxvt.fontSize = 12;
programs.custom.xterm.fontSize = 12;
system.custom.fonts.dpi = 100;
# x11
# ---
configuration.desktop = {
width = 1366;
height = 768;
};
# for congress and streaming
hardware.opengl = {
enable = true;
extraPackages = [ pkgs.vaapiIntel ];
driSupport = true;
driSupport32Bit = true;
};
nixpkgs.config.packageOverrides = pkgs: {
vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
};
services.xserver.displayManager.defaultSession = "plasma5";
services.xserver.desktopManager.pantheon.enable = false;
services.xserver.desktopManager.xfce.enable = false;
services.xserver.desktopManager.gnome3.enable = false;
services.xserver.desktopManager.cinnamon.enable = true;
services.xserver.desktopManager.lxqt.enable = true;
services.xserver.desktopManager.mate.enable = true;
services.xserver.desktopManager.plasma5.enable = true;
services.xserver.desktopManager.plasma5.phononBackend = "vlc";
# stuff
# -----
custom.samba-share = {
enable = false;
folders = {
#public = "/home/palo/movies";
share = "/home/share";
#syncthing = "/mnt/syncthing";
#movies = "/mnt/syncthing/movies";
#series = "/mnt/syncthing/series";
};
};
# enable this to use sidequest
programs.adb.enable = false;
users.users.mainUser.extraGroups = [ "adbusers" "video" ];
virtualisation = {
docker.enable = false;
virtualbox = {
host.enable = false;
guest.x11 = false;
guest.enable = false;
};
};
# keybase
services.keybase.enable = false;
services.kbfs.enable = false;
}

View file

@ -0,0 +1,79 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports = [ <nixpkgs/nixos/modules/installer/scan/not-detected.nix> ];
boot.initrd.availableKernelModules =
[ "ehci_pci" "ahci" "xhci_pci" "usb_storage" "sd_mod" "sdhci_pci" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# Use the systemd-boot EFI boot loader, not grub
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
zramSwap = {
enable = true;
numDevices = 2;
swapDevices = 1;
memoryPercent = 50;
};
#fileSystems."/share/" = {
# device = "/dev/ram1";
# fsType = "tmpfs";
#};
#fileSystems."/browsers/" = {
# #device = "/dev/ram2";
# #fsType = "tmpfs";
# options = [ "noatime" "nodiratime" "discard" ];
# device = "/dev/vg/browser";
# fsType = "ext4";
#};
nix.maxJobs = lib.mkDefault 4;
# lvm volume group
# ----------------
boot.initrd.luks.devices = {
vg = {
device = "/dev/sda2";
preLVM = true;
};
};
# NTFS support
# ------------
environment.systemPackages = [ pkgs.ntfs3g ];
# root
# ----
fileSystems."/" = {
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/vg/root";
fsType = "ext4";
};
# home
# ----
fileSystems."/home" = {
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/vg/home";
fsType = "ext4";
};
# boot
# ----
fileSystems."/boot" = {
#device = "/dev/sda1";
device = "/dev/disk/by-uuid/EDD4-9795";
fsType = "vfat";
};
}

View file

@ -0,0 +1,50 @@
{ config, lib, pkgs, ... }: {
# overlay included
#nixpkgs.overlays = [ (import <mozilla-overlay/rust-overlay.nix>) ];
programs.custom.browser.enable = false;
environment.systemPackages = with pkgs; [
spotify
sweethome3d.application
nextcloud-client
gimp
inkscape
darktable
keepassxc
firefox
google-chrome
skype
tipp10
zoom-us
gnome3.gnome-todo
gnome3.gnome-control-center
# graphic
krita
gthumb
digikam
shotwell
#synfigstudio
# kde programs
okular
ark
gwenview
skanlite
#
#tor-browser-bundle-bin
(tor-browser-bundle-bin.overrideAttrs (old: rec {
version = "10.5";
name = "tor-browser-bundle-${version}";
src = pkgs.fetchurl {
url =
"https://dist.torproject.org/torbrowser/10.5/tor-browser-linux64-10.5_en-US.tar.xz";
sha256 = "1dq9jys60rn2w37dvb2a69gmqsi7278b782am14wci57kmajlpvm";
};
}))
];
}

View file

@ -0,0 +1,12 @@
{ config, pkgs, lib, ... }: {
services.syncthing = {
guiAddress = "localhost:8384";
enable = true;
openDefaultPorts = true;
user = "tina";
dataDir = "/home/tina/.syncthing";
configDir = "/home/tina/.syncthing";
};
}

View file

@ -0,0 +1,24 @@
{ config, lib, pkgs, ... }:
with lib;
{
module.cluster.services.tinc = {
#"private" = {
# enable = true;
# openPort = true;
# connectTo = [ "sputnik" ];
#};
#"retiolum" = {
# enable = true;
# openPort = true;
#};
"secret" = {
enable = true;
openPort = true;
connectTo = [ "sputnik" ];
};
};
}

View file

@ -0,0 +1,84 @@
{ lib, pkgs, ... }:
let
wifi = "wlp0s29u1u2";
ipAddress = "10.123.145.1";
prefixLength = 24;
servedAddressRange = "10.123.145.2,10.123.145.150,12h";
ssid = "bumbumbum";
wifiPassword = lib.fileContents <secrets/wifi-access-point>;
in {
# todo only open needed ports
networking.firewall.trustedInterfaces = [ wifi ];
networking.networkmanager.unmanaged = [ wifi ];
networking.dhcpcd.denyInterfaces = [ wifi ];
networking.interfaces."${wifi}".ipv4.addresses = [{
address = ipAddress;
prefixLength = prefixLength;
}];
# forward traffic coming in trough the access point => provide internet and vpn network access
# todo : forward to own servers
boot.kernel.sysctl = {
"net.ipv4.conf.${wifi}.forwarding" = true;
"net.ipv6.conf.${wifi}.forwarding" = true;
};
systemd.services.hostapd = {
description = "hostapd wireless AP";
path = [ pkgs.hostapd ];
# start manual
# wantedBy = [ "network.target" ];
after = [
"${wifi}-cfg.service"
"nat.service"
"bind.service"
"dhcpd.service"
"sys-subsystem-net-devices-${wifi}.device"
];
serviceConfig = {
ExecStart = "${pkgs.hostapd}/bin/hostapd ${
pkgs.writeText "hostapd.conf" ''
interface=${wifi}
hw_mode=g
channel=10
ieee80211d=1
country_code=DE
ieee80211n=1
wmm_enabled=1
ssid=${ssid}
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=${wifiPassword}
''
}";
Restart = "always";
};
};
services.dnsmasq = {
enable = true;
extraConfig = ''
# Only listen to routers' LAN NIC. Doing so opens up tcp/udp port 53 to
# localhost and udp port 67 to world:
interface=${wifi}
# Explicitly specify the address to listen on
listen-address=${ipAddress}
# Dynamic range of IPs to make available to LAN PC and the lease time.
# Ideally set the lease time to 5m only at first to test everything works okay before you set long-lasting records.
dhcp-range=${servedAddressRange}
'';
};
}

View file

@ -0,0 +1,45 @@
{ pkgs, config, ... }: {
users.users.mainUser.extraGroups = [ "wireshark" ];
programs.wireshark.enable = true;
environment.systemPackages = [
pkgs.wireshark
# alternative packet analyzer (only works with elasticsearch)
pkgs.packetbeat7
];
# elastic search is good for analysing stuff
# https://www.elastic.co/blog/analyzing-network-packets-with-wireshark-elasticsearch-and-kibana
services.elasticsearch.enable = true;
services.elasticsearch.listenAddress =
"${config.networking.hostName}.private";
services.kibana.enable = true;
services.kibana.elasticsearch.hosts =
[ "http://${config.networking.hostName}.private:9200" ];
services.kibana.listenAddress = "${config.networking.hostName}.private";
services.kibana.port = 5601;
# using tshark with elastic search
# --------------------------------
# tshark -r file.pcap -T ek > packages.json
# curl -XPOST "sterni.private:9200/packets/doc/_bulk" -H 'Content-Type: application/json' --data-binary "@packets.json"
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"kibana.${config.networking.hostName}.private" = {
serverAliases = [ ];
locations."/" = {
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.kibana.port
}";
};
};
};
};
}

View file

@ -0,0 +1,86 @@
{ config, pkgs, lib, ... }: {
imports = [
../../system/desktop
./hardware-configuration.nix
./packages.nix
./syncthing.nix
./tinc.nix
#./wifi-access-point.nix
#./wireshark.nix
];
sops.defaultSopsFile = ../../secrets/sterni.yaml;
networking.hostName = "sterni";
system.custom.wifi.interfaces = [ "wlp3s0" ];
security.wrappers = {
pmount.source = "${pkgs.pmount}/bin/pmount";
pumount.source = "${pkgs.pmount}/bin/pumount";
};
# keybase
services.keybase.enable = false;
services.kbfs.enable = false;
programs.custom.steam.enable = true;
programs.custom.video.enable = false;
services.printing.enable = true;
# fonts
# -----
programs.custom.urxvt.fontSize = 12;
programs.custom.xterm.fontSize = 12;
system.custom.fonts.dpi = 100;
virtualisation = {
docker.enable = false;
virtualbox = {
host.enable = false;
guest.x11 = false;
guest.enable = false;
};
};
configuration.desktop = {
width = 1366;
height = 768;
};
services.xserver.desktopManager.gnome.enable = true;
custom.samba-share = {
enable = false;
folders = {
#public = "/home/palo/movies";
share = "/home/share";
#syncthing = "/mnt/syncthing";
#movies = "/mnt/syncthing/movies";
#series = "/mnt/syncthing/series";
};
};
# enable this to use sidequest
programs.adb.enable = true;
users.users.mainUser.extraGroups = [ "adbusers" "video" ];
# for congress and streaming
hardware.opengl = {
enable = true;
extraPackages = [ pkgs.vaapiIntel ];
driSupport = true;
driSupport32Bit = true;
};
nixpkgs.config.packageOverrides = pkgs: {
vaapiIntel = pkgs.vaapiIntel.override { enableHybridCodec = true; };
};
}

View file

@ -0,0 +1,87 @@
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, modulesPath, ... }:
{
imports = [ "${modulesPath}/installer/scan/not-detected.nix" ];
boot.initrd.availableKernelModules =
[ "ehci_pci" "ahci" "xhci_pci" "usb_storage" "sd_mod" "sdhci_pci" ];
boot.initrd.kernelModules = [ "dm-snapshot" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
# Use the systemd-boot EFI boot loader, not grub
boot.loader.systemd-boot.enable = true;
boot.loader.efi.canTouchEfiVariables = true;
zramSwap = {
enable = true;
numDevices = 2;
swapDevices = 1;
memoryPercent = 50;
};
fileSystems."/share" = {
#device = "/dev/ram1";
device = "none";
fsType = "tmpfs";
};
fileSystems."/browsers" = {
#device = "/dev/ram2";
#fsType = "tmpfs";
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/vg/browser";
fsType = "ext4";
};
nix.maxJobs = lib.mkDefault 4;
# lvm volume group
# ----------------
boot.initrd.luks.devices = {
vg = {
device = "/dev/sda2";
preLVM = true;
};
};
# NTFS support
# ------------
environment.systemPackages = [ pkgs.ntfs3g ];
# root
# ----
fileSystems."/" = {
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/vg/root";
fsType = "ext4";
};
# home
# ----
fileSystems."/home" = {
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/vg/home";
fsType = "ext4";
};
# /home/palo/private/.fotos.ct
# --------------------
#fileSystems."/home/palo/private/.fotos.ct" = {
# options = [ "noatime" "nodiratime" "discard" ];
# device = "/dev/vg/fotos";
# fsType = "ext4";
#};
# boot
# ----
fileSystems."/boot" = {
device = "/dev/sda1";
fsType = "vfat";
};
}

View file

@ -0,0 +1,74 @@
{ config, lib, pkgs, ... }:
let
nextcloudSync = folder:
pkgs.writers.writeBashBin "nextcloud-sync-${folder}" ''
${pkgs.nextcloud-client}/bin/nextcloudcmd \
--user palo \
--password `pass show home/nextcloud/palo/nextcloudcmd-token` \
~/Nextcloud/${folder} \
https://nextcloud.ingolf-wagner.de/remote.php/webdav/${folder}
'';
in {
environment.systemPackages = with pkgs; [
(nextcloudSync "InstantUpload")
(nextcloudSync "Pictures")
(nextcloudSync "Unterlagen")
(nextcloudSync "Video")
(pkgs.writeShellScriptBin "nixFlakes" ''
exec ${pkgs.nixUnstable}/bin/nix --experimental-features "nix-command flakes" "$@"
'')
#zeal # offline documentation
#keepassxc
sweethome3d.application
polygon-art
# bluetooth gui
blueberry
nextcloud-client
gimp
inkscape
imagemagick
bitwig-studio3
sonic-pi
jack2
sononym-crawler
darktable
# rust development environment
#cargo
#rustc
rustup
jetbrains.clion
# general
jetbrains.idea-ultimate
vscode
# python
python3Full
jetbrains.pycharm-professional
jetbrains.datagrip
element-desktop
tor-browser-bundle-bin
#(tor-browser-bundle-bin.overrideAttrs (old: rec {
# version = "10.0.15";
# name = "tor-browser-bundle-${version}";
# src = pkgs.fetchurl {
# url =
# "https://dist.torproject.org/torbrowser/10.0.15/tor-browser-linux64-10.0.15_en-US.tar.xz";
# sha256 = "1ah69jmfgik063f9gkvyv9d4k706pqihmzc4k7cc95zyd17v8wrs";
# };
#}))
];
}

View file

@ -0,0 +1,80 @@
{ config, pkgs, lib, ... }: {
sops.secrets.syncthing_cert = { };
sops.secrets.syncthing_key = { };
services.syncthing = {
enable = true;
openDefaultPorts = false;
user = "palo";
dataDir = "/home/palo/.syncthing";
configDir = "/home/palo/.syncthing";
declarative = {
cert = toString config.sops.secrets.syncthing_cert.path;
key = toString config.sops.secrets.syncthing_key.path;
overrideFolders = true;
folders = {
# on encrypted drive
# ------------------
private = {
enable = true;
path = "/home/palo/private";
};
desktop = {
enable = true;
path = "/home/palo/desktop";
};
finance = {
enable = true;
path = "/home/palo/finance";
};
fotos = {
enable = true;
path = "/home/palo/fotos";
};
samples = {
enable = true;
path = "/home/palo/samples";
};
zettlr = {
enable = true;
path = "/home/palo/zettlr";
};
programs = {
enable = true;
path = "/home/palo/programs";
};
# no need to be encrypted
# -----------------------
books = {
enable = true;
path = "/home/palo/books";
};
music-library = {
enable = true;
path = "/home/palo/music-library";
};
music-projects = {
enable = true;
path = "/home/palo/music-projects";
};
smartphone-folder = {
enable = true;
path = "/home/palo/smartphone-folder";
};
processing = {
enable = true;
path = "/home/palo/sketchbook";
};
};
};
};
system.permown."/home/palo/music-library" = {
owner = "palo";
group = "users";
};
}

View file

@ -0,0 +1,24 @@
{ config, lib, pkgs, ... }:
with lib;
{
module.cluster.services.tinc = {
"private" = {
enable = true;
openPort = true;
connectTo = [ "sputnik" ];
};
"retiolum" = {
enable = true;
openPort = true;
};
"secret" = {
enable = true;
openPort = true;
connectTo = [ "sputnik" ];
};
};
}

View file

@ -0,0 +1,84 @@
{ lib, pkgs, ... }:
let
wifi = "wlp0s29u1u2";
ipAddress = "10.123.145.1";
prefixLength = 24;
servedAddressRange = "10.123.145.2,10.123.145.150,12h";
ssid = "bumbumbum";
wifiPassword = lib.fileContents <secrets/wifi-access-point>;
in {
# todo only open needed ports
networking.firewall.trustedInterfaces = [ wifi ];
networking.networkmanager.unmanaged = [ wifi ];
networking.dhcpcd.denyInterfaces = [ wifi ];
networking.interfaces."${wifi}".ipv4.addresses = [{
address = ipAddress;
prefixLength = prefixLength;
}];
# forward traffic coming in trough the access point => provide internet and vpn network access
# todo : forward to own servers
boot.kernel.sysctl = {
"net.ipv4.conf.${wifi}.forwarding" = true;
"net.ipv6.conf.${wifi}.forwarding" = true;
};
systemd.services.hostapd = {
description = "hostapd wireless AP";
path = [ pkgs.hostapd ];
# start manual
# wantedBy = [ "network.target" ];
after = [
"${wifi}-cfg.service"
"nat.service"
"bind.service"
"dhcpd.service"
"sys-subsystem-net-devices-${wifi}.device"
];
serviceConfig = {
ExecStart = "${pkgs.hostapd}/bin/hostapd ${
pkgs.writeText "hostapd.conf" ''
interface=${wifi}
hw_mode=g
channel=10
ieee80211d=1
country_code=DE
ieee80211n=1
wmm_enabled=1
ssid=${ssid}
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
rsn_pairwise=CCMP
wpa_passphrase=${wifiPassword}
''
}";
Restart = "always";
};
};
services.dnsmasq = {
enable = true;
extraConfig = ''
# Only listen to routers' LAN NIC. Doing so opens up tcp/udp port 53 to
# localhost and udp port 67 to world:
interface=${wifi}
# Explicitly specify the address to listen on
listen-address=${ipAddress}
# Dynamic range of IPs to make available to LAN PC and the lease time.
# Ideally set the lease time to 5m only at first to test everything works okay before you set long-lasting records.
dhcp-range=${servedAddressRange}
'';
};
}

View file

@ -0,0 +1,45 @@
{ pkgs, config, ... }: {
users.users.mainUser.extraGroups = [ "wireshark" ];
programs.wireshark.enable = true;
environment.systemPackages = [
pkgs.wireshark
# alternative packet analyzer (only works with elasticsearch)
pkgs.packetbeat7
];
# elastic search is good for analysing stuff
# https://www.elastic.co/blog/analyzing-network-packets-with-wireshark-elasticsearch-and-kibana
services.elasticsearch.enable = true;
services.elasticsearch.listenAddress =
"${config.networking.hostName}.private";
services.kibana.enable = true;
services.kibana.elasticsearch.hosts =
[ "http://${config.networking.hostName}.private:9200" ];
services.kibana.listenAddress = "${config.networking.hostName}.private";
services.kibana.port = 5601;
# using tshark with elastic search
# --------------------------------
# tshark -r file.pcap -T ek > packages.json
# curl -XPOST "sterni.private:9200/packets/doc/_bulk" -H 'Content-Type: application/json' --data-binary "@packets.json"
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"kibana.${config.networking.hostName}.private" = {
serverAliases = [ ];
locations."/" = {
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.kibana.port
}";
};
};
};
};
}

View file

@ -0,0 +1,30 @@
{ lib, config, pkgs, ... }: {
services.borgbackup.repos = {
default = {
quota = "100G";
allowSubRepos = true;
authorizedKeys = [
(lib.fileContents ../../assets/ssh/borg_access.pub)
(lib.fileContents ../../assets/ssh/card_rsa.pub)
];
};
};
# mirror backup folder to /media
systemd.services.borg-mirror-to-media = {
enable = true;
script = ''
${pkgs.rsync}/bin/rsync -a \
/var/lib/borgbackup/ \
/media/borg-backup-mirror \
--delete-after
'';
};
systemd.timers.borg-mirror-to-media = {
enable = true;
timerConfig.OnCalendar = "daily";
wantedBy = [ "multi-user.target" ];
};
}

View file

@ -0,0 +1,21 @@
{ config, lib, ... }:
let
home = "/home/syncthing/podcasts";
in {
custom.services.castget = {
enable = true;
user = "root";
feeds = {
Alternativlos = {
url = "https://alternativlos.org/alternativlos.rss";
spool = "${home}/alternativlos";
};
gegenstandpunkt = {
url = "https://pc.argudiss.de/";
spool = "${home}/GegenStandpunkt";
};
};
};
}

View file

@ -0,0 +1,75 @@
{ lib, config, pkgs, ... }: {
imports = [
<system/server>
./hardware-configuration.nix
./gogs.nix
./grafana.nix
./graylog.nix
./jenkins.nix
./kibana.nix
./mail-fetcher.nix
./packages.nix
./prometheus.nix
./syncthing.nix
./taskserver.nix
./tinc.nix
./transmission.nix
./weechat.nix
./nextcloud.nix
./borg.nix
#./metabase.nix
#./jupyter.nix
./mysql.nix
#./property.nix flask sucks, find something else
./finance.nix
#./mining.nix
];
nixpkgs.config.permittedInsecurePackages =
[ "gogs-0.11.91" "nextcloud-19.0.6" ];
# todo: add this to each file instead summing that here
on-failure.plans = {
gogs.name = "gogs";
jenkins.name = "jenkins";
graylog.name = "graylog";
prometheus.name = "prometheus";
taskserver.name = "taskserver";
weechat.name = "weechat";
transmission.name = "transmission";
mail-fetcher.name = "fetchmail";
};
networking.hostName = "workhorse";
# font
# ----
programs.custom.urxvt.fontSize = 17;
programs.custom.xterm.fontSize = 17;
system.custom.fonts.dpi = 140;
# enable initrd ssh
configuration.init-ssh = {
enable = "enabled";
kernelModules = [ "r8169" ];
hostKey = "/etc/ssh/ssh_host_ed25519_key";
};
# nix-shell -p speedtest_cli --run speedtest
configuration.fireqos = {
enable = true;
interface = "enp2s0f1";
input = 45000;
output = 8000;
balance = false;
};
virtualisation = { docker.enable = false; };
services.printing.enable = false;
services.smartd.enable = true;
}

View file

@ -0,0 +1,55 @@
{ lib, config, pkgs, ... }:
let
# find symbols with
# https://www.alphavantage.co/query?function=SYMBOL_SEARCH&apikey=<api_key>&keywords=<keywords>
# as described here : https://www.alphavantage.co/documentation/#symbolsearch
#
# example:
# --------
# stocks = [
# {
# friendly_name = "google";
# symbol = "GOOGL.DEX";
# name = "google";
# currency = "$";
# }
# ];
# results in
# P 2020-01-30 GOOGL $123
stocks = import <secrets/finance/stocks>;
stocksFile = toString /home/syncthing/finance/hledger/stocks.journal;
in {
systemd.services.pull_stocks = {
enable = true;
description = "pull stocks for hledger";
serviceConfig = {
User = "syncthing";
Type = "oneshot";
};
script = let
command = { symbol, name, currency, ... }: ''
APIKEY=${lib.fileContents <secrets/finance/alphavantage/apikey>}
SYMBOL="${symbol}"
${pkgs.curl}/bin/curl --location --silent \
"https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=$SYMBOL&apikey=$APIKEY" \
| ${pkgs.jq}/bin/jq --raw-output '.["Global Quote"]
| "P \(.["07. latest trading day"]) ${name} ${currency}\(.["05. price"] | tonumber)"' \
>> ${stocksFile}
sleep 1
'';
in lib.concatStringsSep "\n" (map command stocks);
};
systemd.timers.pull_stocks = {
enable = true;
wantedBy = [ "multi-user.target" ];
timerConfig = {
OnCalendar = "weekly";
Persistent = "true";
};
};
}

View file

@ -0,0 +1,121 @@
{ config, lib, pkgs, ... }:
let
errorPages = pkgs.fetchgit {
url = "https://git.ingolf-wagner.de/palo/http-errors.git";
rev = "74b8e4c1d9bbba3db6ad858b888e1867318af1f0";
sha256 = "0czdzafx4k76q773lyf3vsjm74g1995iz542dhw15kpy5xbivsrg";
};
error = {
extraConfig = ''
error_page 400 /errors/400.html;
error_page 401 /errors/401.html;
error_page 402 /errors/402.html;
error_page 403 /errors/403.html;
error_page 404 /errors/404.html;
error_page 405 /errors/405.html;
error_page 406 /errors/406.html;
error_page 500 /errors/500.html;
error_page 501 /errors/501.html;
error_page 502 /errors/502.html;
error_page 503 /errors/503.html;
error_page 504 /errors/504.html;
'';
locations."^~ /errors/" = {
extraConfig = "internal;";
root = "${errorPages}/";
};
};
in {
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"git.${config.networking.hostName}.private" = {
extraConfig = error.extraConfig;
locations."/" = {
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.gogs.httpPort
}";
};
};
};
};
services.gogs = {
enable = true;
appName = "Kruck GoGs";
domain = "git.ingolf-wagner.de";
httpPort = 3000;
repositoryRoot = "/home/gogs/repositories";
stateDir = "/home/gogs";
rootUrl = "https://git.ingolf-wagner.de/";
extraConfig = ''
[service]
DISABLE_REGISTRATION = true
SHOW_REGISTRATION_BUTTON = false
[server]
SSH_DOMAIN = "git.ingolf-wagner.de"
SSH_PORT = 443
START_SSH_SERVER = true
SSH_LISTEN_PORT = 2222
[log.file]
LEVEL = Warn
[log.console]
LEVEL = Warn
[log.sublogger.macaron]
LEVEL = Warn
'';
};
backup.dirs = [ config.services.gogs.repositoryRoot ];
# services.nginx = {
# enable = true;
# statusPage = true;
# virtualHosts = {
# "gitlab.${config.networking.hostName}.private" = {
# extraConfig = error.extraConfig;
# locations."/" = {
# proxyPass = "http://${config.networking.hostName}.private:${
# toString config.services.gitlab.port
# }";
# };
# };
# };
# };
# services.gitlab = {
# enable = true;
# host = "gitlab.${config.networking.hostname}.private";
# port = 9897;
# #databasePasswordFile = "path/todo";
# #initialRootPasswordFile = "path/todo";
#
# secrets = {
# # Make sure the secret is at least 30 characters and all random,
# # no regular words or you'll be exposed to dictionary attacks
# dbFile = "path/todo";
#
# # openssl genrsa 2048
# jwsFile = "path/todo";
#
# # Make sure the secret is at least 30 characters and all random,
# # no regular words or you'll be exposed to dictionary attacks
# otpFile = "path/todo";
#
# # Make sure the secret is at least 30 characters and all random,
# # no regular words or you'll be exposed to dictionary attacks
# secretFile = "path/todo";
# };
#
# # smtp?
#
# # gitlab-runner?
# };
}

View file

@ -0,0 +1,38 @@
{ config, ... }: {
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"grafana.${config.networking.hostName}.private" = {
serverAliases = [ ];
locations."/" = {
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.grafana.port
}";
};
};
};
};
services.grafana = {
enable = true;
port = 5656;
addr =
config.module.cluster.services.tinc."private".hosts."${config.networking.hostName}".tincIp;
auth.anonymous = {
enable = true;
org_role = "Editor";
org_name = "AWESOME";
};
provision = {
enable = true;
datasources = [{
type = "prometheus";
isDefault = true;
name = "Prometheus Workhorse";
url = "http://workhorse.private:9090";
}];
};
};
}

View file

@ -0,0 +1,130 @@
{ config, lib, pkgs, ... }:
let port = 9000;
in {
# configure nginx
services.nginx = {
enable = true;
virtualHosts = {
"graylog.workhorse.private" = {
locations."/" = {
proxyPass = "http://localhost:${toString port}";
extraConfig = ''
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90;
proxy_redirect http://localhost:${
toString port
} https://graylog.workhorse.private/;
'';
};
};
};
};
services.mongodb.enable = true;
services.elasticsearch = {
enable = true;
listenAddress = "${config.networking.hostName}.private";
extraJavaOptions = [ "-Des.http.cname_in_publish_address=true" ];
};
services.graylog.enable = true;
services.graylog.elasticsearchHosts =
[ "http://${config.services.elasticsearch.listenAddress}:9200" ];
# https://docs.graylog.org/en/3.0/pages/configuration/server.conf.html
services.graylog.extraConfig = ''
http_bind_address = 0.0.0.0:${toString port}
http_publish_uri = http://workhorse.private:${toString port}/
'';
# other wise this does not work
services.graylog.nodeIdFile = "/var/lib/graylog/node-id";
# pwgen -N 1 -s 96
services.graylog.passwordSecret =
lib.fileContents <secrets/graylog/password-secret>;
# echo -n yourpassword | shasum -a 256
services.graylog.rootPasswordSha2 =
lib.fileContents <secrets/graylog/root-password-hash>;
services.graylog.plugins = [ pkgs.graylogPlugins.slack ];
# not working at the moment
#services.geoip-updater.enable = true;
# https://wiki.splunk.com/Http_status.csv
environment.etc."graylog/server/httpCodes.csv" = {
enable = true;
text = ''
status,status_description,status_type
100,Continue,Informational
101,Switching Protocols,Informational
200,OK,Successful
201,Created,Successful
202,Accepted,Successful
203,Non-Authoritative Information,Successful
204,No Content,Successful
205,Reset Content,Successful
206,Partial Content,Successful
300,Multiple Choices,Redirection
301,Moved Permanently,Redirection
302,Found,Redirection
303,See Other,Redirection
304,Not Modified,Redirection
305,Use Proxy,Redirection
307,Temporary Redirect,Redirection
400,Bad Request,Client Error
401,Unauthorized,Client Error
402,Payment Required,Client Error
403,Forbidden,Client Error
404,Not Found,Client Error
405,Method Not Allowed,Client Error
406,Not Acceptable,Client Error
407,Proxy Authentication Required,Client Error
408,Request Timeout,Client Error
409,Conflict,Client Error
410,Gone,Client Error
411,Length Required,Client Error
412,Precondition Failed,Client Error
413,Request Entity Too Large,Client Error
414,Request-URI Too Long,Client Error
415,Unsupported Media Type,Client Error
416,Requested Range Not Satisfiable,Client Error
417,Expectation Failed,Client Error
500,Internal Server Error,Server Error
501,Not Implemented,Server Error
502,Bad Gateway,Server Error
503,Service Unavailable,Server Error
504,Gateway Timeout,Server Error
505,HTTP Version Not Supported,Server Error
'';
};
environment.etc."graylog/server/known_servers.csv" = {
enable = true;
text = ''
"ip","host_name"
"95.216.1.150","lassul.us"
'';
};
environment.etc."graylog/systemd/loglevel.csv" = {
enable = true;
text = ''
"value","Servity","Description"
"0","emergency","System is unusable"
"1","alert","Should be corrected immediately"
"2","cirtical","Critical conditions"
"3","error","Error Condition"
"4","warning","May indicate that an error will occur if action is not taken."
"5","notice","Events that are unusual, but not error conditions."
"6","info","Normal operational messages that require no action."
"7","debug","Information useful to developers for debugging the application."
'';
};
}

View file

@ -0,0 +1,117 @@
{ config, lib, pkgs, ... }:
let mainUserHome = "/home/palo";
in {
# grub configuration
# ------------------
boot.loader.grub = {
device = "/dev/sda";
enable = true;
version = 2;
};
# lvm volume group
# ----------------
boot.initrd.luks.devices = {
vg = {
device = "/dev/sda2";
preLVM = true;
};
};
# NTFS support
# ------------
environment.systemPackages = [ pkgs.ntfs3g ];
# root
# ----
fileSystems."/" = {
options = [ "noatime" "nodiratime" "discard" ];
device = "/dev/vg/root";
fsType = "ext4";
};
# boot
# ----
fileSystems."/boot" = {
device = "/dev/sda1";
fsType = "ext4";
};
# home
# ----
fileSystems."/home" = {
options = [ "noatime" "nodiratime" ];
device = "/dev/mapper/decrypted_home";
fsType = "ext4";
encrypted = {
enable = true;
keyFile = "/mnt-root/root/keys/home.key";
label = "decrypted_home";
blkDev = "/dev/mapper/store-home";
};
};
# var/lib/docker
# --------------
fileSystems."/var/lib/borgbackup" = {
options = [ "noatime" "nodiratime" ];
device = "/dev/mapper/decrypted_docker";
fsType = "ext4";
encrypted = {
enable = true;
keyFile = "/mnt-root/root/keys/docker.key";
label = "decrypted_docker";
blkDev = "/dev/mapper/store-docker";
};
};
imports = [
# automount
# ---------
(let mediaUUID = "b8ba192e-e2aa-47dd-85ec-dcf97ec9310a";
in {
fileSystems."/media" = {
device = "/dev/disk/by-uuid/${mediaUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${mediaUUID}";
where = "/media";
}];
})
(let backupUUID = "f7fa1c0e-ac9f-4955-b4bd-644c1ddb0d89";
in {
fileSystems."/backup" = {
device = "/dev/disk/by-uuid/${backupUUID}";
fsType = "ext4";
options = [
"nofail"
"noauto"
#"x-systemd.device-timeout=1ms"
];
};
systemd.mounts = [{
enable = true;
options = "nofail,noauto";
type = "ext4";
wantedBy = [ "multi-user.target" ];
what = "/dev/disk/by-uuid/${backupUUID}";
where = "/backup";
}];
})
];
}

View file

@ -0,0 +1,199 @@
{ config, lib, pkgs, ... }:
with lib;
let
library = import ../../library { inherit pkgs lib; };
sync-repo = library.jenkins.syncJob;
job = library.jenkins.job;
in {
environment.systemPackages = [ pkgs.cabal-install ];
services.nginx = {
enable = true;
virtualHosts = {
"jenkins.${config.networking.hostName}.private" = {
locations."/" = {
proxyPass =
"http://localhost:${toString config.services.jenkins.port}";
extraConfig = ''
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 90;
proxy_redirect http://localhost:${
toString config.services.jenkins.port
} https://jenkins.${config.networking.hostName}.private/;
'';
};
};
};
};
krops.userKeys."accessToken" = {
user = "jenkins";
source = toString <secrets/jenkins/accessToken>;
requiredBy = [ "jenkins-job-builder.service" ];
};
services.jenkins = {
enable = true;
home = "/home/jenkins";
port = 10420;
# Plugins to Install:
# - all the plugins recommended at the setup
# - Build pipeline
# - SSH Agent
# - mattermost plugin
jobBuilder = {
enable = true;
# create an access token in the admin users panel
accessTokenFile = config.krops.userKeys."accessToken".target;
accessUser = "admin";
# https://docs.openstack.org/infra/jenkins-job-builder/definition.html#modules
nixJobs = let
# ssh username + key
gogs-id = "bc584c99-0fb7-43fb-af75-4076d64c51b2";
# ssh username + key
github-id = "bc584c99-0fb7-43fb-af75-4076d64c51b2";
# ssh username + key
sshSputnik = "d91eb57c-5bff-434c-b317-68aad46848d7";
sync-to-github = name: source: target:
sync-repo name {
url = source;
credentialsId = gogs-id;
} {
url = target;
credentialsId = github-id;
};
in [
(job "sync-retiolum" {
url = "git@github.com:krebs/retiolum.git";
credentialsId = github-id;
triggers = [{ timed = "H/30 * * * *"; }];
} [
{
"Download Files" = [
"chmod 755 hosts"
"chmod 755 -R hosts"
''
nix-shell -p curl -p gnutar -p bzip2 --run "curl https://lassul.us/retiolum-hosts.tar.bz2 | tar xvjf - || true"''
"chmod 755 -R etc.hosts"
''
nix-shell -p curl --run "curl https://lassul.us/retiolum.hosts > etc.hosts || true"''
];
}
{
"update repo" = [
''nix-shell -p git --run "git add ."''
''
nix-shell -p git --run "git -c user.name=\'Ingolf Wagner\' -c user.email=\'contact@ingolf-wagner.de\' commit -m update-`date +%Y-%m-%dT%H:%M:%S` || exit 0"''
];
}
{
Push = [{
script = ''nix-shell -p git --run "git push origin master"'';
credentialsId = github-id;
}];
}
])
(job "test-taskninja" {
url = "ssh://gogs@workhorse.private:2222/palo/taskninja.git";
credentialsId = gogs-id;
} [
{
"Create Shell" = [
''
nix-shell -p cabal2nix --run "cabal2nix --shell file://. > jenkins.nix"''
];
}
{ Update = [ ''nix-shell ./jenkins.nix --run "cabal update"'' ]; }
{
Configure = [
''nix-shell ./jenkins.nix --run "cabal configure --enable-tests"''
''
nix-shell ./jenkins.nix --run "cabal install --only-dependencies"''
];
}
{ Build = [ ''nix-shell ./jenkins.nix --run "cabal build"'' ]; }
{ Test = [ ''nix-shell ./jenkins.nix --run "cabal test"'' ]; }
])
# sync to github
# --------------
(sync-to-github "sync-radiodj"
"ssh://gogs@workhorse.private:2222/crashburn_radio/radio-dj2.git"
"git@github.com:crashburn-radio/radio-dj.git")
(sync-to-github "sync-radiodj-tracks"
"ssh://gogs@workhorse.private:2222/crashburn_radio/radio-dj-tracks.git"
"git@github.com:crashburn-radio/radio-dj-tracks.git")
(sync-to-github "sync-krops-module"
"ssh://gogs@workhorse.private:2222/nix-modules/krops.git"
"git@github.com:mrVanDalo/module.krops.git")
(sync-to-github "sync-cluster-module"
"ssh://gogs@workhorse.private:2222/nix-modules/cluster.git"
"git@github.com:mrVanDalo/module.cluster.git")
(sync-to-github "sync-backup-module"
"ssh://gogs@workhorse.private:2222/nix-modules/backup.git"
"git@github.com:mrVanDalo/module.backup.git")
(sync-to-github "sync-module-tinc"
"ssh://gogs@workhorse.private:2222/palo/nixos-tinc.git"
"git@github.com:mrVanDalo/nixos-tinc.git")
(sync-to-github "sync-memo"
"ssh://gogs@workhorse.private:2222/palo/memo.git"
"git@github.com:mrVanDalo/memo.git")
(sync-to-github "sync-diagrams-template"
"ssh://gogs@workhorse.private:2222/palo/diagrams-template.git"
"git@github.com:mrVanDalo/diagrams.git")
(sync-to-github "sync-plops"
"ssh://gogs@workhorse.private:2222/palo/plops.git"
"git@github.com:mrVanDalo/plops.git")
(sync-to-github "sync-image-generator"
"ssh://gogs@workhorse.private:2222/palo/image-generator2.git"
"git@github.com:mrVanDalo/image-generator.git")
(sync-to-github "sync-image-generator-lib"
"ssh://gogs@workhorse.private:2222/palo/image-generator-lib.git"
"git@github.com:mrVanDalo/image-generator-examples.git")
(sync-to-github "sync-tech.ingolf-wagner.de"
"ssh://gogs@workhorse.private:2222/palo/tech.ingolf-wagner.de.git"
"git@github.com:mrVanDalo/tech.ingolf-wagner.de.git")
(sync-to-github "sync-LineageOS-build"
"ssh://gogs@git.ingolf-wagner.de:443/palo/LineagoOS-build.git"
"git@github.com:mrVanDalo/LineagoOS-build.git")
(sync-to-github "sync-http-errors"
"ssh://gogs@git.ingolf-wagner.de:443/palo/http-errors.git"
"git@github.com:mrVanDalo/http-errors.git")
(sync-to-github "sync-light-control"
"ssh://gogs@git.ingolf-wagner.de:443/palo/light-control.git"
"git@github.com:mrVanDalo/light-control.git")
];
};
};
}

View file

@ -0,0 +1,69 @@
{ pkgs, lib, config, ... }: {
services.jupyter = {
enable = true;
ip = "0.0.0.0";
#In [1]: from notebook.auth import passwd
#In [2]: passwd('test')
#Out[2]: 'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'
#NOTE: you need to keep the single quote inside nix string.
password = "'sha1:1b961dc713fb:88483270a63e57d18d43cf337e629539de1436ba'";
kernels = {
python3 = let
env = (pkgs.python3.withPackages (pythonPackages:
with pythonPackages; [
ipykernel
pandas
# database stuff
mysqlclient
databases
asyncpg
psycopg2
aiomysql
pymysql
aiosqlite
#aiopg
sqlalchemy
# pdf export
nbconvert
]));
in {
displayName = "Python 3";
argv = [
"${env.interpreter}"
"-m"
"ipykernel_launcher"
"-f"
"{connection_file}"
];
language = "python";
};
};
};
# to generate pdfs and such
environment.systemPackages = [ pkgs.pandoc ];
backup.dirs = [ "/var/lib/jupyter" ];
backup.exclude =
[ "/var/lib/jupyter/.local" "/var/lib/jupyter/.ipynb_checkpoints" ];
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"python.${config.networking.hostName}.private" = {
serverAliases = [ "jupyter.${config.networking.hostName}.private" ];
locations."/" = {
proxyWebsockets = true;
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.jupyter.port
}";
};
};
};
};
}

View file

@ -0,0 +1,25 @@
{ config, ... }: {
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"kibana.${config.networking.hostName}.private" = {
serverAliases = [ ];
locations."/" = {
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.kibana.port
}";
};
};
};
};
services.elasticsearch.enable = true;
services.elasticsearch.listenAddress = "workhorse.private";
services.kibana.enable = true;
services.kibana.elasticsearch.hosts = [ "http://workhorse.private:9200" ];
services.kibana.listenAddress = "workhorse.private";
services.kibana.port = 5601;
}

View file

@ -0,0 +1,716 @@
# fetches mails for me
{ lib, pkgs, config, ... }:
let
junk_filter = [
"from:booking.com"
"from:brompton.com"
"from:circleci.com OR (from:noreply@github.com AND to:audio-overlay@googlegroups.com)"
"from:codepen.io"
"from:congstarnews.de"
"from:cronullasurfingacademy.com"
"from:cryptohopper.com"
"from:digitalo.de"
"from:facebook.com OR from:facebookmail.com"
"from:fitnessfirst.de"
"from:flixbus.de"
"from:getdigital.de"
"from:getpocket.com"
"from:ghostinspector.com"
"from:globetrotter.de"
"from:hackster.io"
"from:hostelworld.com"
"from:immobilienscout24.de"
"from:kvraudio.com"
"from:letterboxd.com"
"from:linkedin.com"
"from:magix.net"
"from:mailings.gmx.net"
"from:mailings.web.de"
"from:matrix.org"
"from:menospese.com"
"from:microsoftstoreemail.com"
"from:mixcloudmail.com AND subject:Weekly Update"
"from:oknotify2.com AND NOT subject:New message"
"from:paulaschoice.com"
"from:puppet.com"
"from:runtastic.com"
"from:samplemagic.com OR from:wavealchemy.co.uk OR from:creators.gumroad.com"
"from:ticketmaster.de"
"from:trade4less.de"
"from:tumblr.com"
"from:turners.co.nz"
"from:twitch.tv"
"from:vstbuzz.com"
];
filters = [
{
query = "from:hv-geelen.de";
tags = [ "+wohnung" ];
}
{
query = "from:computerfutures.com OR from:computerfutures.de";
tags = [ "+jobs" "-inbox" ];
}
{
query = "from:seek.com.au or from:seek.co.nz";
tags = [ "+jobs" ];
}
{
query = "from:xing.com";
tags = [ "+jobs" "-inbox" ];
}
{
query = "from:no-reply@backtrace.io OR to:sononym@noreply.github.com";
tags = [ "+sononym" "-inbox" ];
}
{
query = "from:ebay.com OR from:ebay.de OR from:ebay.net";
tags = [ "+ebay" "+shop" "+billing" ];
}
{
query = "from:bahn.de";
tags = [ "+billing" "+bahn" ];
}
{
query =
"from:fysitech.atlassian.net OR to:engiadina-pwa@noreply.github.com";
tags = [ "+mia" "+work" "-unread" "-inbox" ];
}
{
query =
"from:space-left.org OR to:space-left.org OR subject:/\\[space-left\\]/";
tags = [ "+spaceleft" "+space-left" ];
}
{
query = "from:landr.com";
tags = [ "+landr" "+music" ];
}
{
query = "tag:landr and tag:billing";
tags = [ "+billing" ];
}
{
query = "from:oknotify2.com";
tags = [ "+okcupid" ];
}
{
query = "from:taxback.de OR to:taxback.de";
tags = [ "+steuer" ];
}
{
query = "from:campact.de";
tags = [ "+campact" "+politics" ];
}
{
query = "from:aliexpress.com";
tags = [ "+shop" "+aliexpress" ];
}
{
query = "from:congstar.de";
tags = [ "+billing" "+congstar" "-inbox" "-unread" ];
}
{
query =
"from:steampowered.com AND NOT ( subject:purchase OR subject:received )";
tags = [ "-inbox" "-unread" ];
}
{
query =
"from:steampowered.com AND ( subject:purchase OR subject:received )";
tags = [ "+billing" "+steam" ];
}
{
query = "from:gog.com AND NOT subject:Bestellung";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:gog.com AND subject:Bestellung";
tags = [ "+billing" "+gog" ];
}
{
query = "from:stadtmobil.de";
tags = [ "+billing" "+stadtmobil" "-inbox" "-unread" ];
}
{
query = "from:drive-now.com";
tags = [ "+billing" "+drivenow" "-inbox" "-unread" ];
}
{
query = "from:data-treuhand.de";
tags = [ "+mindcurv" "+work" "-inbox" "-unread" "-junk" ];
}
{
query = "from:immocation.de";
tags = [ "+immobilien" "-inbox" ];
}
{
query = "from:tinc-vpn.org";
tags = [ "+tinc" ];
}
{
query = "from:mindfactory.de";
tags = [ "+shop" "+billing" ];
}
{
query = "from:zalando.de";
tags = [ "+shop" "+billing" "+zalando" ];
}
{
query = "from:ing.de";
tags = [ "+bank" "+ingdiba" ];
}
{
query = "from:nab.com.au";
tags = [ "+bank" "+nab" "-inbox" "-unread" ];
}
{
query = "from:dkb.de";
tags = [ "+bank" "+dkb" ];
}
{
query = "from:o2online.de";
tags = [ "+billing" "+o2" ];
}
{
query = "from:betfair.com";
tags = [ "+work" "+betfair" ];
}
{
query = "from:notifications@github.com";
tags = [ "+github" ];
}
{
query = "to:NUR@noreply.github.com";
tags = [ "+nur" "+nixos" "+list" ];
}
{
query = "to:nixpkgs@noreply.github.com";
tags = [ "+nixpkgs" "+nixos" "+list" ];
}
{
query = "from:travis-ci.org AND subject:mrVanDalo/navi";
tags = [ "+development" "+navi" ];
}
{
query = "from:travis-ci.org AND subject:nur-packages";
tags = [ "+development" "+nixos" "+nur-packages" ];
}
{
query = "from:travis-ci.org AND subject:csv-to-qif";
tags = [ "+development" "+csv-to-qif" ];
}
{
query = "to:proaudio@lists.tuxfamily.org";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:nixos1@discoursemail.com";
tags = [ "+nixos" "+discourse" "+list" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Development";
tags = [ "+nixos" "+discourse" "+development" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Français";
tags = [ "+nixos" "+discourse" "-inbox" "-unread" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Announcements";
tags = [ "+nixos" "+discourse" "+announcements" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Links";
tags = [ "+nixos" "+discourse" "+links" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Games";
tags = [ "+nixos" "+discourse" "+games" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Meta";
tags = [ "+nixos" "+discourse" "+meta" ];
}
{
query = "from:nixos1@discoursemail.com AND subject:Events";
tags = [ "+nixos" "+discourse" "+events" ];
}
{
query = "from:limebike.com AND (subject:Funds OR subject:Receipt)";
tags = [ "-inbox" "-unread" "+billing" "+limebike" ];
}
{
query = "from:freemusicarchive.org";
tags = [ "+FMA" ];
}
{
query = "from:namecheap.com and subject:auto-renewal";
tags = [ "+namecheap" "+billing" ];
}
{
query = "from:namecheap.com and subject:order";
tags = [ "+namecheap" "+billing" ];
}
{
query = "tag:namecheap.com and tag:billing and body:gaykraft.com";
tags = [ "+namecheap" "+billing" ];
}
{
query = "from:nintendo.com";
tags = [ "+nintendo" "+billing" ];
}
{
query = "from:oculus.com AND subject:receipt";
tags = [ "+oculus" "+billing" ];
}
{
query = "from:car2go.com";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:sixt.de";
tags = [ "-inbox" "-unread" ];
}
{
query = "from:meetup.com";
tags = [ "-inbox" "-unread" "+meetup" ];
}
{
query = "from:slack.com";
tags = [ "+slack" ];
}
{
query = "from:keybase.io";
tags = [ "+keybase" ];
}
{
query = "from:jobs2web.com";
tags = [ "+newzealand" "+jobs" "-inbox" ];
}
{
query = "from:paypal.de AND subject:Bestätigung";
tags = [ "-unread" "+paypal" "+billing" ];
}
{
query = "to:c-base.org";
tags = [ "+cbase" "+list" ];
}
{
query = "to:c-base.org AND subject=[auto-report]";
tags = [ "-unread" "-inbox" ];
}
{
query = "from:browserstack.com";
tags = [ "+browserstack" ];
}
{
query =
"to:renoise@ingolf-wagner.de OR to:root@renoise.com OR from:renoise.com OR to:admin@renoise.com";
tags = [ "+renoise" ];
}
{
query = "from:amazon.de OR from:amazon.com AND NOT to:renoise.com";
tags = [ "+shop" "+amazon" "+billing" ];
}
{
query = "from:hetzner.com OR from:hetzner.de";
tags = [ "+hetzner" ];
}
{
query =
"to:renoise.com AND NOT ( from:renoise.com OR from:root OR from:hetzner.com OR from:hetzner.de OR from:amazon.com OR from:gmail.com )";
tags = [ "-inbox" "-unread" "+junk" "+renoise" ];
}
{
query = "tag:hetzner and subject:Invoice";
tags = [ "+billing" ];
}
# final rules to make imap sync stuff easier
# there can only be one output folder tag, and theses rules are prioritized
{
query = "tag:fraud";
tags = [ "-inbox" "-archive" "-junk" "-unread" ];
message = "clean up tag fraud";
}
{
query = "tag:junk";
tags = [ "-inbox" "-archive" "-fraud" "-unread" ];
message = "clean up tag junk";
}
{
query = "tag:archive";
tags = [ "-inbox" "-junk" "-fraud" "-unread" ];
message = "clean up tag archive";
}
{
query = "tag:inbox";
tags = [ "-archive" "-junk" "-fraud" ];
message = "clean up inbox";
}
{
query = "tag:killed";
tags = [ "-inbox" "-unread" ];
message = "clean up tag killed";
}
{
query = "tag:muted";
tags = [ "-inbox" "-unread" ];
}
# remove new tag at the end
{
query = "tag:new";
tags = [ "-new" ];
message = "remove new tag at the end";
}
];
notmuchTagging = let
template = index:
{ tags, query, message ? "generic", ... }:
let
command = ''
${pkgs.notmuch}/bin/notmuch tag ${
lib.concatStringsSep " " tags
} -- "${query}"
'';
in ''
echo '${command}'
${command}
'';
junk_template = index: query:
template index {
tags = [ "+junk" "-unread" "-inbox" ];
query = query;
message = "generic junk filter";
};
in pkgs.writers.writeBash "notmuch-tagging" (lib.concatStringsSep "\n"
((lib.imap0 junk_template junk_filter) ++ (lib.imap0 template filters)));
notmuchTaggingNew = let
template = index:
{ tags, query, message ? "generic", ... }:
let
command = ''
${pkgs.notmuch}/bin/notmuch tag ${
lib.concatStringsSep " " tags
} -- "${query} AND tag:new"
'';
in ''
echo '${command}'
${command}
'';
junk_template = index: query:
template index {
tags = [ "+junk" "-unread" "-inbox" ];
query = query;
message = "generic junk filter";
};
in pkgs.writers.writeBash "notmuch-tagging-new" (lib.concatStringsSep "\n"
((lib.imap0 junk_template junk_filter) ++ (lib.imap0 template filters)));
in {
backup.dirs = [ "/home/mailfetcher" ];
users.users.mailUser = {
isNormalUser = true;
description = "collects mails for me";
hashedPassword = "!";
name = "mailfetcher";
home = "/home/mailfetcher";
openssh.authorizedKeys.keyFiles =
config.users.users.root.openssh.authorizedKeys.keyFiles;
};
# configure passwords
krops.userKeys = {
"namecheap.terranix.org" = {
user = config.users.users.mailUser.name;
source = toString <secrets/mail/namecheap/terranix.org>;
requiredBy = [ "fetchmail.service" ];
};
"gmail.palipalo9" = {
user = config.users.users.mailUser.name;
source = toString <secrets/mail/gmail/palipalo9>;
requiredBy = [ "fetchmail.service" ];
};
"gmx.palo_van_dalo" = {
user = config.users.users.mailUser.name;
source = toString <secrets/mail/gmx/palo_van_dalo>;
requiredBy = [ "fetchmail.service" ];
};
"gmx.ingolf_wagner" = {
user = config.users.users.mailUser.name;
source = toString <secrets/mail/gmx/ingolf.wagner>;
requiredBy = [ "fetchmail.service" ];
};
"web.pali_palo" = {
user = config.users.users.mailUser.name;
source = toString <secrets/mail/web.de/pali_palo>;
requiredBy = [ "fetchmail.service" ];
};
"siteground.contact" = {
user = config.users.users.mailUser.name;
source = toString <secrets/mail/siteground/contact>;
requiredBy = [ "fetchmail.service" ];
};
"c-base.palo" = {
user = config.users.users.mailUser.name;
source = toString <secrets/mail/c-base/palo>;
requiredBy = [ "fetchmail.service" ];
};
};
environment.systemPackages = [ pkgs.muchsync ];
# configure accounts
home-manager.users.mailUser.accounts.email = {
accounts = {
palo_van_dalo-gmx = {
primary = false;
address = "palo_van_dalo@gmx.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "palo_van_dalo@gmx.de";
passwordCommand =
"cat ${toString config.krops.userKeys."gmx.palo_van_dalo".target}";
imap = {
host = "imap.gmx.net";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
ingolf-wagner-gmx = {
primary = false;
address = "ingolf.wagner@gmx.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "ingolf.wagner@gmx.de";
passwordCommand =
"cat ${toString config.krops.userKeys."gmx.ingolf_wagner".target}";
imap = {
host = "imap.gmx.net";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
pali_palo = {
primary = false;
address = "pali_palo@web.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "pali_palo@web.de";
passwordCommand =
"cat ${toString config.krops.userKeys."web.pali_palo".target}";
imap = {
host = "imap.web.de";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
c-base = {
primary = false;
address = "palo@c-base.org";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "palo";
passwordCommand =
"cat ${toString config.krops.userKeys."c-base.palo".target}";
imap = {
host = "c-mail.c-base.org";
port = 993;
# fetched using : nix-shell -p openssl --run "openssl s_client -connect c-mail.c-base.org:993 -showcerts"
tls.certificatesFile = pkgs.writeText "c-base.pem" ''
-----BEGIN CERTIFICATE-----
MIIFKDCCAxCgAwIBAgIDAtwrMA0GCSqGSIb3DQEBDQUAMFQxFDASBgNVBAoTC0NB
Y2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5vcmcxHDAaBgNV
BAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwHhcNMTkxMTA5MDgzMjUyWhcNMjExMTA4
MDgzMjUyWjBhMQswCQYDVQQGEwJERTEPMA0GA1UECBMGQmVybGluMQ8wDQYDVQQH
EwZCZXJsaW4xFDASBgNVBAoTC2MtYmFzZSBlLlYuMRowGAYDVQQDExFjLW1haWwu
Yy1iYXNlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKR3XBxJ
72MayCS0D5GCoHcY0TQLs1DQSohCCobRdSowFJzNQw/2lL6bb+Q2rmevZQXuM4vP
YbFytvTDmY5y5MNXEqGLfi8D5TcaP/RdXWQU++yUunE6yMdqZNheeXPjM//PnoXG
DyT236BovEi3YipUUsLXFiRj+cAjrQE7a2YUs3fjV3P6grMH0V06J6P6+JJvRgp2
K33uhKhnKyb3s1tbdbu1KeGozx2ws9lg79XV+Py6PXxP6jTZ2PCsaxs3BThSdmsl
vQyk/zoW7tA1m2ntRCoyFHZqfOHsN3UOS/HDRlXqgSf0ah8cPYPsl0ayXhgOv0Tu
1PSMt4Ve2GajX8MCAwEAAaOB9TCB8jAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQE
AwIDqDA0BgNVHSUELTArBggrBgEFBQcDAgYIKwYBBQUHAwEGCWCGSAGG+EIEAQYK
KwYBBAGCNwoDAzAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9v
Y3NwLmNhY2VydC5vcmcvMDgGA1UdHwQxMC8wLaAroCmGJ2h0dHA6Ly9jcmwuY2Fj
ZXJ0Lm9yZy9jbGFzczMtcmV2b2tlLmNybDAtBgNVHREEJjAkghFjLW1haWwuYy1i
YXNlLm9yZ4IPbWFpbC5jLWJhc2Uub3JnMA0GCSqGSIb3DQEBDQUAA4ICAQBjTIa1
xdhUYXJidv1U0qaqSV1DzumakFY20OM4QCV0Qvlq+SQUqroGBTIGodxhTnjG5o01
4xBAnsTcMRNycXHu0j6XYqDS4QhaFcGdFmXP0EQfAFbvhwZdbvgiRHuUTSGEbcLp
Vk8sP8dpzx+zMAZ6PL7XMwPFPTHm7vw40qmTyCuUcnhIwHgwGxu4yu0tdsU+VwRD
RVsdZO4V2GhFTz8oRcHvmC2wmx+Zwx0RXWPXSN5yRDDZWwY4WbECa2MvojH1HlJG
YJkokq7nbYJix/RipSkAXgurcgppVmM5cf7uYgSrltW8pm3IMMPrlaFrlzMMMtdq
SYFk+FDp296B7CDyWpQhDcVjnGQ70JfMWT5S3Lsi8DnI2pul9ljxPOt+Q8XFh1oz
Ofr7y5Qjm72YToOX1j7N8ppCh0RJH4lOsouTPVdp859ch9FxZdceq+nC744wv+Nt
TQPw15Gk3RY5mVYBE/Cw2T7j7qDmBaEUKxkfW7q8t287FXM4XX6C+cKYr6jYx6s1
5/2p4gCuOALYqJ7kD2xjci0VTWu77H4J2QKEZF8AgdI36dIYr7GY0e/+xb/CScwr
uvu2R9jfPOMVu6CiavPGUtcvju4A+qMUDqIyH9dNwkMQRffAtmsF6KR4nMYxhr45
nKY1BaufWLD1UWrjaR1IF6L5qDHOXeMJEChYkg==
-----END CERTIFICATE-----'';
};
mbsync = {
enable = false;
create = "both";
};
notmuch.enable = true;
};
gmail = {
# for google accounts you have to allow 'less secure apps' in accounts.google.com
primary = true;
address = "palipalo9@googlemail.com";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "palipalo9@googlemail.com";
passwordCommand =
"cat ${toString config.krops.userKeys."gmail.palipalo9".target}";
imap = {
host = "imap.gmail.com";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
terranix_org = {
primary = false;
address = "palo@terranix.org";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "palo@terranix.org";
passwordCommand = "cat ${
toString config.krops.userKeys."namecheap.terranix.org".target
}";
imap = {
host = "mail.privateemail.com";
tls.enable = true;
port = 993;
};
mbsync = {
enable = true;
create = "both";
};
notmuch.enable = true;
};
ingolf-wagner-de = {
primary = false;
address = "contact@ingolf-wagner.de";
aliases = [ ];
realName = "Ingolf Wagner";
userName = "contact@ingolf-wagner.de";
passwordCommand =
"cat ${toString config.krops.userKeys."siteground.contact".target}";
imap = {
host = "securees5.sgcpanel.com";
port = 993;
tls.enable = true;
#tls.useStartTls = true;
};
# make sure the upstream mail is deleted
getmail = {
enable = true;
delete = true;
readAll = false;
mailboxes = [ "ALL" ];
};
notmuch.enable = true;
};
};
};
# configure mbsync
home-manager.users.mailUser.programs.mbsync.enable = true;
# re-tag everything once a day
systemd.services.retagmail = {
enable = true;
serviceConfig = { User = config.users.users.mailUser.name; };
environment.NOTMUCH_CONFIG =
"${config.users.users.mailUser.home}/.config/notmuch/notmuchrc";
script = "${notmuchTagging}";
};
systemd.timers.retagmail = {
enable = true;
timerConfig = {
OnCalendar = "daily";
Persistent = "true";
};
wantedBy = [ "multi-user.target" ];
};
# fetch mails every 10 minutes
systemd.services.fetchmail = {
enable = true;
serviceConfig = { User = config.users.users.mailUser.name; };
environment.NOTMUCH_CONFIG =
"${config.users.users.mailUser.home}/.config/notmuch/notmuchrc";
script = ''
echo "run mbsync"
${pkgs.isync}/bin/mbsync \
--all
echo "run getmail"
${pkgs.getmail}/bin/getmail \
--quiet \
--rcfile getmailingolf-wagner-de
echo "run notmuch"
${pkgs.notmuch}/bin/notmuch new
${notmuchTaggingNew}
echo "mute threads"
${pkgs.notmuch}/bin/notmuch tag +muted $(${pkgs.notmuch}/bin/notmuch search --output=threads tag:muted)
echo "delete threads"
${pkgs.notmuch}/bin/notmuch tag +deleted $(${pkgs.notmuch}/bin/notmuch search --output=threads tag:deleted)
'';
};
systemd.timers.fetchmail = {
enable = true;
# timerConfig.OnCalendar = " *-*-* *:00:00";
timerConfig.OnCalendar = "*:0/10";
wantedBy = [ "multi-user.target" ];
};
# configure notmuch
home-manager.users.mailUser.programs.notmuch = {
enable = true;
new.tags = [ "unread" "inbox" "new" ];
};
home-manager.users.mailUser.home.file."notmuch" = {
source = "${config.users.users.mailUser.home}/.config/notmuch/notmuchrc";
target = ".notmuch-config";
};
}

View file

@ -0,0 +1,24 @@
{ pkgs, lib, config, ... }: {
services.metabase = {
listen.port = 3040;
enable = true;
};
backup.dirs = [ "/var/lib/metabase" ];
services.nginx = {
enable = true;
statusPage = true;
virtualHosts = {
"metabase.${config.networking.hostName}.private" = {
locations."/" = {
proxyPass = "http://${config.networking.hostName}.private:${
toString config.services.metabase.listen.port
}";
};
};
};
};
}

View file

@ -0,0 +1,51 @@
{ pkgs, config, ... }:
let
maxPower = 90;
pool = "eu1.ethermine.org";
toolkit = "opencl";
wallet = "";
rig = config.networking.hostName;
recheckInterval = 2000;
package = pkgs.ethminer;
in {
systemd.services.ethminer = {
description = "ethminer ethereum mining service";
wantedBy = [ "multi-user.target" ];
after = [ "network.target" ];
serviceConfig = {
DynamicUser = true;
ExecStartPre = "${package}/bin/.ethminer-wrapped --list-devices";
Restart = "always";
};
script = ''
${package}/bin/.ethminer-wrapped \
--farm-recheck ${toString recheckInterval} \
--report-hashrate \
--${toolkit} \
--pool stratum1+tcp://${wallet}.${rig}@${pool}:4444
'';
};
# https://wiki.archlinux.org/title/GPGPU#Intel
hardware.opengl = {
enable = true;
extraPackages = with pkgs; [ intel-ocl intel-compute-runtime beignet ];
driSupport = true;
driSupport32Bit = true;
};
environment.systemPackages = with pkgs; [
package
# go-ethereum
go-ethereum
# to check opencl config
clinfo
# check temperature
i7z
];
}

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