#!/bin/bash # Collecting authentification data InstallDependencies() { packagesNeeded='curl jq mkpasswd pwgen' if [ -x "$(command -v apk)" ]; then sudo apk add --no-cache $packagesNeeded # Alpine Linux elif [ -x "$(command -v apt-get)" ]; then sudo apt-get install curl jq whois pwgen # Debian/Ubuntu Linux elif [ -x "$(command -v dnf)" ]; then sudo dnf install $packagesNeeded # Fedora Linux elif [ -x "$(command -v rpm-ostree)" ]; then sudo rpm-ostree install $packagesNeeded # Fedora Linux Silverblue elif [ -x "$(command -v zypper)" ]; then sudo zypper install $packagesNeeded # openSUSE Linux elif [ -x "$(command -v pacman)" ]; then sudo pacman -S $packagesNeeded # Arch/Manjaro Linux elif [ -x "$(command -v emerge)" ]; then sudo emerge --ask $packagesNeeded # Gentoo Linux elif [ -x "$(command -v nix-env)" ]; then nix-env -iA $packagesNeeded # NixOS else echo "FAILED TO INSTALL PACKAGE: Package manager not found. You must manually install: $packagesNeeded">&2; fi wget https://selfprivacy.org/configuration.nix wget https://selfprivacy.org/mailserver.nix wget https://selfprivacy.org/goss.nix wget https://selfprivacy.org/goss.yaml wget https://selfprivacy.org/restic.nix wget https://selfprivacy.org/s3cli chmod +x s3cli } CollectData() { read -p "Please, paste your Hetzner API token here: " HETZNER_TOKEN read -p "Please, paste your CloudFlare Token: " CLOUDFLARE_TOKEN read -p "Please, paste your AWS Secret Access Key: " AWS_TOKEN read -p "Please, paste your AWS Access Key ID: " AWS_TOKEN_ID read -p "Please, define your domain there: " DOMAIN read -p "Please, define your mail username: " USERNAME read -p "Please, define your password: " PASSWORD && PASSWORD=$( mkpasswd -m sha-512 "$PASSWORD" ) } # Generate SSH key GenerateSSHKey() { echo "Generating SSH keys..." mkdir ~/.nix-ms ssh-keygen -f ~/.nix-ms/id_rsa > /dev/null export sshKey=$( cat ~/.nix-ms/id_rsa.pub ) } # Add SSH key to Hetzner AddSSHKey() { curl -s \ -X POST \ -H "Authorization: Bearer $HETZNER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"nixosms","public_key":"'"${sshKey}"'","labels":{}}' \ 'https://api.hetzner.cloud/v1/ssh_keys' > /dev/null } # Create NixOS config file MakeConfig() { # Mailserver sed -i '15s/.*/ fqdn = "'$DOMAIN'";/' mailserver.nix sed -i '16s/.*/ domains = [ "'"$DOMAIN"'" ];/' mailserver.nix sed -i '21s/.*/\t"'$USERNAME'@'$DOMAIN'" = {/' mailserver.nix sed -i '22s/.*/\t hashedPassword = "'"$PASSWORD"'";/' mailserver.nix sed -i '31s/.*/\t\t"'"$DOMAIN"'"/' mailserver.nix sed -i '48s/.*/\t "admin@'"$DOMAIN"'" = "'"$USERNAME"'@'"$DOMAIN"'";/' mailserver.nix sed -i '70s/.*/ email = "'"$USERNAME"'@'"$DOMAIN"'";/' mailserver.nix # System Configuration sed -i "16s,.*,\t\"${sshKey}\"," configuration.nix # Restic sed -i '14s/.*/\t\tEnvironment = [ "AWS_ACCESS_KEY_ID='$AWS_TOKEN_ID'" "AWS_SECRET_ACCESS_KEY='$AWS_TOKEN'" ];/' restic.nix sed -i "17s,.*,\t restic -r s3:s3.amazonaws.com/${AWS_BUCKET_NAME} backup /var/vmail /var/vmail ," restic.nix } MakeServer() { echo "Creating Server..." curl -s \ -X POST \ -H "Authorization: Bearer $HETZNER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"nixos-mailserver","server_type":"cx11","start_after_create":true,"image":"ubuntu-20.04","ssh_keys":["nixosms"],"volumes":[],"networks":[],"user_data":"#cloud-config\nruncmd:\n- curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.03 bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":false}' \ 'https://api.hetzner.cloud/v1/servers' > /dev/null } CreateS3BucketRaw() { local date="$(date -u '+%a, %e %b %Y %H:%M:%S +0000')" local string_to_sign local MD5="" printf -v string_to_sign "%s\n%s\n\n%s\n%s" "PUT" "$MD5" "$date" "$path" signature=$(echo -n "$string_to_sign" | openssl sha1 -binary -hmac "${AWS_SECRET_ACCESS_KEY}" | openssl base64) curl -s \ -X PUT \ -H "Host: $AWS_TOKEN_ID.s3-control.us-east-2.amazonaws.com" \ -H "Content-Length: 0" \ -H "Date: $(date +'%a, %d %b %Y %H:%M:%S %Z')" \ -H "Authorization: AWS $AWS_TOKEN_ID:$signature" } CreateS3Bucket() { if test -z $(./s3cli -e http://s3.us-east-2.amazonaws.com --ak "$AWS_TOKEN_ID" --sk "$AWS_TOKEN" --region us-east-2 bucket ls | grep backup) then export AWS_BUCKET_NAME=$(pwgen -1 --no-capitalize 6)-backup ./s3cli -e http://s3.us-east-2.amazonaws.com --ak "$AWS_TOKEN_ID" --sk "$AWS_TOKEN" --region us-east-2 bucket create $AWS_BUCKET_NAME else read -p "Restore from backup? y/n " RESTORE_MAILBACKUP fi } # Get machine IP GetMachineIP() { curl -s \ -H "Authorization: Bearer $HETZNER_TOKEN" \ 'https://api.hetzner.cloud/v1/servers' > .machine.json export machineip=$( for i in {0..24}; do jq 'if .servers['$i'].name == "nixos-mailserver" then .servers['$i'].public_net.ipv4.ip else null end' .machine.json; done | grep -v null | sed 's/"//' | sed 's/"//' ) } # Copy and apply mailserver config ApplyConfig() { ssh -i ~/.nix-ms/id_rsa "root@$machineip" echo "Authentificated" scp -i ~/.nix-ms/id_rsa mailserver.nix "root@$machineip:/root" scp -i ~/.nix-ms/id_rsa configuration.nix "root@$machineip:/root" scp -i ~/.nix-ms/id_rsa goss.nix "root@$machineip:/root" scp -i ~/.nix-ms/id_rsa restic.nix "root@$machineip:/root" scp -i ~/.nix-ms/id_rsa goss.yaml "root@$machineip:/root" ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/mailserver.nix /etc/nixos/mailserver.nix ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/configuration.nix /etc/nixos/configuration.nix ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/goss.nix /etc/nixos/goss.nix ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/restic.nix /etc/nixos/restic.nix sleep 3 ssh -i ~/.nix-ms/id_rsa "root@$machineip" nixos-rebuild switch } RestoreBackup() { ssh -i ~/.nix-ms/id_rsa "root@$machineip" restic -r s3:s3.amazonaws/$AWS_BUCKET_NAME restore latest --target /var/vmail /var/dkim } # Get DKIM keys from remote machine GetDKIM() { scp -i ~/.nix-ms/id_rsa "root@$machineip:/var/dkim/$DOMAIN.selector.txt" . sed -i '1d' $DOMAIN.selector.txt sed -i 's/ ) ; ----- DKIM key selector for '$DOMAIN'//g' $DOMAIN.selector.txt export dkim=$( cat $DOMAIN.selector.txt ) } ClearTempFiles() { rm .machine.json rm .cloudflare.json rm $DOMAIN.selector.txt rm -rf ~/.nix-ms/ rm ~/.ssh/known_hosts rm configuration.nix rm goss.nix rm goss.yaml rm mailserver.nix rm restic.nix rm s3cli exit 0 } # Cloudflare configuration # Get zone ID GetZoneID() { curl -s -X GET "https://api.cloudflare.com/client/v4/zones" \ -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \ -H "Content-Type: application/json" > .cloudflare.json export zoneid=$( for i in {0..24}; do jq 'if .result['$i'].name == "'$DOMAIN'" then .result['$i'].id else null end' .cloudflare.json; done | grep -v null | sed -e 's/^"//' -e 's/"$//' ) } # Create records CreateARecord() { curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \ -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \ -H "Content-Type: application/json" \ --data '{"type":"A","name":"'$DOMAIN'","content":"'$machineip'","ttl":3600,"priority":10,"proxied":false}' > /dev/null } CreateMXRecord() { curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \ -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \ -H "Content-Type: application/json" \ --data '{"type":"MX","name":"@","content":"'$DOMAIN'","ttl":3600,"priority":10,"proxied":false}' > /dev/null } CreateDMARCRecord() { curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \ -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \ -H "Content-Type: application/json" \ --data '{"type":"TXT","name":"_dmarc","content":"v=DMARC1; p=none","ttl":18000,"priority":10,"proxied":false}' > /dev/null } CreateSPFRecord() { curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \ -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \ -H "Content-Type: application/json" \ --data '{"type":"TXT","name":"'$DOMAIN'","content":"v=spf1 a mx ip4:'$machineip' -all","ttl":18000,"priority":10,"proxied":false}' > /dev/null } CreateDKIMRecord() { export dkim=$( echo $dkim | sed -e 's/^"//' -e 's/"$//' ) curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" -H "Authorization: Bearer $CLOUDFLARE_TOKEN" -H "Content-Type: application/json" --data '{"type":"TXT","name":"selector._domainkey","content":"v=DKIM1; '$dkim'","ttl":18000,"priority":10,"proxied":false}' > /dev/null } PostInstallation() { ssh -i ~/.nix-ms/id_rsa "root@$machineip" restic -r s3:s3.amazonaws.com/$AWS_BUCKET_NAME init ssh -i ~/.nix-ms/id_rsa "root@$machineip" restic -r s3:s3.amazonaws.com/$AWS_BUCKET_NAME forget --prune --keep-hourly 2 --keep-daily 7 --keep-weekly 4 #ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/result/bin/goss /root/ #ssh -i ~/.nix-ms/id_rsa "root@$machineip" /root/goss serve --format json & } PerformTests() { curl $machineip:8080/healthz > .healthz.json for i in {0..24}; do jq 'if .results['$i'].err != null then "FAIL" else "OK" end' .healthz.json; done } RunPreFlightChecks() { curl -H "Authorization: Bearer $HETZNER_TOKEN" 'https://api.hetzner.cloud/v1/servers' > .hetzner_test.json jq 'if .error != null then "Preflight checks failed" else "Success" end' .hetzner_test.json | if [ "Preflight checks failed" ] then echo "Hetzner Authorization failed" exit -1 fi } if test -z "$HETZNER_TOKEN" || test -z "$CLOUDFLARE_TOKEN" || test -z "$PASSWORD" then CollectData if [ ${#HETZNER_TOKEN} != 64 ] then echo "Hetzner Token is incorrect. Please double check your input" exit -1 elif [ ${#CLOUDFLARE_TOKEN} != 40 ] then echo "Cloudflare Token is incorrect. Please double check your input" exit -1 elif [[ ${DOMAIN} != *.* ]] then echo "Got unexpected domain. Possibly wrong input" exit -1 fi fi InstallDependencies GenerateSSHKey printf "Importing SSH key into your Hetzner account..." AddSSHKey printf "done\n" printf "Generating config file..." MakeConfig printf "done\n" printf "Waiting for the server to create...\n" MakeServer sleep 30 printf "Waiting for nixos-infect to replace system files(this may take some time)...\n" sleep 280 CreateS3Bucket GetMachineIP ApplyConfig if [ $RESTORE_MAILBACKUP == "y" ] then RestoreBackup fi GetDKIM echo "Beginning CloudFlare configuration" GetZoneID printf "Creating records..." CreateARecord CreateMXRecord CreateDMARCRecord CreateSPFRecord CreateDKIMRecord printf "done\n" PostInstallation printf "Clearing temporary files..." ClearTempFiles printf "done\n"