分享下我的 nas 安全方案 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
happyxhw101
V2EX    程序员

分享下我的 nas 安全方案

  •  
  •   happyxhw101 14 小时 59 分钟前 821 次点击
    1. 物理机直接安装 ubuntu, 所有应用都部署在 docker
    2. ssh 只允许密钥登录, 禁止 root 用户登录
    3. 所有访问( http, tcp)都通过 nginx 代理, ufw 只暴露固定的几个端口, nginx 开启 https 证书
    4. nginx 配置 geolite2, 禁止任何 国外 ip 访问, 异常访问基本都是国外 ip
    5. fail2ban 自动封禁所有 nginx 日志里面国外 ip
    6. 不安装 1panel,宝塔等任何 web 管理工具, 直接 ssh 到机器上命令行管理

    分享下我的 nginx 配置

    load_module "modules/ngx_http_geoip2_module.so"; load_module "modules/ngx_stream_geoip2_module.so"; worker_processes 4; error_log /var/log/nginx/nginx_error.log; error_log /var/log/nginx/nginx_error.log notice; error_log /var/log/nginx/nginx_error.log info; pid /var/log/nginx/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb { auto_reload 24h; $geoip_country_code default=Unknown source=$remote_addr country iso_code; $geoip_country_name country names en; } geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb { auto_reload 24h; $geoip_city default=Unknown city names en; } map $geoip_country_code $allowed_country { default no; CN yes; } map $remote_addr $allowed { default $allowed_country; 127.0.0.1 yes; ~^192\.168\.\\d+\.\\d+$ yes; ~^172\.16\.0\.\\d+$ yes; ~^172\.17\.\\d+\.\\d+$ yes; } map $http_upgrade $connection_upgrade { default upgrade; '' ""; } log_format json_analytics escape=json '{' '"timestamp": "$msec", ' # request unixtime in seconds with a milliseconds resolution '"request_id": "$request_id", ' # the unique request id '"request_length": "$request_length", ' # request length (including headers and body) '"body_bytes_sent": "$body_bytes_sent", ' '"remote_addr": "$remote_addr", ' # client IP '"time_iso8601": "$time_iso8601", ' '"request_uri": "$request_uri", ' # full path and arguments if the request '"code": "$status", ' # response status code '"http_host": "$http_host", ' # the request Host: header '"server_name": "$server_name", ' # the name of the vhost serving the request '"request_time": "$request_time", ' # request processing time in seconds with msec resolution '"upstream": "$upstream_addr", ' # upstream backend server for proxied requests '"request_method": "$request_method", ' # request method '"allowed": "$allowed", ' '"geoip_country_code": "$geoip_country_code", ' '"geoip_country_name": "$geoip_country_name", ' '"geoip_city": "$geoip_city"' '}'; access_log /var/log/nginx/access.log json_analytics; error_log /var/log/nginx/error.log warn; set_real_ip_from 0.0.0.0/0; real_ip_header X-Real-IP; real_ip_recursive on; sendfile on; server_tokens off; keepalive_timeout 65; gzip on; gzip_vary on; gzip_comp_level 4; gzip_min_length 256; gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; gzip_types application/atom+xml application/Javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; proxy_buffering off; proxy_buffers 4 128k; proxy_buffer_size 256k; proxy_busy_buffers_size 256k; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers ALL:!DH:!EXPORT:!RC4:+HIGH:+MEDIUM:-LOW:!aNULL:!eNULL; ssl_certificate /etc/nginx/ssl/fullchain.cer; ssl_certificate_key /etc/nginx/ssl/xxx.cc.key; include /etc/nginx/conf.d/*.conf; } stream { geoip2 /etc/nginx/geoip/GeoLite2-Country.mmdb { auto_reload 24h; $geoip_country_code default=Unknown source=$remote_addr country iso_code; $geoip_country_name country names en; } geoip2 /etc/nginx/geoip/GeoLite2-City.mmdb { auto_reload 24h; $geoip_city default=Unknown city names en; } map $geoip_country_code $allowed_country { default no; CN yes; } map $remote_addr $allowed { default $allowed_country; 127.0.0.1 yes; ~^192\.168\.\\d+\.\\d+$ yes; ~^172\.16\.0\.\\d+$ yes; ~^172\.17\.\\d+\.\\d+$ yes; } log_format json_analytics escape=json '{' '"timestamp": "$msec", ' # request unixtime in seconds with a milliseconds resolution '"connection": "$connection", ' # connection serial number '"pid": "$pid", ' # process pid '"remote_addr": "$remote_addr", ' # client IP '"remote_port": "$remote_port", ' # client port '"time_iso8601": "$time_iso8601", ' # local time in the ISO 8601 standard format '"upstream": "$upstream_addr", ' '"protocol": "$protocol", ' '"allowed": "$allowed", ' '"request_method": "STREAM", ' '"geoip_country_code": "$geoip_country_code", ' '"geoip_country_name": "$geoip_country_name", ' '"geoip_city": "$geoip_city"' '}'; access_log /var/log/nginx/access.log json_analytics; error_log /var/log/nginx/error.log warn; include /etc/nginx/stream.d/*.conf; } 

    ssh 代理

    map $allowed $ssh_server { yes ssh; } upstream ssh { server 192.168.5.1:1234; } server { listen 5678; listen [::]:5678; proxy_pass $ssh_server; proxy_connect_timeout 30s; proxy_timeout 60s; ssl_preread on; } 

    http 代理

    server { server_name x.x.com; listen 1233 ssl; listen [::]:1233 ssl; http2 on; charset "utf-8"; if ($allowed != yes) { return 404; } error_page 497 =307 https://$host:$server_port$request_uri; client_max_body_size 512M; proxy_buffering off; set $backend "http://192.168.5.1:1234"; include /etc/nginx/conf.d/basic/no_log.conf; location / { proxy_redirect off; proxy_set_header Host $host:$server_port; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_pass $backend; } } 
    13 条回复    2026-02-04 08:58:02 +08:00
    xingheng
        1
    xingheng  
       13 小时 29分钟前   1
    这就叫专业.jpg
    swiftg
        2
    swiftg  
       13 小时 24 分钟前 via iPhone
    不对公众提供网络服务,只服务自己和有限的几个人的话,完全不用暴露任何端口到公网
    wonderfulcxm
        3
    wonderfulcxm  
       13 小时 3 分钟前 via iPhone
    你这个也防不了飞牛的路径穿越,还是得加个 waf 。
    dilidilid
        4
    dilidilid  
       13 小时 1 分钟前
    @wonderfulcxm nginx 在 docker 里,只要不是把根目录挂上去了就算穿越也只能在 docker 里面玩
    abc6666
        5
    abc6666  
       12 小时 55 分钟前
    我都是飞牛前面套了一个 WAF 。就可以了。
    wwd179
        6
    wwd179  
       12 小时 54 分钟前
    我用 tailscale 组私网。nas 只对公网暴露 tailscale 端口。
    有些要对外的服务用 docker 运行 nginx 这些反代。
    wonderfulcxm
        7
    wonderfulcxm  
       12 小时 52 分钟前 via iPhone
    @dilidilid 如果是飞牛,我理解没错的话,要用 fn connect 要 nginx 反代到宿主机的 5666 或者 5667 端口。无论是不是 docker ,加 waf 是过滤,nginx 就纯是转发了
    yinmin
        8
    yinmin  
       12 小时 43 分钟前
    nginx 可以换一个思路保护:
    (1) 必须域名访问: stream 模块里加 map $ssl_preread_server_name,不要设置 default 。如果黑客不知道 SNI,nginx 会直接挂断 TCP 连接,不会进行 TLS 协商
    (2) 启用双向证书(mTLS)保护: HTTPS 网站在 nginx 里启用客户端证书认证,如果没有客户端证书 nginx 就无法建立 tls 连接,保护后端业务。windows 、macos 、ios 、android 的浏览器都是原厂支持客户端证书的

    实施以上 2 条后,可以不用禁止国外 ip
    WizardLeo
        9
    WizardLeo  
       7 小时 4 分钟前
    只开放 vpn 、p2p 端口,实在要用反代也套一层 nginx 用 sni 进行端口复用。
    moioooo
        10
    moioooo  
       2 小时 55 分钟前 via iPhone
    防火墙设置只允许特定 ip 访问(内网除外)就行,然后找个小鸡搭个代理。客户端用这个代理访问 nas 就行。
    只要你的代理不泄漏,别人也访问不了 nas 了。
    PerFectTime
        11
    PerFectTime  
       1 小时 11 分钟前
    开 ss 回家,要映射服务到公网的前置套一层 oauth 认证
    benjaminliangcom
        12
    benjaminliangcom  
       58 分钟前
    @wonderfulcxm #3 其实直接用 CLoudfalre tunnel 就行了,可以零信任,可以 mtls ,也可以 WAF
    zhouu
        13
    zhouu  
       47 分钟前
    我用 pangolin 这套方案
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4668 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 01:45 PVG 09:45 LAX 17:45 JFK 20:45
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86