feat: move to own dns

This commit is contained in:
kp2pml30 2026-02-17 19:56:50 +09:00
parent 96e942d8ac
commit eaccf45596
Signed by: kp2pml30
GPG key ID: CD6528BAC23E3E34
15 changed files with 138 additions and 202 deletions

8
flake.lock generated
View file

@ -68,17 +68,17 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1770489230, "lastModified": 1770820613,
"narHash": "sha256-qFcJSENXS9GtBFLpFiVaY6WTmWqt6BarVHcT4uUSAKc=", "narHash": "sha256-I5lMXVML0491AqfSI8DUnw3mhIwCLIfQWwZwrdeRrZY=",
"owner": "kp2pml30", "owner": "kp2pml30",
"repo": "kp2pml30.github.io", "repo": "kp2pml30.github.io",
"rev": "c70b7cc290dc4ca341d791fe952ea5e5e2e36e1b", "rev": "73cbd5858e7196c236029f86756119806d484612",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "kp2pml30", "owner": "kp2pml30",
"repo": "kp2pml30.github.io", "repo": "kp2pml30.github.io",
"rev": "c70b7cc290dc4ca341d791fe952ea5e5e2e36e1b", "rev": "73cbd5858e7196c236029f86756119806d484612",
"type": "github" "type": "github"
} }
}, },

View file

@ -19,7 +19,7 @@
#}; #};
kp2pml30-moe = { kp2pml30-moe = {
url = "github:kp2pml30/kp2pml30.github.io/c70b7cc290dc4ca341d791fe952ea5e5e2e36e1b"; url = "github:kp2pml30/kp2pml30.github.io/73cbd5858e7196c236029f86756119806d484612";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };

View file

@ -1,7 +1,18 @@
{ pkgs { pkgs
, lib
, ... , ...
}: }:
let
ips = import ./server/ips.nix;
groupByAttr = attr: lib.foldlAttrs (acc: _: v:
acc // { ${v.${attr}} = (acc.${v.${attr}} or []) ++ [ v.full-address ]; }
) {} ips.addresses;
groupToLines = lib.mapAttrsToList (ip: domains: "${ip} ${lib.concatStringsSep " " domains}");
in
{ {
networking.extraHosts = lib.concatStringsSep "\n" (
groupToLines (groupByAttr "ip") ++ groupToLines (groupByAttr "ipv6")
);
system.stateVersion = "24.05"; system.stateVersion = "24.05";
users.mutableUsers = false; users.mutableUsers = false;
@ -20,12 +31,7 @@
environment.TMPDIR = "/var/tmp"; environment.TMPDIR = "/var/tmp";
}; };
networking = { networking.firewall.enable = true;
firewall = {
enable = true;
allowedTCPPorts = [ 80 443 ];
};
};
nix.settings.experimental-features = [ "nix-command" "flakes" ]; nix.settings.experimental-features = [ "nix-command" "flakes" ];
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [

View file

@ -47,10 +47,17 @@
prefixLength = 24; prefixLength = 24;
address = "146.103.126.11"; address = "146.103.126.11";
} ]; } ];
interfaces.ens3.ipv6.addresses = [ {
prefixLength = 64;
address = "2a14:1e00:3:44d::1";
} ];
defaultGateway = "146.103.126.1"; defaultGateway = "146.103.126.1";
defaultGateway6 = { address = "fe80::1"; interface = "ens3"; };
nameservers = [ nameservers = [
"1.1.1.1" "1.1.1.1"
"8.8.8.8" "8.8.8.8"
"2606:4700:4700::1111"
"2001:4860:4860::8888"
]; ];
}; };

View file

