nixos-dotfiles

nixos-dotfiles

https://git.tonybtw.com/nixos-dotfiles.git git://git.tonybtw.com/nixos-dotfiles.git
11,359 bytes raw
1
# vultr-security.nix - Hardened security configuration for VPS hosting
2
#
3
# This config implements defense-in-depth for a public XMPP server:
4
# - SSH key-only authentication (no passwords)
5
# - Fail2ban for brute-force prevention
6
# - Firewall with strict rules (iptables via NixOS)
7
# - Disabled root login
8
# - Automatic security updates
9
# - Minimal attack surface
10
#
11
# Usage:
12
#   1. Generate SSH key locally: ssh-keygen -t ed25519 -C "your@email.com"
13
#   2. Add your public key to 'authorizedKeys' below
14
#   3. Import this module in configuration.nix
15
#   4. nixos-rebuild switch
16
#   5. IMPORTANT: Test SSH works BEFORE logging out! (ssh -i ~/.ssh/id_ed25519 youruser@server)
17
#
18
{ config, pkgs, lib, ... }:
19
20
{
21
  # ============================================================================
22
  # SSH Configuration - Key-only, no root, no passwords
23
  # ============================================================================
24
  services.openssh = {
25
    enable = true;
26
    ports = [ 22 ];  # Change to non-standard port if you want (e.g., 2222)
27
28
    settings = {
29
      # Disable password authentication entirely
30
      PasswordAuthentication = false;
31
      KbdInteractiveAuthentication = false;  # No keyboard-interactive auth
32
      ChallengeResponseAuthentication = false;
33
34
      # Disable root login (force use of sudo from normal user)
35
      PermitRootLogin = "no";
36
37
      # Only allow public key authentication
38
      PubkeyAuthentication = true;
39
40
      # Additional hardening
41
      PermitEmptyPasswords = false;
42
      X11Forwarding = false;  # Disable X11 forwarding (not needed)
43
      PrintMotd = false;
44
45
      # Prevent SSH from being used as a tunnel
46
      AllowTcpForwarding = false;
47
      AllowStreamLocalForwarding = false;
48
      GatewayPorts = "no";
49
50
      # Limit login attempts
51
      MaxAuthTries = 3;
52
      MaxSessions = 2;
53
54
      # Use strong ciphers only
55
      Ciphers = [
56
        "chacha20-poly1305@openssh.com"
57
        "aes256-gcm@openssh.com"
58
        "aes128-gcm@openssh.com"
59
        "aes256-ctr"
60
        "aes192-ctr"
61
        "aes128-ctr"
62
      ];
63
64
      KexAlgorithms = [
65
        "curve25519-sha256"
66
        "curve25519-sha256@libssh.org"
67
        "diffie-hellman-group16-sha512"
68
        "diffie-hellman-group18-sha512"
69
        "diffie-hellman-group-exchange-sha256"
70
      ];
71
72
      Macs = [
73
        "hmac-sha2-512-etm@openssh.com"
74
        "hmac-sha2-256-etm@openssh.com"
75
        "umac-128-etm@openssh.com"
76
      ];
77
    };
78
  };
79
80
  # ============================================================================
81
  # User Configuration
82
  # ============================================================================
83
  users.users.tony = {  # CHANGE THIS to your desired username
84
    isNormalUser = true;
85
    description = "Tony (Admin)";
86
87
    # Add user to wheel group for sudo access
88
    extraGroups = [ "wheel" "networkmanager" ];
89
90
    # SSH public keys (ADD YOUR KEY HERE!)
91
    openssh.authorizedKeys.keys = [
92
      # Add your public key from ~/.ssh/id_ed25519.pub
93
      # Example format:
94
      # "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx your@email.com"
95
96
      # IMPORTANT: Replace this with your actual public key!
97
      "ssh-ed25519 REPLACE_THIS_WITH_YOUR_PUBLIC_KEY your@email.com"
98
    ];
99
100
    # Set a strong password as backup (in case SSH key is lost)
101
    # Generate hash with: mkpasswd -m sha-512
102
    # hashedPassword = "$6$rounds=500000$...";  # Uncomment and add hashed password
103
  };
104
105
  # Disable root password (force sudo from normal user)
106
  users.users.root.hashedPassword = "!";  # "!" means locked account
107
108
  # ============================================================================
109
  # Sudo Configuration - Secure but usable
110
  # ============================================================================
111
  # NOTE: We keep sudo enabled because you need it to manage the server
112
  # Disabling sudo would lock you out from system administration
113
  security.sudo = {
114
    enable = true;
115
116
    # Require password for sudo (even with key-based SSH)
117
    execWheelOnly = true;  # Only wheel group can sudo
118
    wheelNeedsPassword = true;  # Require password for sudo commands
119
120
    extraConfig = ''
121
      # Require password every time (no timeout)
122
      Defaults timestamp_timeout=0
123
124
      # Log all sudo commands
125
      Defaults logfile=/var/log/sudo.log
126
      Defaults log_input, log_output
127
128
      # Require password for ALL commands (no exceptions)
129
      Defaults passwd_tries=3
130
      Defaults badpass_message="Authentication failed. This incident will be logged."
131
132
      # Prevent sudo -i or sudo su (force explicit commands)
133
      # Uncomment these if you want to be extra strict:
134
      # Defaults !rootpw
135
      # Defaults !runaspw
136
    '';
137
  };
138
139
  # Disable 'su' command (force sudo for all privilege escalation)
140
  security.sudo.extraConfig = ''
141
    # Block 'su' by preventing root password authentication
142
    # Users must use 'sudo' for specific commands instead of 'su'
143
  '';
144
145
  # Remove 'su' from system entirely (optional - uncomment if desired)
146
  # environment.systemPackages = lib.mkForce (builtins.filter (pkg: pkg.pname or "" != "shadow") config.environment.systemPackages);
147
148
  # ============================================================================
149
  # Firewall - Only allow necessary ports
150
  # ============================================================================
151
  networking.firewall = {
152
    enable = true;
153
154
    # Allowed ports (ONLY what's needed)
155
    allowedTCPPorts = [
156
      22    # SSH (change if you modified SSH port above)
157
      80    # HTTP (for Let's Encrypt ACME challenge)
158
      443   # HTTPS (for Let's Encrypt ACME challenge)
159
      5222  # XMPP client-to-server (C2S)
160
      5269  # XMPP server-to-server (S2S) - remove if no federation
161
      5281  # XMPP HTTPS (file uploads)
162
    ];
163
164
    # Block ping (optional - makes server invisible to ping sweeps)
165
    allowPing = false;
166
167
    # Advanced firewall rules using iptables
168
    extraCommands = ''
169
      # Rate-limit SSH connections (prevent brute-force)
170
      iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
171
      iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
172
173
      # Rate-limit XMPP connections (prevent abuse)
174
      iptables -A INPUT -p tcp --dport 5222 -m state --state NEW -m recent --set --name XMPP
175
      iptables -A INPUT -p tcp --dport 5222 -m state --state NEW -m recent --update --seconds 60 --hitcount 10 --name XMPP -j DROP
176
177
      # Drop invalid packets
178
      iptables -A INPUT -m state --state INVALID -j DROP
179
180
      # Allow established connections
181
      iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
182
183
      # Log dropped packets (for debugging - comment out in production)
184
      # iptables -A INPUT -j LOG --log-prefix "iptables-dropped: " --log-level 4
185
    '';
186
187
    extraStopCommands = ''
188
      iptables -D INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH 2>/dev/null || true
189
      iptables -D INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP 2>/dev/null || true
190
      iptables -D INPUT -p tcp --dport 5222 -m state --state NEW -m recent --set --name XMPP 2>/dev/null || true
191
      iptables -D INPUT -p tcp --dport 5222 -m state --state NEW -m recent --update --seconds 60 --hitcount 10 --name XMPP -j DROP 2>/dev/null || true
192
    '';
193
  };
194
195
  # ============================================================================
196
  # Fail2Ban - Automatic IP banning for repeated failed attempts
197
  # ============================================================================
198
  services.fail2ban = {
199
    enable = true;
200
201
    # Ban for 1 hour after 3 failed attempts within 10 minutes
202
    maxretry = 3;
203
    bantime = "1h";
204
    findtime = "10m";
205
206
    jails = {
207
      # SSH brute-force protection
208
      sshd = ''
209
        enabled = true
210
        filter = sshd
211
        action = iptables[name=SSH, port=22, protocol=tcp]
212
        maxretry = 3
213
        findtime = 600
214
        bantime = 3600
215
      '';
216
217
      # XMPP brute-force protection
218
      # (Custom filter - see below)
219
      prosody = ''
220
        enabled = true
221
        filter = prosody-auth
222
        action = iptables[name=XMPP, port=5222, protocol=tcp]
223
        logpath = /var/log/prosody/prosody.log
224
        maxretry = 5
225
        findtime = 600
226
        bantime = 3600
227
      '';
228
    };
229
  };
230
231
  # Custom fail2ban filter for Prosody auth failures
232
  environment.etc."fail2ban/filter.d/prosody-auth.conf".text = ''
233
    [Definition]
234
    failregex = ^.* \[error\] .* (authentication failure|certificate expired) for .* from <HOST>
235
    ignoreregex =
236
  '';
237
238
  # ============================================================================
239
  # Automatic Security Updates (Optional - Recommended)
240
  # ============================================================================
241
  system.autoUpgrade = {
242
    enable = true;
243
    dates = "weekly";  # Update once per week
244
    allowReboot = false;  # Don't auto-reboot (manual intervention)
245
    channel = "https://nixos.org/channels/nixos-24.05";  # Stable channel
246
  };
247
248
  # ============================================================================
249
  # Additional Hardening
250
  # ============================================================================
251
252
  # Disable unnecessary services
253
  services.avahi.enable = false;  # No mDNS/Bonjour
254
  services.printing.enable = false;  # No CUPS printing
255
256
  # Kernel hardening
257
  boot.kernel.sysctl = {
258
    # Prevent IP spoofing
259
    "net.ipv4.conf.all.rp_filter" = 1;
260
    "net.ipv4.conf.default.rp_filter" = 1;
261
262
    # Ignore ICMP redirects
263
    "net.ipv4.conf.all.accept_redirects" = 0;
264
    "net.ipv4.conf.default.accept_redirects" = 0;
265
    "net.ipv6.conf.all.accept_redirects" = 0;
266
    "net.ipv6.conf.default.accept_redirects" = 0;
267
268
    # Ignore ICMP echo requests (disable ping response)
269
    "net.ipv4.icmp_echo_ignore_all" = 1;
270
271
    # Disable IPv6 if not needed (optional)
272
    # "net.ipv6.conf.all.disable_ipv6" = 1;
273
274
    # Enable TCP SYN cookies (prevent SYN flood attacks)
275
    "net.ipv4.tcp_syncookies" = 1;
276
277
    # Disable source routing
278
    "net.ipv4.conf.all.accept_source_route" = 0;
279
    "net.ipv6.conf.all.accept_source_route" = 0;
280
281
    # Log suspicious packets
282
    "net.ipv4.conf.all.log_martians" = 1;
283
  };
284
285
  # ============================================================================
286
  # Logging and Monitoring
287
  # ============================================================================
288
  services.journald.extraConfig = ''
289
    MaxRetentionSec=1month
290
    SystemMaxUse=1G
291
  '';
292
293
  # Log failed login attempts
294
  security.pam.loginLimits = [
295
    { domain = "*"; type = "hard"; item = "maxlogins"; value = "3"; }
296
  ];
297
298
  # ============================================================================
299
  # Minimal Package Set (Reduce attack surface)
300
  # ============================================================================
301
  environment.systemPackages = with pkgs; [
302
    # Essential tools only
303
    vim
304
    wget
305
    curl
306
    htop
307
    tmux
308
    git
309
310
    # Security tools
311
    fail2ban
312
    iptables
313
314
    # Prosody admin tools
315
    # (prosodyctl is included with services.prosody)
316
  ];
317
318
  # Disable documentation to save space and reduce attack surface
319
  documentation.enable = false;
320
  documentation.man.enable = false;
321
  documentation.nixos.enable = false;
322
}