initial commit
This commit is contained in:
commit
a8947d6822
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pattern filter=crypt diff=crypt
|
||||||
|
secrets/*/** filter=crypt diff=crypt
|
||||||
|
secrets/default.nix filter=crypt diff=crypt
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
result
|
71
assets/magnetico-merge.py
Normal file
71
assets/magnetico-merge.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
|
||||||
|
def main(main_db, merged_db):
|
||||||
|
print(f"Merging {merged_db} into {main_db}")
|
||||||
|
connection = sqlite3.connect(main_db)
|
||||||
|
connection.row_factory = sqlite3.Row
|
||||||
|
connection.text_factory = bytes
|
||||||
|
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute("ATTACH ? AS merged_db", (merged_db,))
|
||||||
|
print("Gathering database statistics:")
|
||||||
|
|
||||||
|
cursor.execute("SELECT count(*) from merged_db.torrents")
|
||||||
|
total_merged = cursor.fetchone()[0]
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT name FROM pragma_table_info('files') "
|
||||||
|
"WHERE name not in ('id', 'torrent_id')"
|
||||||
|
)
|
||||||
|
remaining_file_colums = [row[0].decode() for row in cursor]
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT name FROM pragma_table_info('torrents')"
|
||||||
|
"WHERE name not in ('id')"
|
||||||
|
)
|
||||||
|
remaining_torrent_colums = [row[0].decode() for row in cursor]
|
||||||
|
|
||||||
|
print(f"{total_merged} torrents to merge.")
|
||||||
|
|
||||||
|
insert_files_statement = (
|
||||||
|
f"INSERT INTO files (torrent_id, {','.join(remaining_file_colums)}) "
|
||||||
|
f"SELECT ?, {','.join(remaining_file_colums)} "
|
||||||
|
f"FROM merged_db.files WHERE torrent_id = ?"
|
||||||
|
)
|
||||||
|
insert_torrents_statement = (
|
||||||
|
f"INSERT INTO torrents ({','.join(remaining_torrent_colums)})"
|
||||||
|
f"VALUES ({','.join('?' * len(remaining_torrent_colums))})"
|
||||||
|
)
|
||||||
|
failed_count = 0
|
||||||
|
|
||||||
|
cursor.execute("BEGIN")
|
||||||
|
merged = cursor.execute("SELECT * FROM merged_db.torrents")
|
||||||
|
for i, row in enumerate(merged):
|
||||||
|
try:
|
||||||
|
torrent_merge = connection.execute(
|
||||||
|
insert_torrents_statement, (*row[1:],))
|
||||||
|
# Now merge files
|
||||||
|
connection.execute(
|
||||||
|
insert_files_statement, (torrent_merge.lastrowid, row["id"]))
|
||||||
|
except sqlite3.IntegrityError:
|
||||||
|
failed_count += 1
|
||||||
|
|
||||||
|
print("Comitting… ", end="")
|
||||||
|
connection.commit()
|
||||||
|
print("OK."
|
||||||
|
f"{total_merged} torrents processed.",
|
||||||
|
f"{total_merged - failed_count} new torrents added.",
|
||||||
|
sep="\n")
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Tool to merge magnetico DBs")
|
||||||
|
parser.add_argument("main", type=str,
|
||||||
|
help="main dabatase")
|
||||||
|
parser.add_argument("merge", type=str,
|
||||||
|
help="dabatase to merge into main")
|
||||||
|
args = parser.parse_args()
|
||||||
|
main(args.main, args.merge)
|
439
configuration.nix
Normal file
439
configuration.nix
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./hardware.nix
|
||||||
|
./variables.nix
|
||||||
|
./packages.nix
|
||||||
|
./jobs.nix
|
||||||
|
./matrix.nix
|
||||||
|
./magnetico.nix
|
||||||
|
./nameserver.nix
|
||||||
|
./custom
|
||||||
|
./secrets
|
||||||
|
];
|
||||||
|
|
||||||
|
### State
|
||||||
|
# Stateful things to do before updating:
|
||||||
|
# 1. Postgres migration
|
||||||
|
# 2. Matrix Synapse migration
|
||||||
|
system.stateVersion = "20.03";
|
||||||
|
|
||||||
|
boot.kernelPackages = pkgs.linuxPackages_latest;
|
||||||
|
boot.tmpOnTmpfs = true;
|
||||||
|
boot.kernel.sysctl = {
|
||||||
|
# avoid OOM hangs
|
||||||
|
"vm.admin_reserve_kbytes" = 262144;
|
||||||
|
};
|
||||||
|
|
||||||
|
time.timeZone = "Europe/Rome";
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
systemd.enableEmergencyMode = false;
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
hostName = "maxwell";
|
||||||
|
|
||||||
|
firewall.allowedTCPPorts = [
|
||||||
|
443 80 # reverse proxy
|
||||||
|
8080 # hubot
|
||||||
|
5349 # turn server
|
||||||
|
5350 # turn server
|
||||||
|
3551 # apcups
|
||||||
|
5001 # iperf server
|
||||||
|
18080 # monero p2p
|
||||||
|
20000 # syncthing transfert
|
||||||
|
64738 # mumble server
|
||||||
|
];
|
||||||
|
firewall.allowedUDPPorts = [
|
||||||
|
53 # powerdns
|
||||||
|
1194 # dnscrypt
|
||||||
|
21027 # syncthing discovery
|
||||||
|
64738 # mumble server
|
||||||
|
];
|
||||||
|
firewall.allowedUDPPortRanges = [
|
||||||
|
{ from=49152; to=49999; } # turn relay
|
||||||
|
];
|
||||||
|
|
||||||
|
usePredictableInterfaceNames = false;
|
||||||
|
nameservers = [ "127.0.0.1" ];
|
||||||
|
hosts."127.0.0.1" = [ config.var.hostname ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Only declarative users and no password logins
|
||||||
|
users.mutableUsers = false;
|
||||||
|
|
||||||
|
users.users ={
|
||||||
|
# Only needed for local (read emergency) shell access
|
||||||
|
root.passwordFile = config.secrets.passwords.root;
|
||||||
|
|
||||||
|
# Admin
|
||||||
|
rnhmjoj = {
|
||||||
|
uid = 1000;
|
||||||
|
extraGroups = [ "wheel" ];
|
||||||
|
isNormalUser = true;
|
||||||
|
shell = pkgs.fish;
|
||||||
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.rnhmjoj ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Admin
|
||||||
|
fazo = {
|
||||||
|
extraGroups = [ "wheel" ];
|
||||||
|
isNormalUser = true;
|
||||||
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.fazo];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Runs two chatbots
|
||||||
|
meme = {
|
||||||
|
extraGroups = [ "ubino" "miguelbridge" ];
|
||||||
|
isNormalUser = true;
|
||||||
|
shell = pkgs.fish;
|
||||||
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.meme ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Hosts the cactalogo
|
||||||
|
giu = {
|
||||||
|
isNormalUser = true;
|
||||||
|
shell = pkgs.fish;
|
||||||
|
openssh.authorizedKeys.keyFiles = with config.secrets.publicKeys;
|
||||||
|
[ rnhmjoj giu ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Needed to perform remote builds on Maxwell
|
||||||
|
builder = {
|
||||||
|
description = "Remote Nix builds user";
|
||||||
|
isNormalUser = true;
|
||||||
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.rnhmjoj-builder ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Use "git" instead of the default name to make
|
||||||
|
# SSH operation handier, example:
|
||||||
|
# git clone git@maxwell:user/repo
|
||||||
|
git = {
|
||||||
|
description = "Git server user";
|
||||||
|
home = "/var/lib/gitea";
|
||||||
|
useDefaultShell = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Generate Diffie-Hellman parameters
|
||||||
|
# for TLS applications, like nginx.
|
||||||
|
security.dhparams = {
|
||||||
|
enable = true;
|
||||||
|
params.nginx = 2048; # prime modulus bits
|
||||||
|
};
|
||||||
|
|
||||||
|
security.sudo = {
|
||||||
|
enable = true;
|
||||||
|
# Users don't have a password
|
||||||
|
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 = [
|
||||||
|
{ domain = "@users";
|
||||||
|
type = "hard";
|
||||||
|
item = "nproc";
|
||||||
|
value = "400";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
### ACME certificates
|
||||||
|
security.acme = with config.var; {
|
||||||
|
email = "rnhmjoj@inventati.org";
|
||||||
|
acceptTerms = true;
|
||||||
|
|
||||||
|
certs."${hostname}" = {
|
||||||
|
group = "maxwell-ydns-eu";
|
||||||
|
};
|
||||||
|
|
||||||
|
certs."riot.${hostname}" = {
|
||||||
|
group = "riot-maxwell-ydns-eu";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Allow read access to ACME certificate
|
||||||
|
# to specific (service) users.
|
||||||
|
users.groups."maxwell-ydns-eu".members = [ "murmur" "turnserver" ];
|
||||||
|
users.groups."riot-maxwell-ydns-eu".members = [ "nginx" ];
|
||||||
|
|
||||||
|
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
permitRootLogin = "no";
|
||||||
|
passwordAuthentication = false;
|
||||||
|
challengeResponseAuthentication = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Traceroute easter egg
|
||||||
|
services.fakeroute = {
|
||||||
|
enable = true;
|
||||||
|
route = [
|
||||||
|
"89.111.117.32" "99.97.110.110" "111.116.32.104" "105.100.101.46"
|
||||||
|
"32.73.32.115" "101.101.32.121" "111.117.46.32" "84.104.101.114"
|
||||||
|
"101.32.105.115" "32.110.111.32" "108.105.102.101" "32.105.110.32"
|
||||||
|
"116.104.101.32" "118.111.105.100" "46.32.79.110" "108.121.32.100"
|
||||||
|
"101.97.116.104" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
### Mumble server
|
||||||
|
services.murmur = {
|
||||||
|
enable = true;
|
||||||
|
password = "allwellthatmaxwell";
|
||||||
|
registerHostname = config.var.hostname;
|
||||||
|
registerName = "Maxwell Mumble";
|
||||||
|
registerPassword = config.secrets.murmur.password;
|
||||||
|
users = 10;
|
||||||
|
extraConfig = with config.var; ''
|
||||||
|
sslCert=/var/lib/acme/${hostname}/fullchain.pem
|
||||||
|
sslKey=/var/lib/acme/${hostname}/key.pem
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
### Syncthing node
|
||||||
|
services.syncthing = {
|
||||||
|
enable = true;
|
||||||
|
openDefaultPorts = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
### Monero node with local RPC
|
||||||
|
services.monero = {
|
||||||
|
enable = true;
|
||||||
|
mining = {
|
||||||
|
enable = false;
|
||||||
|
threads = 4;
|
||||||
|
address = config.secrets.monero.address;
|
||||||
|
};
|
||||||
|
limits = {
|
||||||
|
upload = 250;
|
||||||
|
download = 625;
|
||||||
|
threads = 4;
|
||||||
|
};
|
||||||
|
rpc.user = config.secrets.monero.user;
|
||||||
|
rpc.password = config.secrets.monero.password;
|
||||||
|
};
|
||||||
|
|
||||||
|
### URL shortner
|
||||||
|
services.breve = {
|
||||||
|
enable = true;
|
||||||
|
hostname = "localhost";
|
||||||
|
baseUrl = "https://brve.bit/";
|
||||||
|
port = 2000;
|
||||||
|
certificate = "/var/lib/breve/breve.crt";
|
||||||
|
key = "/var/lib/breve/breve.key";
|
||||||
|
};
|
||||||
|
|
||||||
|
### Git server
|
||||||
|
services.gitea = with config.var; {
|
||||||
|
enable = true;
|
||||||
|
domain = hostname;
|
||||||
|
appName = "Maxwell git server";
|
||||||
|
rootUrl = "https://${hostname}/git/";
|
||||||
|
user = "git";
|
||||||
|
database.user = "git";
|
||||||
|
log.level = "Error";
|
||||||
|
cookieSecure = true;
|
||||||
|
disableRegistration = false;
|
||||||
|
settings = {
|
||||||
|
security.LOGIN_REMEMBER_DAYS = 365;
|
||||||
|
attachment.MAX_SIZE = 10;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
### Searx instance
|
||||||
|
services.searx = {
|
||||||
|
enable = true;
|
||||||
|
configFile = ./assets/searx-settings.yml;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
### Reverse Proxy
|
||||||
|
services.nginx =
|
||||||
|
with config.var;
|
||||||
|
let
|
||||||
|
disableLog = ''
|
||||||
|
error_log syslog:server=unix:/dev/log crit;
|
||||||
|
access_log off;
|
||||||
|
'';
|
||||||
|
enableSTS = ''
|
||||||
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
enable = true;
|
||||||
|
enableReload = 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;
|
||||||
|
recommendedProxySettings = true;
|
||||||
|
|
||||||
|
# Large enough to allow file uploads.
|
||||||
|
clientMaxBodySize = "1000M";
|
||||||
|
|
||||||
|
sslDhparam = "${config.security.dhparams.path}/nginx.pem";
|
||||||
|
|
||||||
|
# Maxwell
|
||||||
|
virtualHosts."${hostname}" =
|
||||||
|
{
|
||||||
|
enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
default = true;
|
||||||
|
|
||||||
|
extraConfig = disableLog + enableSTS;
|
||||||
|
|
||||||
|
# Returns IP address
|
||||||
|
locations."/ip".extraConfig = "return 200 $remote_addr;";
|
||||||
|
|
||||||
|
# Asjon code coverage reports
|
||||||
|
locations."/asjon/report/" = {
|
||||||
|
index = "index.html";
|
||||||
|
alias = "/var/lib/asjon/tree/report/";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Searx instance
|
||||||
|
locations."/srx/" = {
|
||||||
|
proxyPass = "http://localhost:8083/";
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_set_header X-Scheme $scheme;
|
||||||
|
proxy_set_header X-Script-Name /srx/;
|
||||||
|
proxy_buffering off;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Git server
|
||||||
|
locations."/git/" .proxyPass = "http://localhost:3000/";
|
||||||
|
|
||||||
|
# Syncthing
|
||||||
|
locations."/sync/".proxyPass = "http://localhost:8384/";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Breve URL shortner
|
||||||
|
virtualHosts."brve.bit" = {
|
||||||
|
forceSSL = true;
|
||||||
|
|
||||||
|
sslCertificate = "/var/lib/breve/breve.crt";
|
||||||
|
sslCertificateKey = "/var/lib/breve/breve.key";
|
||||||
|
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "https://localhost:2000";
|
||||||
|
extraConfig = "proxy_ssl_verify off;";
|
||||||
|
};
|
||||||
|
extraConfig = disableLog;
|
||||||
|
};
|
||||||
|
|
||||||
|
# The Cactalogue
|
||||||
|
virtualHosts."cacta.bit" = {
|
||||||
|
locations."/".alias = "/home/giu/cactalogue/";
|
||||||
|
extraConfig = disableLog;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
### Misc. services
|
||||||
|
services.ubino.enable = true;
|
||||||
|
services.miguelbridge.enable = true;
|
||||||
|
services.asjon.enable = true;
|
||||||
|
|
||||||
|
# Needed for the Asjon memory module
|
||||||
|
services.redis.enable = true;
|
||||||
|
|
||||||
|
|
||||||
|
### Program configuration
|
||||||
|
programs = {
|
||||||
|
fish.enable = true;
|
||||||
|
mosh.enable = true;
|
||||||
|
tmux = {
|
||||||
|
enable = true;
|
||||||
|
newSession = true;
|
||||||
|
baseIndex = 1;
|
||||||
|
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 "
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
nix = {
|
||||||
|
useSandbox = true;
|
||||||
|
# Can connect to the Nix daemon
|
||||||
|
# and upload/run code as root!
|
||||||
|
trustedUsers = [ "builder" "rnhmjoj" ];
|
||||||
|
# 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";
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
17
custom/default.nix
Normal file
17
custom/default.nix
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{ ... }:
|
||||||
|
|
||||||
|
# These are custom NixOS modules that are
|
||||||
|
# not yet in Nixpkgs or can't be upstreamed.
|
||||||
|
{
|
||||||
|
imports =
|
||||||
|
[ # Misc. system services
|
||||||
|
./modules/breve.nix
|
||||||
|
./modules/asjon.nix
|
||||||
|
./modules/ubino.nix
|
||||||
|
./modules/miguelbridge.nix
|
||||||
|
|
||||||
|
# Safely handle secrets
|
||||||
|
./modules/secrets-store.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
109
custom/modules/asjon.nix
Normal file
109
custom/modules/asjon.nix
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.asjon;
|
||||||
|
|
||||||
|
in {
|
||||||
|
|
||||||
|
options.services.asjon = {
|
||||||
|
enable = mkEnableOption "Asjon: our chat bot";
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/lib/asjon";
|
||||||
|
description = ''
|
||||||
|
Path where the settings and source tree will exist.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "asjon";
|
||||||
|
description = ''
|
||||||
|
Asjon 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.extraUsers."${cfg.user}" = {
|
||||||
|
home = cfg.dataDir;
|
||||||
|
createHome = true;
|
||||||
|
description = "asjon user";
|
||||||
|
shell = "${pkgs.bash}/bin/bash";
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.asjon = {
|
||||||
|
description = "asjon: our chat bot";
|
||||||
|
after = [ "nginx.service" "matrix-synapse.service" "asjon-init.service" ];
|
||||||
|
requires = [ "nginx.service" "matrix-synapse.service" "asjon-init.service" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
path = with pkgs; [
|
||||||
|
nodejs nodePackages.coffee-script
|
||||||
|
yarn openssh graphicsmagick git
|
||||||
|
bash
|
||||||
|
];
|
||||||
|
|
||||||
|
environment = {
|
||||||
|
# Matrix login
|
||||||
|
HUBOT_MATRIX_HOST_SERVER = "https://${config.var.hostname}";
|
||||||
|
|
||||||
|
# Git integration
|
||||||
|
HUBOT_GIT_URL = "https://${config.var.hostname}/git";
|
||||||
|
HUBOT_GIT_API = "https://${config.var.hostname}/git/api/v1";
|
||||||
|
HUBOT_GIT_REPO = "rnhmjoj/asjon";
|
||||||
|
|
||||||
|
# Scripts
|
||||||
|
AUTO_KILL_ON_UPDATE = "1";
|
||||||
|
AUTO_INFORM_ON_START = "!kvLvoCovzInhiablSq:maxwell.ydns.eu";
|
||||||
|
ADMIN_ROOM = "!kvLvoCovzInhiablSq:maxwell.ydns.eu";
|
||||||
|
REV_REMOTE_HOST = "proxy@rnhmjoj.ydns.eu";
|
||||||
|
REV_REMOTE_PORT = "22";
|
||||||
|
REV_KEY = "~/.ssh/proxy";
|
||||||
|
};
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
User = cfg.user;
|
||||||
|
ExecStart = "${cfg.dataDir}/tree/bin/hubot -a matrix";
|
||||||
|
Restart = "always";
|
||||||
|
WorkingDirectory = "${cfg.dataDir}/tree";
|
||||||
|
# API keys and passwords definitions
|
||||||
|
EnvironmentFile = config.secrets.asjon.environment;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.asjon-init = {
|
||||||
|
description = "Initialize Asjon service (first time only)";
|
||||||
|
wants = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig.User = cfg.user;
|
||||||
|
path = with pkgs; [ git yarn acl ];
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
if test -d ${cfg.dataDir}/tree/.git; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# clone repository and install packages
|
||||||
|
git clone https://github.com/rnhmjoj/asjon.git ${cfg.dataDir}/tree
|
||||||
|
cd ${cfg.dataDir}/tree
|
||||||
|
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
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
137
custom/modules/breve.nix
Normal file
137
custom/modules/breve.nix
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.breve;
|
||||||
|
dataDir = "/var/lib/breve";
|
||||||
|
|
||||||
|
configFile = pkgs.writeText "breve.conf" ''
|
||||||
|
hostname = "${cfg.hostname}"
|
||||||
|
port = ${toString cfg.port}
|
||||||
|
baseurl = "${cfg.baseUrl}"
|
||||||
|
urltable = "${dataDir}/urls"
|
||||||
|
tls {
|
||||||
|
cert = "${cfg.certificate}"
|
||||||
|
key = "${cfg.key}"
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
in {
|
||||||
|
|
||||||
|
options.services.breve = {
|
||||||
|
enable = mkEnableOption ''
|
||||||
|
Breve: a url shortner service.
|
||||||
|
'';
|
||||||
|
|
||||||
|
openPorts = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
example = literalExample "true";
|
||||||
|
description = ''
|
||||||
|
Open the default ports in the firewall:
|
||||||
|
- TCP 443 (or specific port) for HTTPS
|
||||||
|
- TCP 80 (or specific port) for HTTP->HTTPS redirect
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "breve";
|
||||||
|
description = ''
|
||||||
|
Breve will run under this user (user will be created if it doesn't exist.
|
||||||
|
This can be your user name).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
hostname = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = config.networking.hostName;
|
||||||
|
description = ''
|
||||||
|
Breve will bind and generate URLs accorting to this hostname.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
baseUrl = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "https://localhost:3000/";
|
||||||
|
example = "https://example.com";
|
||||||
|
description = ''
|
||||||
|
URL to reach the breve index page. Needed in case Breve is served by
|
||||||
|
a reverse proxy on a different url.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 443;
|
||||||
|
example = 8080;
|
||||||
|
description = ''
|
||||||
|
Breve main interface will be listening on this port.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
certificate = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "${dataDir}/breve.crt";
|
||||||
|
description = ''
|
||||||
|
The TLS certificate that Breve will be using to encrypt traffic.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
key = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "${dataDir}/breve.key";
|
||||||
|
description = ''
|
||||||
|
The TLS key that Breve will be using to encrypt traffic.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
certificateChain = mkOption {
|
||||||
|
type = types.listOf types.path;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
List of paths to the TLS certificates chain.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
|
||||||
|
users.extraUsers."${cfg.user}" = {
|
||||||
|
isSystemUser = true;
|
||||||
|
description = "Breve daemon user";
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall = mkIf cfg.openPorts {
|
||||||
|
allowedTCPPorts = [ cfg.port ]
|
||||||
|
++ optional (cfg.port == 443) 80;
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.breve = {
|
||||||
|
description = "breve: url shortner";
|
||||||
|
wants = [ "network.target" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
environment.XDG_CONFIG_HOME = "${dataDir}/conf";
|
||||||
|
serviceConfig = {
|
||||||
|
User = cfg.user;
|
||||||
|
ExecStart = "${pkgs.haskellPackages.breve}/bin/breve";
|
||||||
|
Restart = "on-failure";
|
||||||
|
StateDirectory = "breve";
|
||||||
|
};
|
||||||
|
|
||||||
|
preStart = ''
|
||||||
|
# link configuration
|
||||||
|
mkdir -p ${dataDir}/conf
|
||||||
|
if [ "$(realpath ${dataDir}/conf/breve)" != "${configFile}" ]; then
|
||||||
|
rm -f ${dataDir}/conf/breve
|
||||||
|
ln -s ${configFile} ${dataDir}/conf/breve
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
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";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
117
custom/modules/secrets-store.nix
Normal file
117
custom/modules/secrets-store.nix
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.security.runtimeSecrets;
|
||||||
|
|
||||||
|
# A recursive attrset of submodule
|
||||||
|
storeType = types.attrsOf (types.submodule
|
||||||
|
{ freeformType = storeType;
|
||||||
|
options = secretOptions;
|
||||||
|
});
|
||||||
|
|
||||||
|
# Secret file definition
|
||||||
|
secretOptions =
|
||||||
|
{ user = mkOption
|
||||||
|
{ type = types.str;
|
||||||
|
default = "root";
|
||||||
|
description = "Owner of the secret";
|
||||||
|
};
|
||||||
|
group = mkOption
|
||||||
|
{ type = types.str;
|
||||||
|
default = "root";
|
||||||
|
description = "Group with access to the secret.";
|
||||||
|
};
|
||||||
|
mode = mkOption
|
||||||
|
{ type = types.str;
|
||||||
|
default = "0400";
|
||||||
|
description = "File permission (octal format)";
|
||||||
|
};
|
||||||
|
path = mkOption
|
||||||
|
{ type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
apply = toString;
|
||||||
|
description = "File to include in the secret store";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Turns a nested attrset into a list
|
||||||
|
# of (path, value) pairs. It recurs
|
||||||
|
# until `cond val` is false.
|
||||||
|
attrsToIndex = cond: set:
|
||||||
|
let recurse = path: set:
|
||||||
|
let index = name: value:
|
||||||
|
if isAttrs value && cond value
|
||||||
|
then recurse (path ++ [name]) value
|
||||||
|
else singleton { path = path ++ [name]; value = value; };
|
||||||
|
in concatLists (mapAttrsToList index set);
|
||||||
|
in recurse [] set;
|
||||||
|
|
||||||
|
isFile = v: isAttrs v && v.path != "";
|
||||||
|
|
||||||
|
# Secrets flattened to an index. This is needed
|
||||||
|
# to iterate over the set.
|
||||||
|
flatSecrets = attrsToIndex (v: !isFile v) cfg;
|
||||||
|
|
||||||
|
# Secrets with paths rewritten to the store location
|
||||||
|
storedSecrets = mapAttrsRecursiveCond (v: !isFile v)
|
||||||
|
(names: secret:
|
||||||
|
if isFile secret
|
||||||
|
then "/run/secret/${concatStringsSep "-" names}"
|
||||||
|
else secret) cfg;
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.security.runtimeSecrets = mkOption {
|
||||||
|
type = storeType;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Definitions of runtime secrets. This is a freeform attributes
|
||||||
|
set: it can contain arbitrarily nested sets of secrets.
|
||||||
|
Secrets are paths to be copied into the secrets store
|
||||||
|
(/run/secrets) with proper permission and owenership.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
options.security.buildSecrets = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Definitions of build secrets. This is a freeform attrset
|
||||||
|
to be merged with the secrets-store and intended to store
|
||||||
|
unsafe secrets. This will be copied into the world-readable
|
||||||
|
Nix store, only use at a last resort.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
options.secrets = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
readOnly = true;
|
||||||
|
default = recursiveUpdate storedSecrets config.security.buildSecrets;
|
||||||
|
description = ''
|
||||||
|
The attrset used to access stored secrets from NixOS
|
||||||
|
configuration and modules.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config.system.activationScripts.secretsStore = {
|
||||||
|
deps = [ ];
|
||||||
|
text =
|
||||||
|
''
|
||||||
|
# Initialise clean directory
|
||||||
|
rm -rf /run/secrets
|
||||||
|
'' + concatMapStrings (pair:
|
||||||
|
let
|
||||||
|
name = "${concatStringsSep "-" pair.path}";
|
||||||
|
secret = pair.value;
|
||||||
|
in
|
||||||
|
optionalString (isFile secret)
|
||||||
|
''
|
||||||
|
# Install secret ${name}
|
||||||
|
install -m ${secret.mode} \
|
||||||
|
-o ${secret.user} -g ${secret.group} \
|
||||||
|
-D ${secret.path} /run/secrets/${name}
|
||||||
|
'') flatSecrets;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
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";
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
20
custom/packages/maxwell-notify.nix
Normal file
20
custom/packages/maxwell-notify.nix
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{ writeScriptBin, fish, curl
|
||||||
|
, homeserver
|
||||||
|
, roomId
|
||||||
|
, authToken
|
||||||
|
}:
|
||||||
|
|
||||||
|
writeScriptBin "notify" ''
|
||||||
|
#!${fish}/bin/fish
|
||||||
|
|
||||||
|
set token (cat ${authToken})
|
||||||
|
|
||||||
|
if test (id -u) != 0
|
||||||
|
echo 'you must be root to send a notice'
|
||||||
|
exit 1
|
||||||
|
end
|
||||||
|
|
||||||
|
set 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
|
||||||
|
''
|
85
hardware.nix
Normal file
85
hardware.nix
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
<nixpkgs/nixos/modules/installer/scan/not-detected.nix>
|
||||||
|
];
|
||||||
|
|
||||||
|
boot.kernelModules = [ "kvm-intel" ];
|
||||||
|
boot.initrd.availableKernelModules = [
|
||||||
|
"uhci_hcd" "ehci_pci" "ata_piix"
|
||||||
|
"usbhid" "usb_storage" "sd_mod"
|
||||||
|
];
|
||||||
|
boot.loader.grub = {
|
||||||
|
enable = true;
|
||||||
|
version = 2;
|
||||||
|
device = "/dev/sda";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/" =
|
||||||
|
{ device = "/dev/main/nixos";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/home" =
|
||||||
|
{ device = "/dev/main/home";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/var/lib" =
|
||||||
|
{ device = "/dev/data/data";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
nix.maxJobs = lib.mkDefault 16;
|
||||||
|
powerManagement.cpuFreqGovernor = "ondemand";
|
||||||
|
|
||||||
|
services.apcupsd = {
|
||||||
|
enable = true;
|
||||||
|
configText = ''
|
||||||
|
UPSTYPE usb
|
||||||
|
UPSCABLE usb
|
||||||
|
NETSERVER on
|
||||||
|
NISPORT 3551
|
||||||
|
MINUTES 5
|
||||||
|
'';
|
||||||
|
hooks =
|
||||||
|
let
|
||||||
|
# Send notifications on the Maxwell
|
||||||
|
# room when something bad happens.
|
||||||
|
notify = msg: ''${pkgs.maxwell-notify}/bin/notify "UPS: ${msg}"'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
changeme = notify "sostituire le batterie";
|
||||||
|
battdetach = notify "batterie disconnesse";
|
||||||
|
battattach = notify "batterie riconnesse";
|
||||||
|
commfailure = notify "connessione persa";
|
||||||
|
commok = notify "connessione ristabilita";
|
||||||
|
loadlimit = notify "livello batterie critico (5%)";
|
||||||
|
runlimit = notify "autonomia batterie critico (5min)";
|
||||||
|
doshutdown = notify "inizio sequenza di spegnimento";
|
||||||
|
powerout = notify "rete elettrica disconnessa";
|
||||||
|
mainsback = notify "rete elettrica riconnessa";
|
||||||
|
onbattery = notify "attivate batterie";
|
||||||
|
offbattery = notify "disattivate batterie";
|
||||||
|
emergency = notify "malfunzionamento batterie, possibile spegnimento!";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.smartd =
|
||||||
|
let
|
||||||
|
# Send a notification on the Maxwell
|
||||||
|
# when a disk is starting to fail.
|
||||||
|
failHook = with pkgs; writeScript "disk-fail-hook" ''
|
||||||
|
#!/bin/sh
|
||||||
|
${pkgs.maxwell-notify}/bin/notify \
|
||||||
|
"SMART: rilevato problema al disco $SMARTD_DEVICESTRING:"
|
||||||
|
${pkgs.maxwell-notify}/bin/notify "> $SMARTD_MESSAGE"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
enable = true;
|
||||||
|
defaults.monitored = "-a -M exec ${failHook}";
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
129
jobs.nix
Normal file
129
jobs.nix
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
systemd.services.ydns = {
|
||||||
|
description = "update ydns address record";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
startAt = "*:0/30";
|
||||||
|
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
serviceConfig.environmentFile = config.secrets.ydns.environment;
|
||||||
|
|
||||||
|
path = with pkgs; [ curl cacert gawk iproute ];
|
||||||
|
environment = {
|
||||||
|
YDNS_HOST = config.var.hostname;
|
||||||
|
CURL_CA_BUNDLE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||||
|
};
|
||||||
|
|
||||||
|
script = ''
|
||||||
|
update() {
|
||||||
|
ret=$(curl -$1 --basic --silent \
|
||||||
|
-u "$YDNS_USER:$YDNS_PASSWD" \
|
||||||
|
"https://ydns.io/api/v1/update/?host=$YDNS_HOST&ip=$2" || exit 0)
|
||||||
|
|
||||||
|
case "$ret" in
|
||||||
|
ok)
|
||||||
|
echo "updated successfully: $YDNS_HOST ($2)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
badauth)
|
||||||
|
echo "updated failed: $YDNS_HOST (authentication failed)"
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "update failed: $YDNS_HOST ($ret)"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
update 4 "$(curl -s -4 https://ydns.io/api/v1/ip)"
|
||||||
|
update 6 "$(ip addr show mngtmpaddr | awk '/inet6/{print $2; exit}' | cut -d/ -f1)"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
systemd.services.backup = {
|
||||||
|
description = "run system backup";
|
||||||
|
after = [ "network-online.target" ];
|
||||||
|
startAt = "weekly";
|
||||||
|
|
||||||
|
serviceConfig.Type = "oneshot";
|
||||||
|
|
||||||
|
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}";
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
68
magnetico.nix
Normal file
68
magnetico.nix
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{ config, pkgs, ... }:
|
||||||
|
|
||||||
|
# Setup:
|
||||||
|
# Maxwell runs the web UI (magneticow) but doesn't
|
||||||
|
# run the crawler (magneticod) because it's too
|
||||||
|
# network intesive. The latter is run by Wigfrid,
|
||||||
|
# which periodically uploads a sqlite database.
|
||||||
|
# Once received, Maxwell merges it with the local one.
|
||||||
|
|
||||||
|
{
|
||||||
|
### Reverse proxy location
|
||||||
|
services.nginx.virtualHosts."${config.var.hostname}" =
|
||||||
|
{ locations."/dht/" = {
|
||||||
|
proxyPass = "http://localhost:8082/";
|
||||||
|
# Rewrite all absolute paths, magneticow
|
||||||
|
# was not designed to work behind a proxy.
|
||||||
|
extraConfig = ''
|
||||||
|
sub_filter_once off;
|
||||||
|
sub_filter_types *;
|
||||||
|
sub_filter 'action="/' 'action="/dht/';
|
||||||
|
sub_filter 'href="/' 'href="/dht/';
|
||||||
|
sub_filter 'src="/' 'src="/dht/';
|
||||||
|
sub_filter '/api/' '/dht/api/';
|
||||||
|
sub_filter '/feed?' '/dht/feed?';
|
||||||
|
sub_filter 'split("/")[2]' 'split("/").pop()';
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
### Magneticow
|
||||||
|
services.magnetico = {
|
||||||
|
enable = true;
|
||||||
|
web.port = 8082;
|
||||||
|
web.credentialsFile = config.secrets.passwords.magnetico;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Disable the crawler: it's run by wigfrid
|
||||||
|
systemd.services.magneticod.enable = false;
|
||||||
|
|
||||||
|
# Start the database merge as soon
|
||||||
|
# as a new one is uploaded.
|
||||||
|
systemd.paths.merge-magnetico = {
|
||||||
|
pathConfig.PathExists = "/var/lib/magnetico/update.sqlite3";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Merge wigfrid update database with
|
||||||
|
# the current one and restart magneticow.
|
||||||
|
systemd.services.merge-magnetico = {
|
||||||
|
path = [ pkgs.python3 ];
|
||||||
|
script = ''
|
||||||
|
set -e
|
||||||
|
systemctl stop magneticow
|
||||||
|
cd /var/lib/magnetico
|
||||||
|
python3 ${./assets/magnetico-merge.py} database.sqlite3 update.sqlite3
|
||||||
|
rm update.sqlite3
|
||||||
|
systemctl start magneticow
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# SSH access to allow uploading
|
||||||
|
# the magnetico database.
|
||||||
|
users.users.magnetico = {
|
||||||
|
useDefaultShell = true;
|
||||||
|
openssh.authorizedKeys.keyFiles = [ config.secrets.publicKeys.magnetico ];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
180
matrix.nix
Normal file
180
matrix.nix
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with config.var;
|
||||||
|
|
||||||
|
let
|
||||||
|
### 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
|
||||||
|
|
||||||
|
{
|
||||||
|
### Reverse proxy locations
|
||||||
|
services.nginx.virtualHosts."${config.var.hostname}" =
|
||||||
|
let
|
||||||
|
client =
|
||||||
|
{ "m.homeserver" = { "base_url" = "https://${config.var.hostname}"; };
|
||||||
|
"m.identity_server" = { "base_url" = "https://matrix.org"; };
|
||||||
|
};
|
||||||
|
server = { "m.server" = "${config.var.hostname}:443"; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Needed for matrix federation
|
||||||
|
locations."/.well-known/matrix/server".extraConfig = ''
|
||||||
|
add_header Content-Type application/json;
|
||||||
|
return 200 '${builtins.toJSON server}';
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Needed for automatic homeserver
|
||||||
|
# setup of matrix clients
|
||||||
|
locations."/.well-known/matrix/client".extraConfig = ''
|
||||||
|
add_header Content-Type application/json;
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
return 200 '${builtins.toJSON client}';
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Forward matrix API calls to synapse
|
||||||
|
locations."/_matrix".proxyPass = "http://localhost:8448";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
### Element/Riot static location
|
||||||
|
services.nginx.virtualHosts."riot.${config.var.hostname}" =
|
||||||
|
{ enableACME = true;
|
||||||
|
forceSSL = true;
|
||||||
|
|
||||||
|
locations."/" =
|
||||||
|
{ index = "index.html";
|
||||||
|
alias = (pkgs.element-web.override { inherit conf; }) + "/";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
### Homeserver
|
||||||
|
services.matrix-synapse = {
|
||||||
|
enable = true;
|
||||||
|
server_name = 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
|
||||||
|
listeners = [
|
||||||
|
{ bind_address = "localhost";
|
||||||
|
port = 8448;
|
||||||
|
type = "http";
|
||||||
|
tls = false;
|
||||||
|
resources = [
|
||||||
|
{ compress = true; names = [ "client" ] ; }
|
||||||
|
{ compress = false; names = [ "federation" ]; }
|
||||||
|
];
|
||||||
|
x_forwarded = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Connect to Postrges
|
||||||
|
database_type = "psycopg2";
|
||||||
|
database_args = {
|
||||||
|
user = "matrix-synapse";
|
||||||
|
database = "matrix-synapse";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Make logging less verbose
|
||||||
|
logConfig = ''
|
||||||
|
version: 1
|
||||||
|
formatters:
|
||||||
|
journal_fmt:
|
||||||
|
format: '%(name)s: [%(request)s] %(message)s'
|
||||||
|
filters:
|
||||||
|
context:
|
||||||
|
(): synapse.util.logcontext.LoggingContextFilter
|
||||||
|
request: ""
|
||||||
|
handlers:
|
||||||
|
journal:
|
||||||
|
class: systemd.journal.JournalHandler
|
||||||
|
formatter: journal_fmt
|
||||||
|
filters: [context]
|
||||||
|
SYSLOG_IDENTIFIER: synapse
|
||||||
|
root:
|
||||||
|
level: WARN
|
||||||
|
handlers: [journal]
|
||||||
|
disable_existing_loggers: False
|
||||||
|
'';
|
||||||
|
|
||||||
|
allow_guest_access = true;
|
||||||
|
expire_access_token = true;
|
||||||
|
event_cache_size = "2K";
|
||||||
|
max_upload_size = "1000M";
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
# Use the Postrges database
|
||||||
|
services.postgresql.enable = true;
|
||||||
|
|
||||||
|
|
||||||
|
# Handles users behind a NAT,
|
||||||
|
# needed for reliable VoIP.
|
||||||
|
services.coturn = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Only allow users vouched for
|
||||||
|
# by the Matrix server.
|
||||||
|
lt-cred-mech = true;
|
||||||
|
use-auth-secret = true;
|
||||||
|
static-auth-secret = config.secrets.matrix.turn;
|
||||||
|
|
||||||
|
# Use maxwell certificate for TLS
|
||||||
|
realm = config.var.hostname;
|
||||||
|
cert = "/var/lib/acme/${config.var.hostname}/fullchain.pem";
|
||||||
|
pkey = "/var/lib/acme/${config.var.hostname}/key.pem";
|
||||||
|
|
||||||
|
# Port range for TURN relaying
|
||||||
|
min-port = 49152;
|
||||||
|
max-port = 49999;
|
||||||
|
|
||||||
|
# Enable TLS
|
||||||
|
secure-stun = true;
|
||||||
|
no-tcp-relay = false;
|
||||||
|
|
||||||
|
extraConfig = ''
|
||||||
|
external-ip=${config.var.ipAddress}
|
||||||
|
cipher-list=HIGH
|
||||||
|
no-loopback-peers
|
||||||
|
no-multicast-peers
|
||||||
|
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||||
|
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
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
44
nameserver.nix
Normal file
44
nameserver.nix
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{ config, ... }:
|
||||||
|
|
||||||
|
# Setup:
|
||||||
|
# PDNS recursor on port 53
|
||||||
|
# DNSCrypt wrapper on port 1194
|
||||||
|
# NCDNS for Namecoin bit. zone resolution
|
||||||
|
|
||||||
|
{
|
||||||
|
# Recursive DNS resolver
|
||||||
|
services.pdns-recursor = {
|
||||||
|
enable = true;
|
||||||
|
# Configures the bit. zone
|
||||||
|
resolveNamecoin = true;
|
||||||
|
dns.allowFrom = [ "0.0.0.0/0" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Wrap the local recursive resolver
|
||||||
|
# in DNSCrypt on the default OpenVPN port.
|
||||||
|
# This port is chosen because it's usually
|
||||||
|
# not blocked in corporate networks.
|
||||||
|
services.dnscrypt-wrapper = {
|
||||||
|
enable = true;
|
||||||
|
address = "0.0.0.0";
|
||||||
|
port = 1194;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Namecoin resolver
|
||||||
|
services.ncdns = {
|
||||||
|
enable = true;
|
||||||
|
# This is currently broken, see ncdns issue:
|
||||||
|
# https://github.com/namecoin/ncdns/issues/127
|
||||||
|
dnssec.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Namecoin daemon with RPC server
|
||||||
|
services.namecoind = {
|
||||||
|
enable = true;
|
||||||
|
# This are used by the resolver (ncdns)
|
||||||
|
# to query the blockchain.
|
||||||
|
rpc.user = config.secrets.namecoin.user;
|
||||||
|
rpc.password = config.secrets.namecoin.password;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
36
packages.nix
Normal file
36
packages.nix
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{ config, pkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
unstable = import <nixos-unstable> { };
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
nixpkgs.overlays = lib.singleton
|
||||||
|
(self: super:
|
||||||
|
{ maxwell-notify = self.callPackage ./custom/packages/maxwell-notify.nix
|
||||||
|
{ homeserver = "https://${config.var.hostname}/_matrix/client/r0";
|
||||||
|
roomId = "!FsUSHSNMPMVTFFcvJo:maxwell.ydns.eu";
|
||||||
|
authToken = config.secrets.privateKeys.matrix;
|
||||||
|
};
|
||||||
|
|
||||||
|
monero = unstable.monero;
|
||||||
|
element-web = unstable.element-web;
|
||||||
|
});
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
# utilities
|
||||||
|
iftop curl ranger neovim
|
||||||
|
nix-script
|
||||||
|
jq ack
|
||||||
|
|
||||||
|
# backup
|
||||||
|
bup git nfs-utils
|
||||||
|
|
||||||
|
# admin
|
||||||
|
dnsutils
|
||||||
|
matrix-synapse
|
||||||
|
maxwell-notify
|
||||||
|
smartmontools
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
41
secrets/certs/breve.crt
Normal file
41
secrets/certs/breve.crt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
U2FsdGVkX1+v2LZrhijmp31otrHMh+DfaYCLGD/Ne8e30ShI5/q5ZSz7RFqPq6MX
|
||||||
|
p6XliglIJfARnYpLGjZdX1ZWW9vXDyNO5OvQ/LS+sbaSbOWcLQrMtLqUAhbOUzk6
|
||||||
|
seVaK0aCwlUUFCnu80r0MzVvPKMxwEoFBu1fWI1cQqVxyTfoYgbQK68Ple1a4jDc
|
||||||
|
/c0sUyPmXWYZQ+qMGOPWmSW+CeTR7yPplj0lD8xch8WehrBb0oqj1iiGHDIp0PNG
|
||||||
|
OKrUoHs1mUD54m2hXNbX4vji4VUMt3xmTIAlLaGxj637vz0NoaLdscgAXl0c9kPK
|
||||||
|
Vn53o7utJWgvEWeMXGDliRGDQ7F3vNcPwfCO1bNLfDCKJ9Bfm78wrcIWH8SPvwpa
|
||||||
|
XC0cYqPN2gwrPZkR7w42Vu5itkCVkr+V2EhSfioktRRMDrt2mPTIABnaYbfvKlFK
|
||||||
|
p+sO/cT1ONF47rncU60vpt62Q5J/qHLzEqoOCO61uL9SRZ/n7NDn4wYJb+1brWwU
|
||||||
|
Mo2Wgnk1blpJ9EseAXRN9+8Orn3RTkMMp9nRftlGSBNZq3GxTe/RNTIT/bhAcHNr
|
||||||
|
Houv5OgKnKfOB8NW0jshW3NRBMXOAhtloXJ2wmgvw4JI5jVXAvVlAhfyOcU+C3uE
|
||||||
|
NdSz35/SymMkMyRnjPlKHEz6sjNc4DiowRBrA7i/4TNU7bVk5L8+hh4wOa5vZjq0
|
||||||
|
2EJVzPb9bXf1QVVKPNWAYDM0PCHvtP7BK0OvJDPU60GK91CUWoCnOdYTO+/l8ImI
|
||||||
|
3Om86891UWSJKVF0bpYEaS3TXqfWq70dzg13OCB0ue/wxHsZrHUefqYOY0zgeQoP
|
||||||
|
G6jnUpMogXnIhTwcSCRha5vjkc1Vrv8w9riPagpkhzlTjFU535YN7Kta5tGNrZGp
|
||||||
|
7SOPm+hgKPCm0sWlH9QJKES4iIpwohsbm8WBTLl/KDvT1P7ia6UMIbRdZF36ONhG
|
||||||
|
H/rTDRXHwAMu67dM+v93OSc7bq2W9NuCXjkp/7VxR/SmUvygMARNJqEpexWeIU7o
|
||||||
|
OhiKNzjLhOLW1Fp6vM0gJ9iDzN5ng2QG1l1SmhPzYNeNO1YoSIqR6X/GBuq3d0so
|
||||||
|
B+oVBcNCHhWpMKbeH1sQX2ZfbG00I4JHYF4k9b8GDn8ek7f/hFC9CQTtixhnx40m
|
||||||
|
cZqkCu6WBYLOLOgLbn2u+xDHSQT8bbKtbvCJv1d7xHMzmsM1/eRNj1Wl/itEB+ZP
|
||||||
|
XMTuM4x59fr6SyKJ1Gnei8tc59ZVFPJyM48AxWUjp/zfL/RagPMBqG8yTtxJY9GJ
|
||||||
|
ozVlGPprDXMkcS4MNu4iTbRNkbdhQDa83YMzgOGYYsmoQhaZ0yT4SINjfuTFa47l
|
||||||
|
BlbYpUD7TL6vVQaJw99pBig2aUiUGSbUlXUFaaigT4vl922ayjxFilsFSR2K5zdP
|
||||||
|
RAJdMAj+PjXwkmeYf0l6mxQy4EgCqd50thkgFpeRK2oaDZpbF8le0Hv+Lci9QtUD
|
||||||
|
t6nqb8QLMnLzuc02EXJt7wW5HTuTq0B1RYqNepka13Zt1ILxS83Vde3iC72mSr4e
|
||||||
|
ifs+Rk75L+llAKfqhc29YcfRoKqxs2gTBFSOsTuqBA9JwUFWClPS6lg1RKVdeV2s
|
||||||
|
gycdtksZrSDQEyCJuZibx7HDu4o0zbmeIcPreV/LnAOyFS5i75NgzjFVe0VrmVIO
|
||||||
|
FR/T5+4KP3V8WCvbPerDNdsQ+HePkEzToJzbyKWSaqRo+3eyYtlSt9pZ+yrrIKSR
|
||||||
|
8g1pm/my31mOMQn5tZD+NvsXY2PIH69y8ELJwL5Kdpr6NkPKFF/i9upIHqzUcudT
|
||||||
|
FfX/xP/KyEkIOEyhRHoznqDxx8Ya/BLaKWDFCqRSgNmrbnvqqZ4nX0bhzSNM6nhy
|
||||||
|
LX8mexTQjaLXyoexnu8zFYJpp6ss0g1mB/AAE58JNX1crNTpDSYxsje9VR4Ufw3V
|
||||||
|
DnCuWAclwCdI/RPO1YmqvOHzy2qbJ6JW8imV8v5YsM+hwahWVmaw4+H9B50lmq3A
|
||||||
|
qU946wMTlSpLgnIuUPKfuUydB4pGUGMjMCilGwJF/0yVWGcQt04INXDGF6D8eC/l
|
||||||
|
nYyck2w9tHnwDy1Oi0lRWF6x2IfvK5b+g06OIy80i37onySn1cf8zWyvCcsJ84zY
|
||||||
|
K2fDoDZxO4v/b1b1SCkbHhNjaFKxH9oQ7ZkNwDTAsjdzV1DiNM50vI5PkofhRAZe
|
||||||
|
3miMnRdhwebj1JbxPkDhyrNYAS6FPzDOnCgLKqAMcd6Zq1HELrNi1qYZnYywGwr6
|
||||||
|
1Yrn2LxcKgzNVBFIxA5yI8jaeUHnqSLgkVP9G2WsN/6zIRur4R+bJe1VKJfEw1CK
|
||||||
|
Qjn5fmqfxnAUe3W158EfX4AxVSYUAkT+wz5hX23iLeqoXxE4PW0tLXn1Oi1Q0n+S
|
||||||
|
4JHfTF5VKICE52ihuzBl66VtGOpWfkxb7cLrC3i2jwZBxdipJq+jOeOSZeC379pe
|
||||||
|
U0WdVQtml8M+AmAe58FjxY/JL6Gzrmt5qecNQV0qmor40Rvc8/OwlaAaooM1rVQr
|
||||||
|
0vlWHVDo9A6huuKWF0kDwNGt6sz1Nn/E76pTuw+FQORxVrapQpF/V4byOxuyIyMy
|
||||||
|
yaeWJh6O2TknxiBRp76MR2GnjHmkBdADwm2PsoeH/dcXsPnTftZwsg==
|
224
secrets/certs/breve.key
Normal file
224
secrets/certs/breve.key
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
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
|
43
secrets/default.nix
Normal file
43
secrets/default.nix
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
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
|
||||||
|
p6krltwsQGFLXH+N
|
9
secrets/keys/fazo.pub
Normal file
9
secrets/keys/fazo.pub
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
U2FsdGVkX1+Ay4K0S+xuJiyoMRj00oMkaw5sYVvu9VBR68aypK8eLTng8xoqKwzm
|
||||||
|
uD1YNhLdh515CgHyMI7/LraT2yDYIlF+pNEfftH6U2qU5IfWSoukD59RscfaAft+
|
||||||
|
/dend9Y6HyG1WdlyPyLVabruHFScx3d+oaLwEcgggnI/M9coWnHyvBXspo6E75um
|
||||||
|
gyvFntN4GmJLf1sMQIn0I7lW9djC8nupjSTstRo5HNLM/LwlhwAYRb/jbJUOkYSK
|
||||||
|
D/SDHW5p/9OrACQzAKHFB3mg4+9SufD+cju8qIAn9uFcyCJxkri6Mz+SGqdXtSgi
|
||||||
|
fEZE2r1aCUXFa8Nq+qoYbxVue3BFlzxetC7fZrx2zWnmkSgOn7LWDn6q3B3KWeT5
|
||||||
|
om/g+Ph/RE4piKzm9m2jIx+0TlkUHlpOKAf4Xzwdaivmm6HaCNc5pt1Hw0le1fTW
|
||||||
|
JE+6BkXFDJz/8ytROujTGlMaMCB/JHgK04diEAnQJmNQnYVG03PxmRHmmqXc1czQ
|
||||||
|
OnIzyUraBCpBsHSAVsN/afC8
|
3
secrets/keys/giu.pub
Normal file
3
secrets/keys/giu.pub
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
U2FsdGVkX19/6c3AzyWTN5p17ujhKlbDdk91iQs9z7Q0HiyA4L7BKFT/ZOk4VNqY
|
||||||
|
2Yh7r0b1F1ScFjvKH3aJ7jYGHT0i+w3LSHsufCDATEUejN/Z9JtEIXYaodOCJaYE
|
||||||
|
YuIaLuBwWdkAPUl1lhs=
|
3
secrets/keys/magnetico.pub
Normal file
3
secrets/keys/magnetico.pub
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
U2FsdGVkX1/StXpT1GfebxPB+1TyCHLo5fjFZLNkkWXnCS04WnREE2xlV7OXw0Iq
|
||||||
|
llqZTflZ/z1hSz7NuUO/vrR57RRo6icf3UXnxvJ8HD6Z9q7uxI+WpIj+ME2zij6B
|
||||||
|
Jg==
|
7
secrets/keys/matrix.tok
Normal file
7
secrets/keys/matrix.tok
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
U2FsdGVkX19J8VE7lArWiwLIURQ8NjPEUwkOAh4m1oR0yrmBCI5u/vhwSQTC+ETb
|
||||||
|
S3b80dqDKkR8QKRxIaquJHw/KRvQqKViZbu1OsHQTPQhK//mvs6vZ8G00vfucphc
|
||||||
|
6XIuiJS0u1zbzP6CKoLlkUyVOxFsmVSmxRx8460vgqK00JHSXf82mCAXcePVfHX9
|
||||||
|
uV9w34x3QkqSzmptx1orJrWa/Y/+et19ghJ/d6Utll+kg5Ldkd6vYcSA5bYFMe6L
|
||||||
|
LzAjJDLSvRpGkwP7EH2/9Kin5qDA7OUQmrXyFvmb9viCnYSD4TUaxXHYy4SPYcPY
|
||||||
|
qgFFeNDry5PAhkqLCTKgQWylCZXNbnA7JHp5fdbQCyRFD2sNxVN9ptuqNJd5x+hf
|
||||||
|
0PzTFokhgtE=
|
3
secrets/keys/meme.pub
Normal file
3
secrets/keys/meme.pub
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
U2FsdGVkX18yVvW7ZvcS0Xc/LsBJmDBTjmHeODQqsVSq8AlzjHH0Z15cY2ibL0+2
|
||||||
|
/fq+Sb12nfYhXkdFePGNJl+pwTVN2KmQhQtTPUawwa0bmvqC3wPXmHn8O1AndVP9
|
||||||
|
8g==
|
3
secrets/keys/rnhmjoj-builder.pub
Normal file
3
secrets/keys/rnhmjoj-builder.pub
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
U2FsdGVkX19MH3jJZJHEhLZLqIGcQCvd7JS2I8vWztP1Htde6A/xfy3zP8U6NUOc
|
||||||
|
QPBYfycwXLqUM89gVrKnnj28HQiAzQNf2zzPqG7MOpQKA6zdRF6i9n+CGtvXC36u
|
||||||
|
zQ==
|
3
secrets/keys/rnhmjoj.pub
Normal file
3
secrets/keys/rnhmjoj.pub
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
U2FsdGVkX18X2ltRnCWQnXMSt/FSKiq/ScbhdjFP4wmPHi5njgtam/c1Dg+0T1fj
|
||||||
|
JzOYe53LglUBfjDMbIepcIymHXPteizligpJzNE7DwuzsCp2JTkn9KWzKJb45Qa/
|
||||||
|
/UtVdTfkS9WH
|
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==
|
2
secrets/pass/magnetico.pwds
Normal file
2
secrets/pass/magnetico.pwds
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
U2FsdGVkX19YVs+neL4R4JDT1CSsndTtbggYoDxEF2iwRCRDJRtrBBJthnxRrUsr
|
||||||
|
c+A5NSSRRAu0LQ5vjaHlOYiCtmVCdYu7ECrpHQ40KqYgYhXJAw==
|
3
secrets/pass/root.pwd
Normal file
3
secrets/pass/root.pwd
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
U2FsdGVkX1+1zBjw7Y2NlBeTLcGS8o3Er/ngQMU57HLCN8jSfKBU0/C4o9D4NDjl
|
||||||
|
C7pRu3oOHmz0Pn9ipLaP87ST9RzVncHw/kqNBh8Dg29n3jNoTdSfwTn6xV/mBwQO
|
||||||
|
a4OsKusYMI/dCriATixomxe1GkC06YfwJg==
|
888
secrets/transcrypt
Executable file
888
secrets/transcrypt
Executable file
@ -0,0 +1,888 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#! nix-shell -i bash --pure
|
||||||
|
#! nix-shell -p bash openssl git unixtools.column
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
#
|
||||||
|
# transcrypt - https://github.com/elasticdog/transcrypt
|
||||||
|
#
|
||||||
|
# A script to configure transparent encryption of sensitive files stored in
|
||||||
|
# a Git repository. It utilizes OpenSSL's symmetric cipher routines and follows
|
||||||
|
# the gitattributes(5) man page regarding the use of filters.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014-2019 Aaron Bull Schaefer <aaron@elasticdog.com>
|
||||||
|
# This source code is provided under the terms of the MIT License
|
||||||
|
# that can be be found in the LICENSE file.
|
||||||
|
#
|
||||||
|
|
||||||
|
##### CONSTANTS
|
||||||
|
|
||||||
|
# the release version of this script
|
||||||
|
readonly VERSION='2.0.0'
|
||||||
|
|
||||||
|
# the default cipher to utilize
|
||||||
|
readonly DEFAULT_CIPHER='aes-256-ctr'
|
||||||
|
|
||||||
|
# the openssl options to encrypt/decrypt the files
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
readonly ENCRYPT_OPTIONS='-$cipher -pbkdf2 -iter 200000'
|
||||||
|
|
||||||
|
# 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
|
||||||
|
realpath() {
|
||||||
|
local path=$1
|
||||||
|
|
||||||
|
# make path absolute
|
||||||
|
local abspath=$path
|
||||||
|
if [[ -n ${abspath##/*} ]]; then
|
||||||
|
abspath=$(pwd -P)/$abspath
|
||||||
|
fi
|
||||||
|
|
||||||
|
# canonicalize path
|
||||||
|
local dirname=
|
||||||
|
if [[ -d $abspath ]]; then
|
||||||
|
dirname=$(cd "$abspath" && pwd -P)
|
||||||
|
abspath=$dirname
|
||||||
|
elif [[ -e $abspath ]]; then
|
||||||
|
dirname=$(cd "${abspath%/*}/" 2>/dev/null && pwd -P)
|
||||||
|
abspath=$dirname/${abspath##*/}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d $dirname && -e $abspath ]]; then
|
||||||
|
printf '%s\n' "$abspath"
|
||||||
|
else
|
||||||
|
printf 'invalid path: %s\n' "$path" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# the current git repository's .git directory
|
||||||
|
RELATIVE_GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
|
||||||
|
readonly GIT_DIR=$(realpath "$RELATIVE_GIT_DIR" 2>/dev/null)
|
||||||
|
|
||||||
|
# the current git repository's gitattributes file
|
||||||
|
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
|
||||||
|
|
||||||
|
##### FUNCTIONS
|
||||||
|
|
||||||
|
# print a message to stderr
|
||||||
|
warn() {
|
||||||
|
local fmt="$1"
|
||||||
|
shift
|
||||||
|
# shellcheck disable=SC2059
|
||||||
|
printf "transcrypt: $fmt\n" "$@" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
# print a message to stderr and exit with either
|
||||||
|
# the given status or that of the most recent command
|
||||||
|
die() {
|
||||||
|
local st="$?"
|
||||||
|
if [[ "$1" != *[^0-9]* ]]; then
|
||||||
|
st="$1"
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
warn "$@"
|
||||||
|
exit "$st"
|
||||||
|
}
|
||||||
|
|
||||||
|
# verify that all requirements have been met
|
||||||
|
run_safety_checks() {
|
||||||
|
# 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"?'
|
||||||
|
|
||||||
|
# exit if transcrypt is not in the required state
|
||||||
|
if [[ $requires_existing_config ]] && [[ ! $CONFIGURED ]]; then
|
||||||
|
die 1 'the current repository is not configured'
|
||||||
|
elif [[ ! $requires_existing_config ]] && [[ $CONFIGURED ]]; then
|
||||||
|
die 1 'the current repository is already configured; see --display'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check for dependencies
|
||||||
|
for cmd in {column,grep,mktemp,openssl,sed,tee}; do
|
||||||
|
command -v $cmd >/dev/null || die 'required command "%s" was not found' "$cmd"
|
||||||
|
done
|
||||||
|
|
||||||
|
# ensure the repository is clean (if it has a HEAD revision) so we can force
|
||||||
|
# checkout files without the destruction of uncommitted changes
|
||||||
|
if [[ $requires_clean_repo ]] && [[ $HEAD_EXISTS ]] && [[ $IS_BARE == 'false' ]]; then
|
||||||
|
# check if the repo is dirty
|
||||||
|
if ! git diff-index --quiet HEAD --; then
|
||||||
|
die 1 'the repo is dirty; commit or stash your changes before running transcrypt'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# unset the cipher variable if it is not supported by openssl
|
||||||
|
validate_cipher() {
|
||||||
|
local list_cipher_commands
|
||||||
|
list_cipher_commands='openssl enc -ciphers'
|
||||||
|
remove_dash() {
|
||||||
|
sed 's#\(^\| \)-#\1#g'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
local supported
|
||||||
|
supported=$($list_cipher_commands | remove_dash | tr -s ' ' '\n' | grep --line-regexp "$cipher") || true
|
||||||
|
if [[ ! $supported ]]; then
|
||||||
|
if [[ $interactive ]]; then
|
||||||
|
printf '"%s" is not a valid cipher; choose one of the following:\n\n' "$cipher"
|
||||||
|
$list_cipher_commands | remove_dash | column -c 80
|
||||||
|
printf '\n'
|
||||||
|
cipher=''
|
||||||
|
else
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
die 1 '"%s" is not a valid cipher; see `%s`' "$cipher" "$($list_cipher_commands | remove_dash)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ensure we have a cipher to encrypt with
|
||||||
|
get_cipher() {
|
||||||
|
while [[ ! $cipher ]]; do
|
||||||
|
local answer=
|
||||||
|
if [[ $interactive ]]; then
|
||||||
|
printf 'Encrypt using which cipher? [%s] ' "$DEFAULT_CIPHER"
|
||||||
|
read -r answer
|
||||||
|
fi
|
||||||
|
|
||||||
|
# use the default cipher if the user gave no answer;
|
||||||
|
# otherwise verify the given cipher is supported by openssl
|
||||||
|
if [[ ! $answer ]]; then
|
||||||
|
cipher=$DEFAULT_CIPHER
|
||||||
|
else
|
||||||
|
cipher=$answer
|
||||||
|
validate_cipher
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# ensure we have a password to encrypt with
|
||||||
|
get_password() {
|
||||||
|
while [[ ! $password ]]; do
|
||||||
|
local answer=
|
||||||
|
if [[ $interactive ]]; then
|
||||||
|
printf 'Generate a random password? [Y/n] '
|
||||||
|
read -r -n 1 -s answer
|
||||||
|
printf '\n'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# generate a random password if the user answered yes;
|
||||||
|
# otherwise prompt the user for a password
|
||||||
|
if [[ $answer =~ $YES_REGEX ]] || [[ ! $answer ]]; then
|
||||||
|
local password_length=30
|
||||||
|
local random_base64
|
||||||
|
random_base64=$(openssl rand -base64 $password_length)
|
||||||
|
password=$random_base64
|
||||||
|
else
|
||||||
|
printf 'Password: '
|
||||||
|
read -r password
|
||||||
|
[[ $password ]] || printf 'no password was specified\n'
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# confirm the transcrypt configuration
|
||||||
|
confirm_configuration() {
|
||||||
|
local answer=
|
||||||
|
|
||||||
|
printf '\nRepository metadata:\n\n'
|
||||||
|
[[ ! $REPO ]] || printf ' GIT_WORK_TREE: %s\n' "$REPO"
|
||||||
|
printf ' GIT_DIR: %s\n' "$GIT_DIR"
|
||||||
|
printf ' GIT_ATTRIBUTES: %s\n\n' "$GIT_ATTRIBUTES"
|
||||||
|
printf 'The following configuration will be saved:\n\n'
|
||||||
|
printf ' CIPHER: %s\n' "$cipher"
|
||||||
|
printf ' PASSWORD: %s\n\n' "$password"
|
||||||
|
printf 'Does this look correct? [Y/n] '
|
||||||
|
read -r -n 1 -s answer
|
||||||
|
|
||||||
|
# exit if the user did not confirm
|
||||||
|
if [[ $answer =~ $YES_REGEX ]] || [[ ! $answer ]]; then
|
||||||
|
printf '\n\n'
|
||||||
|
else
|
||||||
|
printf '\n'
|
||||||
|
die 1 'configuration has been aborted'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# confirm the rekey configuration
|
||||||
|
confirm_rekey() {
|
||||||
|
local answer=
|
||||||
|
|
||||||
|
printf '\nRepository metadata:\n\n'
|
||||||
|
[[ ! $REPO ]] || printf ' GIT_WORK_TREE: %s\n' "$REPO"
|
||||||
|
printf ' GIT_DIR: %s\n' "$GIT_DIR"
|
||||||
|
printf ' GIT_ATTRIBUTES: %s\n\n' "$GIT_ATTRIBUTES"
|
||||||
|
printf 'The following configuration will be saved:\n\n'
|
||||||
|
printf ' CIPHER: %s\n' "$cipher"
|
||||||
|
printf ' PASSWORD: %s\n\n' "$password"
|
||||||
|
printf 'You are about to re-encrypt all encrypted files using new credentials.\n'
|
||||||
|
printf 'Once you do this, their historical diffs will no longer display in plain text.\n\n'
|
||||||
|
printf 'Proceed with rekey? [y/N] '
|
||||||
|
read -r answer
|
||||||
|
|
||||||
|
# only rekey if the user explicitly confirmed
|
||||||
|
if [[ $answer =~ $YES_REGEX ]]; then
|
||||||
|
printf '\n'
|
||||||
|
else
|
||||||
|
die 1 'rekeying has been aborted'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# automatically stage rekeyed files in preparation for the user to commit them
|
||||||
|
stage_rekeyed_files() {
|
||||||
|
local encrypted_files
|
||||||
|
encrypted_files=$(git ls-crypt)
|
||||||
|
if [[ $encrypted_files ]] && [[ $IS_BARE == 'false' ]]; then
|
||||||
|
# touch all encrypted files to prevent stale stat info
|
||||||
|
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
touch $encrypted_files
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
git update-index --add -- $encrypted_files
|
||||||
|
|
||||||
|
printf '*** rekeyed files have been staged ***\n'
|
||||||
|
printf '*** COMMIT THESE CHANGES RIGHT AWAY! ***\n\n'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# save helper scripts under the repository's git directory
|
||||||
|
save_helper_scripts() {
|
||||||
|
mkdir -p "${GIT_DIR}/crypt"
|
||||||
|
|
||||||
|
openssl_command="openssl enc $ENCRYPT_OPTIONS -pass env:ENC_PASS"
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
cat <<-'EOF' >"${GIT_DIR}/crypt/clean"
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
filename=$1
|
||||||
|
# ignore empty files
|
||||||
|
if [[ -s $filename ]]; then
|
||||||
|
# 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
|
||||||
|
|
||||||
|
cat <<-'EOF' >"${GIT_DIR}/crypt/smudge"
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
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)
|
||||||
|
tee "$tempfile" | ENC_PASS=$password @openssl_command@ -d -a 2>/dev/null || cat "$tempfile"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat <<-'EOF' >"${GIT_DIR}/crypt/textconv"
|
||||||
|
#!/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
|
||||||
|
save_configuration() {
|
||||||
|
save_helper_scripts
|
||||||
|
|
||||||
|
# write the encryption info
|
||||||
|
git config transcrypt.version "$VERSION"
|
||||||
|
git config transcrypt.cipher "$cipher"
|
||||||
|
git config transcrypt.password "$password"
|
||||||
|
|
||||||
|
# write the filter settings
|
||||||
|
if [[ -d $(git rev-parse --git-common-dir) ]]; then
|
||||||
|
# this allows us to support multiple working trees via git-worktree
|
||||||
|
# ...but the --git-common-dir flag was only added in November 2014
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
git config filter.crypt.clean '"$(git rev-parse --git-common-dir)"/crypt/clean %f'
|
||||||
|
# shellcheck disable=SC2016
|
||||||
|
git config filter.crypt.smudge '"$(git rev-parse --git-common-dir)"/crypt/smudge'
|
||||||
|
# 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 diff.crypt.cachetextconv 'true'
|
||||||
|
git config diff.crypt.binary 'true'
|
||||||
|
git config merge.renormalize 'true'
|
||||||
|
|
||||||
|
# add a git alias for listing encrypted files
|
||||||
|
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_configuration() {
|
||||||
|
local current_cipher
|
||||||
|
current_cipher=$(git config --get --local transcrypt.cipher)
|
||||||
|
local current_password
|
||||||
|
current_password=$(git config --get --local transcrypt.password)
|
||||||
|
local escaped_password=${current_password//\'/\'\\\'\'}
|
||||||
|
|
||||||
|
printf 'The current repository was configured using transcrypt version %s\n' "$CONFIGURED"
|
||||||
|
printf 'and has the following configuration:\n\n'
|
||||||
|
[[ ! $REPO ]] || printf ' GIT_WORK_TREE: %s\n' "$REPO"
|
||||||
|
printf ' GIT_DIR: %s\n' "$GIT_DIR"
|
||||||
|
printf ' GIT_ATTRIBUTES: %s\n\n' "$GIT_ATTRIBUTES"
|
||||||
|
printf ' CIPHER: %s\n' "$current_cipher"
|
||||||
|
printf ' PASSWORD: %s\n\n' "$current_password"
|
||||||
|
printf 'Copy and paste the following command to initialize a cloned repository:\n\n'
|
||||||
|
printf " transcrypt -c %s -p '%s'\n" "$current_cipher" "$escaped_password"
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove transcrypt-related settings from the repository's git config
|
||||||
|
clean_gitconfig() {
|
||||||
|
git config --remove-section transcrypt 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 --unset merge.renormalize
|
||||||
|
|
||||||
|
# remove the merge section if it's now empty
|
||||||
|
local merge_values
|
||||||
|
merge_values=$(git config --get-regex --local 'merge\..*') || true
|
||||||
|
if [[ ! $merge_values ]]; then
|
||||||
|
git config --remove-section merge 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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,
|
||||||
|
# or it will encrypt locally decrypted files if you've just flushed the credentials
|
||||||
|
force_checkout() {
|
||||||
|
# make sure a HEAD revision exists
|
||||||
|
if [[ $HEAD_EXISTS ]] && [[ $IS_BARE == 'false' ]]; then
|
||||||
|
# this would normally delete uncommitted changes in the working directory,
|
||||||
|
# but we already made sure the repo was clean during the safety checks
|
||||||
|
local encrypted_files
|
||||||
|
encrypted_files=$(git ls-crypt)
|
||||||
|
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
||||||
|
IFS=$'\n'
|
||||||
|
for file in $encrypted_files; do
|
||||||
|
rm "$file"
|
||||||
|
git checkout --force HEAD -- "$file" >/dev/null
|
||||||
|
done
|
||||||
|
unset IFS
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove the locally cached encryption credentials and
|
||||||
|
# re-encrypt any files that had been previously decrypted
|
||||||
|
flush_credentials() {
|
||||||
|
local answer=
|
||||||
|
|
||||||
|
if [[ $interactive ]]; then
|
||||||
|
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.\n\n'
|
||||||
|
printf 'Proceed with credential flush? [y/N] '
|
||||||
|
read -r answer
|
||||||
|
printf '\n'
|
||||||
|
else
|
||||||
|
# although destructive, we should support the --yes option
|
||||||
|
answer='y'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# only flush if the user explicitly confirmed
|
||||||
|
if [[ $answer =~ $YES_REGEX ]]; then
|
||||||
|
clean_gitconfig
|
||||||
|
|
||||||
|
# re-encrypt any files that had been previously decrypted
|
||||||
|
force_checkout
|
||||||
|
|
||||||
|
printf 'The local transcrypt credentials have been successfully flushed.\n'
|
||||||
|
else
|
||||||
|
die 1 'flushing of credentials has been aborted'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove all transcrypt configuration from the repository
|
||||||
|
uninstall_transcrypt() {
|
||||||
|
local answer=
|
||||||
|
|
||||||
|
if [[ $interactive ]]; then
|
||||||
|
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.\n\n'
|
||||||
|
printf 'Proceed with uninstall? [y/N] '
|
||||||
|
read -r answer
|
||||||
|
printf '\n'
|
||||||
|
else
|
||||||
|
# although destructive, we should support the --yes option
|
||||||
|
answer='y'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# only uninstall if the user explicitly confirmed
|
||||||
|
if [[ $answer =~ $YES_REGEX ]]; then
|
||||||
|
clean_gitconfig
|
||||||
|
|
||||||
|
# remove helper scripts
|
||||||
|
for script in {clean,smudge,textconv}; do
|
||||||
|
[[ ! -f "${GIT_DIR}/crypt/${script}" ]] || rm "${GIT_DIR}/crypt/${script}"
|
||||||
|
done
|
||||||
|
[[ ! -d "${GIT_DIR}/crypt" ]] || rmdir "${GIT_DIR}/crypt"
|
||||||
|
|
||||||
|
# touch all encrypted files to prevent stale stat info
|
||||||
|
local encrypted_files
|
||||||
|
encrypted_files=$(git ls-crypt)
|
||||||
|
if [[ $encrypted_files ]] && [[ $IS_BARE == 'false' ]]; then
|
||||||
|
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
touch $encrypted_files
|
||||||
|
fi
|
||||||
|
|
||||||
|
# remove the `git ls-crypt` alias
|
||||||
|
git config --unset alias.ls-crypt
|
||||||
|
|
||||||
|
# remove the alias section if it's now empty
|
||||||
|
local alias_values
|
||||||
|
alias_values=$(git config --get-regex --local 'alias\..*') || true
|
||||||
|
if [[ ! $alias_values ]]; then
|
||||||
|
git config --remove-section alias 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# remove any defined crypt patterns in gitattributes
|
||||||
|
case $OSTYPE in
|
||||||
|
darwin*)
|
||||||
|
/usr/bin/sed -i '' '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
||||||
|
;;
|
||||||
|
linux*)
|
||||||
|
sed -i '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
printf 'The transcrypt configuration has been completely removed from the repository.\n'
|
||||||
|
else
|
||||||
|
die 1 'uninstallation has been aborted'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# list all of the currently encrypted files in the repository
|
||||||
|
list_files() {
|
||||||
|
if [[ $IS_BARE == 'false' ]]; then
|
||||||
|
cd "$REPO" || die 1 'could not change into the "%s" directory' "$REPO"
|
||||||
|
git ls-files | git check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# show the raw file as stored in the git commit object
|
||||||
|
show_raw_file() {
|
||||||
|
if [[ -f $show_file ]]; then
|
||||||
|
# ensure the file is currently being tracked
|
||||||
|
local escaped_file=${show_file//\//\\\/}
|
||||||
|
if git ls-files --others -- "$show_file" | awk "/${escaped_file}/{ exit 1 }"; then
|
||||||
|
file_paths=$(git ls-tree --name-only --full-name HEAD "$show_file")
|
||||||
|
else
|
||||||
|
die 1 'the file "%s" is not currently being tracked by git' "$show_file"
|
||||||
|
fi
|
||||||
|
elif [[ $show_file == '*' ]]; then
|
||||||
|
file_paths=$(git ls-crypt)
|
||||||
|
else
|
||||||
|
die 1 'the file "%s" does not exist' "$show_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
IFS=$'\n'
|
||||||
|
for file in $file_paths; do
|
||||||
|
printf '==> %s <==\n' "$file" >&2
|
||||||
|
git --no-pager show HEAD:"$file" --no-textconv
|
||||||
|
printf '\n' >&2
|
||||||
|
done
|
||||||
|
unset IFS
|
||||||
|
}
|
||||||
|
|
||||||
|
# export password and cipher to a gpg encrypted file
|
||||||
|
export_gpg() {
|
||||||
|
# check for dependencies
|
||||||
|
command -v gpg >/dev/null || die 'required command "gpg" was not found'
|
||||||
|
|
||||||
|
# ensure the recipient key exists
|
||||||
|
if ! gpg --list-keys "$gpg_recipient" 2>/dev/null; then
|
||||||
|
die 1 'GPG recipient key "%s" does not exist' "$gpg_recipient"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local current_cipher
|
||||||
|
current_cipher=$(git config --get --local transcrypt.cipher)
|
||||||
|
local current_password
|
||||||
|
current_password=$(git config --get --local transcrypt.password)
|
||||||
|
mkdir -p "${GIT_DIR}/crypt"
|
||||||
|
|
||||||
|
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 >"${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"
|
||||||
|
}
|
||||||
|
|
||||||
|
# import password and cipher from a gpg encrypted file
|
||||||
|
import_gpg() {
|
||||||
|
# check for dependencies
|
||||||
|
command -v gpg >/dev/null || die 'required command "gpg" was not found'
|
||||||
|
|
||||||
|
local path
|
||||||
|
if [[ -f "${GIT_DIR}/crypt/${gpg_import_file}" ]]; then
|
||||||
|
path="${GIT_DIR}/crypt/${gpg_import_file}"
|
||||||
|
elif [[ -f "${GIT_DIR}/crypt/${gpg_import_file}.asc" ]]; then
|
||||||
|
path="${GIT_DIR}/crypt/${gpg_import_file}.asc"
|
||||||
|
elif [[ ! -f $gpg_import_file ]]; then
|
||||||
|
die 1 'the file "%s" does not exist' "$gpg_import_file"
|
||||||
|
else
|
||||||
|
path="$gpg_import_file"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local configuration=''
|
||||||
|
local safety_counter=0 # fix for intermittent 'no secret key' decryption failures
|
||||||
|
while [[ ! $configuration ]]; do
|
||||||
|
configuration=$(gpg --batch --quiet --decrypt "$path")
|
||||||
|
|
||||||
|
safety_counter=$((safety_counter + 1))
|
||||||
|
if [[ $safety_counter -eq 3 ]]; then
|
||||||
|
die 1 'unable to decrypt the file "%s"' "$path"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
cipher=$(printf '%s' "$configuration" | grep '^cipher' | cut -d'=' -f 2-)
|
||||||
|
password=$(printf '%s' "$configuration" | grep '^password' | cut -d'=' -f 2-)
|
||||||
|
}
|
||||||
|
|
||||||
|
# print this script's usage message to stderr
|
||||||
|
usage() {
|
||||||
|
cat <<-EOF >&2
|
||||||
|
usage: transcrypt [-c CIPHER] [-p PASSWORD] [-h]
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# print this script's help message to stdout
|
||||||
|
help() {
|
||||||
|
cat <<-EOF
|
||||||
|
NAME
|
||||||
|
transcrypt -- transparently encrypt files within a git repository
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
transcrypt [options...]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
transcrypt will configure a Git repository to support the transparent
|
||||||
|
encryption/decryption of files by utilizing OpenSSL's symmetric cipher
|
||||||
|
routines and Git's built-in clean/smudge filters. It will also add a
|
||||||
|
Git alias "ls-crypt" to list all transparently encrypted files within
|
||||||
|
the repository.
|
||||||
|
|
||||||
|
The transcrypt source code and full documentation may be downloaded
|
||||||
|
from https://github.com/elasticdog/transcrypt.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-c, --cipher=CIPHER
|
||||||
|
the symmetric cipher to utilize for encryption;
|
||||||
|
defaults to aes-256-cbc
|
||||||
|
|
||||||
|
-p, --password=PASSWORD
|
||||||
|
the password to derive the key from;
|
||||||
|
defaults to 30 random base64 characters
|
||||||
|
|
||||||
|
-y, --yes
|
||||||
|
assume yes and accept defaults for non-specified options
|
||||||
|
|
||||||
|
-d, --display
|
||||||
|
display the current repository's cipher and password
|
||||||
|
|
||||||
|
-r, --rekey
|
||||||
|
re-encrypt all encrypted files using new credentials
|
||||||
|
|
||||||
|
-f, --flush-credentials
|
||||||
|
remove the locally cached encryption credentials and re-encrypt
|
||||||
|
any files that had been previously decrypted
|
||||||
|
|
||||||
|
-F, --force
|
||||||
|
ignore whether the git directory is clean, proceed with the
|
||||||
|
possibility that uncommitted changes are overwritten
|
||||||
|
|
||||||
|
-u, --uninstall
|
||||||
|
remove all transcrypt configuration from the repository and
|
||||||
|
leave files in the current working copy decrypted
|
||||||
|
|
||||||
|
-l, --list
|
||||||
|
list all of the transparently encrypted files in the repository,
|
||||||
|
relative to the top-level directory
|
||||||
|
|
||||||
|
-s, --show-raw=FILE
|
||||||
|
show the raw file as stored in the git commit object; use this
|
||||||
|
to check if files are encrypted as expected
|
||||||
|
|
||||||
|
-e, --export-gpg=RECIPIENT
|
||||||
|
export the repository's cipher and password to a file encrypted
|
||||||
|
for a gpg recipient
|
||||||
|
|
||||||
|
-i, --import-gpg=FILE
|
||||||
|
import the password and cipher from a gpg encrypted file
|
||||||
|
|
||||||
|
-v, --version
|
||||||
|
print the version information
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
view this help message
|
||||||
|
|
||||||
|
EXAMPLES
|
||||||
|
|
||||||
|
To initialize a Git repository to support transparent encryption, just
|
||||||
|
change into the repo and run the transcrypt script. transcrypt will
|
||||||
|
prompt you interactively for all required information if the corre-
|
||||||
|
sponding option flags were not given.
|
||||||
|
|
||||||
|
$ cd <path-to-your-repo>/
|
||||||
|
$ transcrypt
|
||||||
|
|
||||||
|
Once a repository has been configured with transcrypt, you can trans-
|
||||||
|
parently encrypt files by applying the "crypt" filter and diff to a
|
||||||
|
pattern in the top-level .gitattributes config. If that pattern matches
|
||||||
|
a file in your repository, the file will be transparently encrypted
|
||||||
|
once you stage and commit it:
|
||||||
|
|
||||||
|
$ echo 'sensitive_file filter=crypt diff=crypt' >> .gitattributes
|
||||||
|
$ git add .gitattributes sensitive_file
|
||||||
|
$ git commit -m 'Add encrypted version of a sensitive file'
|
||||||
|
|
||||||
|
See the gitattributes(5) man page for more information.
|
||||||
|
|
||||||
|
If you have just cloned a repository containing files that are
|
||||||
|
encrypted, you'll want to configure transcrypt with the same cipher and
|
||||||
|
password as the origin repository. Once transcrypt has stored the
|
||||||
|
matching credentials, it will force a checkout of any existing
|
||||||
|
encrypted files in order to decrypt them.
|
||||||
|
|
||||||
|
If the origin repository has just rekeyed, all clones should flush
|
||||||
|
their transcrypt credentials, fetch and merge the new encrypted files
|
||||||
|
via Git, and then re-configure transcrypt with the new credentials.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Aaron Bull Schaefer <aaron@elasticdog.com>
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
enc(1), gitattributes(5)
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
##### MAIN
|
||||||
|
|
||||||
|
# reset all variables that might be set
|
||||||
|
cipher=''
|
||||||
|
password=''
|
||||||
|
interactive='true'
|
||||||
|
display_config=''
|
||||||
|
rekey=''
|
||||||
|
flush_creds=''
|
||||||
|
uninstall=''
|
||||||
|
show_file=''
|
||||||
|
gpg_recipient=''
|
||||||
|
gpg_import_file=''
|
||||||
|
|
||||||
|
# used to bypass certain safety checks
|
||||||
|
requires_existing_config=''
|
||||||
|
requires_clean_repo='true'
|
||||||
|
|
||||||
|
# parse command line options
|
||||||
|
while [[ "${1:-}" != '' ]]; do
|
||||||
|
case $1 in
|
||||||
|
-c | --cipher)
|
||||||
|
cipher=$2
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--cipher=*)
|
||||||
|
cipher=${1#*=}
|
||||||
|
;;
|
||||||
|
-p | --password)
|
||||||
|
password=$2
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--password=*)
|
||||||
|
password=${1#*=}
|
||||||
|
;;
|
||||||
|
-y | --yes)
|
||||||
|
interactive=''
|
||||||
|
;;
|
||||||
|
-d | --display)
|
||||||
|
display_config='true'
|
||||||
|
requires_existing_config='true'
|
||||||
|
requires_clean_repo=''
|
||||||
|
;;
|
||||||
|
-r | --rekey)
|
||||||
|
rekey='true'
|
||||||
|
requires_existing_config='true'
|
||||||
|
;;
|
||||||
|
-f | --flush-credentials)
|
||||||
|
flush_creds='true'
|
||||||
|
requires_existing_config='true'
|
||||||
|
;;
|
||||||
|
-F | --force)
|
||||||
|
requires_clean_repo=''
|
||||||
|
;;
|
||||||
|
-u | --uninstall)
|
||||||
|
uninstall='true'
|
||||||
|
requires_existing_config='true'
|
||||||
|
requires_clean_repo=''
|
||||||
|
;;
|
||||||
|
-l | --list)
|
||||||
|
list_files
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-s | --show-raw)
|
||||||
|
show_file=$2
|
||||||
|
show_raw_file
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--show-raw=*)
|
||||||
|
show_file=${1#*=}
|
||||||
|
show_raw_file
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-e | --export-gpg)
|
||||||
|
gpg_recipient=$2
|
||||||
|
requires_existing_config='true'
|
||||||
|
requires_clean_repo=''
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--export-gpg=*)
|
||||||
|
gpg_recipient=${1#*=}
|
||||||
|
requires_existing_config='true'
|
||||||
|
requires_clean_repo=''
|
||||||
|
;;
|
||||||
|
-i | --import-gpg)
|
||||||
|
gpg_import_file=$2
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--import-gpg=*)
|
||||||
|
gpg_import_file=${1#*=}
|
||||||
|
;;
|
||||||
|
-v | --version)
|
||||||
|
printf 'transcrypt %s\n' "$VERSION"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-h | --help | -\?)
|
||||||
|
help
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
--*)
|
||||||
|
warn 'unknown option -- %s' "${1#--}"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
warn 'unknown option -- %s' "${1#-}"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# always run our safety checks
|
||||||
|
run_safety_checks
|
||||||
|
|
||||||
|
# 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
|
||||||
|
if [[ $uninstall ]]; then
|
||||||
|
uninstall_transcrypt
|
||||||
|
exit 0
|
||||||
|
elif [[ $display_config ]] && [[ $flush_creds ]]; then
|
||||||
|
display_configuration
|
||||||
|
printf '\n'
|
||||||
|
flush_credentials
|
||||||
|
exit 0
|
||||||
|
elif [[ $display_config ]]; then
|
||||||
|
display_configuration
|
||||||
|
exit 0
|
||||||
|
elif [[ $flush_creds ]]; then
|
||||||
|
flush_credentials
|
||||||
|
exit 0
|
||||||
|
elif [[ $gpg_recipient ]]; then
|
||||||
|
export_gpg
|
||||||
|
exit 0
|
||||||
|
elif [[ $gpg_import_file ]]; then
|
||||||
|
import_gpg
|
||||||
|
elif [[ $cipher ]]; then
|
||||||
|
validate_cipher
|
||||||
|
fi
|
||||||
|
|
||||||
|
# perform function calls to configure transcrypt
|
||||||
|
get_cipher
|
||||||
|
get_password
|
||||||
|
|
||||||
|
if [[ $rekey ]] && [[ $interactive ]]; then
|
||||||
|
confirm_rekey
|
||||||
|
elif [[ $interactive ]]; then
|
||||||
|
confirm_configuration
|
||||||
|
fi
|
||||||
|
|
||||||
|
save_configuration
|
||||||
|
|
||||||
|
if [[ $rekey ]]; then
|
||||||
|
stage_rekeyed_files
|
||||||
|
else
|
||||||
|
force_checkout
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ensure the git attributes file exists
|
||||||
|
if [[ ! -f $GIT_ATTRIBUTES ]]; then
|
||||||
|
mkdir -p "${GIT_ATTRIBUTES%/*}"
|
||||||
|
printf '#pattern filter=crypt diff=crypt\n' >"$GIT_ATTRIBUTES"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'The repository has been successfully configured by transcrypt.\n'
|
||||||
|
|
||||||
|
exit 0
|
18
variables.nix
Normal file
18
variables.nix
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{ lib, ... }:
|
||||||
|
|
||||||
|
# This file contains global constants that are
|
||||||
|
# used thoughout the configuration files. They are
|
||||||
|
# "variables", in the sense that they can change
|
||||||
|
# from time to time and we don't like to search-replace.
|
||||||
|
{
|
||||||
|
options.var = lib.mkOption {
|
||||||
|
type = lib.types.attrs;
|
||||||
|
readOnly = true;
|
||||||
|
default = {
|
||||||
|
hostname = "maxwell.ydns.eu";
|
||||||
|
ipAddress = "2.25.5.112";
|
||||||
|
};
|
||||||
|
description = "Global constants.";
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user