@ -40,10 +40,14 @@ in {
./nix-cache.nix ./nix-cache.nix
./xray.nix ./xray.nix
./secrets.nix ./secrets.nix
./firewall.nix
]; ];
config = { config = {
security.pam.enableSSHAgentAuth = true; users.groups.certreaders = {};
users.users.nginx.extraGroups = [ "certreaders" ];
security.pam.sshAgentAuth.enable = true;
users.users."${cfg.username}" = { users.users."${cfg.username}" = {
isNormalUser = true; isNormalUser = true;

View file

@ -11,25 +11,65 @@
let let
cfg = config.kp2pml30.server; cfg = config.kp2pml30.server;
ports = config.kp2pml30.server.ports; ports = config.kp2pml30.server.ports;
ips = import ./ips.nix;
hostname = cfg.hostname;
relName = fullAddr:
let stripped = lib.removeSuffix ".${hostname}" fullAddr;
in if stripped == fullAddr then "@" else stripped;
domainEntries = lib.mapAttrsToList (_: v: {
name = relName v.full-address;
ip = v.ip;
ipv6 = v.ipv6;
}) ips.addresses;
zoneRecords = lib.concatMapStringsSep "\n" (e:
"${e.name} IN A ${e.ip}\n"
+ "${e.name} IN AAAA ${e.ipv6}\n"
+ "${e.name} IN HTTPS 1 . alpn=h2,http/1.1 ipv4hint=${e.ip} ipv6hint=${e.ipv6}"
) domainEntries;
zoneFile = pkgs.writeText "${hostname}.zone" ''
$ORIGIN ${hostname}.
$TTL 300
@ IN SOA dns.${hostname}. admin.${hostname}. ( 1 3600 600 604800 300 )
@ IN NS dns.${hostname}.
@ IN NS dns2.${hostname}.
www IN CNAME ${hostname}.
${zoneRecords}
'';
in lib.mkIf cfg.nginx { in lib.mkIf cfg.nginx {
users.users.coredns = {
isSystemUser = true;
group = "coredns";
extraGroups = [ "certreaders" ];
};
users.groups.coredns = {};
services.coredns.enable = true; services.coredns.enable = true;
services.coredns.config = '' services.coredns.config = ''
dns://.:53 { ${hostname} {
forward . tls://1.1.1.1 { file ${zoneFile}
tls
tls_servername cloudflare-dns.com
}
cache
} }
https://.:${toString ports.coredns-https} { dns://.:53 {
forward . dns://127.0.0.1:53 { forward . tls://1.1.1.1 tls://1.0.0.1 {
tls
tls_servername cloudflare-dns.com tls_servername cloudflare-dns.com
policy random policy random
} }
cache cache
} }
tls://.:853 {
tls /var/lib/acme/${hostname}/fullchain.pem /var/lib/acme/${hostname}/key.pem
forward . dns://127.0.0.1:53
}
https://.:${toString ports.coredns-https} {
forward . dns://127.0.0.1:53
}
''; '';
# networking.networkmanager.insertNameservers = [ "127.0.0.1" ]; # networking.networkmanager.insertNameservers = [ "127.0.0.1" ];
} }

12
nix/server/firewall.nix Normal file
View file

@ -0,0 +1,12 @@
{ ... }:
let
dnsPort = 53;
httpPort = 80;
httpsPort = 443;
dnsOverTlsPort = 853;
in {
networking.firewall = {
allowedTCPPorts = [ dnsPort httpPort httpsPort dnsOverTlsPort ];
allowedUDPPorts = [ dnsPort ];
};
}

16
nix/server/ips.nix Normal file
View file

@ -0,0 +1,16 @@
rec {
machines = {
vdsina = "146.103.126.11";
vdsina-v6 = "2a14:1e00:3:44d::1";
};
addresses = {
forgejo = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "git.kp2pml30.moe"; };
www = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "kp2pml30.moe"; };
xray = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "x.kp2pml30.moe"; };
dns = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "dns.kp2pml30.moe"; };
dns2 = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "dns2.kp2pml30.moe"; };
signal-proxy = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "pr.kp2pml30.moe"; };
nix-cache = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "cache.nix.kp2pml30.moe"; };
backend = { ip = machines.vdsina; ipv6 = machines.vdsina-v6; full-address = "backend.kp2pml30.moe"; };
};
}

View file

