From c7f85bcdd364d5b2b1bfaec55176d620aed2208d Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 12 Aug 2016 12:50:12 +0800 Subject: [PATCH] doc: new features --- README.md | 79 +++++++++++++++++++++++++++++++++++++--- README_zh.md | 81 +++++++++++++++++++++++++++++++++++++++--- doc/pic/dashboard.png | Bin 0 -> 25431 bytes 3 files changed, 151 insertions(+), 9 deletions(-) create mode 100644 doc/pic/dashboard.png diff --git a/README.md b/README.md index 8be4b45..e88bba0 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,14 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi * [Communicate with your computer in LAN by SSH](#communicate-with-your-computer-in-lan-by-ssh) * [Visit your web service in LAN by specific domain](#visit-your-web-service-in-lan-by-specific-domain) * [Features](#features) + * [Dashboard](#dashboard) * [Authentication](#authentication) * [Encryption and Compression](#encryption-and-compression) * [Reload configures without frps stopped](#reload-configures-without-frps-stopped) * [Privilege Mode](#privilege-mode) + * [Port White List](#port-white-list) + * [Connection Pool](#connection-pool) + * [Rewriting the Host Header](#rewriting-the-host-header) * [Development Plan](#development-plan) * [Contributing](#contributing) * [Contributors](#contributors) @@ -136,6 +140,21 @@ Howerver, we can expose a http or https service using frp. ## Features +### Dashboard + +Check frp's status and proxies's statistics information by Dashboard. + +Configure a port for dashboard to enable this feature: + +```ini +[common] +dashboard_port = 7500 +``` + +Then visit `http://[server_addr]:7500` to see dashboard. + +![dashboard](/doc/pic/dashboard.png) + ### Authentication `auth_token` is used in frps.ini for authentication when frpc login in and you should configure it for each proxy. @@ -237,18 +256,70 @@ All proxies's configures are set in frpc.ini when privilege mode is enabled. `ssh -oPort=6000 test@x.x.x.x` +#### Port White List + +`privilege_allow_ports` in frps.ini is used for preventing abuse of ports in privilege mode: + +```ini +# frps.ini +[common] +privilege_mode = true +privilege_token = 1234 +privilege_allow_ports = 2000-3000,3001,3003,4000-50000 +``` + +`privilege_allow_ports` consists of a specific port or a range of ports divided by ','. + +### Connection Pool + +By default, frps send message to frpc for create a new connection to backward service when getting an user request.If a proxy's connection pool is enabled, there will be a specified number of connections pre-established. + +This feature is fit for a large number of short connections. + +1. Configure the limit of pool count each proxy can use in frps.ini: + + ```ini + # frps.ini + [common] + max_pool_count = 50 + ``` + +2. Enable and specify the number of connection pool: + + ```ini + # frpc.ini + [ssh] + type = tcp + local_port = 22 + pool_count = 10 + ``` + +### Rewriting the Host Header + +When forwarding to a local port, frp does not modify the tunneled HTTP requests at all, they are copied to your server byte-for-byte as they are received. Some application servers use the Host header for determining which development site to display. For this reason, frp can rewrite your requests with a modified Host header. Use the `host_header_rewrite` switch to rewrite incoming HTTP requests. + +```ini +# frpc.ini +[web] +privilege_mode = true +type = http +local_port = 80 +custom_domains = test.yourdomain.com +host_header_rewrite = dev.yourdomain.com +``` + +If `host_header_rewrite` is specified, the Host header will be rewritten to match the hostname portion of the forwarding address. + ## Development Plan -* Dashboard page. -* Statistics and prestentation of traffic and connection info, etc. * Support udp protocol. -* Connection pool. -* White list for opening specific ports in privilege mode. * Support wildcard domain name. * Url router. * Load balance to different service in frpc. * Debug mode for frpc, prestent proxy status in terminal. * Inspect all http requests/responses that are transmitted over the tunnel. +* Frpc can directly be a webserver for static files. +* Full control mode, dynamically modify frpc's configure with dashboard in frps. * P2p communicate by make udp hole to penetrate NAT. ## Contributing diff --git a/README_zh.md b/README_zh.md index 2b454a6..028313b 100644 --- a/README_zh.md +++ b/README_zh.md @@ -15,10 +15,14 @@ frp 是一个高性能的反向代理应用,可以帮助您轻松地进行内 * [通过 ssh 访问公司内网机器](#通过-ssh-访问公司内网机器) * [通过指定域名访问部署于内网的 web 服务](#通过指定域名访问部署于内网的-web-服务) * [功能说明](#功能说明) + * [Dashboard](#dashboard) * [身份验证](#身份验证) * [加密与压缩](#加密与压缩) * [服务器端热加载配置文件](#服务器端热加载配置文件) * [特权模式](#特权模式) + * [端口白名单](#端口白名单) + * [连接池](#连接池) + * [修改 Host Header](#修改-host-header) * [开发计划](#开发计划) * [贡献代码](#贡献代码) * [贡献者](#贡献者) @@ -133,6 +137,21 @@ frp 目前正在前期开发阶段,master 分支用于发布稳定版本,dev ## 功能说明 +### Dashboard + +通过浏览器查看 frp 的状态以及代理统计信息展示。 + +需要在 frps.ini 中指定 dashboard 服务使用的端口,即可开启此功能: + +```ini +[common] +dashboard_port = 7500 +``` + +打开浏览器通过 `http://[server_addr]:7500` 访问 dashboard 界面。 + +![dashboard](/doc/pic/dashboard.png) + ### 身份验证 出于安全性的考虑,服务器端可以在 frps.ini 中为每一个代理设置一个 auth_token 用于对客户端连接进行身份验证,例如上文中的 [ssh] 和 [web] 两个代理的 auth_token 都为 123。 @@ -242,20 +261,72 @@ reload 命令仅能用于修改代理的配置内容,[common] 内的公共配 `ssh -oPort=6000 test@x.x.x.x` +#### 端口白名单 + +启用特权模式后为了防止端口被滥用,可以手动指定允许哪些端口被使用,在 frps.ini 中通过 privilege_allow_ports 来指定: + +```ini +# frps.ini +[common] +privilege_mode = true +privilege_token = 1234 +privilege_allow_ports = 2000-3000,3001,3003,4000-50000 +``` + +privilege_allow_ports 可以配置允许使用的某个指定端口或者是一个范围内的所有端口,以 `,` 分隔,指定的范围以 `-` 分隔。 + +### 连接池 + +默认情况下,当用户请求建立连接后,frps 才会请求 frpc 主动与后端服务建立一个连接。当为指定的代理启用连接池后,frp 会预先和后端服务建立起指定数量的连接,每次接收到用户请求后,会从连接池中取出一个连接和用户连接关联起来,避免了等待与后端服务建立连接以及 frpc 和 frps 之间传递控制信息的时间。 + +这一功能比较适合有大量短连接请求时开启。 + +1. 首先可以在 frps.ini 中设置每个代理可以创建的连接池上限,避免大量资源占用,默认为 100,客户端设置超过此配置后会被调整到当前值: + + ```ini + # frps.ini + [common] + max_pool_count = 50 + ``` + +2. 在 frpc.ini 中为指定代理启用连接池,指定预创建连接的数量: + + ```ini + # frpc.ini + [ssh] + type = tcp + local_port = 22 + pool_count = 10 + ``` + +### 修改 Host Header + +通常情况下 frp 不会修改转发的任何数据。但有一些后端服务会根据 http 请求 header 中的 host 字段来展现不同的网站,例如 nginx 的虚拟主机服务,启用 host-header 的修改功能可以动态修改 http 请求中的 host 字段。该功能仅限于 http 类型的代理。 + +```ini +# frpc.ini +[web] +privilege_mode = true +type = http +local_port = 80 +custom_domains = test.yourdomain.com +host_header_rewrite = dev.yourdomain.com +``` + +原来 http 请求中的 host 字段 `test.yourdomain.com` 转发到后端服务时会被替换为 `dev.yourdomain.com`。 + ## 开发计划 计划在后续版本中加入的功能与优化,排名不分先后,如果有其他功能建议欢迎在 [issues](https://github.com/fatedier/frp/issues) 中反馈。 -* Dashboard 界面。 -* 流量,连接数等代理信息统计与展示。 -* udp 协议支持。 -* 针对短连接的连接池优化。 -* 特权模式支持端口白名单。 +* 支持 udp 协议。 * 支持泛域名。 * 支持 url 路由转发。 * frpc 支持负载均衡到后端不同服务。 * frpc debug 模式,控制台显示代理状态,类似 ngrok 启动后的界面。 * frpc http 请求及响应信息展示。 +* frpc 支持直接作为 webserver 访问指定静态页面。 +* frpc 完全控制模式,通过 dashboard 对 frpc 进行在线操作。 * 支持 udp 打洞的方式,提供两边内网机器直接通信,流量不经过服务器转发。 ## 贡献代码 diff --git a/doc/pic/dashboard.png b/doc/pic/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..80d7bfff652fe510509c1dbc5b05cd1dd206064b GIT binary patch literal 25431 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 {aM4(*DOwj8%$M^O_gyGl43w*G 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 G;r{@$KLlt1 literal 0 HcmV?d00001