diff --git a/.gitignore b/.gitignore index c4a847d..812980f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /result +/.env diff --git a/flake.nix b/flake.nix index fba3c52..a029f6c 100644 --- a/flake.nix +++ b/flake.nix @@ -45,6 +45,7 @@ nginx = true; forgejo = true; nix-cache = true; + xray = true; }; } diff --git a/nix/personal/default.nix b/nix/personal/default.nix index 8ba9323..c8057f8 100644 --- a/nix/personal/default.nix +++ b/nix/personal/default.nix @@ -54,6 +54,7 @@ in { "C.UTF-8/UTF-8" "en_US.UTF-8/UTF-8" "ru_RU.UTF-8/UTF-8" + "ja_JP.UTF-8/UTF-8" ]; programs = { @@ -80,7 +81,7 @@ in { "nvidia-settings" "nvidia-persistenced" "opera" - "discord-ptb" + "discord" "slack" "anytype" ]; diff --git a/nix/personal/graphical/messengers.nix b/nix/personal/graphical/messengers.nix index 6805b0c..7f56200 100644 --- a/nix/personal/graphical/messengers.nix +++ b/nix/personal/graphical/messengers.nix @@ -11,13 +11,7 @@ let discord-version = "0.0.160"; in lib.mkIf cfg.messengers.personal { users.users.${cfg.username}.packages = with pkgs; [ - discord-ptb - #(discord-ptb.overrideAttrs(finalAttrs: previousAttrs: { - # src = fetchurl { - # url = "https://ptb.dl2.discordapp.net/apps/linux/${discord-version}/discord-ptb-${discord-version}.tar.gz"; - # hash = lib.fakeHash; - # }; - #})) + discord telegram-desktop # (pkgs.callPackage "${pkgs.path}/pkgs/by-name/si/signal-desktop/generic.nix" { } rec { # pname = "signal-desktop"; diff --git a/nix/server/default.nix b/nix/server/default.nix index 423fa05..767eba1 100644 --- a/nix/server/default.nix +++ b/nix/server/default.nix @@ -22,6 +22,7 @@ in { dns = lib.mkEnableOption ""; nix-cache = lib.mkEnableOption ""; + xray = lib.mkEnableOption ""; sitePath = lib.mkOption { type = lib.types.str; @@ -29,6 +30,7 @@ in { }; imports = [ + ./ports.nix ./ssh.nix ./nginx.nix ./boot.nix @@ -36,6 +38,8 @@ in { ./forgejo.nix ./dns.nix ./nix-cache.nix + ./xray.nix + ./secrets.nix ]; config = { diff --git a/nix/server/dns.nix b/nix/server/dns.nix index 0ba5290..7f5fe8f 100644 --- a/nix/server/dns.nix +++ b/nix/server/dns.nix @@ -10,6 +10,7 @@ }@args: let cfg = config.kp2pml30.server; + ports = config.kp2pml30.server.ports; in lib.mkIf cfg.nginx { services.coredns.enable = true; services.coredns.config = '' @@ -21,7 +22,7 @@ in lib.mkIf cfg.nginx { cache } - https://.:8003 { + https://.:${toString ports.coredns-https} { forward . dns://127.0.0.1:53 { tls tls_servername cloudflare-dns.com diff --git a/nix/server/forgejo.nix b/nix/server/forgejo.nix index c8e68e5..08e2583 100644 --- a/nix/server/forgejo.nix +++ b/nix/server/forgejo.nix @@ -5,6 +5,7 @@ }: let cfg = config.kp2pml30.server; + ports = config.kp2pml30.server.ports; in lib.mkIf cfg.forgejo { services.forgejo = { enable = true; @@ -14,7 +15,7 @@ in lib.mkIf cfg.forgejo { server = { DOMAIN = "git.${cfg.hostname}"; ROOT_URL = "https://git.${cfg.hostname}/"; - HTTP_PORT = 8002; + HTTP_PORT = ports.forgejo; }; service.DISABLE_REGISTRATION = true; }; diff --git a/nix/server/modify-secrets.sh b/nix/server/modify-secrets.sh new file mode 100755 index 0000000..e82f3d7 --- /dev/null +++ b/nix/server/modify-secrets.sh @@ -0,0 +1,26 @@ +#!/bin/sh +set -e + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +if ! command -v nvim +then + echo "no nvim" + exit 1 +fi + +if ! command -v base64 +then + echo "no base64" + exit 1 +fi + +if ! command -v openssl +then + echo "no openssl" + exit 1 +fi + +env $(cat /var/lib/secrets/.env | xargs) nvim --clean -n \ + -u "$SCRIPT_DIR/modify-secrets.vim" \ + "$SCRIPT_DIR/secrets.yaml" diff --git a/nix/server/modify-secrets.vim b/nix/server/modify-secrets.vim new file mode 100644 index 0000000..41f8fff --- /dev/null +++ b/nix/server/modify-secrets.vim @@ -0,0 +1,43 @@ +set nobackup nowritebackup noundofile noswapfile viminfo= history=0 noshelltemp secure + +function! s:OpenSSLReadPre() +endfunction + +function! s:OpenSSLReadPost() + silent! execute "0,$!openssl enc -aes-256-cbc -pbkdf2 -iter 1000000 -base64 -d -k '" . $KP2_DOTFILES_SECRET_KEY . "'" + if v:shell_error + silent! 0,$y + silent! undo + echo "Note that your version of openssl may not have the given cipher engine built-in" + echo "even though the engine may be documented in the openssl man pages." + echo "ERROR FROM OPENSSL:" + echo @" + echo "COULD NOT DECRYPT" + return + endif + redraw! +endfunction + +function! s:OpenSSLWritePre() + silent! execute "0,$!openssl enc -aes-256-cbc -pbkdf2 -iter 1000000 -base64 -k '" . $KP2_DOTFILES_SECRET_KEY . "'" + if v:shell_error + silent! 0,$y + silent! undo + echo "Note that your version of openssl may not have the given cipher engine built in" + echo "even though the engine may be documented in the openssl man pages." + echo "ERROR FROM OPENSSL:" + echo @" + echo "COULD NOT ENCRYPT" + return + endif +endfunction + +function! s:OpenSSLWritePost() + "silent! undo + "redraw! +endfunction + +autocmd BufReadPre,FileReadPre * call s:OpenSSLReadPre() +autocmd BufReadPost,FileReadPost * call s:OpenSSLReadPost() +autocmd BufWritePre,FileWritePre * call s:OpenSSLWritePre() +autocmd BufWritePost,FileWritePost * call s:OpenSSLWritePost() diff --git a/nix/server/nginx.nix b/nix/server/nginx.nix index 57d10fc..0d18f17 100644 --- a/nix/server/nginx.nix +++ b/nix/server/nginx.nix @@ -5,6 +5,7 @@ }: let cfg = config.kp2pml30.server; + ports = config.kp2pml30.server.ports; acmeRoot = "/var/lib/acme/acme-challenge"; pref = "kp2"; in lib.mkIf cfg.nginx { @@ -14,7 +15,7 @@ in lib.mkIf cfg.nginx { defaults.email = "kp2pml30@gmail.com"; #defaults.server = "https://acme-staging-v02.api.letsencrypt.org/directory"; certs."${cfg.hostname}" = { - extraDomainNames = [ "pr.${cfg.hostname}" "www.${cfg.hostname}" "git.${cfg.hostname}" "backend.${cfg.hostname}" "dns.${cfg.hostname}" "cache.nix.${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}" ]; webroot = acmeRoot; group = "nginx"; }; @@ -23,6 +24,9 @@ in lib.mkIf cfg.nginx { services.nginx = { enable = true; + logError = "stderr debug"; + + virtualHosts = { "git.${cfg.hostname}" = { enableACME = true; @@ -33,7 +37,7 @@ in lib.mkIf cfg.nginx { ]; locations."/" = { - proxyPass = "http://127.0.0.1:8002"; + proxyPass = "http://127.0.0.1:${toString ports.forgejo}"; }; }; @@ -46,7 +50,7 @@ in lib.mkIf cfg.nginx { ]; locations."/" = { - proxyPass = "http://127.0.0.1:8001"; + proxyPass = "http://127.0.0.1:${toString ports.backend}"; }; }; @@ -59,7 +63,43 @@ in lib.mkIf cfg.nginx { ]; locations."/" = { - proxyPass = "http://127.0.0.1:8003"; + proxyPass = "http://127.0.0.1:${toString ports.coredns-https}"; + }; + }; + + "x.${cfg.hostname}" = { + enableACME = true; + acmeRoot = acmeRoot; + + listen = [ + { addr = "0.0.0.0"; port = 80; } + ]; + + locations."/" = { + proxyPass = "https://www.lovelive-anime.jp"; + extraConfig = '' + sub_filter $proxy_host $host; + sub_filter_once off; + + proxy_set_header Host $proxy_host; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + proxy_ssl_server_name on; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Real-IP $proxy_protocol_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + resolver 1.1.1.1; + ''; }; }; @@ -79,7 +119,74 @@ in lib.mkIf cfg.nginx { tryFiles = "$uri $uri/ /index.html"; }; }; - } // (if cfg.nix-cache then { + } // (if cfg.xray then { + # Xray fallback proxy servers + "127.0.0.1:${toString ports.xray-fallback}" = { + listen = [ + { addr = "127.0.0.1"; port = ports.xray-fallback; proxyProtocol = true; } + ]; + + locations."/" = { + proxyPass = "https://www.lovelive-anime.jp"; + extraConfig = '' + sub_filter $proxy_host $host; + sub_filter_once off; + + proxy_set_header Host $proxy_host; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + proxy_ssl_server_name on; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Real-IP $proxy_protocol_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + resolver 1.1.1.1; + ''; + }; + }; + + "127.0.0.1:${toString ports.xray-websocket}" = { + listen = [ + { addr = "127.0.0.1"; port = ports.xray-websocket; proxyProtocol = true; } + ]; + + locations."/" = { + proxyPass = "https://www.lovelive-anime.jp"; + extraConfig = '' + sub_filter $proxy_host $host; + sub_filter_once off; + + proxy_set_header Host $proxy_host; + proxy_http_version 1.1; + proxy_cache_bypass $http_upgrade; + proxy_ssl_server_name on; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header X-Real-IP $proxy_protocol_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + + resolver 1.1.1.1; + ''; + }; + }; + } else {}) // (if cfg.nix-cache then { "cache.nix.${cfg.hostname}" = { enableACME = true; acmeRoot = acmeRoot; diff --git a/nix/server/ports.nix b/nix/server/ports.nix new file mode 100644 index 0000000..903ac2f --- /dev/null +++ b/nix/server/ports.nix @@ -0,0 +1,45 @@ +{ lib, ... }: +{ + # Server Port Usage Configuration + # This file documents and centralizes all port assignments + + options.kp2pml30.server.ports = { + # Application Services + backend = lib.mkOption { + type = lib.types.int; + default = 8001; + description = "Backend service port (kp2pml30-moe-backend)"; + }; + + forgejo = lib.mkOption { + type = lib.types.int; + default = 8002; + description = "Forgejo Git service port"; + }; + + coredns-https = lib.mkOption { + type = lib.types.int; + default = 8003; + description = "CoreDNS HTTPS interface port"; + }; + + # Available ports for new services + xray-main = lib.mkOption { + type = lib.types.int; + default = 8010; + description = "Xray VLESS inbound port"; + }; + + xray-fallback = lib.mkOption { + type = lib.types.int; + default = 8011; + description = "Xray fallback proxy port"; + }; + + xray-websocket = lib.mkOption { + type = lib.types.int; + default = 8012; + description = "Xray websocket fallback port"; + }; + }; +} \ No newline at end of file diff --git a/nix/server/secrets.nix b/nix/server/secrets.nix new file mode 100644 index 0000000..1a093b9 --- /dev/null +++ b/nix/server/secrets.nix @@ -0,0 +1,96 @@ +{ config +, pkgs +, lib +, ... +}: +let + cfg = config.kp2pml30.server; + + # Script to decrypt secrets.yaml and extract XRAY_UIDS + decryptSecrets = pkgs.writeShellScript "decrypt-secrets" '' + set -euo pipefail + + source /var/lib/secrets/.env + + if [ -z "''${KP2_DOTFILES_SECRET_KEY:-}" ]; then + echo "Error: KP2_DOTFILES_SECRET_KEY environment variable not set" >&2 + exit 1 + fi + + if [ ! -f "${./secrets.yaml}" ]; then + echo "Error: secrets.yaml not found" >&2 + exit 1 + fi + + # Decrypt and parse XRAY_UIDS + ${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-pre = builtins.toFile "xray-pre.json" (builtins.readFile ./xray-pre.json); + xray-config-post = builtins.toFile "xray-post.json" (builtins.readFile ./xray-post.json); + + # Script to generate complete xray configuration + generateXrayConfig = pkgs.writeShellScript "generate-xray-config" '' + set -euo pipefail + + cat ${xray-config-pre} + + first=true + while IFS= read -r uuid; do + if [ "$first" = true ]; then + first=false + else + echo "," + fi + echo " {" + echo " \"id\": \"$uuid\"," + echo " \"flow\": \"xtls-rprx-vision\"" + echo " }" + done < <(${decryptSecrets}) + + cat ${xray-config-post} + ''; + +in { + options.kp2pml30.server.secretsDir = lib.mkOption { + type = lib.types.str; + default = "/var/lib/secrets"; + description = "Directory for secrets management"; + }; + + config = lib.mkIf cfg.xray { + # Ensure xray user and group exist + users.users.xray = { + isSystemUser = true; + group = "xray"; + }; + + users.groups.xray = {}; + + # Create a systemd service to decrypt and prepare xray clients config + systemd.services.xray-secrets = { + description = "Decrypt Xray client configuration"; + wantedBy = [ "xray.service" ]; + before = [ "xray.service" ]; + + serviceConfig = { + Type = "oneshot"; + User = "root"; + EnvironmentFile = "${cfg.secretsDir}/.env"; + }; + + script = '' + mkdir -p /run/secrets + ${generateXrayConfig} > /run/secrets/xray-config.json + chown xray:xray /run/secrets/xray-config.json + chmod 440 /run/secrets/xray-config.json + ''; + }; + + # Ensure secrets directory exists + systemd.tmpfiles.rules = [ + "d ${cfg.secretsDir} 0750 root root -" + "d /run/secrets 0755 root root -" + ]; + }; +} diff --git a/nix/server/secrets.yaml b/nix/server/secrets.yaml new file mode 100644 index 0000000..de129d6 --- /dev/null +++ b/nix/server/secrets.yaml @@ -0,0 +1,4 @@ +U2FsdGVkX18N4BW9sin9kPVNkpbtVNoDqBAm+080vcYSS7qySHVOCfe94a7S8mh4 +G5tbvoRrOFxJ+RW/WYNMsEZ7wgsJM8b9AiKPaT30BMHXriTdtai80i6xKqv9zdCb +moGUlBSgMtqEhvAnvpYBxHQ+NtDhxw7K9UjaO7eodNp+l9PR6z+IeL29rC2DMxQc +jXAjbfPa3aeSikXF0g118HbUwVJQwlXq99n/fjkJ8XOhBo/S4tWbt0U8O97VKlA6 diff --git a/nix/server/site.nix b/nix/server/site.nix index 243074d..a075874 100644 --- a/nix/server/site.nix +++ b/nix/server/site.nix @@ -9,6 +9,7 @@ }@args: let cfg = config.kp2pml30.server; + ports = config.kp2pml30.server.ports; backend = kp2pml30-moe.packages.${system}.kp2pml30-moe-backend; frontend = kp2pml30-moe.packages.${system}.kp2pml30-moe-frontend; in lib.mkIf cfg.nginx { @@ -45,7 +46,7 @@ in lib.mkIf cfg.nginx { Restart = "on-failure"; RestartSec = "3"; - ExecStart = ''${pkgs.bash}/bin/bash -c "source /home/kp2pml30-moe-backend/env.sh && touch /home/kp2pml30-moe-backend/db.json && ${backend}/bin/kp2pml30-moe-backend --port 8001 --moderated-path /home/kp2pml30-moe-backend/chatbox-db.json"''; + ExecStart = ''${pkgs.bash}/bin/bash -c "source /home/kp2pml30-moe-backend/env.sh && touch /home/kp2pml30-moe-backend/db.json && ${backend}/bin/kp2pml30-moe-backend --port ${toString ports.backend} --moderated-path /home/kp2pml30-moe-backend/chatbox-db.json"''; }; }; } diff --git a/nix/server/stream.nginx b/nix/server/stream.nginx index a77c4b2..eeb2a72 100644 --- a/nix/server/stream.nginx +++ b/nix/server/stream.nginx @@ -12,9 +12,14 @@ map $ssl_preread_server_name $name { updates.signal.org updates; updates2.signal.org updates2; - kp2pml30.moe self; - git.kp2pml30.moe self; - cache.nix.kp2pml30.moe self; + x.kp2pml30.moe xray-entrypoint; + pr.kp2pml30.moe signal-proxy; + + kp2pml30.moe ssl-terminator; + dns.kp2pml30.moe ssl-terminator; + git.kp2pml30.moe ssl-terminator; + cache.nix.kp2pml30.moe ssl-terminator; + backend.kp2pml30.moe ssl-terminator; default deny; } @@ -63,6 +68,10 @@ upstream updates2 { server updates2.signal.org:443; } +upstream xray-entrypoint { + server 127.0.0.1:8010; +} + upstream deny { server 127.0.0.1:9; } @@ -71,23 +80,45 @@ upstream self { server 127.0.0.1:80; } -server { - listen 443 ssl; - server_name pr.kp2pml30.moe; - proxy_pass $name; - ssl_preread on; +upstream ssl-terminator { + server 127.0.0.1:8443; +} - ssl_certificate /var/lib/acme/kp2pml30.moe/fullchain.pem; - ssl_certificate_key /var/lib/acme/kp2pml30.moe/key.pem; - ssl_trusted_certificate /var/lib/acme/kp2pml30.moe/chain.pem; +upstream signal-proxy { + server 127.0.0.1:8444; } server { - listen 443 ssl; - server_name kp2pml30.moe git.kp2pml30.moe backend.kp2pml30.moe dns.kp2pml30.moe cache.nix.kp2pml30.moe; + listen 443; + ssl_preread on; + proxy_pass $name; +} + +server { + listen 8443 ssl; + server_name kp2pml30.moe git.kp2pml30.moe cache.nix.kp2pml30.moe backend.kp2pml30.moe dns.kp2pml30.moe; proxy_pass self; ssl_certificate /var/lib/acme/kp2pml30.moe/fullchain.pem; ssl_certificate_key /var/lib/acme/kp2pml30.moe/key.pem; ssl_trusted_certificate /var/lib/acme/kp2pml30.moe/chain.pem; } + +server { + listen 8444 ssl; + server_name pr.kp2pml30.moe; + ssl_preread on; + proxy_pass $name; + + ssl_certificate /var/lib/acme/kp2pml30.moe/fullchain.pem; + ssl_certificate_key /var/lib/acme/kp2pml30.moe/key.pem; + ssl_trusted_certificate /var/lib/acme/kp2pml30.moe/chain.pem; +} + +log_format proxy_log '$remote_addr [$time_local] ' + '$protocol $status $bytes_sent $bytes_received ' + '$session_time "$upstream_addr" ' + '"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"' + 'Proxy: "$ssl_preread_server_name" $name"'; + +access_log /var/log/nginx/aboba-access.log proxy_log buffer=1k flush=1m; diff --git a/nix/server/xray-post.json b/nix/server/xray-post.json new file mode 100644 index 0000000..dbc38ef --- /dev/null +++ b/nix/server/xray-post.json @@ -0,0 +1,53 @@ + ], + "decryption": "none", + "fallbacks": [ + { + "dest": "8011", + "xver": 1 + } + ] + }, + "streamSettings": { + "network": "tcp", + "security": "tls", + "tlsSettings": { + "rejectUnknownSni": true, + "minVersion": "1.2", + "alpn": ["http/1.1"], + "certificates": [ + { + "ocspStapling": 3600, + "certificateFile": "/var/lib/acme/kp2pml30.moe/fullchain.pem", + "keyFile": "/var/lib/acme/kp2pml30.moe/key.pem" + } + ] + } + }, + "sniffing": { + "enabled": true, + "destOverride": [ + "http", + "tls" + ] + } + } + ], + "outbounds": [ + { + "protocol": "freedom", + "tag": "direct" + }, + { + "protocol": "blackhole", + "tag": "block" + } + ], + "policy": { + "levels": { + "0": { + "handshake": 3, + "connIdle": 127 + } + } + } +} diff --git a/nix/server/xray-pre.json b/nix/server/xray-pre.json new file mode 100644 index 0000000..572d18b --- /dev/null +++ b/nix/server/xray-pre.json @@ -0,0 +1,23 @@ +{ + "log": { + "loglevel": "warning" + }, + "routing": { + "domainStrategy": "IPIfNonMatch", + "rules": [ + { + "type": "field", + "ip": [ + "geoip:cn" + ], + "outboundTag": "block" + } + ] + }, + "inbounds": [ + { + "listen": "127.0.0.1", + "port": 8010, + "protocol": "vless", + "settings": { + "clients": [ diff --git a/nix/server/xray.nix b/nix/server/xray.nix new file mode 100644 index 0000000..49fcf35 --- /dev/null +++ b/nix/server/xray.nix @@ -0,0 +1,21 @@ +{ config +, pkgs +, lib +, ... +}: +let + cfg = config.kp2pml30.server; + ports = config.kp2pml30.server.ports; +in lib.mkIf cfg.xray { + services.xray = { + enable = true; + settingsFile = "/run/secrets/xray-config.json"; + }; + + # Ensure xray can read the certificates + users.users.xray.extraGroups = [ "nginx" ]; + + # Ensure the xray service starts after ACME certificates are available + systemd.services.xray.after = [ "acme-${cfg.hostname}.service" ]; + systemd.services.xray.wants = [ "acme-${cfg.hostname}.service" ]; +} \ No newline at end of file