@ -6,6 +6,7 @@
let let
cfg = config.kp2pml30.server; cfg = config.kp2pml30.server;
ports = config.kp2pml30.server.ports; ports = config.kp2pml30.server.ports;
ips = import ./ips.nix;
acmeRoot = "/var/lib/acme/acme-challenge"; acmeRoot = "/var/lib/acme/acme-challenge";
pref = "kp2"; pref = "kp2";
in lib.mkIf cfg.nginx { in lib.mkIf cfg.nginx {
@ -15,9 +16,9 @@ in lib.mkIf cfg.nginx {
defaults.email = "kp2pml30@gmail.com"; defaults.email = "kp2pml30@gmail.com";
#defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; #defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory";
certs."${cfg.hostname}" = { certs."${cfg.hostname}" = {
extraDomainNames = [ "pr.${cfg.hostname}" "www.${cfg.hostname}" "git.${cfg.hostname}" "backend.${cfg.hostname}" "dns.${cfg.hostname}" "cache.nix.${cfg.hostname}" "x.${cfg.hostname}" ]; extraDomainNames = lib.mapAttrsToList (_: v: v.full-address) ips.addresses;
webroot = acmeRoot; webroot = acmeRoot;
group = "nginx"; group = "certreaders";
}; };
}; };
@ -34,6 +35,7 @@ in lib.mkIf cfg.nginx {
listen = [ listen = [
{ addr = "0.0.0.0"; port = 80; } { addr = "0.0.0.0"; port = 80; }
{ addr = "[::]"; port = 80; }
]; ];
locations."/" = { locations."/" = {
@ -47,6 +49,7 @@ in lib.mkIf cfg.nginx {
listen = [ listen = [
{ addr = "0.0.0.0"; port = 80; } { addr = "0.0.0.0"; port = 80; }
{ addr = "[::]"; port = 80; }
]; ];
locations."/" = { locations."/" = {
@ -60,6 +63,7 @@ in lib.mkIf cfg.nginx {
listen = [ listen = [
{ addr = "0.0.0.0"; port = 80; } { addr = "0.0.0.0"; port = 80; }
{ addr = "[::]"; port = 80; }
]; ];
locations."/" = { locations."/" = {
@ -73,6 +77,7 @@ in lib.mkIf cfg.nginx {
listen = [ listen = [
{ addr = "0.0.0.0"; port = 80; } { addr = "0.0.0.0"; port = 80; }
{ addr = "[::]"; port = 80; }
]; ];
locations."/" = { locations."/" = {
@ -98,7 +103,7 @@ in lib.mkIf cfg.nginx {
proxy_send_timeout 60s; proxy_send_timeout 60s;
proxy_read_timeout 60s; proxy_read_timeout 60s;
resolver 1.1.1.1; resolver 127.0.0.1;
''; '';
}; };
}; };
@ -112,6 +117,7 @@ in lib.mkIf cfg.nginx {
listen = [ listen = [
{ addr = "0.0.0.0"; port = 80; } { addr = "0.0.0.0"; port = 80; }
{ addr = "[::]"; port = 80; }
]; ];
locations."/" = { locations."/" = {
@ -149,7 +155,7 @@ in lib.mkIf cfg.nginx {
proxy_send_timeout 60s; proxy_send_timeout 60s;
proxy_read_timeout 60s; proxy_read_timeout 60s;
resolver 1.1.1.1; resolver 127.0.0.1;
''; '';
}; };
}; };
@ -182,7 +188,7 @@ in lib.mkIf cfg.nginx {
proxy_send_timeout 60s; proxy_send_timeout 60s;
proxy_read_timeout 60s; proxy_read_timeout 60s;
resolver 1.1.1.1; resolver 127.0.0.1;
''; '';
}; };
}; };
@ -192,6 +198,7 @@ in lib.mkIf cfg.nginx {
acmeRoot = acmeRoot; acmeRoot = acmeRoot;
listen = [ listen = [
{ addr = "0.0.0.0"; port = 80; } { addr = "0.0.0.0"; port = 80; }
{ addr = "[::]"; port = 80; }
]; ];
locations."/" = { locations."/" = {
proxyPass = "http://${config.services.nix-serve.bindAddress}:${toString config.services.nix-serve.port}"; proxyPass = "http://${config.services.nix-serve.bindAddress}:${toString config.services.nix-serve.port}";
@ -199,6 +206,9 @@ in lib.mkIf cfg.nginx {
}; };
} else {}); } else {});
streamConfig = (builtins.readFile ./stream.nginx); streamConfig = builtins.replaceStrings
["@SIGNAL_PROXY_PORT@"]
["${toString ports.signal-proxy-port}"]
(builtins.readFile ./stream.nginx);
}; };
} }

View file

@ -41,5 +41,11 @@
default = 8012; default = 8012;
description = "Xray websocket fallback port"; description = "Xray websocket fallback port";
}; };
signal-proxy-port = lib.mkOption {
type = lib.types.int;
default = 8444;
description = "Signal proxy TLS termination port";
};
}; };
} }

View file

