├── .gitignore ├── README.md ├── machines ├── apu.nix └── thinkpad.nix ├── pkgs └── dotfiles.nix ├── roles ├── common.nix ├── entertainment.nix ├── mailserver.nix ├── router.nix ├── webserver.nix └── workstation.nix └── services └── ppp.nix /.gitignore: -------------------------------------------------------------------------------- 1 | configuration.nix 2 | secrets.nix 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | My NixOS configuration for various devices. 2 | 3 | ### Structure 4 | 5 | * A **machine** has one or more role 6 | * A **role** is a collection of **packages** and **services** 7 | 8 | ### Secrets 9 | 10 | Secrets are stored in `secrets.nix`, which looks something like this: 11 | 12 | ``` 13 | { 14 | name = { 15 | username = "foo"; 16 | password = "bar"; 17 | }; 18 | } 19 | ``` 20 | 21 | ### configuration.nix 22 | 23 | ``` 24 | { config, lib, pkgs, ... }: 25 | 26 | { 27 | imports = 28 | [ 29 | ./machines/some-machine.nix 30 | ]; 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /machines/apu.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ../roles/common.nix 6 | ../roles/router.nix 7 | ../roles/webserver.nix 8 | ]; 9 | 10 | networking.hostName = "apu"; 11 | 12 | boot = { 13 | loader.grub = { 14 | enable = true; 15 | version = 2; 16 | device = "/dev/sda"; 17 | extraConfig = "serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1; terminal_input serial; terminal_output serial"; 18 | }; 19 | 20 | kernelParams = [ "console=tty0" "console=ttyS0,115200n8" ]; 21 | initrd.availableKernelModules = [ "ahci" "ohci_pci" "ehci_pci" "usb_storage" ]; 22 | kernelModules = [ "kvm-amd" "tun" "virtio" ]; 23 | }; 24 | 25 | fileSystems."/" = { 26 | device = "/dev/disk/by-uuid/76cce545-e684-45e8-bc13-04dea5dc63dc"; 27 | fsType = "btrfs"; 28 | options = [ "defaults" "compress=lzo" "noatime" ]; 29 | }; 30 | 31 | system.stateVersion = "16.03"; 32 | system.autoUpgrade.enable = true; 33 | 34 | services.haveged.enable = true; 35 | } 36 | -------------------------------------------------------------------------------- /machines/thinkpad.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | { 4 | imports = [ 5 | ../roles/common.nix 6 | ../roles/workstation.nix 7 | ../roles/entertainment.nix 8 | ]; 9 | 10 | networking.hostName = "thinkpad"; 11 | 12 | boot = { 13 | loader.gummiboot.enable = true; 14 | loader.efi.canTouchEfiVariables = true; 15 | initrd.availableKernelModules = [ "xhci_pci" "ehci_pci" "ahci" "usbhid" "sd_mod" "rtsx_pci_sdmmc" ]; 16 | kernelModules = [ "kvm-intel" "tun" "virtio" ]; 17 | }; 18 | 19 | fileSystems."/" = { 20 | device = "/dev/disk/by-uuid/e41cf85e-45ee-4bcf-bf30-1d2432875b0d"; 21 | fsType = "btrfs"; 22 | options = [ "defaults" "compress=lzo" "noatime" ]; 23 | }; 24 | 25 | fileSystems."/boot" = { 26 | device = "/dev/disk/by-uuid/0F93-D786"; 27 | fsType = "vfat"; 28 | }; 29 | 30 | nix.maxJobs = 2; 31 | } 32 | -------------------------------------------------------------------------------- /pkgs/dotfiles.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | 3 | stdenv.mkDerivation rec { 4 | version = "1.4"; 5 | name = "dotfiles-${version}"; 6 | 7 | src = fetchFromGitHub { 8 | owner = "jgillich"; 9 | repo = "dotfiles"; 10 | rev = "v${version}"; 11 | sha256 = "034ii83rjaxncdnaay2scfsjyxcz8fkchjzmf6mxxjkiicv9j442"; 12 | }; 13 | 14 | installPhase = '' 15 | mkdir -p $out/bin 16 | cp dotfiles-update $out/bin 17 | ''; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /roles/common.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | secrets = import ../secrets.nix; 5 | in 6 | { 7 | time.timeZone = "Europe/Berlin"; 8 | 9 | environment.systemPackages = with pkgs; [ 10 | (import ../pkgs/dotfiles.nix) 11 | usbutils pciutils nfs-utils psmisc file gptfdisk 12 | git gitAndTools.git-crypt gitAndTools.hub 13 | python ruby bundler nodejs gcc gnumake 14 | curl wget bind dhcp unzip 15 | htop tmux picocom stow duplicity 16 | neovim 17 | ]; 18 | 19 | environment.variables = { 20 | EDITOR = "${pkgs.neovim}/bin/nvim"; 21 | }; 22 | 23 | programs.fish.enable = true; 24 | 25 | nix.gc.automatic = true; 26 | nix.useChroot = true; 27 | 28 | hardware.enableAllFirmware = true; 29 | 30 | boot.cleanTmpDir = true; 31 | 32 | boot.kernel.sysctl = { 33 | "vm.swappiness" = 20; 34 | }; 35 | 36 | security = { 37 | sudo.enable = true; 38 | sudo.wheelNeedsPassword = false; 39 | }; 40 | 41 | services.openssh = { 42 | enable = true; 43 | passwordAuthentication = false; 44 | }; 45 | 46 | services.ntp.enable = true; 47 | 48 | users = { 49 | mutableUsers = false; 50 | 51 | users.root = { 52 | hashedPassword = secrets.hashedPassword; 53 | shell = "${pkgs.fish}/bin/fish"; 54 | openssh.authorizedKeys.keys = [ 55 | "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCxR6b5+s/Z4sMtSe0p23Vw8o8d7BCQdYy/PUuUloCVArz8A1wx37yOn5Rd1CtS7uGXQYQv1XtEexXv9bSqNHeTcr//ie0R/QVSXilMRlmYH92lXOGwnAaaylgiZ5de8TQ609maiZkAuyMJONRkOhFmGxnKn6VShRS30Dwrsz7zyF5eOyOhMdRPZdrSzPt8MU23OuBfVwhL1gcbAYZP/ujvqgNzv1ba31L+eRnryWaJXpI1D3N21hjVNlZlM3/P5HjpzEDobl+lH0xNtt8bPGQYErNf3jmypRLbzdBiDEa/nNC/22TWCjHeUAlfAqU26ZHPoV3//C08e/5CF9hILok3 jakob@gillich.me" 56 | ]; 57 | }; 58 | 59 | users.jakob = { 60 | hashedPassword = secrets.hashedPassword; 61 | isNormalUser = true; 62 | shell = "${pkgs.fish}/bin/fish"; 63 | uid = 1000; 64 | description = "Jakob Gillich"; 65 | extraGroups = [ "wheel" "disk" "cdrom" "docker" "audio" ]; 66 | openssh.authorizedKeys.keys = [ 67 | "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCxR6b5+s/Z4sMtSe0p23Vw8o8d7BCQdYy/PUuUloCVArz8A1wx37yOn5Rd1CtS7uGXQYQv1XtEexXv9bSqNHeTcr//ie0R/QVSXilMRlmYH92lXOGwnAaaylgiZ5de8TQ609maiZkAuyMJONRkOhFmGxnKn6VShRS30Dwrsz7zyF5eOyOhMdRPZdrSzPt8MU23OuBfVwhL1gcbAYZP/ujvqgNzv1ba31L+eRnryWaJXpI1D3N21hjVNlZlM3/P5HjpzEDobl+lH0xNtt8bPGQYErNf3jmypRLbzdBiDEa/nNC/22TWCjHeUAlfAqU26ZHPoV3//C08e/5CF9hILok3 jakob@gillich.me" 68 | ]; 69 | }; 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /roles/entertainment.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | let 3 | secrets = import ../secrets.nix; 4 | in 5 | { 6 | nixpkgs.config.allowUnfree = true; 7 | 8 | environment.systemPackages = with pkgs; [ 9 | steam 10 | google-chrome 11 | #zeroad openra 12 | wine 13 | ]; 14 | 15 | hardware = { 16 | opengl.driSupport = true; 17 | opengl.driSupport32Bit = true; 18 | pulseaudio.support32Bit = true; 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /roles/mailserver.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | let 3 | secrets = import ../secrets.nix; 4 | in 5 | { 6 | containers.mailserver = { 7 | autoStart = true; 8 | config = { config, pkgs, ... }: { 9 | services.postfix = { 10 | enable = true; 11 | domain = "mail.gillich.me"; 12 | # relayHost = ""; 13 | # sslCACert 14 | # sslCert 15 | # sslKey 16 | }; 17 | 18 | 19 | services.dovecot2 = { 20 | enable = true; 21 | }; 22 | }; 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /roles/router.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | secrets = import ../secrets.nix; 5 | in 6 | { 7 | imports = [ 8 | ../services/ppp.nix 9 | ]; 10 | 11 | networking.domain = "home"; 12 | networking.nameservers = [ "127.0.0.1" "8.8.8.8" ]; 13 | 14 | networking.firewall = { 15 | enable = true; 16 | allowPing = true; 17 | trustedInterfaces = [ "wlp4s0" "enp2s0" "enp3s0" ]; 18 | checkReversePath = false; # https://github.com/NixOS/nixpkgs/issues/10101 19 | allowedTCPPorts = [ 20 | 22 # ssh 21 | 80 # http 22 | 443 # https 23 | 2222 # git 24 | ]; 25 | allowedUDPPorts = [ ]; 26 | }; 27 | 28 | networking.nat = { 29 | enable = true; 30 | internalIPs = [ "192.168.1.0/24" "192.168.2.0/24" "192.168.3.0/24" ]; 31 | externalInterface = "ppp0"; 32 | }; 33 | 34 | networking.interfaces = { 35 | wlp4s0 = { 36 | ipAddress = "192.168.1.1"; 37 | prefixLength = 24; 38 | }; 39 | 40 | enp1s0 = { 41 | useDHCP = false; 42 | }; 43 | 44 | enp2s0 = { 45 | ipAddress = "192.168.2.1"; 46 | prefixLength = 24; 47 | }; 48 | 49 | enp3s0 = { 50 | ipAddress = "192.168.3.1"; 51 | prefixLength = 24; 52 | }; 53 | }; 54 | 55 | services.hostapd = { 56 | enable = true; 57 | interface = "wlp4s0"; 58 | ssid = secrets.hostapd.ssid; 59 | wpaPassphrase = secrets.hostapd.wpaPassphrase; 60 | hwMode = "g"; 61 | channel = 10; 62 | }; 63 | 64 | services.dnsmasq = { 65 | enable = true; 66 | servers = [ "8.8.8.8" "8.8.4.4" ]; 67 | extraConfig = '' 68 | domain=lan 69 | interface=wlp4s0 70 | interface=enp2s0 71 | interface=enp3s0 72 | bind-interfaces 73 | dhcp-range=192.168.1.10,192.168.1.254,24h 74 | dhcp-range=192.168.2.10,192.168.2.254,24h 75 | dhcp-range=192.168.3.10,192.168.3.254,24h 76 | ''; 77 | }; 78 | 79 | services.ppp = { 80 | enable = true; 81 | config.easybell = { 82 | interface = "enp1s0"; 83 | username = secrets.easybell.username; 84 | password = secrets.easybell.password; 85 | pppoe = true; 86 | extraOptions = '' 87 | noauth 88 | defaultroute 89 | persist 90 | maxfail 0 91 | holdoff 5 92 | lcp-echo-interval 15 93 | lcp-echo-failure 3 94 | ''; 95 | }; 96 | }; 97 | 98 | services.miniupnpd = { 99 | enable = false; 100 | externalInterface = "ppp0"; 101 | natpmp = true; 102 | internalIPs = [ "wlp4s0" ]; 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /roles/webserver.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | let 4 | secrets = import ../secrets.nix; 5 | ports = { 6 | subsonic = 8001; 7 | shout = 8003; 8 | gitlab = 8004; 9 | }; 10 | in 11 | { 12 | virtualisation.libvirtd.enable = true; 13 | 14 | services.nginx = { 15 | enable = true; 16 | httpConfig = '' 17 | server { 18 | listen 80; 19 | server_name _; 20 | location /.well-known/acme-challenge { 21 | root /var/www/challenges; 22 | } 23 | location / { 24 | return 301 https://$host$request_uri; 25 | } 26 | } 27 | 28 | server { 29 | listen 443 ssl; 30 | ssl_certificate /var/lib/acme/gillich.me/fullchain.pem; 31 | ssl_certificate_key /var/lib/acme/gillich.me/key.pem; 32 | root /var/www; 33 | } 34 | 35 | server { 36 | listen 443 ssl; 37 | server_name music.gillich.me; 38 | ssl_certificate /var/lib/acme/gillich.me/fullchain.pem; 39 | ssl_certificate_key /var/lib/acme/gillich.me/key.pem; 40 | 41 | location / { 42 | proxy_set_header Host $host; 43 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 44 | proxy_pass https://127.0.0.1:${toString ports.subsonic}; 45 | } 46 | } 47 | 48 | server { 49 | listen 443 ssl; 50 | server_name git.gillich.me; 51 | ssl_certificate /var/lib/acme/gillich.me/fullchain.pem; 52 | ssl_certificate_key /var/lib/acme/gillich.me/key.pem; 53 | 54 | location / { 55 | proxy_set_header Host $host; 56 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 57 | proxy_pass http://127.0.0.1:${toString ports.gitlab}; 58 | } 59 | } 60 | 61 | server { 62 | listen 443 ssl; 63 | server_name irc.gillich.me; 64 | ssl_certificate /var/lib/acme/gillich.me/fullchain.pem; 65 | ssl_certificate_key /var/lib/acme/gillich.me/key.pem; 66 | 67 | location / { 68 | proxy_set_header Host $host; 69 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 70 | proxy_pass http://127.0.0.1:${toString ports.shout}; 71 | } 72 | } 73 | ''; 74 | }; 75 | 76 | security.acme.certs."gillich.me" = { 77 | webroot = "/var/www/challenges"; 78 | email = "jakob@gillich.me"; 79 | extraDomains = { 80 | "www.gillich.me" = null; 81 | "music.gillich.me" = null; 82 | "git.gillich.me" = null; 83 | "sync.gillich.me" = null; 84 | "jakob.gillich.me" = null; 85 | "apu.gillich.me" = null; 86 | "irc.gillich.me" = null; 87 | }; 88 | }; 89 | 90 | services.munin-cron = { 91 | enable = true; 92 | hosts = '' 93 | [${config.networking.hostName}] 94 | address localhost 95 | ''; 96 | }; 97 | services.munin-node.enable = true; 98 | 99 | systemd.services.dyndns = { 100 | description = "Dynamic DNS"; 101 | serviceConfig.Type = "oneshot"; 102 | path = [ pkgs.curl pkgs.bind ]; 103 | 104 | # from http://torb.at/cloudflare-dynamic-dns 105 | script = '' 106 | DOMAIN=apu.sys.gillich.me 107 | NEWIP=`dig +short myip.opendns.com @resolver1.opendns.com` 108 | CURRENTIP=`dig +short $DOMAIN @resolver1.opendns.com` 109 | 110 | if [ "$NEWIP" = "$CURRENTIP" ] 111 | then 112 | echo "IP address unchanged" 113 | else 114 | curl --cacert /etc/ssl/certs/ca-certificates.crt \ 115 | -X PUT "https://api.cloudflare.com/client/v4/zones/ca0fc28b0ea163a97ed05ad2bef5d99d/dns_records/234d4c0bdeac610bac6eb9bcc6617e9d" \ 116 | -H "X-Auth-Email: ${secrets.cloudflare.login}" \ 117 | -H "X-Auth-Key: ${secrets.cloudflare.apiKey}" \ 118 | -H "Content-Type: application/json" \ 119 | --data "{\"type\":\"A\",\"name\":\"$DOMAIN\",\"content\":\"$NEWIP\"}" 120 | fi 121 | ''; 122 | 123 | # every 5 minutes 124 | startAt = "*:0/5"; 125 | }; 126 | 127 | services.gitlab = { 128 | enable = true; 129 | port = ports.gitlab; 130 | emailFrom = "gitlab@xapp.ga"; 131 | host = "git.xapp.ga"; 132 | databasePassword = secrets.gitlab.databasePassword; 133 | }; 134 | 135 | services.shout = { 136 | enable = true; 137 | port = ports.shout; 138 | private = true; 139 | }; 140 | 141 | services.subsonic = { 142 | enable = true; 143 | httpsPort = ports.subsonic; 144 | }; 145 | 146 | } 147 | -------------------------------------------------------------------------------- /roles/workstation.nix: -------------------------------------------------------------------------------- 1 | { config, pkgs, ... }: 2 | 3 | { 4 | networking.firewall = { 5 | enable = true; 6 | checkReversePath = false; # https://github.com/NixOS/nixpkgs/issues/10101 7 | 8 | allowedTCPPorts = [ 9 | 24800 # synergy 10 | ]; 11 | }; 12 | 13 | environment.systemPackages = with pkgs; [ 14 | firefox 15 | rustc rustfmt cargo racerRust 16 | gnupg pass 17 | atom lighttable 18 | gimp inkscape 19 | pitivi 20 | gitg gitAndTools.gitAnnex heroku 21 | parted gnome3.gnome-disk-utility 22 | sshfsFuse stow 23 | gnome3.gnome-boxes 24 | tor torbrowser pybitmessage 25 | androidsdk idea.android-studio 26 | ]; 27 | 28 | environment.variables = { 29 | GTK2_RC_FILES = "${pkgs.gnome_themes_standard}/share/themes/Adwaita/gtk-2.0/gtkrc"; 30 | }; 31 | 32 | virtualisation.docker = { 33 | enable = true; 34 | extraOptions = "--exec-opt native.cgroupdriver=cgroupfs"; 35 | socketActivation = false; 36 | }; 37 | #virtualisation.rkt.enable = true; 38 | #virtualisation.libvirtd.enable = true; 39 | 40 | services.syncthing = { 41 | #enable = true; 42 | user = "jakob"; 43 | dataDir = "/home/jakob"; 44 | }; 45 | 46 | services.xserver = { 47 | enable = true; 48 | displayManager.lightdm.enable = true; 49 | desktopManager.gnome3.enable = true; 50 | #desktopManager.budgie.enable = true; 51 | desktopManager.xterm.enable = false; 52 | synaptics.enable = true; 53 | }; 54 | 55 | services.couchdb = { 56 | enable = true; 57 | extraConfig = '' 58 | [httpd] 59 | enable_cors = true 60 | [cors] 61 | origins = * 62 | credentials = true 63 | methods = GET,PUT,POST,HEAD,DELETE 64 | headers = accept, authorization, content-type, origin 65 | ''; 66 | }; 67 | 68 | services.tarsnap = { 69 | #enable = true; 70 | 71 | archives.machine.directories = [ 72 | "/etc/nixos" 73 | ]; 74 | 75 | archives.jgillich.directories = [ 76 | "/home/jakob/.dotfiles" 77 | "/home/jakob/.password-store" 78 | "/home/jakob/.gnupg2" 79 | "/home/jakob/.ssh" 80 | ]; 81 | }; 82 | 83 | security.polkit = { 84 | enable = true; 85 | extraConfig = '' 86 | polkit.addRule(function(action, subject) { 87 | if (subject.isInGroup('wheel')) { 88 | return polkit.Result.YES; 89 | } 90 | }); 91 | ''; 92 | }; 93 | 94 | } 95 | -------------------------------------------------------------------------------- /services/ppp.nix: -------------------------------------------------------------------------------- 1 | { config, lib, pkgs, ... }: 2 | 3 | with lib; 4 | 5 | let 6 | cfg = config.services.ppp; 7 | in 8 | { 9 | options = { 10 | services.ppp = { 11 | enable = mkEnableOption "ppp client service"; 12 | 13 | config = mkOption { 14 | type = types.attrsOf (types.submodule ( 15 | { 16 | options = { 17 | username = mkOption { 18 | type = types.str; 19 | default = ""; 20 | description = '' 21 | username of the ppp connection. 22 | ''; 23 | }; 24 | 25 | password = mkOption { 26 | type = types.str; 27 | default = ""; 28 | description = '' 29 | password of the ppp connection. 30 | ''; 31 | }; 32 | 33 | interface = mkOption { 34 | type = types.str; 35 | description = "Interface which the ppp connection will use."; 36 | }; 37 | 38 | pppoe = mkEnableOption "pppoe plugin"; 39 | 40 | debug = mkEnableOption "debug mode"; 41 | 42 | extraOptions = mkOption { 43 | type = types.lines; 44 | default = ""; 45 | description = "Extra ppp connection options"; 46 | }; 47 | }; 48 | } 49 | )); 50 | 51 | default = {}; 52 | 53 | example = literalExample '' 54 | { 55 | velox = { 56 | interface = "enp1s0"; 57 | pppoe = true; 58 | username = "0000000000@oi.com.br"; 59 | password = "fulano"; 60 | extraOptions = \'\' 61 | noauth 62 | defaultroute 63 | persist 64 | maxfail 0 65 | holdoff 5 66 | lcp-echo-interval 15 67 | lcp-echo-failure 3 68 | \'\'; 69 | }; 70 | } 71 | ''; 72 | 73 | description = '' 74 | Configuration for a ppp daemon. The daemon can be 75 | started, stopped, or examined using 76 | systemctl, under the name 77 | ppp@foo. 78 | ''; 79 | }; 80 | }; 81 | }; 82 | 83 | config = mkIf cfg.enable { 84 | systemd.services."ppp@" = { 85 | description = "PPP link to '%i'"; 86 | wantedBy = [ "network.target" ]; 87 | 88 | serviceConfig = { 89 | ExecStart = "${pkgs.ppp}/sbin/pppd call %I nodetach nolog"; 90 | }; 91 | }; 92 | 93 | systemd.targets."default-ppp" = { 94 | description = "Target to start all default ppp@ services"; 95 | wants = mapAttrsToList (name: cfg: "ppp@${name}.service") cfg.config; 96 | wantedBy = [ "multi-user.target" ]; 97 | }; 98 | 99 | environment.etc = { 100 | "ppp/pap-secrets".text = concatStringsSep "\n" 101 | (mapAttrsToList (name: cfg: "${cfg.username} * ${cfg.password}") cfg.config); 102 | } // 103 | mapAttrs' (name: cfg: nameValuePair "ppp/peers/${name}" { 104 | text = concatStringsSep "\n" [ 105 | (optionalString cfg.pppoe "plugin rp-pppoe.so") 106 | "${cfg.interface}" 107 | "user \"${cfg.username}\"" 108 | "${cfg.extraOptions}" 109 | (optionalString cfg.debug "debug") 110 | ]; 111 | }) cfg.config; 112 | }; 113 | } 114 | --------------------------------------------------------------------------------