Compare commits
No commits in common. "master" and "vm-test" have entirely different histories.
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -1,3 +1,3 @@
|
|||||||
#pattern filter=crypt diff=crypt merge=crypt
|
#pattern filter=crypt diff=crypt
|
||||||
secrets/*/** filter=crypt diff=crypt merge=crypt
|
secrets/*/** filter=crypt diff=crypt
|
||||||
secrets/default.nix filter=crypt diff=crypt merge=crypt
|
secrets/default.nix filter=crypt diff=crypt
|
||||||
|
32
README.md
32
README.md
@ -1,32 +0,0 @@
|
|||||||
# Maxwell configuration
|
|
||||||
|
|
||||||
The NixOS configuration of Maxwell
|
|
||||||
|
|
||||||
## Switching configuration
|
|
||||||
|
|
||||||
1. Mount remotely the secrets directory
|
|
||||||
|
|
||||||
`$ rsshfs secrets maxwell:$PWD/secrets -o allow_other &`
|
|
||||||
|
|
||||||
2. Run nixos-rebuild
|
|
||||||
|
|
||||||
`$ nixos-rebuild test -I nixos-config=configuration.nix
|
|
||||||
--target-host maxwell --use-remote-sudo`
|
|
||||||
|
|
||||||
3. Unmount the secrets directory
|
|
||||||
|
|
||||||
`kill %%`
|
|
||||||
|
|
||||||
## Testing changes
|
|
||||||
|
|
||||||
1. Build a VM
|
|
||||||
|
|
||||||
`$ nixos-rebuild build-vm -I nixos-config=testing.nix`
|
|
||||||
|
|
||||||
2. Change the open files limit
|
|
||||||
|
|
||||||
`$ ulimit -n 90000`
|
|
||||||
|
|
||||||
3. Run the VM without internet (to avoid a mess)
|
|
||||||
|
|
||||||
`$ unshare -nc result/bin/run-maxwell-vm`
|
|
735
assets/searx-settings.yml
Normal file
735
assets/searx-settings.yml
Normal file
@ -0,0 +1,735 @@
|
|||||||
|
general:
|
||||||
|
debug : False # Debug mode, only for development
|
||||||
|
instance_name : "searxwell" # displayed name
|
||||||
|
|
||||||
|
search:
|
||||||
|
safe_search : 0 # Filter results. 0: None, 1: Moderate, 2: Strict
|
||||||
|
autocomplete : "" # Existing autocomplete backends: "dbpedia", "duckduckgo", "google", "startpage", "wikipedia" - leave blank to turn it off by default
|
||||||
|
language : "en-US"
|
||||||
|
ban_time_on_fail : 5 # ban time in seconds after engine errors
|
||||||
|
max_ban_time_on_fail : 120 # max ban time in seconds after engine errors
|
||||||
|
|
||||||
|
server:
|
||||||
|
port : 8083
|
||||||
|
bind_address : "127.0.0.1" # address to listen on
|
||||||
|
secret_key : "QzsHA00oA7H68Z2OXYYk3MaC6BjxkTiS"
|
||||||
|
base_url : "https://maxwell.ydns.eu/srx/"
|
||||||
|
image_proxy : False # Proxying image results through searx
|
||||||
|
http_protocol_version : "1.0" # 1.0 and 1.1 are supported
|
||||||
|
|
||||||
|
ui:
|
||||||
|
static_path : "" # Custom static path - leave it blank if you didn't change
|
||||||
|
templates_path : "" # Custom templates path - leave it blank if you didn't change
|
||||||
|
default_theme : oscar # ui theme
|
||||||
|
default_locale : "" # Default interface locale - leave blank to detect from browser information or use codes from the 'locales' config section
|
||||||
|
theme_args :
|
||||||
|
oscar_style : logicodev # default style of oscar
|
||||||
|
|
||||||
|
outgoing: # communication with search engines
|
||||||
|
request_timeout : 2.0 # seconds
|
||||||
|
useragent_suffix : "" # suffix of searx_useragent, could contain informations like an email address to the administrator
|
||||||
|
pool_connections : 100 # Number of different hosts
|
||||||
|
pool_maxsize : 10 # Number of simultaneous requests by host
|
||||||
|
|
||||||
|
engines:
|
||||||
|
- name : arch linux wiki
|
||||||
|
engine : archlinux
|
||||||
|
shortcut : al
|
||||||
|
|
||||||
|
- name : archive is
|
||||||
|
engine : xpath
|
||||||
|
search_url : https://archive.is/{query}
|
||||||
|
url_xpath : (//div[@class="TEXT-BLOCK"]/a)/@href
|
||||||
|
title_xpath : (//div[@class="TEXT-BLOCK"]/a)
|
||||||
|
content_xpath : //div[@class="TEXT-BLOCK"]/ul/li
|
||||||
|
categories : general
|
||||||
|
timeout : 7.0
|
||||||
|
disabled : True
|
||||||
|
shortcut : ai
|
||||||
|
|
||||||
|
- name : arxiv
|
||||||
|
engine : arxiv
|
||||||
|
shortcut : arx
|
||||||
|
categories : science
|
||||||
|
timeout : 4.0
|
||||||
|
|
||||||
|
- name : asksteem
|
||||||
|
engine : asksteem
|
||||||
|
shortcut : as
|
||||||
|
|
||||||
|
- name : base
|
||||||
|
engine : base
|
||||||
|
shortcut : bs
|
||||||
|
|
||||||
|
- name : wikipedia
|
||||||
|
engine : wikipedia
|
||||||
|
shortcut : wp
|
||||||
|
base_url : 'https://{language}.wikipedia.org/'
|
||||||
|
|
||||||
|
- name : bing
|
||||||
|
engine : bing
|
||||||
|
shortcut : bi
|
||||||
|
|
||||||
|
- name : bing images
|
||||||
|
engine : bing_images
|
||||||
|
shortcut : bii
|
||||||
|
|
||||||
|
- name : bing news
|
||||||
|
engine : bing_news
|
||||||
|
shortcut : bin
|
||||||
|
|
||||||
|
- name : bing videos
|
||||||
|
engine : bing_videos
|
||||||
|
shortcut : biv
|
||||||
|
|
||||||
|
- name : bitbucket
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
search_url : https://bitbucket.org/repo/all/{pageno}?name={query}
|
||||||
|
url_xpath : //article[@class="repo-summary"]//a[@class="repo-link"]/@href
|
||||||
|
title_xpath : //article[@class="repo-summary"]//a[@class="repo-link"]
|
||||||
|
content_xpath : //article[@class="repo-summary"]/p
|
||||||
|
categories : it
|
||||||
|
timeout : 4.0
|
||||||
|
disabled : True
|
||||||
|
shortcut : bb
|
||||||
|
|
||||||
|
- name : ccc-tv
|
||||||
|
engine : xpath
|
||||||
|
paging : False
|
||||||
|
search_url : https://media.ccc.de/search/?q={query}
|
||||||
|
url_xpath : //div[@class="caption"]/h3/a/@href
|
||||||
|
title_xpath : //div[@class="caption"]/h3/a/text()
|
||||||
|
content_xpath : //div[@class="caption"]/h4/@title
|
||||||
|
categories : videos
|
||||||
|
disabled : True
|
||||||
|
shortcut : c3tv
|
||||||
|
|
||||||
|
- name : crossref
|
||||||
|
engine : json_engine
|
||||||
|
paging : True
|
||||||
|
search_url : http://search.crossref.org/dois?q={query}&page={pageno}
|
||||||
|
url_query : doi
|
||||||
|
title_query : title
|
||||||
|
content_query : fullCitation
|
||||||
|
categories : science
|
||||||
|
shortcut : cr
|
||||||
|
|
||||||
|
- name : currency
|
||||||
|
engine : currency_convert
|
||||||
|
categories : general
|
||||||
|
shortcut : cc
|
||||||
|
|
||||||
|
- name : deezer
|
||||||
|
engine : deezer
|
||||||
|
shortcut : dz
|
||||||
|
|
||||||
|
- name : deviantart
|
||||||
|
engine : deviantart
|
||||||
|
shortcut : da
|
||||||
|
timeout: 3.0
|
||||||
|
|
||||||
|
- name : ddg definitions
|
||||||
|
engine : duckduckgo_definitions
|
||||||
|
shortcut : ddd
|
||||||
|
weight : 2
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : digbt
|
||||||
|
engine : digbt
|
||||||
|
shortcut : dbt
|
||||||
|
timeout : 6.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : digg
|
||||||
|
engine : digg
|
||||||
|
shortcut : dg
|
||||||
|
|
||||||
|
- name : erowid
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
first_page_num : 0
|
||||||
|
page_size : 30
|
||||||
|
search_url : https://www.erowid.org/search.php?q={query}&s={pageno}
|
||||||
|
url_xpath : //dl[@class="results-list"]/dt[@class="result-title"]/a/@href
|
||||||
|
title_xpath : //dl[@class="results-list"]/dt[@class="result-title"]/a/text()
|
||||||
|
content_xpath : //dl[@class="results-list"]/dd[@class="result-details"]
|
||||||
|
categories : general
|
||||||
|
shortcut : ew
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : wikidata
|
||||||
|
engine : wikidata
|
||||||
|
shortcut : wd
|
||||||
|
timeout : 3.0
|
||||||
|
weight : 2
|
||||||
|
|
||||||
|
- name : duckduckgo
|
||||||
|
engine : duckduckgo
|
||||||
|
shortcut : ddg
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : duckduckgo images
|
||||||
|
engine : duckduckgo_images
|
||||||
|
shortcut : ddi
|
||||||
|
timeout: 3.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : etymonline
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
search_url : http://etymonline.com/?search={query}&p={pageno}
|
||||||
|
url_xpath : //a[contains(@class, "word--")]/@href
|
||||||
|
title_xpath : //p[contains(@class, "word__name--")]/text()
|
||||||
|
content_xpath : //section[contains(@class, "word__defination")]/object
|
||||||
|
first_page_num : 0
|
||||||
|
shortcut : et
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : faroo
|
||||||
|
engine : faroo
|
||||||
|
shortcut : fa
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : 1x
|
||||||
|
engine : www1x
|
||||||
|
shortcut : 1x
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : fdroid
|
||||||
|
engine : fdroid
|
||||||
|
shortcut : fd
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : flickr
|
||||||
|
categories : images
|
||||||
|
shortcut : fl
|
||||||
|
# You can use the engine using the official stable API, but you need an API key
|
||||||
|
# See : https://www.flickr.com/services/apps/create/
|
||||||
|
# engine : flickr
|
||||||
|
# api_key: 'apikey' # required!
|
||||||
|
# Or you can use the html non-stable engine, activated by default
|
||||||
|
engine : flickr_noapi
|
||||||
|
|
||||||
|
- name : free software directory
|
||||||
|
engine : mediawiki
|
||||||
|
shortcut : fsd
|
||||||
|
categories : it
|
||||||
|
base_url : https://directory.fsf.org/
|
||||||
|
number_of_results : 5
|
||||||
|
# what part of a page matches the query string: title, text, nearmatch
|
||||||
|
# title - query matches title, text - query matches the text of page, nearmatch - nearmatch in title
|
||||||
|
search_type : title
|
||||||
|
timeout : 5.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : frinkiac
|
||||||
|
engine : frinkiac
|
||||||
|
shortcut : frk
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : genius
|
||||||
|
engine : genius
|
||||||
|
shortcut : gen
|
||||||
|
|
||||||
|
- name : gigablast
|
||||||
|
engine : gigablast
|
||||||
|
shortcut : gb
|
||||||
|
timeout : 3.0
|
||||||
|
disabled: True
|
||||||
|
|
||||||
|
- name : gitlab
|
||||||
|
engine : json_engine
|
||||||
|
paging : True
|
||||||
|
search_url : https://gitlab.com/api/v4/projects?search={query}&page={pageno}
|
||||||
|
url_query : web_url
|
||||||
|
title_query : name_with_namespace
|
||||||
|
content_query : description
|
||||||
|
page_size : 20
|
||||||
|
categories : it
|
||||||
|
shortcut : gl
|
||||||
|
timeout : 10.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : github
|
||||||
|
engine : github
|
||||||
|
shortcut : gh
|
||||||
|
|
||||||
|
- name : google
|
||||||
|
engine : google
|
||||||
|
shortcut : go
|
||||||
|
|
||||||
|
- name : google images
|
||||||
|
engine : google_images
|
||||||
|
shortcut : goi
|
||||||
|
|
||||||
|
- name : google news
|
||||||
|
engine : google_news
|
||||||
|
shortcut : gon
|
||||||
|
|
||||||
|
- name : google videos
|
||||||
|
engine : google_videos
|
||||||
|
shortcut : gov
|
||||||
|
|
||||||
|
- name : google scholar
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
search_url : https://scholar.google.com/scholar?start={pageno}&q={query}&hl=en&as_sdt=0,5&as_vis=1
|
||||||
|
results_xpath : //div[contains(@class, "gs_r")]/div[@class="gs_ri"]
|
||||||
|
url_xpath : .//h3/a/@href
|
||||||
|
title_xpath : .//h3/a
|
||||||
|
content_xpath : .//div[@class="gs_rs"]
|
||||||
|
suggestion_xpath : //div[@id="gs_qsuggest"]/ul/li
|
||||||
|
page_size : 10
|
||||||
|
first_page_num : 0
|
||||||
|
categories : science
|
||||||
|
shortcut : gos
|
||||||
|
|
||||||
|
- name : google play apps
|
||||||
|
engine : xpath
|
||||||
|
search_url : https://play.google.com/store/search?q={query}&c=apps
|
||||||
|
url_xpath : //a[@class="title"]/@href
|
||||||
|
title_xpath : //a[@class="title"]
|
||||||
|
content_xpath : //a[@class="subtitle"]
|
||||||
|
categories : files
|
||||||
|
shortcut : gpa
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : google play movies
|
||||||
|
engine : xpath
|
||||||
|
search_url : https://play.google.com/store/search?q={query}&c=movies
|
||||||
|
url_xpath : //a[@class="title"]/@href
|
||||||
|
title_xpath : //a[@class="title"]/@title
|
||||||
|
content_xpath : //a[contains(@class, "subtitle")]
|
||||||
|
categories : videos
|
||||||
|
shortcut : gpm
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : google play music
|
||||||
|
engine : xpath
|
||||||
|
search_url : https://play.google.com/store/search?q={query}&c=music
|
||||||
|
url_xpath : //a[@class="title"]/@href
|
||||||
|
title_xpath : //a[@class="title"]
|
||||||
|
content_xpath : //a[@class="subtitle"]
|
||||||
|
categories : music
|
||||||
|
shortcut : gps
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : geektimes
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
search_url : https://geektimes.ru/search/page{pageno}/?q={query}
|
||||||
|
url_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]/@href
|
||||||
|
title_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]
|
||||||
|
content_xpath : //article[contains(@class, "post")]//div[contains(@class, "post__text")]
|
||||||
|
categories : it
|
||||||
|
timeout : 4.0
|
||||||
|
disabled : True
|
||||||
|
shortcut : gt
|
||||||
|
|
||||||
|
- name : habrahabr
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
search_url : https://habrahabr.ru/search/page{pageno}/?q={query}
|
||||||
|
url_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]/@href
|
||||||
|
title_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]
|
||||||
|
content_xpath : //article[contains(@class, "post")]//div[contains(@class, "post__text")]
|
||||||
|
categories : it
|
||||||
|
timeout : 4.0
|
||||||
|
disabled : True
|
||||||
|
shortcut : habr
|
||||||
|
|
||||||
|
- name : hoogle
|
||||||
|
engine : json_engine
|
||||||
|
paging : True
|
||||||
|
search_url : https://www.haskell.org/hoogle/?mode=json&hoogle={query}&start={pageno}
|
||||||
|
results_query : results
|
||||||
|
url_query : location
|
||||||
|
title_query : self
|
||||||
|
content_query : docs
|
||||||
|
page_size : 20
|
||||||
|
categories : it
|
||||||
|
shortcut : ho
|
||||||
|
|
||||||
|
- name : ina
|
||||||
|
engine : ina
|
||||||
|
shortcut : in
|
||||||
|
timeout : 6.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name: kickass
|
||||||
|
engine : kickass
|
||||||
|
shortcut : kc
|
||||||
|
timeout : 4.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : library genesis
|
||||||
|
engine : xpath
|
||||||
|
search_url : https://libgen.is/search.php?req={query}
|
||||||
|
url_xpath : //a[contains(@href,"bookfi.net")]/@href
|
||||||
|
title_xpath : //a[contains(@href,"book/")]/text()[1]
|
||||||
|
content_xpath : //td/a[1][contains(@href,"=author")]/text()
|
||||||
|
categories : general
|
||||||
|
timeout : 7.0
|
||||||
|
disabled : True
|
||||||
|
shortcut : lg
|
||||||
|
|
||||||
|
- name : lobste.rs
|
||||||
|
engine : xpath
|
||||||
|
search_url : https://lobste.rs/search?utf8=%E2%9C%93&q={query}&what=stories&order=relevance
|
||||||
|
results_xpath : //li[contains(@class, "story")]
|
||||||
|
url_xpath : .//span[@class="link"]/a/@href
|
||||||
|
title_xpath : .//span[@class="link"]/a
|
||||||
|
content_xpath : .//a[@class="domain"]
|
||||||
|
categories : it
|
||||||
|
shortcut : lo
|
||||||
|
|
||||||
|
- name : microsoft academic
|
||||||
|
engine : microsoft_academic
|
||||||
|
categories : science
|
||||||
|
shortcut : ma
|
||||||
|
|
||||||
|
- name : mixcloud
|
||||||
|
engine : mixcloud
|
||||||
|
shortcut : mc
|
||||||
|
|
||||||
|
- name : nyaa
|
||||||
|
engine : nyaa
|
||||||
|
shortcut : nt
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : openairedatasets
|
||||||
|
engine : json_engine
|
||||||
|
paging : True
|
||||||
|
search_url : https://api.openaire.eu/search/datasets?format=json&page={pageno}&size=10&title={query}
|
||||||
|
results_query : response/results/result
|
||||||
|
url_query : metadata/oaf:entity/oaf:result/children/instance/webresource/url/$
|
||||||
|
title_query : metadata/oaf:entity/oaf:result/title/$
|
||||||
|
content_query : metadata/oaf:entity/oaf:result/description/$
|
||||||
|
categories : science
|
||||||
|
shortcut : oad
|
||||||
|
timeout: 5.0
|
||||||
|
|
||||||
|
- name : openairepublications
|
||||||
|
engine : json_engine
|
||||||
|
paging : True
|
||||||
|
search_url : https://api.openaire.eu/search/publications?format=json&page={pageno}&size=10&title={query}
|
||||||
|
results_query : response/results/result
|
||||||
|
url_query : metadata/oaf:entity/oaf:result/children/instance/webresource/url/$
|
||||||
|
title_query : metadata/oaf:entity/oaf:result/title/$
|
||||||
|
content_query : metadata/oaf:entity/oaf:result/description/$
|
||||||
|
categories : science
|
||||||
|
shortcut : oap
|
||||||
|
timeout: 5.0
|
||||||
|
|
||||||
|
- name : openstreetmap
|
||||||
|
engine : openstreetmap
|
||||||
|
shortcut : osm
|
||||||
|
|
||||||
|
- name : openrepos
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
search_url : https://openrepos.net/search/node/{query}?page={pageno}
|
||||||
|
url_xpath : //li[@class="search-result"]//h3[@class="title"]/a/@href
|
||||||
|
title_xpath : //li[@class="search-result"]//h3[@class="title"]/a
|
||||||
|
content_xpath : //li[@class="search-result"]//div[@class="search-snippet-info"]//p[@class="search-snippet"]
|
||||||
|
categories : files
|
||||||
|
timeout : 4.0
|
||||||
|
disabled : True
|
||||||
|
shortcut : or
|
||||||
|
|
||||||
|
- name : pdbe
|
||||||
|
engine : pdbe
|
||||||
|
shortcut : pdb
|
||||||
|
# Hide obsolete PDB entries.
|
||||||
|
# Default is not to hide obsolete structures
|
||||||
|
# hide_obsolete : False
|
||||||
|
|
||||||
|
- name : photon
|
||||||
|
engine : photon
|
||||||
|
shortcut : ph
|
||||||
|
|
||||||
|
- name : piratebay
|
||||||
|
engine : piratebay
|
||||||
|
shortcut : tpb
|
||||||
|
url: https://pirateproxy.red/
|
||||||
|
timeout : 3.0
|
||||||
|
|
||||||
|
- name : pubmed
|
||||||
|
engine : pubmed
|
||||||
|
shortcut : pub
|
||||||
|
categories: science
|
||||||
|
timeout : 3.0
|
||||||
|
|
||||||
|
- name : qwant
|
||||||
|
engine : qwant
|
||||||
|
shortcut : qw
|
||||||
|
categories : general
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : qwant images
|
||||||
|
engine : qwant
|
||||||
|
shortcut : qwi
|
||||||
|
categories : images
|
||||||
|
|
||||||
|
- name : qwant news
|
||||||
|
engine : qwant
|
||||||
|
shortcut : qwn
|
||||||
|
categories : news
|
||||||
|
|
||||||
|
- name : qwant social
|
||||||
|
engine : qwant
|
||||||
|
shortcut : qws
|
||||||
|
categories : social media
|
||||||
|
|
||||||
|
- name : reddit
|
||||||
|
engine : reddit
|
||||||
|
shortcut : re
|
||||||
|
page_size : 25
|
||||||
|
timeout : 10.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : scanr structures
|
||||||
|
shortcut: scs
|
||||||
|
engine : scanr_structures
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : soundcloud
|
||||||
|
engine : soundcloud
|
||||||
|
shortcut : sc
|
||||||
|
|
||||||
|
- name : stackoverflow
|
||||||
|
engine : stackoverflow
|
||||||
|
shortcut : st
|
||||||
|
|
||||||
|
- name : searchcode doc
|
||||||
|
engine : searchcode_doc
|
||||||
|
shortcut : scd
|
||||||
|
|
||||||
|
- name : searchcode code
|
||||||
|
engine : searchcode_code
|
||||||
|
shortcut : scc
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : framalibre
|
||||||
|
engine : framalibre
|
||||||
|
shortcut : frl
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
# - name : searx
|
||||||
|
# engine : searx_engine
|
||||||
|
# shortcut : se
|
||||||
|
# instance_urls :
|
||||||
|
# - http://127.0.0.1:8888/
|
||||||
|
# - ...
|
||||||
|
# disabled : True
|
||||||
|
|
||||||
|
- name : semantic scholar
|
||||||
|
engine : xpath
|
||||||
|
paging : True
|
||||||
|
search_url : https://www.semanticscholar.org/search?q={query}&sort=relevance&page={pageno}&ae=false
|
||||||
|
results_xpath : //article
|
||||||
|
url_xpath : .//div[@class="search-result-title"]/a/@href
|
||||||
|
title_xpath : .//div[@class="search-result-title"]/a
|
||||||
|
content_xpath : .//div[@class="search-result-abstract"]
|
||||||
|
shortcut : se
|
||||||
|
categories : science
|
||||||
|
|
||||||
|
- name : spotify
|
||||||
|
engine : spotify
|
||||||
|
shortcut : stf
|
||||||
|
|
||||||
|
- name : subtitleseeker
|
||||||
|
engine : subtitleseeker
|
||||||
|
shortcut : ss
|
||||||
|
# The language is an option. You can put any language written in english
|
||||||
|
# Examples : English, French, German, Hungarian, Chinese...
|
||||||
|
# language : English
|
||||||
|
|
||||||
|
- name : startpage
|
||||||
|
engine : startpage
|
||||||
|
shortcut : sp
|
||||||
|
timeout : 6.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : ixquick
|
||||||
|
engine : startpage
|
||||||
|
base_url : 'https://www.ixquick.eu/'
|
||||||
|
search_url : 'https://www.ixquick.eu/do/search'
|
||||||
|
shortcut : iq
|
||||||
|
timeout : 6.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : swisscows
|
||||||
|
engine : swisscows
|
||||||
|
shortcut : sw
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : tokyotoshokan
|
||||||
|
engine : tokyotoshokan
|
||||||
|
shortcut : tt
|
||||||
|
timeout : 6.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : torrentz
|
||||||
|
engine : torrentz
|
||||||
|
shortcut : tor
|
||||||
|
url: https://torrentz2.eu/
|
||||||
|
timeout : 3.0
|
||||||
|
|
||||||
|
- name : twitter
|
||||||
|
engine : twitter
|
||||||
|
shortcut : tw
|
||||||
|
|
||||||
|
# maybe in a fun category
|
||||||
|
# - name : uncyclopedia
|
||||||
|
# engine : mediawiki
|
||||||
|
# shortcut : unc
|
||||||
|
# base_url : https://uncyclopedia.wikia.com/
|
||||||
|
# number_of_results : 5
|
||||||
|
|
||||||
|
# tmp suspended - too slow, too many errors
|
||||||
|
# - name : urbandictionary
|
||||||
|
# engine : xpath
|
||||||
|
# search_url : http://www.urbandictionary.com/define.php?term={query}
|
||||||
|
# url_xpath : //*[@class="word"]/@href
|
||||||
|
# title_xpath : //*[@class="def-header"]
|
||||||
|
# content_xpath : //*[@class="meaning"]
|
||||||
|
# shortcut : ud
|
||||||
|
|
||||||
|
- name : yahoo
|
||||||
|
engine : yahoo
|
||||||
|
shortcut : yh
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : yandex
|
||||||
|
engine : yandex
|
||||||
|
shortcut : yn
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : yahoo news
|
||||||
|
engine : yahoo_news
|
||||||
|
shortcut : yhn
|
||||||
|
|
||||||
|
- name : youtube
|
||||||
|
shortcut : yt
|
||||||
|
api_key: 'AIzaSyDvEpB_xVEk3Xt0IIU8sXbyEGIdjf33CEM'
|
||||||
|
engine : youtube_api
|
||||||
|
|
||||||
|
- name : dailymotion
|
||||||
|
engine : dailymotion
|
||||||
|
shortcut : dm
|
||||||
|
|
||||||
|
- name : vimeo
|
||||||
|
engine : vimeo
|
||||||
|
shortcut : vm
|
||||||
|
|
||||||
|
- name : wolframalpha
|
||||||
|
shortcut : wa
|
||||||
|
api_key: 'AUQ8EY-H452ETQ7RL'
|
||||||
|
engine : wolframalpha_api
|
||||||
|
timeout: 6.0
|
||||||
|
categories : science
|
||||||
|
|
||||||
|
- name : seedpeer
|
||||||
|
engine : seedpeer
|
||||||
|
shortcut: speu
|
||||||
|
categories: files, music, videos
|
||||||
|
disabled: True
|
||||||
|
|
||||||
|
- name : dictzone
|
||||||
|
engine : dictzone
|
||||||
|
shortcut : dc
|
||||||
|
|
||||||
|
- name : mymemory translated
|
||||||
|
engine : translated
|
||||||
|
shortcut : tl
|
||||||
|
timeout : 5.0
|
||||||
|
disabled : True
|
||||||
|
# You can use without an API key, but you are limited to 1000 words/day
|
||||||
|
# See : http://mymemory.translated.net/doc/usagelimits.php
|
||||||
|
# api_key : ''
|
||||||
|
|
||||||
|
- name : voat
|
||||||
|
engine: xpath
|
||||||
|
shortcut: vo
|
||||||
|
categories: social media
|
||||||
|
search_url : https://searchvoat.co/?t={query}
|
||||||
|
url_xpath : //div[@class="entry"]/p/a[@class="title"]/@href
|
||||||
|
title_xpath : //div[@class="entry"]/p/a[@class="title"]
|
||||||
|
content_xpath : //div[@class="entry"]/p/span[@class="domain"]
|
||||||
|
timeout : 10.0
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : 1337x
|
||||||
|
engine : 1337x
|
||||||
|
shortcut : 1337x
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
- name : seznam
|
||||||
|
shortcut: szn
|
||||||
|
engine: xpath
|
||||||
|
paging : True
|
||||||
|
search_url : https://search.seznam.cz/?q={query}&count=10&from={pageno}
|
||||||
|
results_xpath: //div[@class="Page-content"]//div[@class="Result "]
|
||||||
|
url_xpath : ./h3/a/@href
|
||||||
|
title_xpath : ./h3
|
||||||
|
content_xpath : .//p[@class="Result-description"]
|
||||||
|
first_page_num : 0
|
||||||
|
page_size : 10
|
||||||
|
disabled : True
|
||||||
|
|
||||||
|
# - name : yacy
|
||||||
|
# engine : yacy
|
||||||
|
# shortcut : ya
|
||||||
|
# base_url : 'http://localhost:8090'
|
||||||
|
# number_of_results : 5
|
||||||
|
# timeout : 3.0
|
||||||
|
|
||||||
|
# Doku engine lets you access to any Doku wiki instance:
|
||||||
|
# A public one or a privete/corporate one.
|
||||||
|
# - name : ubuntuwiki
|
||||||
|
# engine : doku
|
||||||
|
# shortcut : uw
|
||||||
|
# base_url : 'http://doc.ubuntu-fr.org'
|
||||||
|
|
||||||
|
locales:
|
||||||
|
en : English
|
||||||
|
ar : العَرَبِيَّة (Arabic)
|
||||||
|
bg : Български (Bulgarian)
|
||||||
|
cs : Čeština (Czech)
|
||||||
|
da : Dansk (Danish)
|
||||||
|
de : Deutsch (German)
|
||||||
|
el_GR : Ελληνικά (Greek_Greece)
|
||||||
|
eo : Esperanto (Esperanto)
|
||||||
|
es : Español (Spanish)
|
||||||
|
fi : Suomi (Finnish)
|
||||||
|
fil : Wikang Filipino (Filipino)
|
||||||
|
fr : Français (French)
|
||||||
|
he : עברית (Hebrew)
|
||||||
|
hr : Hrvatski (Croatian)
|
||||||
|
hu : Magyar (Hungarian)
|
||||||
|
it : Italiano (Italian)
|
||||||
|
ja : 日本語 (Japanese)
|
||||||
|
nl : Nederlands (Dutch)
|
||||||
|
pl : Polski (Polish)
|
||||||
|
pt : Português (Portuguese)
|
||||||
|
pt_BR : Português (Portuguese_Brazil)
|
||||||
|
ro : Română (Romanian)
|
||||||
|
ru : Русский (Russian)
|
||||||
|
sk : Slovenčina (Slovak)
|
||||||
|
sl : Slovenski (Slovene)
|
||||||
|
sr : српски (Serbian)
|
||||||
|
sv : Svenska (Swedish)
|
||||||
|
tr : Türkçe (Turkish)
|
||||||
|
uk : українська мова (Ukrainian)
|
||||||
|
zh : 中文 (Chinese)
|
||||||
|
zh_TW : 國語 (Taiwanese Mandarin)
|
||||||
|
|
||||||
|
doi_resolvers :
|
||||||
|
oadoi.org : 'https://oadoi.org/'
|
||||||
|
doi.org : 'https://doi.org/'
|
||||||
|
doai.io : 'https://doai.io/'
|
||||||
|
sci-hub.tw : 'https://sci-hub.tw/'
|
||||||
|
|
||||||
|
default_doi_resolver : 'sci-hub.tw'
|
@ -7,28 +7,20 @@
|
|||||||
./packages.nix
|
./packages.nix
|
||||||
./jobs.nix
|
./jobs.nix
|
||||||
./matrix.nix
|
./matrix.nix
|
||||||
./email.nix
|
|
||||||
./magnetico.nix
|
./magnetico.nix
|
||||||
./nameserver.nix
|
./nameserver.nix
|
||||||
./custom
|
./custom
|
||||||
./secrets
|
./secrets
|
||||||
./fish.nix
|
|
||||||
./neovim.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
### State
|
### State
|
||||||
# Stateful things to do before updating:
|
# Stateful things to do before updating:
|
||||||
# 1. Postgres migration (https://www.postgresql.org/docs/current/upgrading.html)
|
# 1. Postgres migration
|
||||||
# 2. Matrix Synapse migration (https://matrix-org.github.io/synapse/latest/upgrade.html)
|
# 2. Matrix Synapse migration
|
||||||
system.stateVersion = "23.05";
|
system.stateVersion = "20.03";
|
||||||
|
|
||||||
nixpkgs.source = builtins.fetchTarball
|
|
||||||
{ url = "https://github.com/NixOS/nixpkgs/archive/3f0a8ac25fb6.tar.gz";
|
|
||||||
sha256 = "10i7fllqjzq171afzhdf2d9r1pk9irvmq5n55h92rc47vlaabvr4";
|
|
||||||
};
|
|
||||||
|
|
||||||
boot.kernelPackages = pkgs.linuxPackages_latest;
|
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||||
boot.tmp.useTmpfs = true;
|
boot.tmpOnTmpfs = true;
|
||||||
boot.kernel.sysctl = {
|
boot.kernel.sysctl = {
|
||||||
# avoid OOM hangs
|
# avoid OOM hangs
|
||||||
"vm.admin_reserve_kbytes" = 262144;
|
"vm.admin_reserve_kbytes" = 262144;
|
||||||
@ -38,41 +30,34 @@
|
|||||||
i18n.defaultLocale = "en_US.UTF-8";
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
systemd.enableEmergencyMode = false;
|
systemd.enableEmergencyMode = false;
|
||||||
systemd.oomd.enable = false;
|
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
hostName = "maxwell";
|
hostName = "maxwell";
|
||||||
|
|
||||||
firewall.allowedTCPPorts = [
|
firewall.allowedTCPPorts = [
|
||||||
53 # dns
|
|
||||||
443 80 # reverse proxy
|
443 80 # reverse proxy
|
||||||
993 # imaps server
|
8080 # hubot
|
||||||
25 465 # smtp(s) server
|
5349 # turn server
|
||||||
|
5350 # turn server
|
||||||
|
3551 # apcups
|
||||||
|
5001 # iperf server
|
||||||
18080 # monero p2p
|
18080 # monero p2p
|
||||||
22000 # syncthing transfer
|
20000 # syncthing transfert
|
||||||
64738 # mumble server
|
64738 # mumble server
|
||||||
];
|
];
|
||||||
firewall.allowedUDPPorts = [
|
firewall.allowedUDPPorts = [
|
||||||
500 # ipsec
|
53 # powerdns
|
||||||
53 # dns
|
1194 # dnscrypt
|
||||||
21027 # syncthing discovery
|
21027 # syncthing discovery
|
||||||
64738 # mumble server
|
64738 # mumble server
|
||||||
];
|
];
|
||||||
|
firewall.allowedUDPPortRanges = [
|
||||||
nftables.enable = true;
|
{ from=49152; to=49999; } # turn relay
|
||||||
firewall.extraInputRules = ''
|
];
|
||||||
meta l4proto esp counter accept comment "allow IPsec"
|
|
||||||
ip saddr 192.168.1.0/24 tcp dport apcupsd accept comment "allow UPS from LAN"
|
|
||||||
'';
|
|
||||||
|
|
||||||
usePredictableInterfaceNames = false;
|
usePredictableInterfaceNames = false;
|
||||||
nameservers = [ "127.0.0.1" ];
|
nameservers = [ "127.0.0.1" ];
|
||||||
|
hosts."127.0.0.1" = [ config.var.hostname ];
|
||||||
# ensure hostname works without DNS
|
|
||||||
hosts = with config.var;
|
|
||||||
{ ${ipv4LanAddress} = [ hostname ];
|
|
||||||
${ipv6Address} = [ hostname ];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Only declarative users and no password logins
|
# Only declarative users and no password logins
|
||||||
@ -80,7 +65,7 @@
|
|||||||
|
|
||||||
users.users ={
|
users.users ={
|
||||||
# Only needed for local (read emergency) shell access
|
# Only needed for local (read emergency) shell access
|
||||||
root.hashedPasswordFile = config.secrets.passwords.root;
|
root.passwordFile = config.secrets.passwords.root;
|
||||||
|
|
||||||
# Admin
|
# Admin
|
||||||
rnhmjoj = {
|
rnhmjoj = {
|
||||||
@ -95,11 +80,12 @@
|
|||||||
fazo = {
|
fazo = {
|
||||||
extraGroups = [ "wheel" ];
|
extraGroups = [ "wheel" ];
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.fazo ];
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.fazo];
|
||||||
};
|
};
|
||||||
|
|
||||||
# User
|
# Runs two chatbots
|
||||||
meme = {
|
meme = {
|
||||||
|
extraGroups = [ "ubino" "miguelbridge" ];
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
shell = pkgs.fish;
|
shell = pkgs.fish;
|
||||||
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.meme ];
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.meme ];
|
||||||
@ -117,26 +103,19 @@
|
|||||||
builder = {
|
builder = {
|
||||||
description = "Remote Nix builds user";
|
description = "Remote Nix builds user";
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
openssh.authorizedKeys.keyFiles = with config.secrets.publicKeys; [
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.rnhmjoj-builder ];
|
||||||
rnhmjoj-builder
|
|
||||||
giu-builder
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Use "git" instead of the default name to make
|
# Use "git" instead of the default name to make
|
||||||
# SSH operation handier, example:
|
# SSH operation handier, example:
|
||||||
# git clone git@maxwell:user/repo
|
# git clone git@maxwell:user/repo
|
||||||
git = {
|
git = {
|
||||||
group = "git";
|
|
||||||
description = "Git server user";
|
description = "Git server user";
|
||||||
home = "/var/lib/gitea";
|
home = "/var/lib/gitea";
|
||||||
isSystemUser = true;
|
|
||||||
useDefaultShell = true;
|
useDefaultShell = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups.git = { };
|
|
||||||
|
|
||||||
# Generate Diffie-Hellman parameters
|
# Generate Diffie-Hellman parameters
|
||||||
# for TLS applications, like nginx.
|
# for TLS applications, like nginx.
|
||||||
security.dhparams = {
|
security.dhparams = {
|
||||||
@ -148,57 +127,64 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
# Users don't have a password
|
# Users don't have a password
|
||||||
wheelNeedsPassword = false;
|
wheelNeedsPassword = false;
|
||||||
|
extraConfig =
|
||||||
|
let
|
||||||
|
path = "/run/current-system/sw/bin";
|
||||||
|
journal = name: "${path}/journalctl -* ${name}";
|
||||||
|
services = lib.concatMapStringsSep "," (name: "${journal name}");
|
||||||
|
in ''
|
||||||
|
# Allow meme to see his logs.
|
||||||
|
Cmnd_Alias MEME_UNITS = ${services ["ubino" "miguelbridge"]}
|
||||||
|
meme ALL=(root) NOPASSWD: MEME_UNITS
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
security.polkit.extraConfig = ''
|
||||||
|
// Allow meme to manage his services.
|
||||||
|
polkit.addRule(function(action, subject) {
|
||||||
|
if (action.id == "org.freedesktop.systemd1.manage-units" &&
|
||||||
|
subject.user == "meme" &&
|
||||||
|
(action.lookup("unit") == "ubino.service" ||
|
||||||
|
action.lookup("unit") == "miguelbridge.service")) {
|
||||||
|
return polkit.Result.YES;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Limit user process to stop fork bombs
|
||||||
security.pam.loginLimits = [
|
security.pam.loginLimits = [
|
||||||
# Limit user process to stop fork bombs
|
|
||||||
{ domain = "@users";
|
{ domain = "@users";
|
||||||
type = "hard";
|
type = "hard";
|
||||||
item = "nproc";
|
item = "nproc";
|
||||||
value = "400";
|
value = "400";
|
||||||
}
|
}
|
||||||
# Disable core dumping
|
|
||||||
{ domain = "*";
|
|
||||||
type = "soft";
|
|
||||||
item = "core";
|
|
||||||
value = "0";
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
### ACME certificates
|
### ACME certificates
|
||||||
security.acme = {
|
security.acme = with config.var; {
|
||||||
defaults.email = "rnhmjoj@inventati.org";
|
email = "rnhmjoj@inventati.org";
|
||||||
acceptTerms = true;
|
acceptTerms = true;
|
||||||
|
|
||||||
certs."maxwell.eurofusion.eu" = {
|
certs."${hostname}" = {
|
||||||
group = "maxwell-eurofusion-eu";
|
group = "maxwell-ydns-eu";
|
||||||
};
|
};
|
||||||
|
|
||||||
certs."eurofusion.eu" = {
|
certs."riot.${hostname}" = {
|
||||||
group = "eurofusion-eu";
|
group = "riot-maxwell-ydns-eu";
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Allow read access to ACME certificate
|
# Allow read access to ACME certificate
|
||||||
# to specific (service) users.
|
# to specific (service) users.
|
||||||
users.groups."maxwell-eurofusion-eu".members = [ "murmur" "nginx" ];
|
users.groups."maxwell-ydns-eu".members = [ "murmur" "turnserver" ];
|
||||||
users.groups."eurofusion-eu".members = [ "nginx" ];
|
users.groups."riot-maxwell-ydns-eu".members = [ "nginx" ];
|
||||||
|
|
||||||
|
|
||||||
# sensible logging
|
|
||||||
services.journald = {
|
|
||||||
storage = "volatile";
|
|
||||||
extraConfig = ''
|
|
||||||
RuntimeMaxUse=2G
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
services.openssh = {
|
services.openssh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings.PermitRootLogin = "no";
|
permitRootLogin = "no";
|
||||||
settings.PasswordAuthentication = false;
|
passwordAuthentication = false;
|
||||||
settings.KbdInteractiveAuthentication = false;
|
challengeResponseAuthentication = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Traceroute easter egg
|
# Traceroute easter egg
|
||||||
@ -215,19 +201,21 @@
|
|||||||
### Mumble server
|
### Mumble server
|
||||||
services.murmur = {
|
services.murmur = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
password = "allwellthatmaxwell";
|
||||||
registerHostname = config.var.hostname;
|
registerHostname = config.var.hostname;
|
||||||
registerName = "Maxwell Mumble";
|
registerName = "Maxwell Mumble";
|
||||||
registerPassword = "$REG_PASSWORD";
|
registerPassword = config.secrets.murmur.password;
|
||||||
password = "$JOIN_PASSWORD";
|
|
||||||
users = 10;
|
users = 10;
|
||||||
environmentFile = config.secrets.environments.murmur;
|
extraConfig = with config.var; ''
|
||||||
sslCert = "/var/lib/acme/${config.var.hostname}/fullchain.pem";
|
sslCert=/var/lib/acme/${hostname}/fullchain.pem
|
||||||
sslKey = "/var/lib/acme/${config.var.hostname}/key.pem";
|
sslKey=/var/lib/acme/${hostname}/key.pem
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
### Syncthing node
|
### Syncthing node
|
||||||
services.syncthing = {
|
services.syncthing = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
openDefaultPorts = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
### Monero node with local RPC
|
### Monero node with local RPC
|
||||||
@ -260,88 +248,26 @@
|
|||||||
### Git server
|
### Git server
|
||||||
services.gitea = with config.var; {
|
services.gitea = with config.var; {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
domain = hostname;
|
||||||
appName = "Maxwell git server";
|
appName = "Maxwell git server";
|
||||||
|
rootUrl = "https://${hostname}/git/";
|
||||||
user = "git";
|
user = "git";
|
||||||
database.user = "git";
|
database.user = "git";
|
||||||
|
log.level = "Error";
|
||||||
|
cookieSecure = true;
|
||||||
|
disableRegistration = false;
|
||||||
settings = {
|
settings = {
|
||||||
server.ROOT_URL = "https://${hostname}/git/";
|
|
||||||
server.domain = hostname;
|
|
||||||
|
|
||||||
session.COOKIE_SECURE = true;
|
|
||||||
log.LEVEL = "Error";
|
|
||||||
service.DISABLE_REGISTRATION = false;
|
|
||||||
|
|
||||||
# increase cookie expiration time
|
|
||||||
security.LOGIN_REMEMBER_DAYS = 365;
|
security.LOGIN_REMEMBER_DAYS = 365;
|
||||||
|
|
||||||
# file upload size (MB)
|
|
||||||
attachment.MAX_SIZE = 10;
|
attachment.MAX_SIZE = 10;
|
||||||
|
|
||||||
# new users can only create PR/issues
|
|
||||||
service.DEFAULT_ALLOW_CREATE_ORGANIZATION = false;
|
|
||||||
repository.MAX_CREATION_LIMIT = 0;
|
|
||||||
|
|
||||||
# somewhat limit spam
|
|
||||||
service.EMAIL_DOMAIN_BLOCKLIST = "gmail.com";
|
|
||||||
|
|
||||||
# allow the notify webhook to use matrix
|
|
||||||
webhook.ALLOWED_HOST_LIST = "maxwell.eurofusion.eu";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
### Searx instance
|
### Searx instance
|
||||||
services.searx = {
|
services.searx = {
|
||||||
enable = true;
|
enable = true;
|
||||||
environmentFile = config.secrets.environments.searx;
|
configFile = ./assets/searx-settings.yml;
|
||||||
package = pkgs.searxng;
|
|
||||||
|
|
||||||
# Use nginx+uWSGI
|
|
||||||
runInUwsgi = true;
|
|
||||||
uwsgiConfig = {
|
|
||||||
disable-logging = true;
|
|
||||||
|
|
||||||
# serve using the uwsgi protocol
|
|
||||||
socket = "/run/searx/uwsgi.sock";
|
|
||||||
chmod-socket = "660";
|
|
||||||
|
|
||||||
# use /searx as url "mountpoint"
|
|
||||||
mount = "/srx=searx.webapp:application";
|
|
||||||
module = "";
|
|
||||||
manage-script-name = true;
|
|
||||||
|
|
||||||
# caching
|
|
||||||
cache2 = lib.concatStringsSep ","
|
|
||||||
[ "name=searxcache"
|
|
||||||
"items=2000"
|
|
||||||
"blocks=2000"
|
|
||||||
"blocksize=4096"
|
|
||||||
"bitmap=1"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
settings =
|
|
||||||
{ general.instance_name = "searxwell";
|
|
||||||
server.base_url = "https://${config.var.hostname}/";
|
|
||||||
server.secret_key = "@SEARX_SECRET@";
|
|
||||||
|
|
||||||
# Replace DOI links with Sci-Hub
|
|
||||||
default_doi_resolver = "sci-hub.st";
|
|
||||||
|
|
||||||
## Use authenticated APIs for some services
|
|
||||||
engines = [
|
|
||||||
{ name = "wolframalpha";
|
|
||||||
api_key = "@WOLFRAM_API_KEY@";
|
|
||||||
}
|
|
||||||
{ name = "youtube";
|
|
||||||
api_key = "@YOUTUBE_API_KEY@";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Allow nginx access to the uwsgi socket
|
|
||||||
users.groups."searx".members = [ "nginx" ];
|
|
||||||
|
|
||||||
|
|
||||||
### Reverse Proxy
|
### Reverse Proxy
|
||||||
services.nginx =
|
services.nginx =
|
||||||
@ -355,13 +281,21 @@
|
|||||||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
rec {
|
{
|
||||||
enable = true;
|
enable = true;
|
||||||
enableReload = true;
|
enableReload = true;
|
||||||
recommendedTlsSettings = true;
|
commonHttpConfig = ''
|
||||||
|
# recommendedTlsSettings = true;
|
||||||
|
# android doesn't like this one:
|
||||||
|
# ssl_ecdh_curve secp384r1;
|
||||||
|
ssl_session_cache shared:SSL:42m;
|
||||||
|
ssl_session_timeout 23m;
|
||||||
|
ssl_prefer_server_ciphers on;
|
||||||
|
ssl_stapling on;
|
||||||
|
ssl_stapling_verify on;
|
||||||
|
'';
|
||||||
recommendedGzipSettings = true;
|
recommendedGzipSettings = true;
|
||||||
recommendedProxySettings = true;
|
recommendedProxySettings = true;
|
||||||
appendHttpConfig = disableLog;
|
|
||||||
|
|
||||||
# Large enough to allow file uploads.
|
# Large enough to allow file uploads.
|
||||||
clientMaxBodySize = "1000M";
|
clientMaxBodySize = "1000M";
|
||||||
@ -369,51 +303,38 @@
|
|||||||
sslDhparam = "${config.security.dhparams.path}/nginx.pem";
|
sslDhparam = "${config.security.dhparams.path}/nginx.pem";
|
||||||
|
|
||||||
# Maxwell
|
# Maxwell
|
||||||
virtualHosts."${hostname}" = {
|
virtualHosts."${hostname}" =
|
||||||
enableACME = true;
|
{
|
||||||
forceSSL = true;
|
enableACME = true;
|
||||||
default = true;
|
forceSSL = true;
|
||||||
extraConfig = enableSTS;
|
default = true;
|
||||||
|
|
||||||
|
extraConfig = disableLog + enableSTS;
|
||||||
|
|
||||||
# Returns IP address
|
# Returns IP address
|
||||||
locations."/ip".extraConfig = ''
|
locations."/ip".extraConfig = "return 200 $remote_addr;";
|
||||||
default_type text/plain;
|
|
||||||
return 200 $remote_addr;
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Asjon code coverage reports
|
# Asjon code coverage reports
|
||||||
locations."/asjon/report/" = {
|
locations."/asjon/report/" = {
|
||||||
index = "index.html";
|
index = "index.html";
|
||||||
alias = "/run/nginx/static/asjon/";
|
alias = "/var/lib/asjon/tree/report/";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Searx instance
|
# Searx instance
|
||||||
locations."/srx/".extraConfig =
|
locations."/srx/" = {
|
||||||
''
|
proxyPass = "http://localhost:8083/";
|
||||||
include ${pkgs.nginx}/conf/uwsgi_params;
|
extraConfig = ''
|
||||||
uwsgi_pass unix:/run/searx/uwsgi.sock;
|
proxy_set_header X-Scheme $scheme;
|
||||||
|
proxy_set_header X-Script-Name /srx/;
|
||||||
|
proxy_buffering off;
|
||||||
'';
|
'';
|
||||||
locations."/srx/static/".alias = "${config.services.searx.package}/share/static/";
|
};
|
||||||
|
|
||||||
# Git server
|
# Git server
|
||||||
locations."/git/".proxyPass = "http://localhost:3000/";
|
locations."/git/" .proxyPass = "http://localhost:3000/";
|
||||||
|
|
||||||
# Syncthing
|
# Syncthing
|
||||||
locations."/sync/".proxyPass = "http://localhost:8384/";
|
locations."/sync/".proxyPass = "http://localhost:8384/";
|
||||||
|
|
||||||
# User static files
|
|
||||||
locations."/~rnhmjoj/" = {
|
|
||||||
alias = "/run/nginx/static/rnhmjoj/";
|
|
||||||
extraConfig = ''
|
|
||||||
charset UTF-8;
|
|
||||||
# directories with listing
|
|
||||||
location ~ /~rnhmjoj/[^/]+.index/ { autoindex on; }
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
locations."/~giu/" = {
|
|
||||||
alias = "/run/nginx/static/giu/";
|
|
||||||
extraConfig = "charset UTF-8;";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Breve URL shortner
|
# Breve URL shortner
|
||||||
@ -431,82 +352,87 @@
|
|||||||
|
|
||||||
# The Cactalogue
|
# The Cactalogue
|
||||||
virtualHosts."cacta.bit" = {
|
virtualHosts."cacta.bit" = {
|
||||||
root = "/run/nginx/static/cactalogue";
|
locations."/".alias = "/home/giu/cactalogue/";
|
||||||
extraConfig = disableLog;
|
extraConfig = disableLog;
|
||||||
};
|
};
|
||||||
virtualHosts."cacta.eurofusion.eu" = virtualHosts."cacta.bit";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Bind mount directories for Nginx
|
|
||||||
# This avoids giving nginx traversal permission
|
|
||||||
systemd.mounts =
|
|
||||||
let bindNginx = from: to:
|
|
||||||
{ what = from;
|
|
||||||
where = "/run/nginx/static/" + to;
|
|
||||||
type = "none";
|
|
||||||
options = "bind";
|
|
||||||
wantedBy = [ "nginx.service" ];
|
|
||||||
};
|
|
||||||
in [ (bindNginx "/home/rnhmjoj/www" "rnhmjoj")
|
|
||||||
(bindNginx "/home/giu/www" "giu")
|
|
||||||
(bindNginx "/home/giu/cactalogue" "cactalogue")
|
|
||||||
(bindNginx "/var/lib/asjon/tree/report" "asjon")
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
### IPsec mesh
|
|
||||||
|
|
||||||
environment.etc."ipsec.d/mesh.secrets".source = config.secrets.passwords.mesh;
|
|
||||||
|
|
||||||
services.libreswan.enable = true;
|
|
||||||
services.libreswan.connections.mesh =
|
|
||||||
''
|
|
||||||
leftid=@wes
|
|
||||||
left=2a01:e11:1001:53ea::1
|
|
||||||
rightid=@maxwell
|
|
||||||
right=${config.var.ipv6Address}
|
|
||||||
authby=secret
|
|
||||||
type=transport
|
|
||||||
|
|
||||||
auto=ondemand
|
|
||||||
failureshunt=drop
|
|
||||||
negotiationshunt=hold
|
|
||||||
'';
|
|
||||||
|
|
||||||
|
|
||||||
### Misc. services
|
### Misc. services
|
||||||
|
services.ubino.enable = true;
|
||||||
|
services.miguelbridge.enable = true;
|
||||||
services.asjon.enable = true;
|
services.asjon.enable = true;
|
||||||
|
|
||||||
# Needed for the Asjon memory module
|
# Needed for the Asjon memory module
|
||||||
services.redis.servers."asjon" =
|
services.redis.enable = true;
|
||||||
{ enable = true;
|
|
||||||
user = "asjon";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Emergency SSH access via tor
|
|
||||||
services.tor =
|
|
||||||
{ enable = true;
|
|
||||||
client.enable = false;
|
|
||||||
relay.onionServices.emergency-access.map = [ 22 ];
|
|
||||||
};
|
|
||||||
|
|
||||||
nix.settings = {
|
### Program configuration
|
||||||
# Can connect to the Nix daemon
|
programs = {
|
||||||
# and upload/run code as root!
|
fish.enable = true;
|
||||||
trusted-users = [ "builder" "rnhmjoj" ];
|
mosh.enable = true;
|
||||||
# Use at most half the cores
|
tmux = {
|
||||||
cores = 8;
|
enable = true;
|
||||||
max-jobs = 16;
|
newSession = true;
|
||||||
# Always keep at least 256MiB free
|
baseIndex = 1;
|
||||||
min-free = 268435456;
|
escapeTime = 0;
|
||||||
|
historyLimit = 4096;
|
||||||
|
keyMode = "vi";
|
||||||
|
terminal = "screen-256color";
|
||||||
|
customPaneNavigationAndResize = true;
|
||||||
|
extraConfig = ''
|
||||||
|
set -g mouse on
|
||||||
|
|
||||||
|
# bindings
|
||||||
|
bind | split-window -h
|
||||||
|
bind - split-window -v
|
||||||
|
bind : command-prompt
|
||||||
|
bind -n C-k clear-history
|
||||||
|
|
||||||
|
# colors
|
||||||
|
set -g pane-border-style fg=brightblack
|
||||||
|
set -g pane-active-border fg=green
|
||||||
|
set -g message-style fg=white,bg=black
|
||||||
|
set -g status-style fg=brightblue,bg=black
|
||||||
|
setw -g mode-style fg=black,bg=cyan
|
||||||
|
|
||||||
|
# status line
|
||||||
|
set -g status on
|
||||||
|
set -g status-justify left
|
||||||
|
set -g status-left ""
|
||||||
|
set -g status-right-length 60
|
||||||
|
set -g status-right '#[fg=yellow]#(cut -d\ -f 1-3 /proc/loadavg) | #[fg=brightgreen]%a %H:%M'
|
||||||
|
setw -g window-status-format "#[fg=black#,bg=brightblack] #I #[fg=blue#,bg=black] #W "
|
||||||
|
setw -g window-status-current-format "#[fg=white#,bg=cyan] #I #[fg=black#,bg=brightblack] #W "
|
||||||
|
'';
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.sessionVariables = {
|
nix = {
|
||||||
PATH = [ "$HOME/bin" ];
|
useSandbox = true;
|
||||||
XDG_CONFIG_HOME = "$HOME/etc";
|
# Can connect to the Nix daemon
|
||||||
XDG_DATA_HOME = "$HOME/var/lib";
|
# and upload/run code as root!
|
||||||
XDG_CACHE_HOME = "$HOME/var/cache";
|
trustedUsers = [ "builder" "rnhmjoj" ];
|
||||||
SYSTEMD_COLORS = "16";
|
# Use at most half the cores
|
||||||
|
buildCores = 8;
|
||||||
|
extraOptions = ''
|
||||||
|
# Always keep at least 256MiB free
|
||||||
|
min-free = 268435456
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
environment.variables = {
|
||||||
|
PATH = "$HOME/.local/bin/:$PATH";
|
||||||
|
XDG_CONFIG_HOME = "$HOME/.config";
|
||||||
|
XDG_DATA_HOME = "$HOME/.local/share";
|
||||||
|
XDG_CACHE_HOME = "$HOME/.cache";
|
||||||
|
NIX_PROFILE = "$XDG_CONFIG_HOME/nix/profile";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Needed to make the mosh server survive a
|
||||||
|
# user logout: systemd kills everything by default
|
||||||
|
environment.shellAliases = {
|
||||||
|
mosh-server = "systemd-run --user --scope mosh-server";
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,11 @@
|
|||||||
[ # Misc. system services
|
[ # Misc. system services
|
||||||
./modules/breve.nix
|
./modules/breve.nix
|
||||||
./modules/asjon.nix
|
./modules/asjon.nix
|
||||||
|
./modules/ubino.nix
|
||||||
|
./modules/miguelbridge.nix
|
||||||
|
|
||||||
# Safely handle secrets
|
# Safely handle secrets
|
||||||
./modules/secrets-store.nix
|
./modules/secrets-store.nix
|
||||||
|
|
||||||
# Pin Nixpkgs
|
|
||||||
./modules/nixpkgs.nix
|
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,33 +27,21 @@ in {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
group = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "asjon";
|
|
||||||
description = ''
|
|
||||||
Asjon will be run under this group (user will be created if it doesn't exist.
|
|
||||||
This can be your user name).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
users.users.${cfg.user} = {
|
users.extraUsers."${cfg.user}" = {
|
||||||
group = cfg.group;
|
|
||||||
home = cfg.dataDir;
|
home = cfg.dataDir;
|
||||||
isSystemUser = true;
|
|
||||||
createHome = true;
|
createHome = true;
|
||||||
description = "asjon user";
|
description = "asjon user";
|
||||||
shell = "${pkgs.bash}/bin/bash";
|
shell = "${pkgs.bash}/bin/bash";
|
||||||
};
|
};
|
||||||
users.groups.${cfg.group} = { };
|
|
||||||
|
|
||||||
systemd.services.asjon = {
|
systemd.services.asjon = {
|
||||||
description = "asjon: our chat bot";
|
description = "asjon: our chat bot";
|
||||||
after = [ "nginx.service" "matrix-synapse.service" "asjon-init.service" ];
|
after = [ "nginx.service" "matrix-synapse.service" "asjon-init.service" ];
|
||||||
partOf = [ "nginx.service" "matrix-synapse.service" "asjon-init.service" ];
|
requires = [ "nginx.service" "matrix-synapse.service" "asjon-init.service" ];
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
path = with pkgs; [
|
path = with pkgs; [
|
||||||
@ -73,12 +61,11 @@ in {
|
|||||||
|
|
||||||
# Scripts
|
# Scripts
|
||||||
AUTO_KILL_ON_UPDATE = "1";
|
AUTO_KILL_ON_UPDATE = "1";
|
||||||
AUTO_INFORM_ON_START = "!XQJXsOXfTevAiEbDTA:eurofusion.eu";
|
AUTO_INFORM_ON_START = "!kvLvoCovzInhiablSq:maxwell.ydns.eu";
|
||||||
ADMIN_ROOM = "!XQJXsOXfTevAiEbDTA:eurofusion.eu";
|
ADMIN_ROOM = "!kvLvoCovzInhiablSq:maxwell.ydns.eu";
|
||||||
REV_REMOTE_HOST = "proxy@rnhmjoj.ydns.eu";
|
REV_REMOTE_HOST = "proxy@rnhmjoj.ydns.eu";
|
||||||
REV_REMOTE_PORT = "22";
|
REV_REMOTE_PORT = "22";
|
||||||
REV_KEY = "~/.ssh/proxy";
|
REV_KEY = "~/.ssh/proxy";
|
||||||
REDIS_URL = "redis:///run/redis-asjon/redis.sock";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
@ -87,7 +74,7 @@ in {
|
|||||||
Restart = "always";
|
Restart = "always";
|
||||||
WorkingDirectory = "${cfg.dataDir}/tree";
|
WorkingDirectory = "${cfg.dataDir}/tree";
|
||||||
# API keys and passwords definitions
|
# API keys and passwords definitions
|
||||||
EnvironmentFile = config.secrets.environments.asjon;
|
EnvironmentFile = config.secrets.asjon.environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -109,6 +96,11 @@ in {
|
|||||||
git clone https://github.com/rnhmjoj/asjon.git ${cfg.dataDir}/tree
|
git clone https://github.com/rnhmjoj/asjon.git ${cfg.dataDir}/tree
|
||||||
cd ${cfg.dataDir}/tree
|
cd ${cfg.dataDir}/tree
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
|
# give read/traverse permission to nginx
|
||||||
|
setfacl -m g:nginx:x ${cfg.dataDir}
|
||||||
|
setfacl -m g:nginx:x ${cfg.dataDir}/tree
|
||||||
|
setfacl -Rdm g:nginx:rx ${cfg.dataDir}/tree/report
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -44,15 +44,6 @@ in {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
group = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "breve";
|
|
||||||
description = ''
|
|
||||||
Breve will run under this group (user will be created if it doesn't exist.
|
|
||||||
This can be your user name).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
hostname = mkOption {
|
hostname = mkOption {
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = config.networking.hostName;
|
default = config.networking.hostName;
|
||||||
@ -107,14 +98,11 @@ in {
|
|||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
users.users.${cfg.user} = {
|
users.extraUsers."${cfg.user}" = {
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
group = cfg.group;
|
|
||||||
description = "Breve daemon user";
|
description = "Breve daemon user";
|
||||||
};
|
};
|
||||||
|
|
||||||
users.groups.${cfg.group} = {};
|
|
||||||
|
|
||||||
networking.firewall = mkIf cfg.openPorts {
|
networking.firewall = mkIf cfg.openPorts {
|
||||||
allowedTCPPorts = [ cfg.port ]
|
allowedTCPPorts = [ cfg.port ]
|
||||||
++ optional (cfg.port == 443) 80;
|
++ optional (cfg.port == 443) 80;
|
||||||
@ -128,7 +116,6 @@ in {
|
|||||||
environment.XDG_CONFIG_HOME = "${dataDir}/conf";
|
environment.XDG_CONFIG_HOME = "${dataDir}/conf";
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
User = cfg.user;
|
User = cfg.user;
|
||||||
Group = cfg.group;
|
|
||||||
ExecStart = "${pkgs.haskellPackages.breve}/bin/breve";
|
ExecStart = "${pkgs.haskellPackages.breve}/bin/breve";
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
StateDirectory = "breve";
|
StateDirectory = "breve";
|
||||||
|
52
custom/modules/miguelbridge.nix
Normal file
52
custom/modules/miguelbridge.nix
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.miguelbridge;
|
||||||
|
|
||||||
|
in {
|
||||||
|
|
||||||
|
options.services.miguelbridge = {
|
||||||
|
enable = mkEnableOption "miguelbridge: Bridge Telegram - Matrix.";
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "miguelbridge";
|
||||||
|
description = ''
|
||||||
|
miguelbridge will be run under this user (user will be created if it doesn't exist.
|
||||||
|
This can be your user name).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
|
users.groups.miguelbridge = {};
|
||||||
|
|
||||||
|
users.extraUsers."${cfg.user}" = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "miguelbridge";
|
||||||
|
description = "miguelbridge user";
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.miguelbridge = {
|
||||||
|
description = "miguelbridge: Bridge Telegram - Matrix";
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
User = cfg.user;
|
||||||
|
Group = "miguelbridge";
|
||||||
|
ExecStart = "${pkgs.openjdk}/bin/java -jar MiguelBridge.jar";
|
||||||
|
Restart = "always";
|
||||||
|
StateDirectory = "miguelbridge";
|
||||||
|
WorkingDirectory = "%S/miguelbridge";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -1,39 +0,0 @@
|
|||||||
{ config, lib, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
nixpkgs = config.nixpkgs.source;
|
|
||||||
|
|
||||||
conf = "${toString ../..}/configuration.nix";
|
|
||||||
|
|
||||||
rebuild = self: super:
|
|
||||||
{ nixos-rebuild = super.nixos-rebuild.overrideAttrs (old:
|
|
||||||
{ postInstall = old.postInstall +
|
|
||||||
''
|
|
||||||
sed -i "$target" \
|
|
||||||
-e '/^export PATH/ a \
|
|
||||||
export NIX_PATH="nixpkgs=${nixpkgs}:nixos-config=${conf}"' \
|
|
||||||
-e 's/remoteSudo=/remoteSudo=1/' \
|
|
||||||
-e 's/-A system/-A system --no-out-link/'
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
options.nixpkgs.source = lib.mkOption
|
|
||||||
{ type = lib.types.path;
|
|
||||||
description = "Nixpkgs sources";
|
|
||||||
};
|
|
||||||
|
|
||||||
config =
|
|
||||||
{ nixpkgs.overlays = [ rebuild ];
|
|
||||||
nix.nixPath =
|
|
||||||
[ "nixpkgs=/run/current-system/nixpkgs"
|
|
||||||
"nixos-config=${conf}"
|
|
||||||
];
|
|
||||||
system.extraSystemBuilderCmds = "ln -s ${nixpkgs} $out/nixpkgs";
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -5,8 +5,6 @@ with lib;
|
|||||||
let
|
let
|
||||||
cfg = config.security.runtimeSecrets;
|
cfg = config.security.runtimeSecrets;
|
||||||
|
|
||||||
secretsStore = "/var/secrets";
|
|
||||||
|
|
||||||
# A recursive attrset of submodule
|
# A recursive attrset of submodule
|
||||||
storeType = types.attrsOf (types.submodule
|
storeType = types.attrsOf (types.submodule
|
||||||
{ freeformType = storeType;
|
{ freeformType = storeType;
|
||||||
@ -51,24 +49,23 @@ let
|
|||||||
let index = name: value:
|
let index = name: value:
|
||||||
if isAttrs value && cond value
|
if isAttrs value && cond value
|
||||||
then recurse (path ++ [name]) value
|
then recurse (path ++ [name]) value
|
||||||
else singleton { loc = path ++ [name]; value = value; };
|
else singleton { path = path ++ [name]; value = value; };
|
||||||
in concatLists (mapAttrsToList index set);
|
in concatLists (mapAttrsToList index set);
|
||||||
in recurse [] set;
|
in recurse [] set;
|
||||||
|
|
||||||
isFile = v: isAttrs v && v.path != "";
|
isFile = v: isAttrs v && v.path != "";
|
||||||
|
|
||||||
# Secret files flattened to an index. This is needed
|
# Secret files flattened to an index. This is needed
|
||||||
# to iterate over the set. It contains: {name, path, value}
|
# to iterate over the set.
|
||||||
secretFiles =
|
secretFiles =
|
||||||
(map (x: x // { name = concatStringsSep "-" x.loc; })
|
filter (pair: isFile pair.value)
|
||||||
(filter (pair: isFile pair.value)
|
(attrsToIndex (v: !isFile v) cfg);
|
||||||
(attrsToIndex (v: !isFile v) cfg)));
|
|
||||||
|
|
||||||
# Secrets with paths rewritten to the store location
|
# Secrets with paths rewritten to the store location
|
||||||
storedSecrets = mapAttrsRecursiveCond (v: !isFile v)
|
storedSecrets = mapAttrsRecursiveCond (v: !isFile v)
|
||||||
(names: secret:
|
(names: secret:
|
||||||
if isFile secret
|
if isFile secret
|
||||||
then "${secretsStore}/${concatStringsSep "-" names}"
|
then "/run/secrets/${concatStringsSep "-" names}"
|
||||||
else secret) cfg;
|
else secret) cfg;
|
||||||
|
|
||||||
in {
|
in {
|
||||||
@ -79,7 +76,7 @@ in {
|
|||||||
Definitions of runtime secrets. This is a freeform attributes
|
Definitions of runtime secrets. This is a freeform attributes
|
||||||
set: it can contain arbitrarily nested sets of secrets.
|
set: it can contain arbitrarily nested sets of secrets.
|
||||||
Secrets are paths to be copied into the secrets store
|
Secrets are paths to be copied into the secrets store
|
||||||
(${secretsStore}) with proper permission and ownership.
|
(/run/secrets) with proper permission and owenership.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -112,28 +109,33 @@ in {
|
|||||||
deps = [ ];
|
deps = [ ];
|
||||||
text =
|
text =
|
||||||
''
|
''
|
||||||
secret=${(head secretFiles).value.path}
|
echo setting up secrets store...
|
||||||
if test -f "$secret"; then
|
rm -rf /run/secrets
|
||||||
echo copying secrets...
|
'' + concatMapStrings (pair:
|
||||||
rm -rf ${secretsStore}
|
let
|
||||||
${concatMapStrings (f: ''
|
name = "${concatStringsSep "-" pair.path}";
|
||||||
install -m ${f.value.mode} -D ${f.value.path} ${secretsStore}/${f.name}
|
secret = pair.value;
|
||||||
'') secretFiles}
|
in
|
||||||
fi
|
''
|
||||||
'';
|
# Install secret ${name}
|
||||||
|
install -m ${secret.mode} -D ${secret.path} /run/secrets/${name}
|
||||||
|
'') secretFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Set secrets ownership, later because the
|
# Set secrets ownership, later because the
|
||||||
# `user` activation script hasn't run yet.
|
# `user` activation script hasn't run yet.
|
||||||
config.system.activationScripts.secrets-own = {
|
config.system.activationScripts.secrets-own = {
|
||||||
deps = [ "users" "groups" ];
|
deps = [ "secrets-copy" "users" ];
|
||||||
text =
|
text = concatMapStrings (pair:
|
||||||
''
|
let
|
||||||
echo setting secrets ownership...
|
name = "${concatStringsSep "-" pair.path}";
|
||||||
${concatMapStrings (f: ''
|
secret = pair.value;
|
||||||
chown ${f.value.user}:${f.value.group} ${secretsStore}/${f.name}
|
in
|
||||||
'') secretFiles}
|
''
|
||||||
'';
|
echo setting secrets store ownership...
|
||||||
|
# Set ownership of ${name}
|
||||||
|
chown ${secret.user}:${secret.group} /run/secrets/${name}
|
||||||
|
'') secretFiles;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
52
custom/modules/ubino.nix
Normal file
52
custom/modules/ubino.nix
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.ubino;
|
||||||
|
|
||||||
|
in {
|
||||||
|
|
||||||
|
options.services.ubino = {
|
||||||
|
enable = mkEnableOption "Ubino: assistente virtuale di Ube, sottoforma di bot di Telegram.";
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "ubino";
|
||||||
|
description = ''
|
||||||
|
Ubino will be run under this user (user will be created if it doesn't exist.
|
||||||
|
This can be your user name).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
|
users.groups.ubino = {};
|
||||||
|
|
||||||
|
users.extraUsers."${cfg.user}" = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "ubino";
|
||||||
|
description = "Ubino user";
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.ubino = {
|
||||||
|
description = "Ubino: assistente virtuale di Ube, sottoforma di bot di Telegram.";
|
||||||
|
after = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
User = cfg.user;
|
||||||
|
Group = "ubino";
|
||||||
|
ExecStart = "${pkgs.openjdk}/bin/java -jar UbinoBot.jar";
|
||||||
|
Restart = "always";
|
||||||
|
StateDirectory = "ubino";
|
||||||
|
WorkingDirectory = "%S/ubino";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -1,35 +1,20 @@
|
|||||||
{ lib
|
{ writeScriptBin, fish, curl
|
||||||
, writers
|
|
||||||
, curl
|
|
||||||
, jq
|
|
||||||
, homeserver
|
, homeserver
|
||||||
, roomId
|
, roomId
|
||||||
, authToken
|
, authToken
|
||||||
}:
|
}:
|
||||||
|
|
||||||
writers.writeDashBin "notify" ''
|
writeScriptBin "notify" ''
|
||||||
export PATH="$PATH:${lib.makeBinPath [ curl jq ]}"
|
#!${fish}/bin/fish
|
||||||
|
|
||||||
if test $(id -u) != 0; then
|
set token (cat ${authToken})
|
||||||
|
|
||||||
|
if test (id -u) != 0
|
||||||
echo 'you must be root to send a notice'
|
echo 'you must be root to send a notice'
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
end
|
||||||
|
|
||||||
token=$(cat ${authToken})
|
set url '${homeserver}/rooms/${roomId}/send/m.room.message?access_token='$token
|
||||||
url="${homeserver}/rooms/${roomId}/send/m.room.message?access_token=$token"
|
set msg '{"msgtype":"m.text", "body": "'$argv[1]'"}'
|
||||||
|
${curl}/bin/curl -s -XPOST -d $msg $url
|
||||||
if test $# -eq 1; then
|
|
||||||
# send first arg as text
|
|
||||||
msg=$(printf "%s" "$1" | jq -Rsc '{ "msgtype": "m.text", "body": . }')
|
|
||||||
else
|
|
||||||
# send stdin formatted as code
|
|
||||||
msg=$(jq -Rsc '{
|
|
||||||
"msgtype": "m.text",
|
|
||||||
"format": "org.matrix.custom.html",
|
|
||||||
"body": "",
|
|
||||||
"formatted_body": ("<pre><code>" + . + "</code></pre>")
|
|
||||||
}')
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -s "$url" -d "$msg"
|
|
||||||
''
|
''
|
||||||
|
96
email.nix
96
email.nix
@ -1,96 +0,0 @@
|
|||||||
{ config, pkgs, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
(builtins.fetchTarball {
|
|
||||||
url = "https://gitlab.com/simple-nixos-mailserver/nixos-mailserver/-/archive/nixos-24.05/nixos-mailserver-nixos-24.11.tar.gz";
|
|
||||||
sha256 = "08zdidja5kdqgskynxsmcd8skh1b7cfl9ijjy9pak4b5h3aw2iqv";
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
mailserver = {
|
|
||||||
enable = true;
|
|
||||||
fqdn = "mail.eurofusion.eu";
|
|
||||||
domains = [ "eurofusion.eu" ];
|
|
||||||
|
|
||||||
messageSizeLimit = 78643200; # ~50MiB of base64 binary
|
|
||||||
|
|
||||||
loginAccounts = config.secrets.emailAccounts;
|
|
||||||
extraVirtualAliases = config.secrets.emailAliases;
|
|
||||||
|
|
||||||
# store state under /var
|
|
||||||
mailDirectory = "/var/lib/mail";
|
|
||||||
dkimKeyDirectory = "/var/lib/dkim";
|
|
||||||
|
|
||||||
mailboxes = {
|
|
||||||
# default IMAP folders
|
|
||||||
Sent = { specialUse = "Sent"; auto = "subscribe"; };
|
|
||||||
Drafts = { specialUse = "Drafts"; auto = "subscribe"; };
|
|
||||||
Spam = { specialUse = "Junk"; auto = "subscribe"; };
|
|
||||||
Trash = { specialUse = "Trash"; auto = "no"; };
|
|
||||||
};
|
|
||||||
|
|
||||||
# Use Let's Encrypt certificate
|
|
||||||
certificateScheme = "acme-nginx";
|
|
||||||
|
|
||||||
# There is one already (pdns-recursor)
|
|
||||||
localDnsResolver = false;
|
|
||||||
|
|
||||||
# Enable IMAPS (993), SMTPS (465)
|
|
||||||
enableImapSsl = true;
|
|
||||||
enableImap = false;
|
|
||||||
enableSubmissionSsl = true;
|
|
||||||
enableSubmission = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
services.dovecot2.extraConfig = ''
|
|
||||||
# Improve hashing speed
|
|
||||||
auth_cache_verify_password_with_worker = yes
|
|
||||||
'';
|
|
||||||
|
|
||||||
services.postfix.extraConfig = ''
|
|
||||||
# Prefer IPv6
|
|
||||||
smtp_address_preference = ipv6
|
|
||||||
# Prevent binding on temporary addresses
|
|
||||||
smtp_bind_address6 = ${config.var.ipv6Address}
|
|
||||||
'';
|
|
||||||
|
|
||||||
# Keep the key stable across renewals (for DANE)
|
|
||||||
security.acme.certs.${config.mailserver.fqdn}.extraLegoRenewFlags = [ "--reuse-key" ];
|
|
||||||
|
|
||||||
# Utilities
|
|
||||||
environment.systemPackages = [
|
|
||||||
# computes the DANE records
|
|
||||||
(pkgs.writers.writeDashBin "mailserver-dane" ''
|
|
||||||
set -e
|
|
||||||
export PATH=${with pkgs; lib.makeBinPath [ coreutils openssl gawk ]}:$PATH
|
|
||||||
|
|
||||||
pubkey_hash() {
|
|
||||||
openssl x509 -noout -pubkey | \
|
|
||||||
openssl pkey -pubin -outform DER | \
|
|
||||||
sha256sum | cut -f1 -d' '
|
|
||||||
}
|
|
||||||
|
|
||||||
fqdn=${config.mailserver.fqdn}
|
|
||||||
cert="/var/lib/acme/$fqdn/cert.pem"
|
|
||||||
self=$(awk '{print $0} /END CERT/{exit}' "$cert" | pubkey_hash)
|
|
||||||
ca=$(awk '{if(keep) print $0} /END CERT/{keep=1}' "$cert" | pubkey_hash)
|
|
||||||
|
|
||||||
# main: DANE-EE(3) SPKI(1) SHA2-256(1)
|
|
||||||
printf '_25._tcp.%s. IN TLSA 3 1 1 %s\n' "$fqdn" "$self"
|
|
||||||
# fallback: DANE-TA(2) SPKI(1) SHA2-256(1)
|
|
||||||
printf '_25._tcp.%s. IN TLSA 2 1 1 %s\n' "$fqdn" "$ca"
|
|
||||||
'')
|
|
||||||
|
|
||||||
# computes the DKIM record
|
|
||||||
(pkgs.writers.writeDashBin "mailserver-dkim" ''
|
|
||||||
set -e
|
|
||||||
export PATH=${with pkgs; lib.makeBinPath [ coreutils gawk ]}:$PATH
|
|
||||||
|
|
||||||
domain=${builtins.elemAt config.mailserver.domains 0}
|
|
||||||
raw=$(cat ${config.mailserver.dkimKeyDirectory}/*.txt | tr -d '\n\t' | awk -F'"' '{print $2$4}')
|
|
||||||
printf 'mail._domainkey.%s IN TXT %s' "$domain" "$raw"
|
|
||||||
'')
|
|
||||||
|
|
||||||
];
|
|
||||||
}
|
|
211
fish.nix
211
fish.nix
@ -1,211 +0,0 @@
|
|||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
programs.fish.enable = true;
|
|
||||||
|
|
||||||
programs.fish.shellAbbrs =
|
|
||||||
{ e = "nvim";
|
|
||||||
l = "ls -lh";
|
|
||||||
ip = "ip -c";
|
|
||||||
iftop = "iftop -m 70M";
|
|
||||||
};
|
|
||||||
|
|
||||||
programs.fish.shellAliases =
|
|
||||||
{ namecoin-cli = "namecoin-cli -conf=$XDG_CONFIG_HOME/namecoin"; };
|
|
||||||
|
|
||||||
programs.fish.loginShellInit =
|
|
||||||
''
|
|
||||||
# Start abduco in ssh
|
|
||||||
if set -q SSH_CLIENT
|
|
||||||
# start abduco on ssh
|
|
||||||
if not set -q ABDUCO_SESSION
|
|
||||||
exec abduco -A ssh fish
|
|
||||||
else if test $SHLVL -eq 1
|
|
||||||
tput rmcup
|
|
||||||
end
|
|
||||||
end
|
|
||||||
'';
|
|
||||||
|
|
||||||
programs.fish.interactiveShellInit =
|
|
||||||
''
|
|
||||||
## Fish settings
|
|
||||||
|
|
||||||
# mixed emacs/vi
|
|
||||||
fish_hybrid_key_bindings
|
|
||||||
|
|
||||||
# kj to normal mode
|
|
||||||
bind -M insert kj "
|
|
||||||
if commandline -P;
|
|
||||||
commandline -f cancel;
|
|
||||||
else;
|
|
||||||
set fish_bind_mode default;
|
|
||||||
commandline -f backward-char repaint-mode;
|
|
||||||
end"
|
|
||||||
|
|
||||||
# fix unquoted URLs
|
|
||||||
set -U fish_features ampersand-nobg-in-token qmark-noglob
|
|
||||||
# change default cursor
|
|
||||||
set fish_cursor_insert underscore
|
|
||||||
|
|
||||||
|
|
||||||
## Color scheme
|
|
||||||
|
|
||||||
# syntax highlighting
|
|
||||||
set fish_color_command green
|
|
||||||
set fish_color_param normal
|
|
||||||
set fish_color_comment brcyan
|
|
||||||
set fish_color_operator purple
|
|
||||||
set fish_color_escape bryellow
|
|
||||||
set fish_color_redirection blue
|
|
||||||
set fish_color_selection --background=black
|
|
||||||
|
|
||||||
# completion/history
|
|
||||||
set fish_pager_color_prefix yellow
|
|
||||||
set fish_pager_color_description brblue
|
|
||||||
set fish_pager_color_progress brblack --background=black
|
|
||||||
set fish_color_search_match --background=black
|
|
||||||
|
|
||||||
# man pages colors
|
|
||||||
# bold, blink stop
|
|
||||||
set -x LESS_TERMCAP_md (set_color -o bryellow)
|
|
||||||
set -x LESS_TERMCAP_mb (set_color -u magenta)
|
|
||||||
set -x LESS_TERMCAP_me (set_color normal)
|
|
||||||
# standout start/stop
|
|
||||||
set -x LESS_TERMCAP_so (set_color brblue -b black)
|
|
||||||
set -x LESS_TERMCAP_se (set_color normal -b normal)
|
|
||||||
# underline start/stop
|
|
||||||
set -x LESS_TERMCAP_us (set_color -u brmagenta)
|
|
||||||
set -x LESS_TERMCAP_ue (set_color normal)
|
|
||||||
|
|
||||||
# used default LS_COLORS
|
|
||||||
eval (dircolors | sed 's/\(\w\+\)=/set \1 /')
|
|
||||||
|
|
||||||
|
|
||||||
## Aliases
|
|
||||||
|
|
||||||
# start process and detach
|
|
||||||
function start
|
|
||||||
nohup $argv > /dev/null 2>&1 &; disown
|
|
||||||
end
|
|
||||||
|
|
||||||
# start process without network access
|
|
||||||
function nnet
|
|
||||||
unshare -nc fish -ic "$argv"
|
|
||||||
end
|
|
||||||
|
|
||||||
# interactively rename files
|
|
||||||
function vimv
|
|
||||||
set tmp (mktemp --tmpdir -d vimv.XXX)
|
|
||||||
if set -q argv[1]
|
|
||||||
# directory listing
|
|
||||||
find $argv[1] -maxdepth 1 | sort | cat -n | tee $tmp/before > $tmp/after
|
|
||||||
else
|
|
||||||
# read from stdin
|
|
||||||
cat -n - | tee $tmp/before > $tmp/after
|
|
||||||
end
|
|
||||||
$EDITOR $tmp/after
|
|
||||||
# only print differing lines
|
|
||||||
awk '
|
|
||||||
NR==FNR { line=$0; sub($1, "", line); sub(/\s+/, "", line); lines[$1]=line; next }
|
|
||||||
NR!=FNR { line=$0; sub($1, "", line); sub(/\s+/, "", line);
|
|
||||||
if (!($1 in lines)) { printf("rm -vr \"%s\"\n", line); next }
|
|
||||||
if (lines[$1] != line) printf("mv -vin \"%s\" \"%s\"\n", line, lines[$1])
|
|
||||||
}
|
|
||||||
' $tmp/after $tmp/before | sh
|
|
||||||
rm -r $tmp
|
|
||||||
end
|
|
||||||
'';
|
|
||||||
|
|
||||||
programs.fish.promptInit =
|
|
||||||
''
|
|
||||||
# Outputs colored text
|
|
||||||
function color
|
|
||||||
set_color $argv[1]
|
|
||||||
for i in $argv[2..-1]
|
|
||||||
echo -n $i
|
|
||||||
end
|
|
||||||
set_color normal
|
|
||||||
end
|
|
||||||
|
|
||||||
# Git branch info
|
|
||||||
function git_branch
|
|
||||||
if not test -f .git/HEAD
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
set branch (git rev-parse --abbrev-ref HEAD 2> /dev/null)
|
|
||||||
if test $status -ne 0
|
|
||||||
set branch (cut -f 3 -d '/' .git/HEAD)
|
|
||||||
else if test $branch = HEAD
|
|
||||||
set branch (head -c 10 .git/HEAD)
|
|
||||||
end
|
|
||||||
|
|
||||||
timeout 0.1 git diff-files --quiet 2>/dev/null
|
|
||||||
if test $status -eq 1
|
|
||||||
set branch (color yellow $branch'*')
|
|
||||||
else
|
|
||||||
set branch (color green $branch)
|
|
||||||
end
|
|
||||||
|
|
||||||
timeout 0.1 git status --porcelain 2>/dev/null 1>| read untracked
|
|
||||||
if test -n "$untracked"
|
|
||||||
set dirty (color red '*')
|
|
||||||
end
|
|
||||||
|
|
||||||
echo " <$branch$dirty>"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Left prompt
|
|
||||||
function fish_prompt
|
|
||||||
if fish_is_root_user
|
|
||||||
set prompt (color blue Λ)
|
|
||||||
set user (color red (whoami))
|
|
||||||
else
|
|
||||||
set prompt (color blue λ)
|
|
||||||
set user (color green (whoami))
|
|
||||||
end
|
|
||||||
|
|
||||||
if test \( "$LINES" -lt 10 \) -o \( "$COLUMNS" -lt 30 \)
|
|
||||||
echo "$prompt "
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if set -q SSH_CLIENT
|
|
||||||
set host (color yellow (hostname))
|
|
||||||
else
|
|
||||||
set host (color green (hostname))
|
|
||||||
end
|
|
||||||
|
|
||||||
set git (git_branch)
|
|
||||||
set path (color cyan (prompt_pwd))
|
|
||||||
|
|
||||||
switch $fish_bind_mode
|
|
||||||
case default
|
|
||||||
set mode (color blue n)
|
|
||||||
case insert
|
|
||||||
set mode (color red i)
|
|
||||||
case visual
|
|
||||||
set mode (color yellow v)
|
|
||||||
end
|
|
||||||
|
|
||||||
echo "$user@$host $mode$path$git"
|
|
||||||
echo "$prompt "
|
|
||||||
end
|
|
||||||
|
|
||||||
# Right prompt
|
|
||||||
function fish_right_prompt
|
|
||||||
set code $status
|
|
||||||
if test $code -ne 0
|
|
||||||
set exitcode (color red $code" ⏎")
|
|
||||||
end
|
|
||||||
if string match -q '/nix*' $PATH[1]
|
|
||||||
set nix " <"(color blue nix)">"
|
|
||||||
end
|
|
||||||
echo "$exitcode$nix"
|
|
||||||
end
|
|
||||||
|
|
||||||
function fish_mode_prompt; end
|
|
||||||
function fish_greeting; end
|
|
||||||
'';
|
|
||||||
|
|
||||||
}
|
|
35
hardware.nix
35
hardware.nix
@ -12,6 +12,7 @@
|
|||||||
];
|
];
|
||||||
boot.loader.grub = {
|
boot.loader.grub = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
version = 2;
|
||||||
device = "/dev/sda";
|
device = "/dev/sda";
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,6 +31,7 @@
|
|||||||
fsType = "ext4";
|
fsType = "ext4";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nix.maxJobs = lib.mkDefault 16;
|
||||||
powerManagement.cpuFreqGovernor = "ondemand";
|
powerManagement.cpuFreqGovernor = "ondemand";
|
||||||
|
|
||||||
services.apcupsd = {
|
services.apcupsd = {
|
||||||
@ -38,33 +40,36 @@
|
|||||||
UPSTYPE usb
|
UPSTYPE usb
|
||||||
UPSCABLE usb
|
UPSCABLE usb
|
||||||
NETSERVER on
|
NETSERVER on
|
||||||
|
NISPORT 3551
|
||||||
MINUTES 5
|
MINUTES 5
|
||||||
'';
|
'';
|
||||||
hooks =
|
hooks =
|
||||||
let
|
let
|
||||||
# Send notifications when something bad happens
|
# Send notifications on the Maxwell
|
||||||
|
# room when something bad happens.
|
||||||
notify = msg: ''${pkgs.maxwell-notify}/bin/notify "UPS: ${msg}"'';
|
notify = msg: ''${pkgs.maxwell-notify}/bin/notify "UPS: ${msg}"'';
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
changeme = notify "replace batteries";
|
changeme = notify "sostituire le batterie";
|
||||||
battdetach = notify "batteries disconnected";
|
battdetach = notify "batterie disconnesse";
|
||||||
battattach = notify "batteries reconnected";
|
battattach = notify "batterie riconnesse";
|
||||||
commfailure = notify "connection lost";
|
commfailure = notify "connessione persa";
|
||||||
commok = notify "connection enstablished";
|
commok = notify "connessione ristabilita";
|
||||||
loadlimit = notify "critical battery level (5%)";
|
loadlimit = notify "livello batterie critico (5%)";
|
||||||
runlimit = notify "critical battery life (5min)";
|
runlimit = notify "autonomia batterie critico (5min)";
|
||||||
doshutdown = notify "shutting down!";
|
doshutdown = notify "inizio sequenza di spegnimento";
|
||||||
powerout = notify "main power is out";
|
powerout = notify "rete elettrica disconnessa";
|
||||||
mainsback = notify "main power is back";
|
mainsback = notify "rete elettrica riconnessa";
|
||||||
onbattery = notify "batteries connected";
|
onbattery = notify "attivate batterie";
|
||||||
offbattery = notify "batteries disconnected";
|
offbattery = notify "disattivate batterie";
|
||||||
emergency = notify "battery malfunction, possible shutdown!";
|
emergency = notify "malfunzionamento batterie, possibile spegnimento!";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
services.smartd =
|
services.smartd =
|
||||||
let
|
let
|
||||||
# Send a notification when a disk is starting to fail
|
# Send a notification on the Maxwell
|
||||||
|
# when a disk is starting to fail.
|
||||||
failHook = with pkgs; writeScript "disk-fail-hook" ''
|
failHook = with pkgs; writeScript "disk-fail-hook" ''
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
${pkgs.maxwell-notify}/bin/notify \
|
${pkgs.maxwell-notify}/bin/notify \
|
||||||
|
204
jobs.nix
204
jobs.nix
@ -4,114 +4,126 @@ with lib;
|
|||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
systemd.services."notify-failed@" = {
|
systemd.services.ydns = {
|
||||||
description = "notify that %i has failed";
|
description = "update ydns address record";
|
||||||
scriptArgs = "%i";
|
after = [ "network-online.target" ];
|
||||||
path = [ pkgs.maxwell-notify ];
|
startAt = "*:0/30";
|
||||||
script = ''
|
|
||||||
unit=$1
|
|
||||||
notify "$unit: failed. last log lines:"
|
|
||||||
journalctl -u "$unit" -o cat -n 15 | notify
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.backup =
|
serviceConfig.Type = "oneshot";
|
||||||
let
|
serviceConfig.environmentFile = config.secrets.ydns.environment;
|
||||||
saved = pkgs.writeText "backup-saved" ''
|
|
||||||
/etc/lvm
|
|
||||||
/var/lib
|
|
||||||
/home
|
|
||||||
'';
|
|
||||||
|
|
||||||
excluded = pkgs.writeText "backup-excluded" ''
|
path = with pkgs; [ curl cacert gawk iproute ];
|
||||||
/var/lib/systemd
|
environment = {
|
||||||
/var/lib/udisks2
|
YDNS_HOST = config.var.hostname;
|
||||||
/var/lib/postgresql
|
CURL_CA_BUNDLE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||||
/var/lib/matrix-synapse/media_store/url_cache
|
|
||||||
/var/lib/matrix-synapse/media_store/url_cache_thumbnails
|
|
||||||
'';
|
|
||||||
|
|
||||||
in {
|
|
||||||
description = "system backup";
|
|
||||||
startAt = "*-*-* 03:00"; # every day at 3:00
|
|
||||||
onFailure = [ "notify-failed@backup.service" ];
|
|
||||||
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
PrivateTmp = true;
|
|
||||||
PrivateMounts = true;
|
|
||||||
LimitNOFILE = 65536;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
environment.BUP_DIR = "/mnt/backup";
|
|
||||||
path = with pkgs; [ bup git util-linux sudo gzip postgresql ];
|
|
||||||
|
|
||||||
script = ''
|
script = ''
|
||||||
# mount repository
|
update() {
|
||||||
mount -m -L backup "$BUP_DIR"
|
ret=$(curl -$1 --basic --silent \
|
||||||
|
-u "$YDNS_USER:$YDNS_PASSWD" \
|
||||||
|
"https://ydns.io/api/v1/update/?host=$YDNS_HOST&ip=$2" || exit 0)
|
||||||
|
|
||||||
# init backup, if empty
|
case "$ret" in
|
||||||
! test -e $BUP_DIR/bupindex && bup init
|
ok)
|
||||||
|
echo "updated successfully: $YDNS_HOST ($2)"
|
||||||
|
;;
|
||||||
|
|
||||||
# build indices and save
|
badauth)
|
||||||
while read -r dir; do
|
echo "updated failed: $YDNS_HOST (authentication failed)"
|
||||||
{
|
;;
|
||||||
name=$(basename "$dir")
|
|
||||||
echo indexing $name...
|
|
||||||
bup index "$dir" --exclude-from="${excluded}"
|
|
||||||
echo done
|
|
||||||
|
|
||||||
echo saving $name...
|
*)
|
||||||
bup save -n "$name" "$dir" || true
|
echo "update failed: $YDNS_HOST ($ret)"
|
||||||
echo done
|
;;
|
||||||
} || true
|
esac
|
||||||
done < "${saved}"
|
}
|
||||||
|
|
||||||
# postgresql backup
|
update 4 "$(curl -s -4 https://ydns.io/api/v1/ip)"
|
||||||
dir=/tmp/postgresql
|
update 6 "$(ip addr show mngtmpaddr | awk '/inet6/{print $2; exit}' | cut -d/ -f1)"
|
||||||
mkdir -p "$dir"
|
|
||||||
|
|
||||||
echo dumping databases...
|
|
||||||
sudo -u postgres pg_dumpall > "$dir"/db.bak
|
|
||||||
echo done
|
|
||||||
|
|
||||||
echo saving...
|
|
||||||
bup index "$dir"
|
|
||||||
bup save -n postgresql "$dir" --strip-path=/tmp
|
|
||||||
echo done
|
|
||||||
|
|
||||||
echo generating par2 files...
|
|
||||||
bup fsck -j 8 -g
|
|
||||||
echo done
|
|
||||||
|
|
||||||
# prune backups every week
|
|
||||||
if test $(( $(date +%s) / 86400 % 7 )) -eq 0; then
|
|
||||||
echo pruning...
|
|
||||||
bup prune-older --keep-all-for 6m --keep-monthlies-for 2y --unsafe
|
|
||||||
echo done
|
|
||||||
fi
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
systemd.services.namecoin-update =
|
systemd.services.backup = {
|
||||||
let
|
description = "run system backup";
|
||||||
userFile = with config.services.namecoind;
|
after = [ "network-online.target" ];
|
||||||
pkgs.writeText "namecoin.conf" ''
|
startAt = "weekly";
|
||||||
rpcbind=${rpc.address}
|
|
||||||
rpcport=${toString rpc.port}
|
|
||||||
rpcuser=${rpc.user}
|
|
||||||
rpcpassword=${rpc.password}
|
|
||||||
'';
|
|
||||||
in {
|
|
||||||
description = "update namecoin names";
|
|
||||||
after = [ "namecoind.service" ];
|
|
||||||
startAt = "hourly";
|
|
||||||
onFailure = [ "notify-failed@namecoin-update.service" ];
|
|
||||||
|
|
||||||
path = [ pkgs.namecoind ];
|
serviceConfig.Type = "oneshot";
|
||||||
serviceConfig.Type = "oneshot";
|
|
||||||
serviceConfig.ExecStart = "${pkgs.haskellPackages.namecoin-update}/bin/namecoin-update ${userFile}";
|
path = with pkgs; [ bup git nfs-utils ];
|
||||||
};
|
|
||||||
|
environment.BUP_DIR = "/mnt/backup";
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
${pkgs.fish}/bin/fish << 'EOF'
|
||||||
|
|
||||||
|
set locations \
|
||||||
|
/etc/lvm \
|
||||||
|
/etc/nixos \
|
||||||
|
/var/lib \
|
||||||
|
/home
|
||||||
|
|
||||||
|
set excluded \
|
||||||
|
/var/lib/alsa \
|
||||||
|
/var/lib/systemd \
|
||||||
|
/var/lib/udisks2 \
|
||||||
|
/var/lib/udev \
|
||||||
|
/var/lib/postgresql
|
||||||
|
|
||||||
|
# mount NFS share
|
||||||
|
mkdir -p $BUP_DIR
|
||||||
|
mount.nfs -o nolock 192.168.1.3:/maxwell $BUP_DIR
|
||||||
|
|
||||||
|
# check if properly mounted
|
||||||
|
if not mountpoint -q $BUP_DIR
|
||||||
|
echo mount failed! 1>&2
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# init backup
|
||||||
|
if not test -e $BUP_DIR/bupindex
|
||||||
|
bup init
|
||||||
|
end
|
||||||
|
|
||||||
|
# build indices and copy
|
||||||
|
for i in $locations
|
||||||
|
eval bup index $i --exclude=(string join " --exclude=" $excluded)
|
||||||
|
bup save -n (basename $i) $i
|
||||||
|
end
|
||||||
|
|
||||||
|
# postgresql backup
|
||||||
|
set dir /var/lib/postgresql-backup
|
||||||
|
mkdir -p $dir
|
||||||
|
sudo -u postgres pg_dumpall | gzip > $dir/db.bak
|
||||||
|
bup index $dir
|
||||||
|
bup save -n postgresql $dir
|
||||||
|
rm -rf $dir
|
||||||
|
|
||||||
|
umount /mnt/backup
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
systemd.services.namecoin-update =
|
||||||
|
let
|
||||||
|
userFile = with config.services.namecoind;
|
||||||
|
pkgs.writeText "namecoin.conf" ''
|
||||||
|
rpcbind=${rpc.address}
|
||||||
|
rpcport=${toString rpc.port}
|
||||||
|
rpcuser=${rpc.user}
|
||||||
|
rpcpassword=${rpc.password}
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
description = "update namecoin names";
|
||||||
|
after = [ "namecoind.service" ];
|
||||||
|
startAt = "hourly";
|
||||||
|
|
||||||
|
path = [ pkgs.namecoind ];
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
serviceConfig.ExecStart = "${pkgs.haskellPackages.namecoin-update}/bin/namecoin-update ${userFile}";
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# Setup:
|
# Setup:
|
||||||
# Maxwell runs the web UI (magneticow) but doesn't
|
# Maxwell runs the web UI (magneticow) but doesn't
|
||||||
# run the crawler (magneticod) because it's too
|
# run the crawler (magneticod) because it's too
|
||||||
# network intensive. The latter is run by Wigfrid,
|
# network intesive. The latter is run by Wigfrid,
|
||||||
# which periodically uploads a sqlite database.
|
# which periodically uploads a sqlite database.
|
||||||
# Once received, Maxwell merges it with the local one.
|
# Once received, Maxwell merges it with the local one.
|
||||||
|
|
||||||
|
187
matrix.nix
187
matrix.nix
@ -1,14 +1,31 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with config.var;
|
||||||
|
|
||||||
let
|
let
|
||||||
domain = "eurofusion.eu";
|
### Element (Riot) configuration
|
||||||
|
conf = with config.var; {
|
||||||
|
default_server_config."m.homeserver" =
|
||||||
|
{ base_url = "https://${hostname}";
|
||||||
|
server_name = "Maxwell";
|
||||||
|
};
|
||||||
|
default_server_config."m.identity_server" =
|
||||||
|
{ base_url = "https://matrix.org"; };
|
||||||
|
roomDirectory.servers = [ "matrix.org" hostname ];
|
||||||
|
|
||||||
|
brand = "Maxwell matrix";
|
||||||
|
defaultCountryCode = "IT";
|
||||||
|
showLabsSettings = true;
|
||||||
|
|
||||||
|
# Use a trusted Jitsi instance
|
||||||
|
jitsi.preferredDomain = "jitsi.openspeed.org";
|
||||||
|
jitsi.externalApiUrl = "https://jitsi.openspeed.org/libs/external_api.min.js";
|
||||||
|
};
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
### Reverse proxy locations
|
### Reverse proxy locations
|
||||||
|
services.nginx.virtualHosts."${config.var.hostname}" =
|
||||||
# Setup for well-known on the bare domain
|
|
||||||
services.nginx.virtualHosts.${domain} =
|
|
||||||
let
|
let
|
||||||
client =
|
client =
|
||||||
{ "m.homeserver" = { "base_url" = "https://${config.var.hostname}"; };
|
{ "m.homeserver" = { "base_url" = "https://${config.var.hostname}"; };
|
||||||
@ -17,9 +34,6 @@ in
|
|||||||
server = { "m.server" = "${config.var.hostname}:443"; };
|
server = { "m.server" = "${config.var.hostname}:443"; };
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
enableACME = true;
|
|
||||||
forceSSL = true;
|
|
||||||
|
|
||||||
# Needed for matrix federation
|
# Needed for matrix federation
|
||||||
locations."/.well-known/matrix/server".extraConfig = ''
|
locations."/.well-known/matrix/server".extraConfig = ''
|
||||||
add_header Content-Type application/json;
|
add_header Content-Type application/json;
|
||||||
@ -33,24 +47,40 @@ in
|
|||||||
add_header Access-Control-Allow-Origin *;
|
add_header Access-Control-Allow-Origin *;
|
||||||
return 200 '${builtins.toJSON client}';
|
return 200 '${builtins.toJSON client}';
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Forward matrix API calls to synapse
|
||||||
|
locations."/_matrix".proxyPass = "http://localhost:8448";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Forward matrix/admin API calls to synapse
|
|
||||||
services.nginx.virtualHosts.${config.var.hostname} =
|
### Element/Riot static location
|
||||||
{ locations."/_matrix".proxyPass = "http://localhost:8448";
|
services.nginx.virtualHosts."riot.${config.var.hostname}" =
|
||||||
locations."/_synapse".proxyPass = "http://localhost:8448";
|
{ enableACME = true;
|
||||||
};
|
forceSSL = true;
|
||||||
|
|
||||||
|
locations."/" =
|
||||||
|
{ index = "index.html";
|
||||||
|
alias = (pkgs.element-web.override { inherit conf; }) + "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
### Homeserver
|
### Homeserver
|
||||||
services.matrix-synapse.enable = true;
|
services.matrix-synapse = {
|
||||||
services.matrix-synapse.settings = {
|
enable = true;
|
||||||
server_name = domain;
|
server_name = config.var.hostname;
|
||||||
public_baseurl = "https://${config.var.hostname}/";
|
|
||||||
|
# Tell users about our TURN server
|
||||||
|
turn_uris = [
|
||||||
|
"turn:${config.var.hostname}:5349?transport=udp"
|
||||||
|
"turn:${config.var.hostname}:5350?transport=udp"
|
||||||
|
"turn:${config.var.hostname}:5349?transport=tcp"
|
||||||
|
"turn:${config.var.hostname}:5350?transport=tcp"
|
||||||
|
];
|
||||||
|
|
||||||
# Bind on localhost and used a reverse proxy
|
# Bind on localhost and used a reverse proxy
|
||||||
listeners = [
|
listeners = [
|
||||||
{ bind_addresses = [ "localhost" ];
|
{ bind_address = "localhost";
|
||||||
port = 8448;
|
port = 8448;
|
||||||
type = "http";
|
type = "http";
|
||||||
tls = false;
|
tls = false;
|
||||||
@ -64,30 +94,30 @@ in
|
|||||||
|
|
||||||
# Connect to Postrges
|
# Connect to Postrges
|
||||||
database_type = "psycopg2";
|
database_type = "psycopg2";
|
||||||
database_args =
|
database_args = {
|
||||||
{ user = "matrix-synapse";
|
user = "matrix-synapse";
|
||||||
database = "matrix-synapse";
|
database = "matrix-synapse";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Make logging less verbose
|
# Make logging less verbose
|
||||||
log_config = pkgs.writeText "synapse-log.yml" ''
|
logConfig = ''
|
||||||
version: 1
|
version: 1
|
||||||
formatters:
|
formatters:
|
||||||
journal_fmt:
|
journal_fmt:
|
||||||
format: '%(name)s: [%(request)s] %(message)s'
|
format: '%(name)s: [%(request)s] %(message)s'
|
||||||
filters:
|
filters:
|
||||||
context:
|
context:
|
||||||
(): synapse.util.logcontext.LoggingContextFilter
|
(): synapse.util.logcontext.LoggingContextFilter
|
||||||
request: ""
|
request: ""
|
||||||
handlers:
|
handlers:
|
||||||
journal:
|
journal:
|
||||||
class: systemd.journal.JournalHandler
|
class: systemd.journal.JournalHandler
|
||||||
formatter: journal_fmt
|
formatter: journal_fmt
|
||||||
filters: [context]
|
filters: [context]
|
||||||
SYSLOG_IDENTIFIER: synapse
|
SYSLOG_IDENTIFIER: synapse
|
||||||
root:
|
root:
|
||||||
level: WARN
|
level: WARN
|
||||||
handlers: [journal]
|
handlers: [journal]
|
||||||
disable_existing_loggers: False
|
disable_existing_loggers: False
|
||||||
'';
|
'';
|
||||||
|
|
||||||
@ -95,68 +125,65 @@ in
|
|||||||
expire_access_token = true;
|
expire_access_token = true;
|
||||||
event_cache_size = "2K";
|
event_cache_size = "2K";
|
||||||
max_upload_size = "1000M";
|
max_upload_size = "1000M";
|
||||||
dynamic_thumbnails = true;
|
turn_user_lifetime = "1d";
|
||||||
|
|
||||||
|
# Needed to restrict access to the TURN
|
||||||
|
# server to only our matrix users.
|
||||||
|
turn_shared_secret = config.secrets.matrix.turn;
|
||||||
|
# Needed by the register_new_matrix_user script
|
||||||
|
registration_shared_secret = config.secrets.matrix.registration;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Secrets
|
|
||||||
services.matrix-synapse.extraConfigFiles =
|
|
||||||
[
|
|
||||||
# Password reset via email
|
|
||||||
# Note: can't be put here, see NixOS/nixpkgs#158605
|
|
||||||
config.secrets.matrix.email.conf
|
|
||||||
|
|
||||||
# Needed by the register_new_matrix_user script
|
|
||||||
config.secrets.matrix.registration
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
### Database
|
### Database
|
||||||
services.postgresql.enable = true;
|
services.postgresql.enable = true;
|
||||||
|
|
||||||
# Create databases on the first run
|
# Create database on the first run
|
||||||
services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
|
services.postgresql.initialScript = pkgs.writeText "synapse-init.sql" ''
|
||||||
CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
|
CREATE ROLE "matrix-synapse" WITH LOGIN PASSWORD 'synapse';
|
||||||
CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
|
CREATE DATABASE "matrix-synapse" WITH OWNER "matrix-synapse"
|
||||||
TEMPLATE template0
|
TEMPLATE template0
|
||||||
LC_COLLATE = "C"
|
LC_COLLATE = "C"
|
||||||
LC_CTYPE = "C";
|
LC_CTYPE = "C";
|
||||||
|
|
||||||
CREATE ROLE "mautrix-whatsapp" WITH LOGIN PASSWORD 'whatsapp';
|
|
||||||
CREATE DATABASE "mautrix-whatsapp" WITH OWNER "mautrix-whatsapp"
|
|
||||||
TEMPLATE template0
|
|
||||||
LC_COLLATE = "C"
|
|
||||||
LC_CTYPE = "C";
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
### Whatsapp bridge
|
|
||||||
|
|
||||||
# allow synapse to read the shared secrets
|
# Handles users behind a NAT,
|
||||||
users.users.matrix-synapse.extraGroups = [ "mautrix-whatsapp" ];
|
# needed for reliable VoIP.
|
||||||
|
services.coturn = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
# Allow olm for mautrix-whatsapp
|
# Only allow users vouched for
|
||||||
nixpkgs.config.permittedInsecurePackages = [ "olm-3.2.16" ];
|
# by the Matrix server.
|
||||||
|
lt-cred-mech = true;
|
||||||
|
use-auth-secret = true;
|
||||||
|
static-auth-secret = config.secrets.matrix.turn;
|
||||||
|
|
||||||
services.mautrix-whatsapp =
|
# Use maxwell certificate for TLS
|
||||||
{
|
realm = config.var.hostname;
|
||||||
enable = true;
|
cert = "/var/lib/acme/${config.var.hostname}/fullchain.pem";
|
||||||
serviceDependencies = [ "postgresql.service" ];
|
pkey = "/var/lib/acme/${config.var.hostname}/key.pem";
|
||||||
settings.appservice =
|
|
||||||
{ database.type = "postgres";
|
# Port range for TURN relaying
|
||||||
database.uri = "postgresql:///mautrix-whatsapp?host=/run/postgresql";
|
min-port = 49152;
|
||||||
};
|
max-port = 49999;
|
||||||
settings.bridge =
|
|
||||||
{ encryption =
|
# Enable TLS
|
||||||
{ allow = true;
|
secure-stun = true;
|
||||||
default = true;
|
no-tcp-relay = false;
|
||||||
require = true;
|
|
||||||
};
|
extraConfig = ''
|
||||||
permissions =
|
external-ip=${config.var.ipAddress}
|
||||||
{ "eurofusion.eu" = "user";
|
cipher-list=HIGH
|
||||||
"@rnhmjoj:eurofusion.eu" = "admin";
|
no-loopback-peers
|
||||||
};
|
no-multicast-peers
|
||||||
relay.enabled = false;
|
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||||
mute_bridging = true;
|
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||||
};
|
allowed-peer-ip=192.168.1.5
|
||||||
};
|
user-quota=12
|
||||||
|
total-quota=1200
|
||||||
|
verbose=true
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,46 @@
|
|||||||
{ config, lib, ... }:
|
{ config, ... }:
|
||||||
|
|
||||||
# Setup:
|
# Setup:
|
||||||
# pdns-recursor on localhost:54
|
# PDNS recursor on port 53
|
||||||
# dnsdist on port 53 (DNS)
|
# DNSCrypt wrapper on port 1194
|
||||||
# ncdns for Namecoin bit. zone resolution
|
# NCDNS for Namecoin bit. zone resolution
|
||||||
|
|
||||||
{
|
{
|
||||||
# Recursive DNS resolver
|
# Recursive DNS resolver
|
||||||
services.pdns-recursor =
|
services.pdns-recursor = {
|
||||||
{ enable = true;
|
enable = true;
|
||||||
# Configures the bit. zone
|
# Configures the bit. zone
|
||||||
resolveNamecoin = true;
|
resolveNamecoin = true;
|
||||||
dns.port = 54;
|
dns.allowFrom = [ "0.0.0.0/0" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Public DNS resolver
|
# Wrap the local recursive resolver
|
||||||
services.dnsdist =
|
# in DNSCrypt on the default OpenVPN port.
|
||||||
{ enable = true;
|
# This port is chosen because it's usually
|
||||||
extraConfig = ''
|
# not blocked in corporate networks.
|
||||||
-- Listen on IPv6 and IPv4
|
services.dnscrypt-wrapper = {
|
||||||
setLocal("[::]:53"); addLocal("0.0.0.0:53")
|
enable = true;
|
||||||
|
address = "0.0.0.0";
|
||||||
-- Allow everything
|
port = 1194;
|
||||||
setACL({"0.0.0.0/0", "::/0"})
|
providerKey.public = config.secrets.dnscrypt.pub;
|
||||||
|
providerKey.secret = config.secrets.dnscrypt.sec;
|
||||||
-- Set upstream resolver
|
};
|
||||||
newServer({address="[::1]:54", name="pdns"})
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Namecoin resolver
|
# Namecoin resolver
|
||||||
services.ncdns =
|
services.ncdns = {
|
||||||
{ enable = true;
|
enable = true;
|
||||||
# This is currently broken, see ncdns issue:
|
# This is currently broken, see ncdns issue:
|
||||||
# https://github.com/namecoin/ncdns/issues/127
|
# https://github.com/namecoin/ncdns/issues/127
|
||||||
dnssec.enable = false;
|
dnssec.enable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Namecoin daemon with RPC server
|
# Namecoin daemon with RPC server
|
||||||
services.namecoind =
|
services.namecoind = {
|
||||||
{ enable = true;
|
enable = true;
|
||||||
# This are used by the resolver (ncdns)
|
# This are used by the resolver (ncdns)
|
||||||
# to query the blockchain.
|
# to query the blockchain.
|
||||||
rpc.user = config.secrets.namecoin.user;
|
rpc.user = config.secrets.namecoin.user;
|
||||||
rpc.password = config.secrets.namecoin.password;
|
rpc.password = config.secrets.namecoin.password;
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.namecoin.group = "namecoin";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
481
neovim.nix
481
neovim.nix
@ -1,481 +0,0 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
|
||||||
|
|
||||||
let
|
|
||||||
frameline = pkgs.callPackage (pkgs.fetchFromGitea
|
|
||||||
{ domain = "maxwell.eurofusion.eu/git";
|
|
||||||
owner = "rnhmjoj";
|
|
||||||
repo = "nvim-frameline";
|
|
||||||
rev = "v0.1.0";
|
|
||||||
sha256 = "PrTSSoXbu+qtTsJUv81z+MuTUmB1RHLPEWFQQnu6+J8=";
|
|
||||||
}) { };
|
|
||||||
|
|
||||||
plugins = with pkgs.vimPlugins;
|
|
||||||
[ # UI
|
|
||||||
undotree gitsigns-nvim
|
|
||||||
frameline nvim-fzf
|
|
||||||
|
|
||||||
# Syntax
|
|
||||||
playground
|
|
||||||
vim-pandoc-syntax
|
|
||||||
nix-queries fortran-queries
|
|
||||||
(nvim-treesitter.withPlugins (p: with p;
|
|
||||||
[ bash fish
|
|
||||||
c fortran haskell
|
|
||||||
html css
|
|
||||||
nix python lua
|
|
||||||
]))
|
|
||||||
|
|
||||||
# Misc
|
|
||||||
vim-fugitive supertab neomake
|
|
||||||
auto-pairs plenary-nvim
|
|
||||||
];
|
|
||||||
|
|
||||||
pack = pkgs.linkFarm "neovim-plugins"
|
|
||||||
(map (pkg:
|
|
||||||
{ name = "pack/${pkg.name}/start/${pkg.name}";
|
|
||||||
path = toString pkg;
|
|
||||||
}) (lib.concatMap (p: [p] ++ p.dependencies or []) plugins));
|
|
||||||
|
|
||||||
neovim-wrapped = pkgs.runCommand "${pkgs.neovim-unwrapped.name}"
|
|
||||||
{ nativeBuildInputs = [ pkgs.makeWrapper ]; }
|
|
||||||
''
|
|
||||||
mkdir -p "$out"
|
|
||||||
makeWrapper '${pkgs.neovim-unwrapped}/bin/nvim' "$out/bin/nvim" \
|
|
||||||
--add-flags "-u ${conf}"
|
|
||||||
'';
|
|
||||||
|
|
||||||
nix-queries = pkgs.writeTextDir "/queries/nix/injections.scm"
|
|
||||||
''
|
|
||||||
;; extends
|
|
||||||
|
|
||||||
; writeText highlight
|
|
||||||
(apply_expression function:
|
|
||||||
(apply_expression function: (_) @_func
|
|
||||||
argument: (string_expression (string_fragment) @injection.language))
|
|
||||||
argument: [
|
|
||||||
(string_expression (string_fragment) @injection.content)
|
|
||||||
(indented_string_expression (string_fragment) @injection.content)]
|
|
||||||
(#match? @_func "(^|\\.)writeText(Dir)?$")
|
|
||||||
(#gsub! @injection.language ".*%.(.*)" "%1")
|
|
||||||
(#set! injection.combined))
|
|
||||||
'';
|
|
||||||
|
|
||||||
fortran-queries = pkgs.writeTextDir "/queries/fortran/highlights.scm"
|
|
||||||
''
|
|
||||||
;; extends
|
|
||||||
|
|
||||||
(end_block_construct_statement) @keyword
|
|
||||||
(implicit_statement) @keyword
|
|
||||||
'';
|
|
||||||
|
|
||||||
conf = pkgs.writeText "init.lua" ''
|
|
||||||
local opt = vim.opt
|
|
||||||
local cmd = vim.api.nvim_command
|
|
||||||
local keymap = vim.keymap.set
|
|
||||||
local autocmd = vim.api.nvim_create_autocmd
|
|
||||||
|
|
||||||
-- Load plugins
|
|
||||||
opt.packpath = ${builtins.toJSON pack}
|
|
||||||
opt.runtimepath:prepend(${builtins.toJSON pack})
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Options
|
|
||||||
--
|
|
||||||
|
|
||||||
local cache = os.getenv('XDG_CACHE_HOME')..'/nvim'
|
|
||||||
opt.directory = cache..'/tmp'
|
|
||||||
opt.backupdir = cache..'/tmp'
|
|
||||||
opt.shadafile = cache..'/shada'
|
|
||||||
opt.undodir = cache..'/undo'
|
|
||||||
|
|
||||||
opt.hidden = true -- Hide buffers
|
|
||||||
opt.mouse = 'a' -- Enable mouse support
|
|
||||||
opt.ignorecase = true -- Case insensitive search...
|
|
||||||
opt.smartcase = true -- ...for lowercase terms
|
|
||||||
opt.splitkeep = 'screen' -- keep text still when splitting
|
|
||||||
opt.laststatus = 3 -- global statusline
|
|
||||||
|
|
||||||
opt.fsync = true -- Sync writes
|
|
||||||
opt.swapfile = false -- Disable swap files
|
|
||||||
opt.writebackup = true -- Backup file before overwriting...
|
|
||||||
opt.backup = false -- ...but delete it on success
|
|
||||||
opt.undofile = true -- Store all changes
|
|
||||||
|
|
||||||
opt.modeline = false -- Disable for Security
|
|
||||||
opt.showmatch = true -- Highlight matched parenthesis
|
|
||||||
opt.clipboard = 'unnamedplus' -- Yank to clipboard
|
|
||||||
|
|
||||||
-- Files to ignore
|
|
||||||
opt.wildignore = {
|
|
||||||
'*.so', '*.hi', '*.a', '*.la', '*.mod',
|
|
||||||
'*/__pycache__/*',
|
|
||||||
'*/dist/*',
|
|
||||||
'*/result/*',
|
|
||||||
'*/.git/*'
|
|
||||||
}
|
|
||||||
|
|
||||||
opt.shiftwidth = 2 -- Tabs
|
|
||||||
opt.tabstop = 2 --
|
|
||||||
opt.expandtab = true --
|
|
||||||
|
|
||||||
opt.number = true -- Line numbering
|
|
||||||
opt.smartindent = true -- Indentation
|
|
||||||
opt.showmode = false -- Disable printing of mode changes
|
|
||||||
opt.ruler = false -- Already in statusline
|
|
||||||
opt.fillchars = {
|
|
||||||
eob=' ', -- Hide ~ on empty lines
|
|
||||||
vert='│', -- make vertical split sign better
|
|
||||||
fold=' ', -- Hide . in fold markers
|
|
||||||
}
|
|
||||||
|
|
||||||
opt.foldmethod = "expr" -- Folding
|
|
||||||
opt.foldexpr = 'nvim_treesitter#foldexpr()' --
|
|
||||||
opt.foldlevel = 99 -- open by default
|
|
||||||
opt.foldopen:remove("search") -- don't open when searching
|
|
||||||
|
|
||||||
--
|
|
||||||
-- OSC 52 clipboard for SSH
|
|
||||||
--
|
|
||||||
|
|
||||||
if os.getenv('SSH_TTY') ~= nil then
|
|
||||||
vim.g.clipboard = {
|
|
||||||
name = 'OSC 52',
|
|
||||||
copy = {
|
|
||||||
['+'] = require('vim.ui.clipboard.osc52').copy('+'),
|
|
||||||
['*'] = require('vim.ui.clipboard.osc52').copy('*'),
|
|
||||||
},
|
|
||||||
paste = {
|
|
||||||
['+'] = require('vim.ui.clipboard.osc52').paste('+'),
|
|
||||||
['*'] = require('vim.ui.clipboard.osc52').paste('*'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Terminal mode
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Hide some UI elements
|
|
||||||
autocmd('TermOpen', {pattern='*', command='setlocal nonumber'})
|
|
||||||
autocmd('TermEnter', {pattern='*', command='set cmdheight=0 laststatus=0'})
|
|
||||||
autocmd('TermLeave', {pattern='*', command='set cmdheight=1 laststatus=3'})
|
|
||||||
autocmd('VimResized', {pattern='*', command='wincmd ='})
|
|
||||||
|
|
||||||
-- Exit without confirmation
|
|
||||||
autocmd('TermClose', {pattern='*', command='call feedkeys("\\<CR>")'})
|
|
||||||
|
|
||||||
keymap('t', '<C-b>', '<C-\\><C-n>', {noremap=true}) -- Easier escape
|
|
||||||
keymap('n', '<C-b>', '<Nop>', {noremap=true}) --
|
|
||||||
keymap('n', '<C-w>-', ':split +term<CR>', {silent=true}) -- Tmux-like moves
|
|
||||||
keymap('n', '<C-w>|', ':vsplit +term<CR>', {silent=true}) --
|
|
||||||
keymap('n', '<C-w>t', ':tabnew +term<CR>', {silent=true}) --
|
|
||||||
keymap('n', '<C-w>c', ':quit<CR>', {silent=true}) --
|
|
||||||
keymap('n', '<M-h>', '<C-w>h', {noremap=true}) --
|
|
||||||
keymap('n', '<M-j>', '<C-w>j', {noremap=true}) --
|
|
||||||
keymap('n', '<M-k>', '<C-w>k', {noremap=true}) --
|
|
||||||
keymap('n', '<M-l>', '<C-w>l', {noremap=true}) --
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Keybindings
|
|
||||||
--
|
|
||||||
|
|
||||||
vim.g.mapleader = ','
|
|
||||||
|
|
||||||
function listToggle()
|
|
||||||
for _, win in ipairs(vim.fn.getwininfo()) do
|
|
||||||
if win.loclist == 1 then return cmd('lclose') end
|
|
||||||
end
|
|
||||||
cmd('lopen')
|
|
||||||
end
|
|
||||||
|
|
||||||
fzf = require'fzf'
|
|
||||||
|
|
||||||
function searchFiles()
|
|
||||||
local query = [[\( -name .git -o -name __pycache__ -o -path ./dist -o -path ./build \) -prune -o -type f]]
|
|
||||||
coroutine.wrap(function()
|
|
||||||
res = fzf.fzf('find '..query, "", {border='none'})
|
|
||||||
cmd('edit '..res[1])
|
|
||||||
end)()
|
|
||||||
end
|
|
||||||
|
|
||||||
function searchCommands()
|
|
||||||
coroutine.wrap(function()
|
|
||||||
local history = {}
|
|
||||||
for i = 1, vim.fn.histnr("cmd") do
|
|
||||||
history[i] = vim.fn.histget("cmd", i)
|
|
||||||
end
|
|
||||||
res = fzf.fzf(history, "", {border='none'})
|
|
||||||
vim.fn.feedkeys(':'..res[1], 't')
|
|
||||||
end)()
|
|
||||||
end
|
|
||||||
|
|
||||||
keymap('n', '<C-p>', searchFiles, {silent=true}) -- Fuzzy search files
|
|
||||||
keymap('n', '<C-e>', searchCommands, {silent=true}) -- Fuzzy search command history
|
|
||||||
keymap('n', '<Leader>u', ':UndotreeToggle<CR>', {silent=true}) -- Toggle UndoTree
|
|
||||||
keymap('n', '<leader>l', listToggle, {silent=true}) -- Toggle Neomake errors
|
|
||||||
|
|
||||||
keymap('i', 'kj', '<ESC>', {noremap=true}) -- Exit with kj
|
|
||||||
keymap('n', 'o', 'o<ESC>', {noremap=true}) -- Add empty lines
|
|
||||||
keymap('n', 'O', 'O<ESC>', {noremap=true}) --
|
|
||||||
keymap('x', 'p', 'p:let @+=@0<CR>', {noremap=true, silent=true}) -- Keep selection after p
|
|
||||||
keymap('c', 'w!!', 'w !sudo tee >/dev/null %', {silent=true}) -- Save with sudo
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Colors
|
|
||||||
--
|
|
||||||
|
|
||||||
opt.bg = 'light' -- Use dark colors
|
|
||||||
|
|
||||||
function color(group, args)
|
|
||||||
vim.api.nvim_set_hl(0, group, args)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Source code
|
|
||||||
color('Cursor', {ctermfg=14})
|
|
||||||
color('Keyword', {ctermfg=04})
|
|
||||||
color('Define', {ctermfg=03})
|
|
||||||
color('Type', {ctermfg=06})
|
|
||||||
color('Identifier', {ctermfg=13})
|
|
||||||
color('Constant', {ctermfg=03})
|
|
||||||
color('Function', {ctermfg=02})
|
|
||||||
color('Include', {ctermfg=04})
|
|
||||||
color('Statement', {ctermfg=11})
|
|
||||||
color('String', {ctermfg=03})
|
|
||||||
color('Number', {ctermfg=04})
|
|
||||||
color('Comment', {ctermfg=07})
|
|
||||||
color('SpecialComment', {ctermfg=15})
|
|
||||||
color('Operator', {ctermfg='none'})
|
|
||||||
color('Conceal', {ctermfg='none', ctermbg='none'})
|
|
||||||
|
|
||||||
-- Text
|
|
||||||
color('Title', {ctermfg=4})
|
|
||||||
color('Special', {ctermfg=12})
|
|
||||||
color('Delimiter', {ctermfg=1})
|
|
||||||
color('PandocReferenceURL', {ctermfg=3})
|
|
||||||
color('PandocCiteKey', {ctermfg=6})
|
|
||||||
color('PandocTableDelims', {ctermfg=8})
|
|
||||||
color('texBeginEndName', {ctermfg=2})
|
|
||||||
color('Error', {ctermfg='none', ctermbg='none', underline=true})
|
|
||||||
|
|
||||||
-- Editor UI
|
|
||||||
color('NonText', {ctermfg=0})
|
|
||||||
color('LineNr', {ctermfg=8})
|
|
||||||
color('Pmenu', {ctermfg=12, ctermbg=0})
|
|
||||||
color('Folded', {ctermfg=7, ctermbg=0, cterm={}})
|
|
||||||
color('VertSplit', {ctermfg=8, cterm={}})
|
|
||||||
color('FoldColumn', {ctermfg='none', cterm={}})
|
|
||||||
color('Visual', {ctermfg='none', ctermbg=0})
|
|
||||||
color('Search', {ctermfg='none', ctermbg=0})
|
|
||||||
color('CurSearch', {ctermfg='none', ctermbg=0})
|
|
||||||
|
|
||||||
-- Diff mode
|
|
||||||
color('DiffAdd', {ctermfg=2, ctermbg=0, underline=true})
|
|
||||||
color('DiffChange', {ctermfg=3, ctermbg=0, underline=true})
|
|
||||||
color('DiffText', {ctermfg=1, ctermbg=0, underline=true})
|
|
||||||
color('DiffDelete', {ctermfg=0, ctermbg=1, underline=true})
|
|
||||||
|
|
||||||
-- Spelling
|
|
||||||
color('SpellBad', {ctermfg=1, ctermbg=0, underline=true})
|
|
||||||
color('SpellCap', {ctermfg=3, underline=true})
|
|
||||||
|
|
||||||
-- Neomake
|
|
||||||
color('NeomakeWarning', {ctermfg=3, underline=true})
|
|
||||||
color('ErrorMsg', {ctermfg=1, ctermbg='none'})
|
|
||||||
color('WarningMsg', {ctermfg=3})
|
|
||||||
|
|
||||||
-- Git signs
|
|
||||||
color('SignColumn', {ctermfg=1, ctermbg='none', cterm={}})
|
|
||||||
color('GitSignsAdd', {ctermfg=2})
|
|
||||||
color('GitSignsChange', {ctermfg=3})
|
|
||||||
color('GitSignsDelete', {ctermfg=1})
|
|
||||||
|
|
||||||
-- Statusline
|
|
||||||
color('StatusLine', {ctermfg=8, ctermbg=0, cterm={}})
|
|
||||||
color('StatusLineNC', {ctermfg=4, ctermbg=0, cterm={}})
|
|
||||||
color('User1', {ctermfg=8, ctermbg=0}) -- base
|
|
||||||
color('User2', {ctermfg=3, ctermbg=0}) -- location
|
|
||||||
color('StatusLineErr', {ctermfg=0, ctermbg=1})
|
|
||||||
color('StatusLineWarn', {ctermfg=0, ctermbg=3})
|
|
||||||
color('TablineTab', {ctermfg=8, ctermbg=0})
|
|
||||||
color('TablineTabCur', {ctermfg=7, ctermbg=8})
|
|
||||||
color('ModeNormal', {ctermfg=7, ctermbg=14})
|
|
||||||
color('ModeInsert', {ctermfg=7, ctermbg=1})
|
|
||||||
color('ModeCommand', {ctermfg=7, ctermbg=4})
|
|
||||||
color('ModeVisual', {ctermfg=7, ctermbg=3})
|
|
||||||
color('ModeVLine', {ctermfg=7, ctermbg=11})
|
|
||||||
color('ModeVBloc', {ctermfg=7, ctermbg=11})
|
|
||||||
color('ModeTerm', {ctermfg=7, ctermbg=2})
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Plugin options
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Neomake
|
|
||||||
vim.call('neomake#configure#automake', 'nwr', 750)
|
|
||||||
vim.g.neomake_warning_sign = {text='W→', texthl='WarningMsg'}
|
|
||||||
vim.g.neomake_error_sign = {text='E→', texthl='ErrorMsg'}
|
|
||||||
vim.g.neomake_highlight_lines = 1
|
|
||||||
vim.g.neomake_virtualtext_current_error = 0
|
|
||||||
vim.g.neomake_fortran_gfortran_args =
|
|
||||||
{'-fsyntax-only', '-Wall', '-Wextra', '-Jbuild/obj'}
|
|
||||||
|
|
||||||
-- Pandoc Markdown
|
|
||||||
autocmd({'BufNewFile', 'BufFilePre', 'BufRead'},
|
|
||||||
{pattern='*.md', command='set filetype=markdown.pandoc | TSBufDisable highlight'})
|
|
||||||
|
|
||||||
-- Git signs
|
|
||||||
gitsigns = require'gitsigns'
|
|
||||||
gitsigns.setup{signs={
|
|
||||||
add = {text='+'},
|
|
||||||
change = {text='δ'},
|
|
||||||
delete = {text='-'},
|
|
||||||
topdelete = {text='‾'},
|
|
||||||
changedelete = {text='~'}}
|
|
||||||
}
|
|
||||||
keymap('n', '<leader>gb', function() gitsigns.blame_line{full=true} end, {silent=true})
|
|
||||||
keymap('n', '<leader>gp', function() gitsigns.preview_hunk() end, {silent=true})
|
|
||||||
|
|
||||||
-- Tree-sitter
|
|
||||||
require'nvim-treesitter.configs'.setup{
|
|
||||||
highlight={enable=true},
|
|
||||||
indent={enable=true},
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Non built-in filetypes
|
|
||||||
autocmd({'BufNewFile', 'BufRead'},
|
|
||||||
{pattern='*.nix', command='setlocal filetype=nix'})
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Statusline
|
|
||||||
--
|
|
||||||
--
|
|
||||||
|
|
||||||
local frameline = require 'frameline'
|
|
||||||
local utils = frameline.utils
|
|
||||||
|
|
||||||
function mode(win, buf)
|
|
||||||
if not win.is_active then return end
|
|
||||||
|
|
||||||
local k = vim.api.nvim_get_mode().mode
|
|
||||||
local modes = {
|
|
||||||
n={'Normal', 'Normal'},
|
|
||||||
i={'Insert', 'Insert'},
|
|
||||||
R={'Replas', 'Insert'},
|
|
||||||
v={'Visual', 'Visual'},
|
|
||||||
V={'V⋅Line', 'VLine'},
|
|
||||||
t={'Termin', 'Term'},
|
|
||||||
['']={'V⋅Bloc', 'VBloc'}
|
|
||||||
}
|
|
||||||
return utils.highlight('Mode'..modes[k][2], ' '..modes[k][1]..' ')
|
|
||||||
end
|
|
||||||
|
|
||||||
function branch(_, buf)
|
|
||||||
local head = vim.fn.FugitiveHead()
|
|
||||||
if buf.modifiable and head ~= "" then return '⚑ '..head end
|
|
||||||
end
|
|
||||||
|
|
||||||
function filename(_, buf)
|
|
||||||
local delta = ""
|
|
||||||
if buf.modified then delta = 'Δ' end
|
|
||||||
if not buf.modifiable then delta = '∇' end
|
|
||||||
local fname = buf.name ~= "" and utils.filename or 'new-file'
|
|
||||||
return delta..fname
|
|
||||||
end
|
|
||||||
|
|
||||||
function neomake()
|
|
||||||
local res, msg = vim.call('neomake#statusline#LoclistCounts'), ""
|
|
||||||
if res.E then
|
|
||||||
msg = msg..utils.highlight('StatusLineErr', ' '..res.E..'E ')
|
|
||||||
end
|
|
||||||
if res.W then
|
|
||||||
msg = msg..utils.highlight('StatusLineWarn', ' '..res.W..'W ')
|
|
||||||
end
|
|
||||||
return msg
|
|
||||||
end
|
|
||||||
|
|
||||||
function readonly(_, buf)
|
|
||||||
if buf.modifiable and buf.readonly then return '∅' end
|
|
||||||
end
|
|
||||||
|
|
||||||
function encoding(_, buf)
|
|
||||||
return buf.fileencoding ~= "" and buf.fileencoding or nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function filetype(_, buf)
|
|
||||||
return buf.filetype ~= "" and buf.filetype or 'no ft'
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Tabline
|
|
||||||
frameline.setup_tabline(function()
|
|
||||||
local segments = {}
|
|
||||||
local api = vim.api
|
|
||||||
local color = '%#StatusLine#'
|
|
||||||
|
|
||||||
-- Tabs
|
|
||||||
local current = api.nvim_get_current_tabpage()
|
|
||||||
local label = ' %d %s '
|
|
||||||
for i, tab in pairs(api.nvim_list_tabpages()) do
|
|
||||||
-- tab -> active win -> active buf -> name
|
|
||||||
local active_buf = api.nvim_win_get_buf(api.nvim_tabpage_get_win(tab))
|
|
||||||
local name = api.nvim_buf_get_name(active_buf)
|
|
||||||
name = vim.fn.fnamemodify(name, ':t') -- filename only
|
|
||||||
|
|
||||||
local group = tab == current and 'TablineTabCur' or 'TablineTab'
|
|
||||||
table.insert(segments, utils.highlight(group, label:format(i, name)))
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(segments, color)
|
|
||||||
table.insert(segments, utils.split)
|
|
||||||
|
|
||||||
-- Current date
|
|
||||||
table.insert(segments, vim.fn.strftime("%a %H:%M"))
|
|
||||||
|
|
||||||
-- Battery level
|
|
||||||
local level = io.popen('cat /sys/class/power_supply/BAT*/capacity'):read()
|
|
||||||
local symbol = ' ∘ '..utils.highlight('User2', '⚡')..color
|
|
||||||
local battery = level and symbol..level..'%% ' or ""
|
|
||||||
table.insert(segments, battery)
|
|
||||||
|
|
||||||
return table.concat(segments)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Statusline
|
|
||||||
frameline.setup_statusline(function()
|
|
||||||
local segments = {}
|
|
||||||
|
|
||||||
-- Left section
|
|
||||||
table.insert(segments, utils.subsection{items={mode}})
|
|
||||||
table.insert(segments, utils.subsection{
|
|
||||||
separator=' → ',
|
|
||||||
items={branch, filename, readonly},
|
|
||||||
})
|
|
||||||
table.insert(segments, utils.split)
|
|
||||||
|
|
||||||
-- Right section
|
|
||||||
table.insert(segments, utils.subsection{items={neomake}})
|
|
||||||
table.insert(segments, utils.subsection{
|
|
||||||
user=2,
|
|
||||||
separator=':', stop=' ',
|
|
||||||
items={utils.line_number, utils.column_number},
|
|
||||||
})
|
|
||||||
table.insert(segments, utils.subsection{
|
|
||||||
user=1,
|
|
||||||
separator=' ∘ ',
|
|
||||||
items={utils.percent, encoding, filetype}
|
|
||||||
})
|
|
||||||
|
|
||||||
return segments
|
|
||||||
end)
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
# nix build -f '<nixpkgs/nixos>' pkgs.neovim for testing
|
|
||||||
nixpkgs.overlays = lib.singleton (self: super: {
|
|
||||||
neovim = neovim-wrapped;
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
22
packages.nix
22
packages.nix
@ -1,38 +1,36 @@
|
|||||||
{ config, pkgs, lib, ... }:
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
unstable = import <nixos-unstable> { };
|
||||||
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
nixpkgs.overlays = lib.singleton
|
nixpkgs.overlays = lib.singleton
|
||||||
(self: super:
|
(self: super:
|
||||||
{ maxwell-notify = self.callPackage ./custom/packages/maxwell-notify.nix
|
{ maxwell-notify = self.callPackage ./custom/packages/maxwell-notify.nix
|
||||||
{ homeserver = "https://${config.var.hostname}/_matrix/client/r0";
|
{ homeserver = "https://${config.var.hostname}/_matrix/client/r0";
|
||||||
roomId = "!mKSxsQWEtUvOBTfjDU:eurofusion.eu";
|
roomId = "!FsUSHSNMPMVTFFcvJo:maxwell.ydns.eu";
|
||||||
authToken = config.secrets.passwords.matrix;
|
authToken = config.secrets.passwords.matrix;
|
||||||
};
|
};
|
||||||
|
|
||||||
haskellPackages = super.haskellPackages.extend (hself: hsuper:
|
monero = unstable.monero;
|
||||||
{ breve = super.haskell.lib.overrideCabal hsuper.breve
|
element-web = unstable.element-web;
|
||||||
(old: { broken = false; });
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
# utilities
|
# utilities
|
||||||
iftop curl tree neovim
|
iftop curl ranger neovim
|
||||||
nix-script openssl
|
nix-script openssl
|
||||||
jq ack sshfs abduco
|
jq ack
|
||||||
|
|
||||||
# backup
|
# backup
|
||||||
bup git
|
bup git nfs-utils
|
||||||
|
|
||||||
# admin
|
# admin
|
||||||
dnsutils
|
dnsutils
|
||||||
matrix-synapse
|
matrix-synapse
|
||||||
matrix-synapse-tools.synadm
|
|
||||||
maxwell-notify
|
maxwell-notify
|
||||||
smartmontools
|
smartmontools
|
||||||
|
|
||||||
# namecoin
|
|
||||||
namecoind haskellPackages.rosa
|
|
||||||
];
|
];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
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
|
||||||
HnLfAynO1w==
|
yaeWJh6O2TknxiBRp76MR2GnjHmkBdADwm2PsoeH/dcXsPnTftZwsg==
|
||||||
|
@ -1,224 +1,224 @@
|
|||||||
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
|
||||||
|
@ -1,63 +1,44 @@
|
|||||||
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
|
Rm3AOulV1eQ8pzOfXrkmDYIwLE49yNGH+w==
|
||||||
kWlBFqtEV39j0lBVf/9JOEayveFbVqzzqpwZ+azyo64XQt6CFH92WzOTiS7/HThS
|
|
||||||
S+46LDSfIs64VU0wEKDJpbbLP4vLjC3s/9qWFrQ7f97DjSZl+zv3pMldF8pInv8x
|
|
||||||
UaXNG3C7tQd1pD433MGXDWeygNZ2Fuv139lrjzwlXYkrq9OZg+bdeNm/uSfAQSsW
|
|
||||||
U39HMaZrgc8cANoFXIabPAtnePdS3ne+qr2JhYCGztYfyFQlaa97FeveYxqXMtt+
|
|
||||||
go8FgTvg7mBEtv6IAKwty/VFv2C7s5TCdExAfg2KQETjEDmSN8OtxPTx4Xqf7q8K
|
|
||||||
rVI1vsXDKsgwG4Uh8oEtWH6PaImbkD6Dx1751frsTIqJmP/WJithNiim/KA8+aBC
|
|
||||||
G0RTdl2oDEyyS64i6g7oscPkRjOFxfT0asdSLJcK1EFaZ3epyYNN2EXSLPeHZiRD
|
|
||||||
y8cv+dGq/7Oi0Vyw62jH9e+6uQyKHdP4oKJ58Dbm8B7KkY58tfCME7lK96uZtxiM
|
|
||||||
qPeeJmB/tYUu2O4HQhkmopixhTxnPiJkIx8qfVgKkrl/JqXYJH8R6Ud2bYVW4HYR
|
|
||||||
Tr2fkfQONaSx4DE85UI80A49KA5X+mZR8XOcJRhOPQRFmIM//goJlmGdcCQ3ddCp
|
|
||||||
r6/C8gTwbLXvcvJ6cvkyn0Z042sgj8i7m6eXQADgM/eQo4ki1SlmwxJgzC2350/5
|
|
||||||
dxjUitBLijrNvUeepX4xOSFkr2Wu3u7aQXt+19fokaP+U2wbXKqCNqrumDVBPWbP
|
|
||||||
mxnO7rX9wCb6+kfBJiQ4Tqbsh/TxMKVOzK4xFB0vgJOACkj6a5dVjOaJshVwrA28
|
|
||||||
/F35nwIBo3ig4LaT271gImo+XY36TZEe42MtSJ+Oopy1ENsK4Ii8gORi3lQhsozC
|
|
||||||
RdwN8jgL4ejc+NS6/a4bjLFrt1oMka2xPuIaNGpX/EzuSO+syk2sxk2vg52Qk5Oa
|
|
||||||
Zobc4MULf3/vT4rpgQuCUgyeHqzDFd3SlpXllpb6MXbP0K/ZNtg+VTJ6aGOZafKv
|
|
||||||
F49gm+bcC70SEMqIqHV2Fn0Z6DFhnbeFtq/uUUNN0bMelHghnvNNQ6FvPZOOmPdH
|
|
||||||
YcHX4LzrTtak7Xfu5lWT1h5leWG5iEHTpzj3Lvkdfb1Q38maqGGv7TQEQkHhQIfe
|
|
||||||
/RRwMmPObImKt129XslxTT7g5j0=
|
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
U2FsdGVkX19SrNDcNQsdjfo/tfZAOq9SOyciusnUhmQwo3TTr+19L5zWErgFPF7n
|
|
||||||
FLIPBXWOHWJp8S8NwGbwGmg22Pp7rvns/5QeMVlwrRZErsdcmW16OOx36s0fO6Yj
|
|
||||||
xH2wLL8P3YDsEdTRAKwWmjzng/DCFH+3xhy2NbpsdAoiDIQTXt33v0damhj7Mj0I
|
|
||||||
Yc2uoSzYjGuV/AMmIW56uR9GLY27dFT+AdbSoQThRHiy9EMjPDsyg1e4a1TjOje+
|
|
||||||
Ls/DO7nGpqIwZx2ZMbo8glTJPR+oYnzepmGNB3ehdLsivJirDxbsIc6Js9uE36er
|
|
||||||
qB4usUsIrz8H6HYNg/VHVQBNm4BhLDf9ij0NZC1dryuY/zBGgLYHcoVa9Bj7qhOn
|
|
||||||
A3tWtKc9/MgINf/kRlMdchKZG6sGRc3VcsB9mDYngJeuiShuysmCgajBCahdY4O1
|
|
||||||
Ctx9VSIFhVlkrlZoE6SoCw/z3MmJsJa/7Aoa2c2hzbIj+m6C0LyEHbCleP+6zCAF
|
|
||||||
YY5aJdXWbr2mDTOiaFYprtcYC4A7ZEgt3c51H+Lx/r/GLzhU28BYc81SL4+9LUwv
|
|
||||||
4cDJf/HNTxxp4YyDl52BZD5wS2tzlgwW5ekkXWC3OVpcC0Nlstrd6blHGVM=
|
|
@ -1,3 +0,0 @@
|
|||||||
U2FsdGVkX1+iMoNxF8ynmoanDXQhWQndNxROETBHF/DNS0Guiy+YKkV6GhpotHjY
|
|
||||||
0xWuD3VVFP8zwOx3rWfnlvd34+cqrjgbBoyXDh9Q+mlMMGd3HMHJ1UxnXZSJV5Jx
|
|
||||||
6+NIKbE=
|
|
@ -1,5 +0,0 @@
|
|||||||
U2FsdGVkX1/iadIyfkanSYsP/deFkKz7qDB6n1fgcrrwJRFipVuyd1R5ph4g9+fs
|
|
||||||
jI0a9x+VDI5BWiEVnG6jPHH0uYf5OvAEJp+lGAB2Qqs3TyNEQzAUTs80Ag2V6SuF
|
|
||||||
KPAiahzl0afwe37jWmjktO7nuMqc5aZZdF+SpgFW9rM6UCsOwe7DYXMOWxUZ+7Lu
|
|
||||||
f1aCBQOgSq1ISkY2if80RrsYqDiOED7GuEWSoxRA7oS3LWVE0Ieic3WdvHm1+4gP
|
|
||||||
r8hqrKB44MtDSg/GCSlECM0jBy0yMfEqlY45EaogS54LUphox/o3luVaV+A=
|
|
1
secrets/keys/dnscrypt.pub
Normal file
1
secrets/keys/dnscrypt.pub
Normal file
@ -0,0 +1 @@
|
|||||||
|
U2FsdGVkX18b5NlteArdllLCmrXQfit7yWS6pgZ4896+2BLosJFP0y/BauxAkNjU
|
2
secrets/keys/dnscrypt.sec
Normal file
2
secrets/keys/dnscrypt.sec
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
U2FsdGVkX1+O0rTJQvBCLAdswjOexMM5VjlnGw3sVO/i1dYF/T0UrhTBhsqLboLA
|
||||||
|
jMv9wkzfbf2LiejBVhnOM0BEOU2FYRt5crZDkUAO090=
|
@ -1,9 +1,9 @@
|
|||||||
U2FsdGVkX190tK2fRtlU6XqGGKq0rzfIAZQz12ysC+Ltrjgwt95tduUxOcHr32x1
|
U2FsdGVkX1+Ay4K0S+xuJiyoMRj00oMkaw5sYVvu9VBR68aypK8eLTng8xoqKwzm
|
||||||
sXgMjc+ZjL76jQV5UVxCRSqpLj46lqJoAQX4CujTk1yoRbG0fZFGqgJr8OQIQGI3
|
uD1YNhLdh515CgHyMI7/LraT2yDYIlF+pNEfftH6U2qU5IfWSoukD59RscfaAft+
|
||||||
gkwVqtpMyxSE91o62IAmnHFztL+MUTJ+hZjpDo8IkcHDAZIXB7gRgrRUye1Jj0hV
|
/dend9Y6HyG1WdlyPyLVabruHFScx3d+oaLwEcgggnI/M9coWnHyvBXspo6E75um
|
||||||
f6eDIaw5P0wzzq5y7fNzbNzM4cK5IjWQtOyhrpcsmjaTzy2S9qzKib3xtAqwMfUC
|
gyvFntN4GmJLf1sMQIn0I7lW9djC8nupjSTstRo5HNLM/LwlhwAYRb/jbJUOkYSK
|
||||||
7or6RLdKWrDcUOZPJWp8nI/cbIITGpNA0hsSK+LV4gbSwzcfhyr00OCGHKMVTf/5
|
D/SDHW5p/9OrACQzAKHFB3mg4+9SufD+cju8qIAn9uFcyCJxkri6Mz+SGqdXtSgi
|
||||||
sEBFZmrcdsGxqvWe1D3Hf2CZS3e9iWzBlu4v78jHhLuXc/6+ltPFosZ5AwSJbnMo
|
fEZE2r1aCUXFa8Nq+qoYbxVue3BFlzxetC7fZrx2zWnmkSgOn7LWDn6q3B3KWeT5
|
||||||
m7tTLPMWMmUYmD4E5I2znITrtg7dtPYXItqsTMoHQJFMX0Kst/U6TqFF3Jcxv55s
|
om/g+Ph/RE4piKzm9m2jIx+0TlkUHlpOKAf4Xzwdaivmm6HaCNc5pt1Hw0le1fTW
|
||||||
uTS1p0I/kqldY4p8Fmz2XgNCgm0QvQu5UJukQkuAj0PVOdRtpOR47CqqF0basD82
|
JE+6BkXFDJz/8ytROujTGlMaMCB/JHgK04diEAnQJmNQnYVG03PxmRHmmqXc1czQ
|
||||||
LQ6EbLe863TL1hhdo+bP907A
|
OnIzyUraBCpBsHSAVsN/afC8
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
U2FsdGVkX1/H11TSJKN/qICnL3UpcHDu7b7mnYfvFE76gKtmueKaKgT45+XTI+Ye
|
|
||||||
3IBFJDs2p7goQawjXjbN4IWS+6q+DQFnLXcOwQiJiMJtCipLLDImg/TrK/9yyiC1
|
|
||||||
M7AmptY=
|
|
@ -1,3 +1,3 @@
|
|||||||
U2FsdGVkX1/UgigdAI8G1DADfHlOdqKX2TLIy/33gEUA+sADMY2MjCsBnVgirHe6
|
U2FsdGVkX19/6c3AzyWTN5p17ujhKlbDdk91iQs9z7Q0HiyA4L7BKFT/ZOk4VNqY
|
||||||
oFyts9qlwfUmRjiIiB1GYm0GhZ9YkgzZLgRSw7sSqtqiyjt6glZd7OXwt3/pyTs8
|
2Yh7r0b1F1ScFjvKH3aJ7jYGHT0i+w3LSHsufCDATEUejN/Z9JtEIXYaodOCJaYE
|
||||||
TY7U3rA=
|
YuIaLuBwWdkAPUl1lhs=
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
U2FsdGVkX1+j3nwfqf4SUkH2HoSAMqkoYJYgRDPRknNOur7PZ5Ctx+eh2RYufCXd
|
U2FsdGVkX1/StXpT1GfebxPB+1TyCHLo5fjFZLNkkWXnCS04WnREE2xlV7OXw0Iq
|
||||||
5yrTs/23jetpqjBGztmrPwZcN1AioGDEMKruyI+Cpr1RLE8SKrRjSvTXXK7nJHhI
|
llqZTflZ/z1hSz7NuUO/vrR57RRo6icf3UXnxvJ8HD6Z9q7uxI+WpIj+ME2zij6B
|
||||||
Fw==
|
Jg==
|
||||||
|
@ -1,2 +1,7 @@
|
|||||||
U2FsdGVkX1/284cSqdL3Tn4Yv682x+kNJ4OMUrOgaD05vJpoQ284b8Bji4PYvOYG
|
U2FsdGVkX19J8VE7lArWiwLIURQ8NjPEUwkOAh4m1oR0yrmBCI5u/vhwSQTC+ETb
|
||||||
UazEOc260aNPofw=
|
S3b80dqDKkR8QKRxIaquJHw/KRvQqKViZbu1OsHQTPQhK//mvs6vZ8G00vfucphc
|
||||||
|
6XIuiJS0u1zbzP6CKoLlkUyVOxFsmVSmxRx8460vgqK00JHSXf82mCAXcePVfHX9
|
||||||
|
uV9w34x3QkqSzmptx1orJrWa/Y/+et19ghJ/d6Utll+kg5Ldkd6vYcSA5bYFMe6L
|
||||||
|
LzAjJDLSvRpGkwP7EH2/9Kin5qDA7OUQmrXyFvmb9viCnYSD4TUaxXHYy4SPYcPY
|
||||||
|
qgFFeNDry5PAhkqLCTKgQWylCZXNbnA7JHp5fdbQCyRFD2sNxVN9ptuqNJd5x+hf
|
||||||
|
0PzTFokhgtE=
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
U2FsdGVkX1//NF0E4XObFG2/rvsrFG7AG6OaLwJDSaJlOed/DbGkXE1d7bTa9SX1
|
U2FsdGVkX18yVvW7ZvcS0Xc/LsBJmDBTjmHeODQqsVSq8AlzjHH0Z15cY2ibL0+2
|
||||||
7g7SeTKm7KdXsb8vxyjWbjRIBw/RQln3IM77bdDBCSCmsLp+HsHndv0QeXSUhSMP
|
/fq+Sb12nfYhXkdFePGNJl+pwTVN2KmQhQtTPUawwa0bmvqC3wPXmHn8O1AndVP9
|
||||||
rg==
|
8g==
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
U2FsdGVkX18Qd6gBIE7im5jGZlFK2r9QRPtRj/MtweDdMtXPxO/JbN8zxlGIibcx
|
U2FsdGVkX19MH3jJZJHEhLZLqIGcQCvd7JS2I8vWztP1Htde6A/xfy3zP8U6NUOc
|
||||||
5XxR5dtAQE5++pBw1mY8nxrtZIJLAWSS4r3TWDQnNr7XSM3wP8/kxCUL7KvhJ0d+
|
QPBYfycwXLqUM89gVrKnnj28HQiAzQNf2zzPqG7MOpQKA6zdRF6i9n+CGtvXC36u
|
||||||
Tw==
|
zQ==
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
U2FsdGVkX1/OVD62ZQPn1AUSo2ZWvjHbmfv9x52rv//gH+5wO0bApyxtHHsCZe63
|
U2FsdGVkX18X2ltRnCWQnXMSt/FSKiq/ScbhdjFP4wmPHi5njgtam/c1Dg+0T1fj
|
||||||
DbfdwiQNPCYKZOYvvf6tzqQsxbZwN0kBeyWKmxK35ZZpuVqA1RGgMB1pk7Ue1iBQ
|
JzOYe53LglUBfjDMbIepcIymHXPteizligpJzNE7DwuzsCp2JTkn9KWzKJb45Qa/
|
||||||
z9Ta00qOXeWw
|
/UtVdTfkS9WH
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
U2FsdGVkX186wcN9NuydqFDXes3OG2eoric99wmndrgVNV0RXxmEYK3MRkJHFYwj
|
|
||||||
24nAVlJ8yc8jzXbd7tcewDCzXn0Ac1ERKsuxvFw=
|
|
@ -1,6 +0,0 @@
|
|||||||
U2FsdGVkX19+JQDp/hdBBgL5TR0tiYujBpbUQ3e7ArhQI9xbGKeRiKi4Bk4tw8rV
|
|
||||||
MrxwU3Fk95sY75vsnU3uvkMSo6KFVbiOLiGTiFwnT3gwwHWKem1yxJCLmxcP+h4G
|
|
||||||
SGu8lcGpM4ZUy2yAnt7WowyzQiYmO0Vp8xP1RCmH0z2UdcDhqZB9LjKgEnpVSC4I
|
|
||||||
i5Se0fX9PB5/oWMCc0kPX9XYz0+/hPlgzcbaS6GT8mN0o08rHtMhN2gvV/xlONZ4
|
|
||||||
V0JXg7SYuTXz8cRtjLIr3sIwCOU+uBqrIHHvtjFclto0/zsFtfa00FomIMFDCv40
|
|
||||||
UHE2e7HJc4EXQT55QlcIbL4PdtxTI5gp+Id+eSI6vZF+dPWHKYO4Ug==
|
|
@ -1,2 +0,0 @@
|
|||||||
U2FsdGVkX1/r1JwvxVZfT2snMpDmQdldp0FvWJ+szrSoIpvmW0MRzqu9t5sC/hvC
|
|
||||||
XZPhTw7lwakhlw+sERpjQBxBN5TFVy1OBenOERtnnvW9D+E=
|
|
10
secrets/misc/asjon.env
Normal file
10
secrets/misc/asjon.env
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
U2FsdGVkX18ufnreJQQJJ52gMxajdK5bLn8A7Gqb3OvqThlWWb5mo4UV+VqEf/ob
|
||||||
|
VSydFr03zlSYuAuyvpHcunlTHnJR6RPgEdv0qV2NFBaVAlVjqJDgZHPKNLCp2Zws
|
||||||
|
LOgrWiaRGTKrBAD/80JlzsFyk5YVSXd9fTqo05bTym8qKv39vFrrmZQu1SOKRKmn
|
||||||
|
qrIUr9MjG25iLCxR6ajcANgfb3+hgQMo5ypr7AwjMp1PwkU/IWf1atIWFJzf0ZU1
|
||||||
|
4JOsDB4FvuX0hdi8J8LKRe+t0hsjQxb4FS3sMWrSDKhjvjRP+AEEwdj/3YbX856i
|
||||||
|
l9h2Yd36BtKOrwgrMQTS0pHvnUwj+o/4KeFrteccwgJP5bBJYVts10vg52FldNTg
|
||||||
|
qTrnnjVrjVm/by8Of435ttSXNmqn5g10MUKKLIIgNZXJcUY/fW4v07xduDHFMUYA
|
||||||
|
YJWfOfyR4Jlb2lJjmG0VwgPqhVMLAqFrL8XLGlqv1D/nKchktwp58cOqo95js+BT
|
||||||
|
Q8yvEzMbbtPM4MIGUhzfMbVhXFMmQRgfSpQFAPHe/33V0Ddsp7nCj0n7P+g0b8Ka
|
||||||
|
2BBS8ez8+7DCyTIerKCwB2+Hu9vy1bkhr8ugZXbmxvL3+fSHgMJ//KWFClQ=
|
2
secrets/misc/ydns.env
Normal file
2
secrets/misc/ydns.env
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
U2FsdGVkX1+nBNkZvBovUtzVk+hzQxFfQJ2NoORch7iPe33Zf+UIKOqkAWK3hjgb
|
||||||
|
aYDcTVL3ef1iD4saMpueUpoz36+TtwXAowPzGq0+BVLDyVikU9LM6QBlQQ==
|
@ -1,2 +0,0 @@
|
|||||||
U2FsdGVkX19LFU3NQZnhBBFTrDOdHX2yWfKjrFlvjtajfTZQhkpXIxRDiUnUntr4
|
|
||||||
lZ5dHXrJok1O7bWlVKCZ59zLtA+e5hHBrjxSnYIErWvnUUDO
|
|
@ -1,2 +1,2 @@
|
|||||||
U2FsdGVkX1/pO8dHm5elQ+5CIICbNLeWCuO5rVEPX6Are8Hxj9SYXfM47dWUTm4z
|
U2FsdGVkX19YVs+neL4R4JDT1CSsndTtbggYoDxEF2iwRCRDJRtrBBJthnxRrUsr
|
||||||
wLMdCqxgQBKTxxfs7l+P9RCDLG03K8d6Z9Aqw5S9j1oFoxxANQ==
|
c+A5NSSRRAu0LQ5vjaHlOYiCtmVCdYu7ECrpHQ40KqYgYhXJAw==
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
U2FsdGVkX19/kwiv3qT/dPLvt7EyuI9Bq8A8G3EPIHDqAOZNBnVpjoYL5Lohhjn1
|
|
||||||
rAlHir5QMigMm6nNBF0dIYh4vIKyWtD4g+6btiagcke3MYCtcz3zVdeZdzpVOHvP
|
|
||||||
tRoTA9ZQNw==
|
|
@ -1,2 +0,0 @@
|
|||||||
U2FsdGVkX1+N08asPnuy9GeOFSnq7pgilg4VwkPidE3n6qu934b9crw009t05+kE
|
|
||||||
kTfFouHdrcIDLX+Gry1kn3WSZNgIT47A8N9tSE2h33aRRqJV
|
|
@ -1,3 +1,3 @@
|
|||||||
U2FsdGVkX18ukCHRMwgdh9+FALCgM9f4u6hqx2xC/6OB/XhMSkRHllfF5GUJ6kYk
|
U2FsdGVkX1+1zBjw7Y2NlBeTLcGS8o3Er/ngQMU57HLCN8jSfKBU0/C4o9D4NDjl
|
||||||
O6UyamMNnMyrg0Us2RkP4ax95HskaPSt6uy7DsmV53cZ0hpoxQAfN+SxBLOU36TW
|
C7pRu3oOHmz0Pn9ipLaP87ST9RzVncHw/kqNBh8Dg29n3jNoTdSfwTn6xV/mBwQO
|
||||||
RhMdt6gT/t/zN+yBwFStJi13oCVQXWDMQA==
|
a4OsKusYMI/dCriATixomxe1GkC06YfwJg==
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
#! nix-shell -i bash --pure
|
#! nix-shell -i bash --pure
|
||||||
#! nix-shell -p bash openssl git unixtools.column perl
|
#! nix-shell -p bash openssl git unixtools.column
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -18,15 +18,37 @@ set -euo pipefail
|
|||||||
##### CONSTANTS
|
##### CONSTANTS
|
||||||
|
|
||||||
# the release version of this script
|
# the release version of this script
|
||||||
readonly VERSION='2.2.0'
|
readonly VERSION='2.0.0'
|
||||||
|
|
||||||
# the default cipher to utilize
|
# the default cipher to utilize
|
||||||
readonly DEFAULT_CIPHER='aes-256-ctr'
|
readonly DEFAULT_CIPHER='aes-256-ctr'
|
||||||
|
|
||||||
# arguments of the openssl enc command
|
# the openssl options to encrypt/decrypt the files
|
||||||
readonly ENCRYPT_OPTIONS='-pbkdf2 -iter 200000 -pass env:ENC_PASS'
|
# shellcheck disable=SC2016
|
||||||
|
readonly ENCRYPT_OPTIONS='-$cipher -pbkdf2 -iter 200000'
|
||||||
|
|
||||||
##### FUNCTIONS
|
# regular expression used to test user input
|
||||||
|
readonly YES_REGEX='^[Yy]$'
|
||||||
|
|
||||||
|
## Repository Metadata
|
||||||
|
|
||||||
|
# whether or not transcrypt is already configured
|
||||||
|
readonly CONFIGURED=$(git config --get --local transcrypt.version 2>/dev/null)
|
||||||
|
|
||||||
|
# the current git repository's top-level directory
|
||||||
|
readonly REPO=$(git rev-parse --show-toplevel 2>/dev/null)
|
||||||
|
|
||||||
|
# whether or not a HEAD revision exists
|
||||||
|
readonly HEAD_EXISTS=$(git rev-parse --verify --quiet HEAD 2>/dev/null)
|
||||||
|
|
||||||
|
# https://github.com/RichiH/vcsh
|
||||||
|
# whether or not the git repository is running under vcsh
|
||||||
|
readonly IS_VCSH=$(git config --get --local --bool vcsh.vcsh 2>/dev/null)
|
||||||
|
|
||||||
|
# whether or not the git repository is bare
|
||||||
|
readonly IS_BARE=$(git rev-parse --is-bare-repository 2>/dev/null)
|
||||||
|
|
||||||
|
## Git Directory Handling
|
||||||
|
|
||||||
# print a canonicalized absolute pathname
|
# print a canonicalized absolute pathname
|
||||||
realpath() {
|
realpath() {
|
||||||
@ -56,46 +78,21 @@ realpath() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# establish repository metadata and directory handling
|
# the current git repository's .git directory
|
||||||
# shellcheck disable=SC2155
|
RELATIVE_GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
|
||||||
gather_repo_metadata() {
|
readonly GIT_DIR=$(realpath "$RELATIVE_GIT_DIR" 2>/dev/null)
|
||||||
# whether or not transcrypt is already configured
|
|
||||||
readonly CONFIGURED=$(git config --get --local transcrypt.version 2>/dev/null)
|
|
||||||
|
|
||||||
# the current git repository's top-level directory
|
# the current git repository's gitattributes file
|
||||||
readonly REPO=$(git rev-parse --show-toplevel 2>/dev/null)
|
readonly CORE_ATTRIBUTES=$(git config --get --local --path core.attributesFile)
|
||||||
|
if [[ $CORE_ATTRIBUTES ]]; then
|
||||||
|
readonly GIT_ATTRIBUTES=$CORE_ATTRIBUTES
|
||||||
|
elif [[ $IS_BARE == 'true' ]] || [[ $IS_VCSH == 'true' ]]; then
|
||||||
|
readonly GIT_ATTRIBUTES="${GIT_DIR}/info/attributes"
|
||||||
|
else
|
||||||
|
readonly GIT_ATTRIBUTES="${REPO}/.gitattributes"
|
||||||
|
fi
|
||||||
|
|
||||||
# whether or not a HEAD revision exists
|
##### FUNCTIONS
|
||||||
readonly HEAD_EXISTS=$(git rev-parse --verify --quiet HEAD 2>/dev/null)
|
|
||||||
|
|
||||||
# https://github.com/RichiH/vcsh
|
|
||||||
# whether or not the git repository is running under vcsh
|
|
||||||
readonly IS_VCSH=$(git config --get --local --bool vcsh.vcsh 2>/dev/null)
|
|
||||||
|
|
||||||
# whether or not the git repository is bare
|
|
||||||
readonly IS_BARE=$(git rev-parse --is-bare-repository 2>/dev/null || printf 'false')
|
|
||||||
|
|
||||||
# the current git repository's .git directory
|
|
||||||
readonly RELATIVE_GIT_DIR=$(git rev-parse --git-dir 2>/dev/null || printf '')
|
|
||||||
readonly GIT_DIR=$(realpath "$RELATIVE_GIT_DIR" 2>/dev/null)
|
|
||||||
|
|
||||||
# Respect transcrypt.crypt-dir if present. Default to crypt/ in Git dir
|
|
||||||
readonly CRYPT_DIR=$(git config transcrypt.crypt-dir 2>/dev/null || printf '%s/crypt' "${RELATIVE_GIT_DIR}")
|
|
||||||
|
|
||||||
# respect core.hooksPath setting, without trailing slash. Fall back to default hooks dir
|
|
||||||
readonly GIT_HOOKS=$(git config core.hooksPath | sed 's:/*$::' 2>/dev/null || printf "%s/hooks" "${RELATIVE_GIT_DIR}")
|
|
||||||
|
|
||||||
# the current git repository's gitattributes file
|
|
||||||
local CORE_ATTRIBUTES
|
|
||||||
CORE_ATTRIBUTES=$(git config --get --local --path core.attributesFile 2>/dev/null || git config --get --path core.attributesFile 2>/dev/null || printf '')
|
|
||||||
if [[ $CORE_ATTRIBUTES ]]; then
|
|
||||||
readonly GIT_ATTRIBUTES=$CORE_ATTRIBUTES
|
|
||||||
elif [[ $IS_BARE == 'true' ]] || [[ $IS_VCSH == 'true' ]]; then
|
|
||||||
readonly GIT_ATTRIBUTES="${GIT_DIR}/info/attributes"
|
|
||||||
else
|
|
||||||
readonly GIT_ATTRIBUTES="${REPO}/.gitattributes"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# print a message to stderr
|
# print a message to stderr
|
||||||
warn() {
|
warn() {
|
||||||
@ -117,209 +114,26 @@ die() {
|
|||||||
exit "$st"
|
exit "$st"
|
||||||
}
|
}
|
||||||
|
|
||||||
# The `decryption -> encryption` process on an unchanged file must be
|
|
||||||
# deterministic for everything to work transparently. To do that, the same
|
|
||||||
# salt must be used each time we encrypt the same file. An HMAC has been
|
|
||||||
# proven to be a PRF, so we generate an HMAC-SHA256 for each decrypted file
|
|
||||||
# (keyed with a combination of the filename and transcrypt password), and
|
|
||||||
# then use the last 16 bytes of that HMAC for the file's unique salt.
|
|
||||||
|
|
||||||
git_clean() {
|
|
||||||
filename=$1
|
|
||||||
# ignore empty files
|
|
||||||
if [[ ! -s $filename ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
# cache STDIN to test if it's already encrypted
|
|
||||||
tempfile=$(mktemp 2>/dev/null || mktemp -t tmp)
|
|
||||||
trap 'rm -f "$tempfile"' EXIT
|
|
||||||
tee "$tempfile" &>/dev/null
|
|
||||||
# the first bytes of an encrypted file are always "Salted" in Base64
|
|
||||||
# The `head + LC_ALL=C tr` command handles binary data in old and new Bash (#116)
|
|
||||||
firstbytes=$(head -c8 "$tempfile" | LC_ALL=C tr -d '\0')
|
|
||||||
if [[ $firstbytes == "U2FsdGVk" ]]; then
|
|
||||||
cat "$tempfile"
|
|
||||||
else
|
|
||||||
cipher=$(git config --get --local transcrypt.cipher)
|
|
||||||
password=$(git config --get --local transcrypt.password)
|
|
||||||
openssl_path=$(git config --get --local transcrypt.openssl-path)
|
|
||||||
salt=$("${openssl_path}" dgst -hmac "${filename}:${password}" -sha256 "$tempfile" | tr -d '\r\n' | tail -c16)
|
|
||||||
|
|
||||||
openssl_major_version=$($openssl_path version | cut -d' ' -f2 | cut -d'.' -f1)
|
|
||||||
if [ "$openssl_major_version" -ge "3" ]; then
|
|
||||||
# Encrypt the file to base64, ensuring it includes the prefix 'Salted__' with the salt. #133
|
|
||||||
(
|
|
||||||
echo -n "Salted__" && echo -n "$salt" | perl -pe 's/(..)/chr(hex($1))/ge' &&
|
|
||||||
# Encrypt file to binary ciphertext
|
|
||||||
ENC_PASS="$password" "$openssl_path" enc -e -$cipher $ENCRYPT_OPTIONS -S "$salt" -in "$tempfile"
|
|
||||||
) |
|
|
||||||
openssl base64
|
|
||||||
else
|
|
||||||
# Encrypt file to base64 ciphertext
|
|
||||||
ENC_PASS="$password" "$openssl_path" enc -e -a -$cipher $ENCRYPT_OPTIONS -S "$salt" -in "$tempfile"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
git_smudge() {
|
|
||||||
tempfile=$(mktemp 2>/dev/null || mktemp -t tmp)
|
|
||||||
trap 'rm -f "$tempfile"' EXIT
|
|
||||||
cipher=$(git config --get --local transcrypt.cipher)
|
|
||||||
password=$(git config --get --local transcrypt.password)
|
|
||||||
openssl_path=$(git config --get --local transcrypt.openssl-path)
|
|
||||||
tee "$tempfile" | ENC_PASS="$password" "$openssl_path" enc -d -$cipher $ENCRYPT_OPTIONS -a 2>/dev/null || cat "$tempfile"
|
|
||||||
}
|
|
||||||
|
|
||||||
git_textconv() {
|
|
||||||
filename=$1
|
|
||||||
# ignore empty files
|
|
||||||
if [[ ! -s $filename ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
cipher=$(git config --get --local transcrypt.cipher)
|
|
||||||
password=$(git config --get --local transcrypt.password)
|
|
||||||
openssl_path=$(git config --get --local transcrypt.openssl-path)
|
|
||||||
ENC_PASS="$password" "$openssl_path" enc -d -$cipher $ENCRYPT_OPTIONS -a -in "$filename" 2>/dev/null || cat "$filename"
|
|
||||||
}
|
|
||||||
|
|
||||||
# shellcheck disable=SC2005,SC2002,SC2181
|
|
||||||
git_merge() {
|
|
||||||
# Get path to transcrypt in this script's directory
|
|
||||||
TRANSCRYPT_PATH="$(dirname "$0")/transcrypt"
|
|
||||||
# Look up name of local branch/ref to which changes are being merged
|
|
||||||
OURS_LABEL=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
# Look up name of the incoming "theirs" branch/ref being merged in.
|
|
||||||
# TODO There must be a better way of doing this than relying on this reflog
|
|
||||||
# action environment variable, but I don't know what it is
|
|
||||||
if [[ "$GIT_REFLOG_ACTION" = "merge "* ]]; then
|
|
||||||
THEIRS_LABEL=$(echo "$GIT_REFLOG_ACTION" | awk '{print $2}')
|
|
||||||
fi
|
|
||||||
if [[ ! "$THEIRS_LABEL" ]]; then
|
|
||||||
THEIRS_LABEL="theirs"
|
|
||||||
fi
|
|
||||||
# Decrypt BASE $1, LOCAL $2, and REMOTE $3 versions of file being merged
|
|
||||||
echo "$(cat "$1" | "${TRANSCRYPT_PATH}" smudge)" >"$1"
|
|
||||||
echo "$(cat "$2" | "${TRANSCRYPT_PATH}" smudge)" >"$2"
|
|
||||||
echo "$(cat "$3" | "${TRANSCRYPT_PATH}" smudge)" >"$3"
|
|
||||||
# Merge the decrypted files to the temp file named by $2
|
|
||||||
git merge-file --marker-size="$4" -L "$OURS_LABEL" -L base -L "$THEIRS_LABEL" "$2" "$1" "$3"
|
|
||||||
# If the merge was not successful (has conflicts) exit with an error code to
|
|
||||||
# leave the partially-merged file in place for a manual merge.
|
|
||||||
if [[ "$?" != "0" ]]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# If the merge was successful (no conflicts) re-encrypt the merged temp file $2
|
|
||||||
# which git will then update in the index in a following "Auto-merging" step.
|
|
||||||
# We must explicitly encrypt/clean the file, rather than leave Git to do it,
|
|
||||||
# because we can otherwise trigger safety check failure errors like:
|
|
||||||
# error: add_cacheinfo failed to refresh for path 'FILE'; merge aborting.
|
|
||||||
# To re-encrypt we must first copy the merged file to $5 (the name of the
|
|
||||||
# working-copy file) so the crypt `clean` script can generate the correct hash
|
|
||||||
# salt based on the file's real name, instead of the $2 temp file name.
|
|
||||||
cp "$2" "$5"
|
|
||||||
# Now we use the `clean` script to encrypt the merged file contents back to the
|
|
||||||
# temp file $2 where Git expects to find the merge result content.
|
|
||||||
cat "$5" | "${TRANSCRYPT_PATH}" clean "$5" >"$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
# shellcheck disable=SC2155
|
|
||||||
git_pre_commit() {
|
|
||||||
# Transcrypt pre-commit hook: fail if secret file in staging lacks the magic prefix "Salted" in B64
|
|
||||||
tmp=$(mktemp)
|
|
||||||
IFS=$'\n'
|
|
||||||
slow_mode_if_failed() {
|
|
||||||
for secret_file in $(git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }'); do
|
|
||||||
# Skip symlinks, they contain the linked target file path not plaintext
|
|
||||||
if [[ -L $secret_file ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get prefix of raw file in Git's index using the :FILENAME revision syntax
|
|
||||||
local firstbytes=$(git show :"${secret_file}" | head -c8)
|
|
||||||
# An empty file does not need to be, and is not, encrypted
|
|
||||||
if [[ $firstbytes == "" ]]; then
|
|
||||||
: # Do nothing
|
|
||||||
# The first bytes of an encrypted file must be "Salted" in Base64
|
|
||||||
elif [[ $firstbytes != "U2FsdGVk" ]]; then
|
|
||||||
printf 'Transcrypt managed file is not encrypted in the Git index: %s\n' "$secret_file" >&2
|
|
||||||
printf '\n' >&2
|
|
||||||
printf 'You probably staged this file using a tool that does not apply' >&2
|
|
||||||
printf ' .gitattribute filters as required by Transcrypt.\n' >&2
|
|
||||||
printf '\n' >&2
|
|
||||||
printf 'Fix this by re-staging the file with a compatible tool or with'
|
|
||||||
printf ' Git on the command line:\n' >&2
|
|
||||||
printf '\n' >&2
|
|
||||||
printf ' git rm --cached -- %s\n' "$secret_file" >&2
|
|
||||||
printf ' git add %s\n' "$secret_file" >&2
|
|
||||||
printf '\n' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# validate file to see if it failed or not, We don't care about the filename currently for speed, we only care about pass/fail, slow_mode_if_failed() is for what failed.
|
|
||||||
validate_file() {
|
|
||||||
secret_file=${1}
|
|
||||||
# Skip symlinks, they contain the linked target file path not plaintext
|
|
||||||
if [[ -L $secret_file ]]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
# Get prefix of raw file in Git's index using the :FILENAME revision syntax
|
|
||||||
# The first bytes of an encrypted file are always "Salted" in Base64
|
|
||||||
local firstbytes=$(git show :"${secret_file}" | head -c8)
|
|
||||||
if [[ $firstbytes != "U2FsdGVk" ]]; then
|
|
||||||
echo "true" >>"${tmp}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# if bash version is 4.4 or greater than fork to number of threads otherwise run normally
|
|
||||||
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]] && [[ "${BASH_VERSINFO[1]}" -ge 4 ]]; then
|
|
||||||
num_procs=$(nproc)
|
|
||||||
num_jobs="\j"
|
|
||||||
for secret_file in $(git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }'); do
|
|
||||||
while ((${num_jobs@P} >= num_procs)); do
|
|
||||||
wait -n
|
|
||||||
done
|
|
||||||
validate_file "${secret_file}" &
|
|
||||||
done
|
|
||||||
wait
|
|
||||||
if [[ -s ${tmp} ]]; then
|
|
||||||
slow_mode_if_failed
|
|
||||||
rm -f "${tmp}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
slow_mode_if_failed
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -f "${tmp}"
|
|
||||||
unset IFS
|
|
||||||
}
|
|
||||||
|
|
||||||
# verify that all requirements have been met
|
# verify that all requirements have been met
|
||||||
run_safety_checks() {
|
run_safety_checks() {
|
||||||
# validate that we're in a git repository
|
# validate that we're in a git repository
|
||||||
[[ $GIT_DIR ]] || die 'you are not currently in a git repository; did you forget to run "git init"?'
|
[[ $GIT_DIR ]] || die 'you are not currently in a git repository; did you forget to run "git init"?'
|
||||||
|
|
||||||
# exit if transcrypt is not in the required state
|
# exit if transcrypt is not in the required state
|
||||||
if [[ $ignore_config_status ]]; then
|
if [[ $requires_existing_config ]] && [[ ! $CONFIGURED ]]; then
|
||||||
: # no-op, no need to check $CONFIGURED status
|
|
||||||
elif [[ $requires_existing_config ]] && [[ ! $CONFIGURED ]]; then
|
|
||||||
die 1 'the current repository is not configured'
|
die 1 'the current repository is not configured'
|
||||||
elif [[ ! $requires_existing_config ]] && [[ $CONFIGURED ]]; then
|
elif [[ ! $requires_existing_config ]] && [[ $CONFIGURED ]]; then
|
||||||
die 1 'the current repository is already configured; see --display'
|
die 1 'the current repository is already configured; see --display'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# check for dependencies
|
# check for dependencies
|
||||||
for cmd in {column,grep,mktemp,"${openssl_path}",sed,tee}; do
|
for cmd in {column,grep,mktemp,openssl,sed,tee}; do
|
||||||
command -v "$cmd" >/dev/null || die 'required command "%s" was not found' "$cmd"
|
command -v $cmd >/dev/null || die 'required command "%s" was not found' "$cmd"
|
||||||
done
|
done
|
||||||
|
|
||||||
# ensure the repository is clean (if it has a HEAD revision) so we can force
|
# ensure the repository is clean (if it has a HEAD revision) so we can force
|
||||||
# checkout files without the destruction of uncommitted changes
|
# checkout files without the destruction of uncommitted changes
|
||||||
if [[ $requires_clean_repo ]] && [[ $HEAD_EXISTS ]] && [[ $IS_BARE == 'false' ]]; then
|
if [[ $requires_clean_repo ]] && [[ $HEAD_EXISTS ]] && [[ $IS_BARE == 'false' ]]; then
|
||||||
# ensure index is up-to-date before dirty check
|
|
||||||
git update-index -q --really-refresh
|
|
||||||
# check if the repo is dirty
|
# check if the repo is dirty
|
||||||
if ! git diff-index --quiet HEAD --; then
|
if ! git diff-index --quiet HEAD --; then
|
||||||
die 1 'the repo is dirty; commit or stash your changes before running transcrypt'
|
die 1 'the repo is dirty; commit or stash your changes before running transcrypt'
|
||||||
@ -329,20 +143,24 @@ run_safety_checks() {
|
|||||||
|
|
||||||
# unset the cipher variable if it is not supported by openssl
|
# unset the cipher variable if it is not supported by openssl
|
||||||
validate_cipher() {
|
validate_cipher() {
|
||||||
local list_cipher_commands
|
local list_cipher_commands
|
||||||
list_cipher_commands="${openssl_path} enc -ciphers"
|
list_cipher_commands='openssl enc -ciphers'
|
||||||
|
remove_dash() {
|
||||||
|
sed 's#\(^\| \)-#\1#g'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
local supported
|
local supported
|
||||||
supported=$($list_cipher_commands | tr -s ' ' '\n' | grep -Fx -- "-$cipher") || true
|
supported=$($list_cipher_commands | remove_dash | tr -s ' ' '\n' | grep --line-regexp "$cipher") || true
|
||||||
if [[ ! $supported ]]; then
|
if [[ ! $supported ]]; then
|
||||||
if [[ $interactive ]]; then
|
if [[ $interactive ]]; then
|
||||||
printf '"%s" is not a valid cipher; choose one of the following:\n\n' "$cipher"
|
printf '"%s" is not a valid cipher; choose one of the following:\n\n' "$cipher"
|
||||||
$list_cipher_commands | column -c 80
|
$list_cipher_commands | remove_dash | column -c 80
|
||||||
printf '\n'
|
printf '\n'
|
||||||
cipher=''
|
cipher=''
|
||||||
else
|
else
|
||||||
# shellcheck disable=SC2016
|
# shellcheck disable=SC2016
|
||||||
die 1 '"%s" is not a valid cipher; see `%s`' "$cipher" "$list_cipher_commands"
|
die 1 '"%s" is not a valid cipher; see `%s`' "$cipher" "$($list_cipher_commands | remove_dash)"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
@ -382,7 +200,7 @@ get_password() {
|
|||||||
if [[ $answer =~ $YES_REGEX ]] || [[ ! $answer ]]; then
|
if [[ $answer =~ $YES_REGEX ]] || [[ ! $answer ]]; then
|
||||||
local password_length=30
|
local password_length=30
|
||||||
local random_base64
|
local random_base64
|
||||||
random_base64=$(${openssl_path} rand -base64 $password_length)
|
random_base64=$(openssl rand -base64 $password_length)
|
||||||
password=$random_base64
|
password=$random_base64
|
||||||
else
|
else
|
||||||
printf 'Password: '
|
printf 'Password: '
|
||||||
@ -458,73 +276,100 @@ stage_rekeyed_files() {
|
|||||||
|
|
||||||
# save helper scripts under the repository's git directory
|
# save helper scripts under the repository's git directory
|
||||||
save_helper_scripts() {
|
save_helper_scripts() {
|
||||||
mkdir -p "${CRYPT_DIR}"
|
mkdir -p "${GIT_DIR}/crypt"
|
||||||
|
|
||||||
local current_transcrypt
|
openssl_command="openssl enc $ENCRYPT_OPTIONS -pass env:ENC_PASS"
|
||||||
current_transcrypt=$(realpath "$0" 2>/dev/null)
|
|
||||||
echo '#!/usr/bin/env bash' > "${CRYPT_DIR}/transcrypt"
|
|
||||||
tail -n +4 "$current_transcrypt" >> "${CRYPT_DIR}/transcrypt"
|
|
||||||
|
|
||||||
# make scripts executable
|
# The `decryption -> encryption` process on an unchanged file must be
|
||||||
for script in {transcrypt,}; do
|
# deterministic for everything to work transparently. To do that, the same
|
||||||
chmod 0755 "${CRYPT_DIR}/${script}"
|
# salt must be used each time we encrypt the same file. An HMAC has been
|
||||||
done
|
# proven to be a PRF, so we generate an HMAC-SHA256 for each decrypted file
|
||||||
}
|
# (keyed with a combination of the filename and transcrypt password), and
|
||||||
|
# then use the last 16 bytes of that HMAC for the file's unique salt.
|
||||||
|
|
||||||
# save helper hooks under the repository's git directory
|
cat <<-'EOF' >"${GIT_DIR}/crypt/clean"
|
||||||
save_helper_hooks() {
|
|
||||||
# Install pre-commit-crypt hook script
|
|
||||||
[[ ! -d "${GIT_HOOKS}" ]] && mkdir -p "${GIT_HOOKS}"
|
|
||||||
pre_commit_hook_installed="${GIT_HOOKS}/pre-commit-crypt"
|
|
||||||
cat <<-'EOF' >"$pre_commit_hook_installed"
|
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Transcrypt pre-commit hook: fail if secret file in staging lacks the magic prefix "Salted" in B64
|
filename=$1
|
||||||
RELATIVE_GIT_DIR=$(git rev-parse --git-dir 2>/dev/null || printf '')
|
# ignore empty files
|
||||||
CRYPT_DIR=$(git config transcrypt.crypt-dir 2>/dev/null || printf '%s/crypt' "${RELATIVE_GIT_DIR}")
|
if [[ -s $filename ]]; then
|
||||||
"${CRYPT_DIR}/transcrypt" pre_commit
|
# cache STDIN to test if it's already encrypted
|
||||||
|
tempfile=$(mktemp 2>/dev/null || mktemp -t tmp)
|
||||||
|
trap 'rm -f "$tempfile"' EXIT
|
||||||
|
tee "$tempfile" &>/dev/null
|
||||||
|
# the first bytes of an encrypted file are always "Salted" in Base64
|
||||||
|
read -n 8 firstbytes <"$tempfile"
|
||||||
|
if [[ $firstbytes == "U2FsdGVk" ]]; then
|
||||||
|
cat "$tempfile"
|
||||||
|
else
|
||||||
|
cipher=$(git config --get --local transcrypt.cipher)
|
||||||
|
password=$(git config --get --local transcrypt.password)
|
||||||
|
salt=$(openssl dgst -hmac "${filename}:${password}" -sha256 "$filename" | tr -d '\r\n' | tail -c 16)
|
||||||
|
ENC_PASS=$password @openssl_command@ -e -a -S "$salt" -in "$tempfile"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Activate hook by copying it to the pre-commit script name, if safe to do so
|
cat <<-'EOF' >"${GIT_DIR}/crypt/smudge"
|
||||||
pre_commit_hook="${GIT_HOOKS}/pre-commit"
|
#!/usr/bin/env bash
|
||||||
if [[ -f "$pre_commit_hook" ]]; then
|
tempfile=$(mktemp 2>/dev/null || mktemp -t tmp)
|
||||||
printf 'WARNING:\n' >&2
|
trap 'rm -f "$tempfile"' EXIT
|
||||||
printf 'Cannot install Git pre-commit hook script because file already exists: %s\n' "$pre_commit_hook" >&2
|
cipher=$(git config --get --local transcrypt.cipher)
|
||||||
printf 'Please manually install the pre-commit script saved as: %s\n' "$pre_commit_hook_installed" >&2
|
password=$(git config --get --local transcrypt.password)
|
||||||
printf '\n'
|
tee "$tempfile" | ENC_PASS=$password @openssl_command@ -d -a 2>/dev/null || cat "$tempfile"
|
||||||
else
|
EOF
|
||||||
cp "$pre_commit_hook_installed" "$pre_commit_hook"
|
|
||||||
chmod 0755 "$pre_commit_hook"
|
cat <<-'EOF' >"${GIT_DIR}/crypt/textconv"
|
||||||
fi
|
#!/usr/bin/env bash
|
||||||
|
filename=$1
|
||||||
|
# ignore empty files
|
||||||
|
if [[ -s $filename ]]; then
|
||||||
|
cipher=$(git config --get --local transcrypt.cipher)
|
||||||
|
password=$(git config --get --local transcrypt.password)
|
||||||
|
ENC_PASS=$password @openssl_command@ -d -a -in "$filename" 2>/dev/null || cat "$filename"
|
||||||
|
fi
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# make scripts executable
|
||||||
|
for script in {clean,smudge,textconv}; do
|
||||||
|
chmod 0755 "${GIT_DIR}/crypt/${script}"
|
||||||
|
sed "s/@openssl_command@/$openssl_command/" -i "${GIT_DIR}/crypt/${script}"
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# write the configuration to the repository's git config
|
# write the configuration to the repository's git config
|
||||||
save_configuration() {
|
save_configuration() {
|
||||||
save_helper_scripts
|
save_helper_scripts
|
||||||
save_helper_hooks
|
|
||||||
|
|
||||||
# write the encryption info
|
# write the encryption info
|
||||||
git config transcrypt.version "$VERSION"
|
git config transcrypt.version "$VERSION"
|
||||||
git config transcrypt.cipher "$cipher"
|
git config transcrypt.cipher "$cipher"
|
||||||
git config transcrypt.password "$password"
|
git config transcrypt.password "$password"
|
||||||
git config transcrypt.openssl-path "$openssl_path"
|
|
||||||
|
|
||||||
# write the filter settings. Sorry for the horrific quote escaping below...
|
# write the filter settings
|
||||||
# shellcheck disable=SC2016
|
if [[ -d $(git rev-parse --git-common-dir) ]]; then
|
||||||
git config filter.crypt.clean '"$(git config transcrypt.crypt-dir 2>/dev/null || printf ''%s/crypt'' ""$(git rev-parse --git-dir)"")"/transcrypt clean %f'
|
# this allows us to support multiple working trees via git-worktree
|
||||||
# shellcheck disable=SC2016
|
# ...but the --git-common-dir flag was only added in November 2014
|
||||||
git config filter.crypt.smudge '"$(git config transcrypt.crypt-dir 2>/dev/null || printf ''%s/crypt'' ""$(git rev-parse --git-dir)"")"/transcrypt smudge'
|
# shellcheck disable=SC2016
|
||||||
# shellcheck disable=SC2016
|
git config filter.crypt.clean '"$(git rev-parse --git-common-dir)"/crypt/clean %f'
|
||||||
git config diff.crypt.textconv '"$(git config transcrypt.crypt-dir 2>/dev/null || printf ''%s/crypt'' ""$(git rev-parse --git-dir)"")"/transcrypt textconv'
|
# shellcheck disable=SC2016
|
||||||
# shellcheck disable=SC2016
|
git config filter.crypt.smudge '"$(git rev-parse --git-common-dir)"/crypt/smudge'
|
||||||
git config merge.crypt.driver '"$(git config transcrypt.crypt-dir 2>/dev/null || printf ''%s/crypt'' ""$(git rev-parse --git-dir)"")"/transcrypt merge %O %A %B %L %P'
|
# shellcheck disable=SC2016
|
||||||
|
git config diff.crypt.textconv '"$(git rev-parse --git-common-dir)"/crypt/textconv'
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
git config filter.crypt.clean '"$(git rev-parse --git-dir)"/crypt/clean %f'
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
git config filter.crypt.smudge '"$(git rev-parse --git-dir)"/crypt/smudge'
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
git config diff.crypt.textconv '"$(git rev-parse --git-dir)"/crypt/textconv'
|
||||||
|
fi
|
||||||
git config filter.crypt.required 'true'
|
git config filter.crypt.required 'true'
|
||||||
git config diff.crypt.cachetextconv 'true'
|
git config diff.crypt.cachetextconv 'true'
|
||||||
git config diff.crypt.binary 'true'
|
git config diff.crypt.binary 'true'
|
||||||
git config merge.renormalize 'true'
|
git config merge.renormalize 'true'
|
||||||
git config merge.crypt.name 'Merge transcrypt secret files'
|
|
||||||
|
|
||||||
# add a git alias for listing encrypted files
|
# add a git alias for listing encrypted files
|
||||||
git config alias.ls-crypt "!git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; /crypt$/{ print \$1 }'"
|
git config alias.ls-crypt "!git ls-files | git check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; /crypt$/{ print \$1 }'"
|
||||||
}
|
}
|
||||||
|
|
||||||
# display the current configuration settings
|
# display the current configuration settings
|
||||||
@ -551,7 +396,6 @@ clean_gitconfig() {
|
|||||||
git config --remove-section transcrypt 2>/dev/null || true
|
git config --remove-section transcrypt 2>/dev/null || true
|
||||||
git config --remove-section filter.crypt 2>/dev/null || true
|
git config --remove-section filter.crypt 2>/dev/null || true
|
||||||
git config --remove-section diff.crypt 2>/dev/null || true
|
git config --remove-section diff.crypt 2>/dev/null || true
|
||||||
git config --remove-section merge.crypt 2>/dev/null || true
|
|
||||||
git config --unset merge.renormalize
|
git config --unset merge.renormalize
|
||||||
|
|
||||||
# remove the merge section if it's now empty
|
# remove the merge section if it's now empty
|
||||||
@ -562,20 +406,6 @@ clean_gitconfig() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Remove from the local Git DB any objects containing the cached plaintext of
|
|
||||||
# secret files, created due to the setting diff.crypt.cachetextconv='true'
|
|
||||||
remove_cached_plaintext() {
|
|
||||||
# Delete ref to cached plaintext objects, to leave these objects
|
|
||||||
# unreferenced and available for removal
|
|
||||||
git update-ref -d refs/notes/textconv/crypt
|
|
||||||
|
|
||||||
# Remove ANY unreferenced objects in Git's object DB (packed or unpacked),
|
|
||||||
# to ensure that cached plaintext objects are also removed.
|
|
||||||
# The vital sub-commands equivalents we require this `gc` command to do are:
|
|
||||||
# `git prune`, `git repack -ad`
|
|
||||||
git gc --prune=now --quiet
|
|
||||||
}
|
|
||||||
|
|
||||||
# force the checkout of any files with the crypt filter applied to them;
|
# force the checkout of any files with the crypt filter applied to them;
|
||||||
# this will decrypt existing encrypted files if you've just cloned a repository,
|
# this will decrypt existing encrypted files if you've just cloned a repository,
|
||||||
# or it will encrypt locally decrypted files if you've just flushed the credentials
|
# or it will encrypt locally decrypted files if you've just flushed the credentials
|
||||||
@ -589,7 +419,7 @@ force_checkout() {
|
|||||||
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
for file in $encrypted_files; do
|
for file in $encrypted_files; do
|
||||||
rm -f "$file"
|
rm "$file"
|
||||||
git checkout --force HEAD -- "$file" >/dev/null
|
git checkout --force HEAD -- "$file" >/dev/null
|
||||||
done
|
done
|
||||||
unset IFS
|
unset IFS
|
||||||
@ -603,8 +433,7 @@ flush_credentials() {
|
|||||||
|
|
||||||
if [[ $interactive ]]; then
|
if [[ $interactive ]]; then
|
||||||
printf 'You are about to flush the local credentials; make sure you have saved them elsewhere.\n'
|
printf 'You are about to flush the local credentials; make sure you have saved them elsewhere.\n'
|
||||||
printf 'All previously decrypted files will revert to their encrypted form, and your\n'
|
printf 'All previously decrypted files will revert to their encrypted form.\n\n'
|
||||||
printf 'repo will be garbage collected to remove any cached plaintext of secret files.\n\n'
|
|
||||||
printf 'Proceed with credential flush? [y/N] '
|
printf 'Proceed with credential flush? [y/N] '
|
||||||
read -r answer
|
read -r answer
|
||||||
printf '\n'
|
printf '\n'
|
||||||
@ -617,8 +446,6 @@ flush_credentials() {
|
|||||||
if [[ $answer =~ $YES_REGEX ]]; then
|
if [[ $answer =~ $YES_REGEX ]]; then
|
||||||
clean_gitconfig
|
clean_gitconfig
|
||||||
|
|
||||||
remove_cached_plaintext
|
|
||||||
|
|
||||||
# re-encrypt any files that had been previously decrypted
|
# re-encrypt any files that had been previously decrypted
|
||||||
force_checkout
|
force_checkout
|
||||||
|
|
||||||
@ -634,8 +461,7 @@ uninstall_transcrypt() {
|
|||||||
|
|
||||||
if [[ $interactive ]]; then
|
if [[ $interactive ]]; then
|
||||||
printf 'You are about to remove all transcrypt configuration from your repository.\n'
|
printf 'You are about to remove all transcrypt configuration from your repository.\n'
|
||||||
printf 'All previously encrypted files will remain decrypted in this working copy, but your\n'
|
printf 'All previously encrypted files will remain decrypted in this working copy.\n\n'
|
||||||
printf 'repo will be garbage collected to remove any cached plaintext of secret files.\n\n'
|
|
||||||
printf 'Proceed with uninstall? [y/N] '
|
printf 'Proceed with uninstall? [y/N] '
|
||||||
read -r answer
|
read -r answer
|
||||||
printf '\n'
|
printf '\n'
|
||||||
@ -648,30 +474,11 @@ uninstall_transcrypt() {
|
|||||||
if [[ $answer =~ $YES_REGEX ]]; then
|
if [[ $answer =~ $YES_REGEX ]]; then
|
||||||
clean_gitconfig
|
clean_gitconfig
|
||||||
|
|
||||||
if [[ ! $upgrade ]]; then
|
|
||||||
remove_cached_plaintext
|
|
||||||
fi
|
|
||||||
|
|
||||||
# remove helper scripts
|
# remove helper scripts
|
||||||
# Keep obsolete clean,smudge,textconv,merge refs here to remove them on upgrade
|
for script in {clean,smudge,textconv}; do
|
||||||
for script in {transcrypt,clean,smudge,textconv,merge}; do
|
[[ ! -f "${GIT_DIR}/crypt/${script}" ]] || rm "${GIT_DIR}/crypt/${script}"
|
||||||
[[ ! -f "${CRYPT_DIR}/${script}" ]] || rm "${CRYPT_DIR}/${script}"
|
|
||||||
done
|
done
|
||||||
[[ ! -d "${CRYPT_DIR}" ]] || rmdir "${CRYPT_DIR}"
|
[[ ! -d "${GIT_DIR}/crypt" ]] || rmdir "${GIT_DIR}/crypt"
|
||||||
|
|
||||||
# rename helper hooks (don't delete, in case user has custom changes)
|
|
||||||
pre_commit_hook="${GIT_HOOKS}/pre-commit"
|
|
||||||
pre_commit_hook_installed="${GIT_HOOKS}/pre-commit-crypt"
|
|
||||||
if [[ -f "$pre_commit_hook" ]]; then
|
|
||||||
hook_md5=$("${openssl_path}" md5 -hex <"$pre_commit_hook")
|
|
||||||
installed_md5=$("${openssl_path}" md5 -hex <"$pre_commit_hook_installed")
|
|
||||||
if [[ "$hook_md5" = "$installed_md5" ]]; then
|
|
||||||
rm "$pre_commit_hook"
|
|
||||||
else
|
|
||||||
printf 'WARNING: Cannot safely disable Git pre-commit hook %s please check it yourself\n' "$pre_commit_hook"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
[[ -f "$pre_commit_hook_installed" ]] && rm "$pre_commit_hook_installed"
|
|
||||||
|
|
||||||
# touch all encrypted files to prevent stale stat info
|
# touch all encrypted files to prevent stale stat info
|
||||||
local encrypted_files
|
local encrypted_files
|
||||||
@ -696,85 +503,23 @@ uninstall_transcrypt() {
|
|||||||
case $OSTYPE in
|
case $OSTYPE in
|
||||||
darwin*)
|
darwin*)
|
||||||
/usr/bin/sed -i '' '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
/usr/bin/sed -i '' '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
||||||
/usr/bin/sed -i '' '/filter=crypt diff=crypt merge=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
|
||||||
;;
|
;;
|
||||||
linux*)
|
linux*)
|
||||||
sed -i '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
sed -i '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
||||||
sed -i '/filter=crypt diff=crypt merge=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [[ ! $upgrade ]]; then
|
printf 'The transcrypt configuration has been completely removed from the repository.\n'
|
||||||
printf 'The transcrypt configuration has been completely removed from the repository.\n'
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
die 1 'uninstallation has been aborted'
|
die 1 'uninstallation has been aborted'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# uninstall and re-install transcrypt to upgrade scripts and update configuration
|
|
||||||
upgrade_transcrypt() {
|
|
||||||
CURRENT_VERSION=$(git config --get --local transcrypt.version 2>/dev/null)
|
|
||||||
|
|
||||||
if [[ $interactive ]]; then
|
|
||||||
printf 'You are about to upgrade the transcrypt scripts in your repository.\n'
|
|
||||||
printf 'Your configuration settings will not be changed.\n\n'
|
|
||||||
printf ' Current version: %s\n' "$CURRENT_VERSION"
|
|
||||||
printf 'Upgraded version: %s\n\n' "$VERSION"
|
|
||||||
printf 'Proceed with upgrade? [y/N] '
|
|
||||||
read -r answer
|
|
||||||
printf '\n'
|
|
||||||
|
|
||||||
if [[ $answer =~ $YES_REGEX ]]; then
|
|
||||||
# User confirmed, don't prompt again
|
|
||||||
interactive=''
|
|
||||||
else
|
|
||||||
# User did not confirm, exit
|
|
||||||
# Exit if user did not confirm
|
|
||||||
die 1 'upgrade has been aborted'
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Keep current cipher and password
|
|
||||||
cipher=$(git config --get --local transcrypt.cipher)
|
|
||||||
password=$(git config --get --local transcrypt.password)
|
|
||||||
# Keep current openssl-path, or set to default if no existing value
|
|
||||||
openssl_path=$(git config --get --local transcrypt.openssl-path 2>/dev/null || printf '%s' "$openssl_path")
|
|
||||||
|
|
||||||
# Keep contents of .gitattributes
|
|
||||||
ORIG_GITATTRIBUTES=$(cat "$GIT_ATTRIBUTES")
|
|
||||||
|
|
||||||
uninstall_transcrypt
|
|
||||||
save_configuration
|
|
||||||
|
|
||||||
# Re-instate contents of .gitattributes
|
|
||||||
echo "$ORIG_GITATTRIBUTES" >"$GIT_ATTRIBUTES"
|
|
||||||
|
|
||||||
# Update .gitattributes for transcrypt'ed files to include "merge=crypt" config
|
|
||||||
case $OSTYPE in
|
|
||||||
darwin*)
|
|
||||||
/usr/bin/sed -i '' 's/=crypt\(.*\)/=crypt diff=crypt merge=crypt/' "$GIT_ATTRIBUTES"
|
|
||||||
;;
|
|
||||||
linux*)
|
|
||||||
sed -i 's/=crypt\(.*\)/=crypt diff=crypt merge=crypt/' "$GIT_ATTRIBUTES"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
printf 'Upgrade is complete\n'
|
|
||||||
|
|
||||||
LATEST_GITATTRIBUTES=$(cat "$GIT_ATTRIBUTES")
|
|
||||||
if [[ "$LATEST_GITATTRIBUTES" != "$ORIG_GITATTRIBUTES" ]]; then
|
|
||||||
printf '\nYour gitattributes file has been updated with the latest recommended values.\n'
|
|
||||||
printf 'Please review and commit the new values in:\n'
|
|
||||||
printf '%s\n' "$GIT_ATTRIBUTES"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# list all of the currently encrypted files in the repository
|
# list all of the currently encrypted files in the repository
|
||||||
list_files() {
|
list_files() {
|
||||||
if [[ $IS_BARE == 'false' ]]; then
|
if [[ $IS_BARE == 'false' ]]; then
|
||||||
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
||||||
git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }'
|
git ls-files | git check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,8 +528,8 @@ show_raw_file() {
|
|||||||
if [[ -f $show_file ]]; then
|
if [[ -f $show_file ]]; then
|
||||||
# ensure the file is currently being tracked
|
# ensure the file is currently being tracked
|
||||||
local escaped_file=${show_file//\//\\\/}
|
local escaped_file=${show_file//\//\\\/}
|
||||||
if git -c core.quotePath=false ls-files --others -- "$show_file" | awk "/${escaped_file}/{ exit 1 }"; then
|
if git ls-files --others -- "$show_file" | awk "/${escaped_file}/{ exit 1 }"; then
|
||||||
file_paths=$(git -c core.quotePath=false ls-tree --name-only --full-name HEAD "$show_file")
|
file_paths=$(git ls-tree --name-only --full-name HEAD "$show_file")
|
||||||
else
|
else
|
||||||
die 1 'the file "%s" is not currently being tracked by git' "$show_file"
|
die 1 'the file "%s" is not currently being tracked by git' "$show_file"
|
||||||
fi
|
fi
|
||||||
@ -817,10 +562,10 @@ export_gpg() {
|
|||||||
current_cipher=$(git config --get --local transcrypt.cipher)
|
current_cipher=$(git config --get --local transcrypt.cipher)
|
||||||
local current_password
|
local current_password
|
||||||
current_password=$(git config --get --local transcrypt.password)
|
current_password=$(git config --get --local transcrypt.password)
|
||||||
mkdir -p "${CRYPT_DIR}"
|
mkdir -p "${GIT_DIR}/crypt"
|
||||||
|
|
||||||
local gpg_encrypt_cmd="gpg --batch --recipient $gpg_recipient --trust-model always --yes --armor --quiet --encrypt -"
|
local gpg_encrypt_cmd="gpg --batch --recipient $gpg_recipient --trust-model always --yes --armor --quiet --encrypt -"
|
||||||
printf 'password=%s\ncipher=%s\n' "$current_password" "$current_cipher" | $gpg_encrypt_cmd >"${CRYPT_DIR}/${gpg_recipient}.asc"
|
printf 'password=%s\ncipher=%s\n' "$current_password" "$current_cipher" | $gpg_encrypt_cmd >"${GIT_DIR}/crypt/${gpg_recipient}.asc"
|
||||||
printf "The transcrypt configuration has been encrypted and exported to:\n%s/crypt/%s.asc\n" "$GIT_DIR" "$gpg_recipient"
|
printf "The transcrypt configuration has been encrypted and exported to:\n%s/crypt/%s.asc\n" "$GIT_DIR" "$gpg_recipient"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -830,10 +575,10 @@ import_gpg() {
|
|||||||
command -v gpg >/dev/null || die 'required command "gpg" was not found'
|
command -v gpg >/dev/null || die 'required command "gpg" was not found'
|
||||||
|
|
||||||
local path
|
local path
|
||||||
if [[ -f "${CRYPT_DIR}/${gpg_import_file}" ]]; then
|
if [[ -f "${GIT_DIR}/crypt/${gpg_import_file}" ]]; then
|
||||||
path="${CRYPT_DIR}/${gpg_import_file}"
|
path="${GIT_DIR}/crypt/${gpg_import_file}"
|
||||||
elif [[ -f "${CRYPT_DIR}/${gpg_import_file}.asc" ]]; then
|
elif [[ -f "${GIT_DIR}/crypt/${gpg_import_file}.asc" ]]; then
|
||||||
path="${CRYPT_DIR}/${gpg_import_file}.asc"
|
path="${GIT_DIR}/crypt/${gpg_import_file}.asc"
|
||||||
elif [[ ! -f $gpg_import_file ]]; then
|
elif [[ ! -f $gpg_import_file ]]; then
|
||||||
die 1 'the file "%s" does not exist' "$gpg_import_file"
|
die 1 'the file "%s" does not exist' "$gpg_import_file"
|
||||||
else
|
else
|
||||||
@ -891,9 +636,6 @@ help() {
|
|||||||
the password to derive the key from;
|
the password to derive the key from;
|
||||||
defaults to 30 random base64 characters
|
defaults to 30 random base64 characters
|
||||||
|
|
||||||
--set-openssl-path=PATH_TO_OPENSSL
|
|
||||||
use OpenSSL at this path; defaults to 'openssl' in \$PATH
|
|
||||||
|
|
||||||
-y, --yes
|
-y, --yes
|
||||||
assume yes and accept defaults for non-specified options
|
assume yes and accept defaults for non-specified options
|
||||||
|
|
||||||
@ -915,10 +657,6 @@ help() {
|
|||||||
remove all transcrypt configuration from the repository and
|
remove all transcrypt configuration from the repository and
|
||||||
leave files in the current working copy decrypted
|
leave files in the current working copy decrypted
|
||||||
|
|
||||||
--upgrade
|
|
||||||
apply the latest transcrypt scripts in the repository without
|
|
||||||
changing your configuration settings
|
|
||||||
|
|
||||||
-l, --list
|
-l, --list
|
||||||
list all of the transparently encrypted files in the repository,
|
list all of the transparently encrypted files in the repository,
|
||||||
relative to the top-level directory
|
relative to the top-level directory
|
||||||
@ -951,12 +689,12 @@ help() {
|
|||||||
$ transcrypt
|
$ transcrypt
|
||||||
|
|
||||||
Once a repository has been configured with transcrypt, you can trans-
|
Once a repository has been configured with transcrypt, you can trans-
|
||||||
parently encrypt files by applying the "crypt" filter, diff and merge
|
parently encrypt files by applying the "crypt" filter and diff to a
|
||||||
to a pattern in the top-level .gitattributes config. If that pattern
|
pattern in the top-level .gitattributes config. If that pattern matches
|
||||||
matches a file in your repository, the file will be transparently
|
a file in your repository, the file will be transparently encrypted
|
||||||
encrypted once you stage and commit it:
|
once you stage and commit it:
|
||||||
|
|
||||||
$ echo 'sensitive_file filter=crypt diff=crypt merge=crypt' >> .gitattributes
|
$ echo 'sensitive_file filter=crypt diff=crypt' >> .gitattributes
|
||||||
$ git add .gitattributes sensitive_file
|
$ git add .gitattributes sensitive_file
|
||||||
$ git commit -m 'Add encrypted version of a sensitive file'
|
$ git commit -m 'Add encrypted version of a sensitive file'
|
||||||
|
|
||||||
@ -984,52 +722,23 @@ help() {
|
|||||||
|
|
||||||
# reset all variables that might be set
|
# reset all variables that might be set
|
||||||
cipher=''
|
cipher=''
|
||||||
display_config=''
|
|
||||||
flush_creds=''
|
|
||||||
gpg_import_file=''
|
|
||||||
gpg_recipient=''
|
|
||||||
interactive='true'
|
|
||||||
list=''
|
|
||||||
password=''
|
password=''
|
||||||
|
interactive='true'
|
||||||
|
display_config=''
|
||||||
rekey=''
|
rekey=''
|
||||||
show_file=''
|
flush_creds=''
|
||||||
uninstall=''
|
uninstall=''
|
||||||
upgrade=''
|
show_file=''
|
||||||
openssl_path='openssl'
|
gpg_recipient=''
|
||||||
|
gpg_import_file=''
|
||||||
|
|
||||||
# used to bypass certain safety checks
|
# used to bypass certain safety checks
|
||||||
requires_existing_config=''
|
requires_existing_config=''
|
||||||
requires_clean_repo='true'
|
requires_clean_repo='true'
|
||||||
ignore_config_status='' # Set for operations where config can exist or not
|
|
||||||
|
|
||||||
# parse command line options
|
# parse command line options
|
||||||
while [[ "${1:-}" != '' ]]; do
|
while [[ "${1:-}" != '' ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
clean)
|
|
||||||
shift
|
|
||||||
git_clean "$@"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
smudge)
|
|
||||||
shift
|
|
||||||
git_smudge "$@"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
textconv)
|
|
||||||
shift
|
|
||||||
git_textconv "$@"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
merge)
|
|
||||||
shift
|
|
||||||
git_merge "$@"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
pre_commit)
|
|
||||||
shift
|
|
||||||
git_pre_commit "$@"
|
|
||||||
exit $?
|
|
||||||
;;
|
|
||||||
-c | --cipher)
|
-c | --cipher)
|
||||||
cipher=$2
|
cipher=$2
|
||||||
shift
|
shift
|
||||||
@ -1044,11 +753,6 @@ while [[ "${1:-}" != '' ]]; do
|
|||||||
--password=*)
|
--password=*)
|
||||||
password=${1#*=}
|
password=${1#*=}
|
||||||
;;
|
;;
|
||||||
--set-openssl-path=*)
|
|
||||||
openssl_path=${1#*=}
|
|
||||||
# Immediately apply config setting
|
|
||||||
git config transcrypt.openssl-path "$openssl_path"
|
|
||||||
;;
|
|
||||||
-y | --yes)
|
-y | --yes)
|
||||||
interactive=''
|
interactive=''
|
||||||
;;
|
;;
|
||||||
@ -1073,15 +777,9 @@ while [[ "${1:-}" != '' ]]; do
|
|||||||
requires_existing_config='true'
|
requires_existing_config='true'
|
||||||
requires_clean_repo=''
|
requires_clean_repo=''
|
||||||
;;
|
;;
|
||||||
--upgrade)
|
|
||||||
upgrade='true'
|
|
||||||
requires_existing_config='true'
|
|
||||||
requires_clean_repo=''
|
|
||||||
;;
|
|
||||||
-l | --list)
|
-l | --list)
|
||||||
list='true'
|
list_files
|
||||||
requires_clean_repo=''
|
exit 0
|
||||||
ignore_config_status='true'
|
|
||||||
;;
|
;;
|
||||||
-s | --show-raw)
|
-s | --show-raw)
|
||||||
show_file=$2
|
show_file=$2
|
||||||
@ -1133,25 +831,14 @@ while [[ "${1:-}" != '' ]]; do
|
|||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
gather_repo_metadata
|
|
||||||
|
|
||||||
# always run our safety checks
|
# always run our safety checks
|
||||||
run_safety_checks
|
run_safety_checks
|
||||||
|
|
||||||
# regular expression used to test user input
|
|
||||||
readonly YES_REGEX='^[Yy]$'
|
|
||||||
|
|
||||||
# in order to keep behavior consistent no matter what order the options were
|
# in order to keep behavior consistent no matter what order the options were
|
||||||
# specified in, we must run these here rather than in the case statement above
|
# specified in, we must run these here rather than in the case statement above
|
||||||
if [[ $list ]]; then
|
if [[ $uninstall ]]; then
|
||||||
list_files
|
|
||||||
exit 0
|
|
||||||
elif [[ $uninstall ]]; then
|
|
||||||
uninstall_transcrypt
|
uninstall_transcrypt
|
||||||
exit 0
|
exit 0
|
||||||
elif [[ $upgrade ]]; then
|
|
||||||
upgrade_transcrypt
|
|
||||||
exit 0
|
|
||||||
elif [[ $display_config ]] && [[ $flush_creds ]]; then
|
elif [[ $display_config ]] && [[ $flush_creds ]]; then
|
||||||
display_configuration
|
display_configuration
|
||||||
printf '\n'
|
printf '\n'
|
||||||
@ -1193,7 +880,7 @@ fi
|
|||||||
# ensure the git attributes file exists
|
# ensure the git attributes file exists
|
||||||
if [[ ! -f $GIT_ATTRIBUTES ]]; then
|
if [[ ! -f $GIT_ATTRIBUTES ]]; then
|
||||||
mkdir -p "${GIT_ATTRIBUTES%/*}"
|
mkdir -p "${GIT_ATTRIBUTES%/*}"
|
||||||
printf '#pattern filter=crypt diff=crypt merge=crypt\n' >"$GIT_ATTRIBUTES"
|
printf '#pattern filter=crypt diff=crypt\n' >"$GIT_ATTRIBUTES"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf 'The repository has been successfully configured by transcrypt.\n'
|
printf 'The repository has been successfully configured by transcrypt.\n'
|
||||||
|
35
testing.nix
35
testing.nix
@ -1,4 +1,8 @@
|
|||||||
{ pkgs, lib, ... }:
|
{ lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
secrets = toString ./secrets;
|
||||||
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
@ -6,25 +10,18 @@
|
|||||||
./configuration.nix
|
./configuration.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
# VM hardware setup
|
|
||||||
virtualisation.memorySize = 4000; # MB
|
|
||||||
virtualisation.graphics = false;
|
|
||||||
virtualisation.cores = 4;
|
|
||||||
virtualisation.msize = 1 * 1024 * 1024;
|
|
||||||
|
|
||||||
# Ensure secrets are accessible by the
|
# Ensure secrets are accessible by the
|
||||||
# activation scripts at runtime.
|
# activation scripts at runtime.
|
||||||
virtualisation.sharedDirectories.secrets =
|
virtualisation.qemu.options = [
|
||||||
{ source = toString ./secrets;
|
"-virtfs local,path=${secrets},security_model=none,mount_tag=secrets"
|
||||||
target = toString ./secrets;
|
];
|
||||||
};
|
fileSystems = lib.mkVMOverride {
|
||||||
|
"${secrets}" =
|
||||||
# These don't work in a virtual machine
|
{ device = "secrets";
|
||||||
systemd.services.smartd.enable = lib.mkForce false;
|
fsType = "9p";
|
||||||
systemd.services.apcupsd.enable = lib.mkForce false;
|
options = [ "trans=virtio" "version=9p2000.L" ];
|
||||||
|
neededForBoot = true;
|
||||||
# Automatically resize the console
|
};
|
||||||
environment.systemPackages = [ pkgs.xterm ];
|
};
|
||||||
environment.shellInit = "resize > /dev/null";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,8 @@
|
|||||||
type = lib.types.attrs;
|
type = lib.types.attrs;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
default = {
|
default = {
|
||||||
hostname = "maxwell.eurofusion.eu";
|
hostname = "maxwell.ydns.eu";
|
||||||
ipv4WanAddress = "2.35.5.112";
|
ipAddress = "2.25.5.112";
|
||||||
ipv4LanAddress = "192.168.1.5";
|
|
||||||
ipv6Address = "2001:470:b576:0:230:48ff:fefa:91e1";
|
|
||||||
};
|
};
|
||||||
description = "Global constants.";
|
description = "Global constants.";
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user