@ -26,7 +26,12 @@ let
${pkgs.openssl}/bin/openssl enc -aes-256-cbc -pbkdf2 -iter 1000000 -base64 -d -k "$KP2_DOTFILES_SECRET_KEY" -in "${./secrets.yaml}" | ${pkgs.yq}/bin/yq '.XRAY_UIDS[]' -r ${pkgs.openssl}/bin/openssl enc -aes-256-cbc -pbkdf2 -iter 1000000 -base64 -d -k "$KP2_DOTFILES_SECRET_KEY" -in "${./secrets.yaml}" | ${pkgs.yq}/bin/yq '.XRAY_UIDS[]' -r
''; '';
xray-config-base = builtins.toFile "xray.json" (builtins.readFile ./xray.json); xray-config-base = builtins.toFile "xray.json" (builtins.toJSON (
let base = builtins.fromJSON (builtins.readFile ./xray.json);
in base // {
inbounds = map (ib: ib // { port = config.kp2pml30.server.ports.xray-main; }) base.inbounds;
}
));
# Script to generate complete xray configuration # Script to generate complete xray configuration
generateXrayConfig = pkgs.writeShellScript "generate-xray-config" '' generateXrayConfig = pkgs.writeShellScript "generate-xray-config" ''

View file

@ -31,6 +31,7 @@ in lib.mkIf cfg.nginx {
systemd.services.kp2pml30-moe-backend-service = { systemd.services.kp2pml30-moe-backend-service = {
enable = true; enable = true;
wants = [ "network-online.target" ];
after = [ "network-online.target" ]; after = [ "network-online.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];

View file

@ -12,7 +12,6 @@ map $ssl_preread_server_name $name {
updates.signal.org updates; updates.signal.org updates;
updates2.signal.org updates2; updates2.signal.org updates2;
www.microsoft.com xray-entrypoint;
x.kp2pml30.moe xray-entrypoint; x.kp2pml30.moe xray-entrypoint;
pr.kp2pml30.moe signal-proxy; pr.kp2pml30.moe signal-proxy;
@ -86,11 +85,12 @@ upstream ssl-terminator {
} }
upstream signal-proxy { upstream signal-proxy {
server 127.0.0.1:8444; server 127.0.0.1:@SIGNAL_PROXY_PORT@;
} }
server { server {
listen 443; listen 443;
listen [::]:443;
ssl_preread on; ssl_preread on;
proxy_pass $name; proxy_pass $name;
} }
@ -106,7 +106,7 @@ server {
} }
server { server {
listen 8444 ssl; listen @SIGNAL_PROXY_PORT@ ssl;
server_name pr.kp2pml30.moe; server_name pr.kp2pml30.moe;
ssl_preread on; ssl_preread on;
proxy_pass $name; proxy_pass $name;

View file

@ -1,81 +0,0 @@
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 1080,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": {
"udp": true
}
},
{
"port": 1081,
"listen": "127.0.0.1",
"protocol": "http"
}
],
"outbounds": [
{
"tag": "proxy",
"protocol": "vless",
"settings": {
"vnext": [
{
"address": "x.kp2pml30.moe",
"port": 443,
"users": [
{
"id": "YOUR-UUID-HERE",
"encryption": "none",
"flow": "xtls-rprx-vision"
}
]
}
]
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"fingerprint": "chrome",
"serverName": "www.microsoft.com",
"publicKey": "dRvlorHTupOukJ7aFZNPx-wXUMYJt3GQNrtSjMm9lAg",
"shortId": "deadbabe",
"spiderX": "/"
}
}
},
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"domain": [
"regexp:\\.ru$",
"geosite:category-ru",
"regexp:\\.рф$",
"regexp:(^|\\.)vk\\.com$"
],
"outboundTag": "block"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "proxy"
}
]
}
}

View file

@ -1,90 +0,0 @@
{
"log": {
"loglevel": "debug"
},
"routing": {
"domainStrategy": "IPIfNonMatch",
"rules": [
{
"type": "field",
"domain": [
"regexp:\\.ru$",
"regexp:\\.рф$",
"domain:vk.com"
],
"outboundTag": "block"
},
{
"type": "field",
"ip": [
"geoip:cn",
"geoip:ru"
],
"outboundTag": "block"
},
{
"type": "field",
"network": "tcp,udp",
"outboundTag": "direct"
}
]
},
"inbounds": [
{
"listen": "127.0.0.1",
"port": 8010,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "YOUR-UUID-HERE",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": true,
"dest": "www.microsoft.com:443",
"xver": 0,
"serverNames": [
"www.microsoft.com"
],
"privateKey": "",
"shortIds": [
"deadbabe"
],
"debug": true
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls"
]
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
},
{
"protocol": "blackhole",
"tag": "block"
}
],
"policy": {
"levels": {
"0": {
"handshake": 3,
"connIdle": 127
}
}
}
}