├── .git-crypt ├── .gitattributes └── keys │ └── default │ └── 0 │ └── 87184F1CA00EAF3A38E95DF8E4DD675ECF1DC4FC.gpg ├── .gitattributes ├── .gitignore ├── README.md ├── configuration.nix ├── manage ├── nixpkgs.nix ├── overlays ├── nixops.nix └── overlay.nix ├── secrets ├── DO_token.txt ├── openvpn.ovpn └── otp.txt └── xserver.nix /.git-crypt/.gitattributes: -------------------------------------------------------------------------------- 1 | # Do not edit this file. To specify the files to encrypt, create your own 2 | # .gitattributes file in the directory where your files are. 3 | * !filter !diff 4 | -------------------------------------------------------------------------------- /.git-crypt/keys/default/0/87184F1CA00EAF3A38E95DF8E4DD675ECF1DC4FC.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomberek/digital-nix/ab96f09183750e9876162c446bb1de545e6521c5/.git-crypt/keys/default/0/87184F1CA00EAF3A38E95DF8E4DD675ECF1DC4FC.gpg -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.nixops binary filter=git-crypt diff=git-crypt 2 | secrets/** filter=git-crypt diff=git-crypt 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.nixops* 2 | backup/** 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Manging Digital Ocean server 2 | 3 | ### Why this is awesome 4 | 5 | 1. Reproducible server - kill it all and regenerate. Only things lost are user accounts and user-specific settings. 6 | 2. Like Terraform: manages VPC, subnets, elasticIP (unused for now), route tables, security groups, etc. 7 | 3. Updates are transactional and allows for rollback. 8 | 4. [Fail2ban](https://github.com/fail2ban/fail2ban) to quiet the noise of the internet 9 | 5. [sslh](https://linux.die.net/man/8/sslh) to allow 443 be used for SSH, HTTP, SSL, TINC, etc. 10 | 6. [Tinc VPN](https://www.tinc-vpn.org/) is a mesh-capable VPN. 11 | 7. [Mosh](https://mosh.org) for fast ssh 12 | 8. Can support larger networks of machines. 13 | 14 | ### Problems 15 | 1. Using a non-released and bleeding-edge version of nixops. This can be relaxed soon and we can clean up the boilerplate. 16 | 2. Collaborating in a team involves sharing the statefile. This can be done with GPG sharing, but is clunky. S3 support soon. 17 | 3. Supports Virtual Box, NixOS, AWS, GCE, Azure, Hetzner, Digital Ocean, libvirtd 18 | 19 | ### Using 20 | These commands require three things: 21 | 22 | 1. [nix](https://nixos.org/nix/download.html) or run `make nix` in the top of this repo 23 | 2. access to `/server/.nixops`: this is a statefile which manages deployments 24 | 3. access to `/server/secrets` directory (recommend to use git-crypt and the git-crypt.attributes file) 25 | 26 | Both statefile and secrets are encrypted. Decrypt by having an admin add you to the [git-crypt](https://www.agwa.name/projects/git-crypt/) repo. You must have a GPG key. This is a **rough** overview of how to do this: 27 | ```bash 28 | nix-env -iA nixpkgs.gnupg 29 | gpg2 --full-generate-key 30 | gpg2 -k # this will show you your keys, find the id of your pub key 31 | gpg2 --send-key 32 | # Contact an Admin who will add your key to the git repo, then in the repo: 33 | git-crypt unlock 34 | ``` 35 | For a better GPG walkthrough, see [this page](https://alexcabal.com/creating-the-perfect-gpg-keypair/) or [GitHub's tutorial](https://help.github.com/articles/generating-a-new-gpg-key/). 36 | 37 | #### Deploying the server 38 | Ensure you have your authtoken in `./secrets/DO_token.txt` or wherever configuration.nix points to. 39 | Pick a `NAME` such that the following will create `NAME.nixops` file in the current directory to track the state of this deployment: 40 | ```bash 41 | ./manage NAME create '' 42 | ``` 43 | or 44 | ```bash 45 | ./manage NAME create ./configuration.nix 46 | ``` 47 | Note: the former is harder to understand, but will be filesystem-location-independent. (You can move this folder around and everything will still work, portable). Then: 48 | ```bash 49 | ./manage NAME deploy 50 | ``` 51 | This will create the server, infect it with NixOS, deploy the configuration, activate services, etc. 52 | 53 | #### SSH 54 | At the moment, this will create a single machine, but can be easily extend such that this deployment manages multiple machines to whatever infrastructure desired. The following will SSH into your newly deployed box. 55 | ```bash 56 | ./manage NAME ssh machine 57 | ``` 58 | 59 | #### Update 60 | ```bash 61 | ./manage NAME deploy 62 | ``` 63 | 64 | ### Destroy and Delete 65 | ```bash 66 | ./manage NAME destroy 67 | ./manage NAME delete 68 | ``` 69 | 70 | ### Using Tinc VPN 71 | `nix-env -iA nixpkgs.tinc_pre` 72 | 73 | SSH into the server, adjust the `/var/run/tinc.NAME*/` file permissions and create an invitation. As root on the server: 74 | ```bash 75 | ./manage live ssh server 76 | chown tinc.name /var/run/tinc.name* 77 | tinc -n name invite 78 | systemctl restart tinc.name 79 | ``` 80 | 81 | The output of the last command is used to join the network. You must also establish an IP. Avahi is the best. On your own computer: 82 | ```bash 83 | tinc -n name join 84 | echo "avahi-autoipd name -D" >> /etc/tinc/name/tinc-up 85 | tinc -n name start 86 | ``` 87 | Then a cheap SOCKSv5 proxy for browsing can be made using the Avahi local link hostname: 88 | ``` 89 | ssh -D 8080 username@your-server -N 90 | ``` 91 | 92 | ## Random notes 93 | ### Add user to GPG 94 | ```bash 95 | git-crypt add-gpg-user USERID 96 | ``` 97 | -------------------------------------------------------------------------------- /configuration.nix: -------------------------------------------------------------------------------- 1 | let name = "test"; 2 | in 3 | { 4 | resources.sshKeyPairs.ssh-key = {}; 5 | 6 | machine = { config, pkgs, lib, ... }: { 7 | #imports = [ ./xserver.nix ]; 8 | deployment.targetEnv = "digitalOcean"; 9 | deployment.digitalOcean.enableIpv6 = true; 10 | deployment.digitalOcean.region = "nyc1"; 11 | deployment.digitalOcean.size = "1gb"; 12 | deployment.digitalOcean.authToken = builtins.readFile ./secrets/DO_token.txt; 13 | 14 | # System packages installed 15 | environment.systemPackages = with pkgs; [ 16 | openssh openssl sqlite-interactive vim tree gitAndTools.git tinc_pre]; 17 | 18 | deployment.keys."openvpn.ovpn" = { 19 | text = builtins.readFile ./secrets/openvpn.ovpn; 20 | }; 21 | services.openvpn.servers.testing = { 22 | config = '' 23 | config /run/keys/openvpn.ovpn 24 | ''; 25 | autoStart = true; 26 | }; 27 | 28 | # key-based access only 29 | services.openssh = { 30 | enable = true; 31 | challengeResponseAuthentication = false; 32 | passwordAuthentication = false; 33 | }; 34 | 35 | users.users.tom = { 36 | initialPassword = builtins.readFile ./secrets/otp.txt; 37 | isNormalUser = true; 38 | }; 39 | users.mutableUsers = false; 40 | #config.networking.hostName = "digital.tomberek.info"; 41 | networking = { 42 | firewall = { 43 | allowedTCPPorts = [22 443 655 3389 8080]; 44 | allowedUDPPorts = [443 655 1194]; 45 | }; 46 | }; 47 | networking.nat = { 48 | enable = true; 49 | externalInterface = "ens3"; 50 | internalInterfaces = [ "vpn-dev" ]; 51 | }; 52 | networking.firewall.trustedInterfaces = [ "vpn-dev" ]; 53 | 54 | # General security setting 55 | services.fail2ban.enable=true; 56 | services.fail2ban.jails.ssh-iptables2 = '' 57 | filter = sshd[mode=aggressive] 58 | action = iptables-multiport[name=SSH, port="22", protocol=tcp] 59 | maxretry = 10 60 | ''; 61 | services.fail2ban.jails.nginx-botsearch = '' 62 | filter = nginx-botsearch 63 | action = iptables-multiport[name=NGINXBOT, port="443", protocol=tcp] 64 | ''; 65 | services.fail2ban.jails.nginx-http-auth = '' 66 | filter = nginx-http-auth 67 | action = iptables-multiport[name=NGINXAUTH, port="443", protocol=tcp] 68 | ''; 69 | 70 | # Uses sslh to serve ssh and tinc over 443 if needed 71 | services.sslh = { enable = true; 72 | listenAddress = "0.0.0.0"; 73 | verbose = false; 74 | appendConfig = '' 75 | protocols: 76 | ( 77 | { name: "ssh"; service: "ssh"; host: "localhost"; port: "22"; probe: "builtin"; }, 78 | { name: "openvpn"; host: "localhost"; port: "1194"; probe: "builtin"; }, 79 | { name: "http"; host: "localhost"; port: "80"; probe: "builtin"; }, 80 | { name: "ssl"; host: "localhost"; port: "4430"; probe: "builtin"; }, 81 | { name: "tinc"; host: "localhost"; port: "655"; probe: "builtin"; } 82 | ); 83 | ''; 84 | }; 85 | 86 | 87 | /* 88 | # Layer 3 89 | services.tinc.networks."${name}" = { 90 | name = lib.replaceChars ["." "-"] ["_" "_"] config.networking.hostName; 91 | #interfaceType = "tap"; 92 | chroot = false; 93 | # Mode = switch 94 | extraConfig = '' 95 | AutoConnect = yes 96 | LocalDiscovery = yes 97 | ''; 98 | listenAddress = "0.0.0.0 655"; 99 | }; 100 | */ 101 | 102 | # Mosh for latency-free interaction 103 | programs.mosh.enable=true; 104 | 105 | # Avahi for discovery - currently off 106 | services.avahi = { enable = false; 107 | #interfaces = [ "lo" "${name}" ]; 108 | ipv6 = true; 109 | nssmdns = true; 110 | hostName = lib.replaceChars ["." "-"] ["_" "_"] config.networking.hostName; 111 | domainName = "local"; 112 | wideArea = true; 113 | publish.enable=true; 114 | publish.domain=true; 115 | publish.addresses=true; 116 | publish.hinfo=true; 117 | publish.workstation=true; 118 | publish.userServices=true; 119 | }; 120 | }; 121 | 122 | } 123 | -------------------------------------------------------------------------------- /manage: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # A NixOps Wrapper for Git Projects 4 | # --------------------------------- 5 | # 6 | # Repo: https://github.com/grafted-in/nixops-manager 7 | # 8 | # This tool is a simple wrapper around NixOps. The goal is to make it easier to use NixOps when you 9 | # want to share your deployment state between members of a team. 10 | # 11 | # To achieve this, this wrapper gives every deployment as a separate state file which is placed 12 | # in the same directory as this script. The files have the `.nixops` extension. 13 | # 14 | # You are expected to keep these files in version control. It's also *highly* recommended that you 15 | # use a tool like git-crypt to keep them encrypted with this entry in .gitattributes: 16 | # 17 | # *.nixops binary filter=git-crypt diff=git-crypt 18 | # 19 | # This tool also enforces a per-repository version of Nixpkgs via a `nixpkgs-version.sh` file in the 20 | # same directory as the script. This ensures that all users have a consistent version of NixOps and 21 | # deploy a consistent set of packages to servers. 22 | # 23 | # Most commands work identically to NixOps. However, instead of specifying deployments with 24 | # the `--deployment/-d` flag, you select a deployment in the first argument. In other words, instead 25 | # of the normal NixOps usage of 26 | # 27 | # nixops deploy -d stage --check # Normal nixops usage. 28 | # 29 | # You'd run: 30 | # 31 | # ./manage stage deploy --check # Manage script usage. 32 | # 33 | # This assume there is a file ./stage.nixops where this state is being stored. 34 | # 35 | # Use `./manage --help` to see normal NixOps help. 36 | # Use `./manage {deployment} .shell` to open a Nix shell where the environment is set up to use 37 | # `nixops` directly with the same behavior as running `./manage` commands. 38 | 39 | set -e 40 | 41 | here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 42 | 43 | deployment="$1" 44 | command="$2" 45 | state_file="$here/${deployment}.nixops" 46 | 47 | function colon(){ 48 | echo ${1:+${1}:} 49 | } 50 | export NIX_PATH=nixpkgs-overlays=./overlays:nixpkgs=$(nix-instantiate --eval ./nixpkgs.nix | tr -d '"' ):. 51 | export NIXOPS_STATE="$state_file" 52 | export NIXOPS_DEPLOYMENT="$deployment" 53 | export nixops_version="nixops" 54 | export DIGITAL_OCEAN_AUTH_TOKEN=$(cat ~/.digital) 55 | 56 | withNixops="nix-shell -p $nixops_version --run" 57 | 58 | # Arg list trick: 59 | # https://stackoverflow.com/questions/3104209 60 | # ARGS=$(printf "%q"" " "$@") 61 | 62 | if [[ $deployment == --* ]]; then 63 | ARGS=$(printf "%q"" " "$@") 64 | $withNixops "nixops $ARGS" 65 | exit $? 66 | elif [ "$command" == ".shell" ]; then 67 | nix-shell -p "$nixops_version" 68 | elif [ ! -e "$state_file" ] && [ "$command" != "create" ]; then 69 | >&2 echo "You're trying to use a deployment that doesn't exist yet. Try running $0 $deployment create" 70 | exit 1 71 | elif [ -e "$state_file" ] && [ "$command" == "create" ]; then 72 | >&2 echo "You're trying to create a deployment that already exists." 73 | exit 1 74 | else 75 | ARGS=$(printf "%q"" " "${@:2}") 76 | $withNixops "nixops $ARGS" 77 | fi 78 | -------------------------------------------------------------------------------- /nixpkgs.nix: -------------------------------------------------------------------------------- 1 | let 2 | nixpkgs = builtins.fetchTarball { 3 | url = "https://releases.nixos.org/nixpkgs/nixpkgs-19.03pre155263.20c4986c4dd/nixexprs.tar.xz"; 4 | sha256 = "1nkvh8ypbckx4f47p6hdc66ban2irvb0bqj6zci8kkd59ssncldp"; 5 | #url = "https://d3g5gsiof5omrk.cloudfront.net/nixos/unstable/nixos-18.09pre133640.ea145b68a01/nixexprs.tar.xz"; 6 | #sha256 = "18x1wab5skbffaizwpavip4jqf7d7bmkaxzna95hd4ypa9xmynwx"; 7 | }; 8 | 9 | pkgs = import nixpkgs { config = {}; }; 10 | in nixpkgs 11 | -------------------------------------------------------------------------------- /overlays/nixops.nix: -------------------------------------------------------------------------------- 1 | self: super: { 2 | nixopsMaster = super.nixopsUnstable.overrideAttrs (old: { 3 | src = super.fetchurl { 4 | url = "https://github.com/NixOS/nixops/archive/v1.6.tar.gz"; 5 | sha256 = "00y2arc5rffvy6xmx4p6ibpjyc61k8dkiabq7ccwwjgckz1d2dpb"; 6 | }; 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /overlays/overlay.nix: -------------------------------------------------------------------------------- 1 | self: super: rec { 2 | nixopsUnstable2 = (super.callPackage ( rec { 3 | version = "1.6pre9999_f06763a"; #pre2282_08bb06c"; 4 | src = super.fetchgit { 5 | url = "https://github.com/NixOS/nixops"; 6 | #url = "https://hydra.nixos.org/build/64518294/download/2/nixops-${version}.tar.bz2"; 7 | #sha256 = "1cl0869nl67fr5xk0jl9cvqbmma7d4vz5xbg56jpl7casrr3i51x"; 8 | sha256 = "004hyp5bw9p9v7fag8d6ahnfz32bzfa5s7vqgkj06wizymcgb06h"; 9 | rev = "f06763add1056124aaaa199d6616781b16d79b8e"; 10 | }; 11 | } 12 | )).overrideDerivation (oldAttrs: rec{ 13 | namePrefix=""; 14 | version = "1.6pre9999_f06763a"; #pre2282_08bb06c"; 15 | #buildInputs = [ self.pkgs.git self.pkgs.libxslt self.pkgs.docbook5_xsl ]; 16 | patchPhase = '' 17 | for i in scripts/nixops setup.py doc/manual/manual.xml; do 18 | substituteInPlace $i --subst-var-by version ${version} 19 | done 20 | substituteInPlace nix/eval-machine-info.nix \ 21 | --replace 'system.nixosVersion' 'system.nixos.version' 22 | ''; 23 | postInstall = 24 | '' 25 | # Backward compatibility symlink. 26 | ln -s nixops $out/bin/charon 27 | mkdir -p $out/share/nix/nixops 28 | cp -av nix/* $out/share/nix/nixops 29 | ''; 30 | propagatedBuildInputs = with self.python2Packages; 31 | [ prettytable 32 | boto 33 | boto3 34 | hetzner 35 | libcloud 36 | libvirt 37 | azure-storage 38 | azure-mgmt-compute 39 | azure-mgmt-network 40 | azure-mgmt-resource 41 | azure-mgmt-storage 42 | adal 43 | # Go back to sqlite once Python 2.7.13 is released 44 | pysqlite 45 | datadog 46 | digital-ocean 47 | ]; 48 | 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /secrets/DO_token.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomberek/digital-nix/ab96f09183750e9876162c446bb1de545e6521c5/secrets/DO_token.txt -------------------------------------------------------------------------------- /secrets/openvpn.ovpn: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomberek/digital-nix/ab96f09183750e9876162c446bb1de545e6521c5/secrets/openvpn.ovpn -------------------------------------------------------------------------------- /secrets/otp.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomberek/digital-nix/ab96f09183750e9876162c446bb1de545e6521c5/secrets/otp.txt -------------------------------------------------------------------------------- /xserver.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ...}: 2 | { 3 | services.xrdp.enable = true; 4 | services.xrdp.defaultWindowManager = "${pkgs.icewm}/bin/icewm"; 5 | # Enable the X11 windowing system. 6 | services.xserver = { 7 | enable = true; 8 | autorun = true; 9 | }; 10 | } 11 | --------------------------------------------------------------------------------