插件体系
Pingap中通过Locaton添加各种插件支持更多的应用场景,如鉴权、流控、设置响应头等场景。
插件执行时点
现支持将插件添加到以下各阶段时点中执行:
Request
: 请求的最开始阶段,适用于针对一些权限类的拦截等处理ProxyUpstream
: 请求转发至上流节点之前,因为此流程是在读取缓存之后,因此若不希望针对缓存前限制,但转发至上游前限制的可配置为此阶段。如限制IP访问频繁,但允许高并发读取缓存数据。Response
: 上游数据响应之后,用于针对上游响应数据做调整时使用。
#[async_trait]
pub trait Plugin: Sync + Send {
fn category(&self) -> PluginCategory;
fn step(&self) -> String;
async fn handle_request(
&self,
_step: PluginStep,
_session: &mut Session,
_ctx: &mut State,
) -> pingora::Result<Option<HttpResponse>> {
Ok(None)
}
async fn handle_response(
&self,
_step: PluginStep,
_session: &mut Session,
_ctx: &mut State,
_upstream_response: &mut ResponseHeader,
) -> pingora::Result<Option<Bytes>> {
Ok(None)
}
}
主要分三个实现:
category
: 插件类型,用于区分该插件是哪类形式的插件step
: 插件的执行阶段,现只支持在request_filter
与proxy_upstream_filter
阶段执行handle_request
: 插件的转发前执行逻辑,若返回的是Ok(Some(HttpResponse))
,则表示请求已处理完成,不再转发到上游节点,并将该响应传输至请求端handle_response
: 插件的响应前执逻辑,若返回的是Ok(Some(Bytes))
,则表示要重写响应数据
Stats
获取应用性能指标等统计性能,配置是指定对应的访问路径即可,也可直接使用自带的pingap:stats
。如配置为/stats
后,访问该location的/stats
目录即可获取到应用的统计指标。具体配置如下:
[plugins.stats]
category = "stats"
path = "/stats"
remark = "用于获取性能指标"
path
: 响应性能指标的路径
界面配置如图所示,主要是配置其对应的请求路径即可:
Ping
Ping->pong的响应处理,可用于判断程序是否正常运行等。
[plugins.pingpong]
category = "ping"
path = "/ping"
path
: 响应pong的路径
Admin
管理后台配置,可在现在的现有的location中添加支持管理后台服务,YWRtaW46MTIzMTIz
为base64(admin:123123)
,将该配置关联至对应location后,即可使用该location的/pingap/访问管理后台,账号为admin
,密码为123123
[plugins.admin]
authorizations = ["YWRtaW46MTIzMTIz"]
category = "admin"
ip_fail_limit = 10
path = "/pingap"
remark = "管理后台"
authorizations
: Basic认证的密钥列表ip_fail_limit
: 认证失败时的IP限制次数path
: 管理后台的路径
Directory
静态文件目录服务,为指定目录提供静态文件服务,说明如下:
[plugins.downloadsServe]
category = "directory"
charset = "utf-8"
chunk_size = "4kb"
index = "index.html"
max_age = "1h"
path = "~/Downloads"
path
: 静态文件目录路径chunk_size
: Http chunk的大小,默认为8192
max_age
: 设置http响应的的缓存时间,默认无。此值对于text/html
无效,html均设置为不可缓存。如设置为1h
表示缓存有效期1小时private
: 缓存是否设置为private
,默认为public
index
: 设置默认的index文件,默认为index.html
charset
: 指定charset类型,默认无autoindex
: 是否允许目录以浏览形式展示,需要注意若指定了目录允许浏览,则index
参 数无效download
: 是否支持下载,指定该参数后响应时会设置响应头Content-Disposition
headers
: 需要添加的http响应头列表
界面配置如图所示,配置对应的静态文件目录,并按需要添加对应的query参数即可:
Mock
用于对特定路径(若不设置则所有)mock响应,用于测试或暂停服务使用。
[plugins.errorMock]
category = "mock"
data = '{"error": "error message"}'
delay = "1s"
headers = [
"X-Error:CustomRrror",
"Content-Type:application/json",
]
name = "errorMock"
path = "/"
status = 500
step = "request"
data
: Mock的响应数据headers
: Mock的响应头path
: Mock请求的路径,如果不配置则匹配所有status
: Mock响应的状态码delay
: 延时响应
界面配置如图所示,配置对应响应数据既可,需要注意如果指定响应类型,如json等:
Redirect
http重定向,可在重定向时添加前缀或指定为https。
[plugins.http2https]
category = "redirect"
http_to_https = true
name = "http2https"
prefix = "/api"
step = "request"
http_to_https
: 是否从http重定向至httpsprefix
: 重定向时添加的前缀
界面配置如图所示,若需要重定向时添加前缀,可配置对应的前缀,若无需要调整则不配置值即可:
Cache
缓存中间件,用于缓存http请求,由于缓存模块是全局使用,因此如果不同的Location均使用缓存,则需要设置不同的namespace。
[plugins.chartsCache]
category = "cache"
eviction = true
headers = ["Accept-Encoding"]
lock = "3s"
max_file_size = "100kb"
max_ttl = "1h"
namespace = "charts"
predictor = true
lock
: 缓存不存在时,相同请求的等待时长max_file_size
: 单个缓存文件的最大长度,建议设置合理的值,避免过大的响应缓存在内存中导致内存占用过高namespace
: 由于缓存是应用共享,而缓存的key是基于path+querystring,因此如果是多域名共享时,不同的域名使用不同的缓存中间件,并设置对应的namespacemax_ttl
: 设置缓存的最长有效期,一般建议由upstream服务响应时,若Cache-Control
的max-age
较长,则设置较短的s-maxage
,若upstream
未设置s-maxage
,可通过此配置限制缓存的最大有效期eviction
: 当缓存超限时,触发缓存清除,需要注意,如tinyufo暂时不支持主动清除predictor
: 是否记录无法缓存的请求,可避免后续重复的等待确认请求是否可缓存
RequestId
用于在请求头中添加X-Request-Id
(也可指定对应的请求头),若已有则忽略,可指定使用uuid
或nanoid
两种形式,nanoid
可以指定长度。
[plugins.customReqId]
algorithm = "nanoid"
category = "request_id"
size = 8
algorithm
: 生成请求id的算法size
: 请求id的长度,只对于nanoid
有效。
界面配置如图所示:
Compression
压缩中间件,处理从上游返回的相关数据压缩,由于pingora
对于压缩的匹配顺序为gzip --> br --> zstd
,官方暂未支持调整优先级,而对于现代浏览器,基本都支持gzip
,大部分支持br
,少部分支持zstd
,为了使用更好的压缩方式,此插件会调整请求的Accept-Encoding
,让压缩的顺序调整为zstd --> br --> gzip
。配置如下:
[plugins.commonCompression]
br_level = 6
category = "compression"
gzip_level = 6
zstd_level = 5
br_level
: brotli压缩算法的压缩级别gzip_level
: gzip压缩算法的压缩级别zstd_level
: zstd压缩算法的压缩级别
需要注意三种压缩算法的压缩级别不一样,按需选择即可,也可使用自带的pingap:compression
,它的压缩级别配置为gzip_level = 6
, br_level = 6
, zstd_level = 3
。
界面配置如图所示,按需分别配置对应的压缩级别即可,若不想启用该压缩算法则配置为0:
AcceptEncoding
调整客户端接受编码的方式,可设置支持的编码,根据客户端与设置的编码调整相应的编码顺序。
[plugins.acceptEncoding]
category = "accept_encoding"
encodings = "zstd, br, gzip"
only_one_encoding = true
step = "request"
encodings
: 支持的编码only_one_encoding
: 是否只使用单一编码
KeyAuth
KeyAuth用于提供简单的认证方式,支持配置从query或header中获取值,可配置多个校验值,方便多系统接入。
从query中的app字段中获取校验:
category = "key_auth"
delay = "1s"
hide_credentials = true
keys = [
"KOXQaw",
"GKvXY2",
]
query = "app"
step = "request"
从header中的X-App字段中获取校验:
[plugins.appAuth]
category = "key_auth"
delay = "1s"
hide_credentials = true
keys = [
"KOXQaw",
"GKvXY2",
]
header = "X-App"
step = "request"
hide_credentials
: 转发至upstream时是否删除认证信息query
: 从query中获取认证信息header
: 从请求头中获取认证信息(与query二选一,,使用使用query)keys
: 认证的key列表
界面配置如图所示,配置key的名称,再配置符合的值即可:
BasicAuth
BasicAuth鉴权,配置时需要使用保存base64(user:pass)
的值,若有多个则配置多个即可。
[plugins.testBasicAuth]
authorizations = [
"YWRtaW46dGVzdA==",
"YWRtaW46MTIzMTIz",
]
category = "basic_auth"
delay = "1s"
hide_credentials = true
step = "request"
authorizations
: Basic认证的信息,它使用的是base64(user:password)后的数据,可以配置多个hide_credentials
: 转发至upstream时是否删除认证信息
界面配置如图所示,配置basic auth的值,需要注意配置已做base64处理后的值即可:
Jwt
用于生成jwt认证信息以及针对相关请求判断jwt相关信息是否符合。需要注意请求路径的响应数据会使用jwt的形式重新签名生成对应的token返回,其它路径则校验其token是否符合。
[plugins.jwtAuth]
algorithm = "HS256"
auth_path = "/jwt-sign"
category = "jwt"
delay = "1s"
header = "X-Jwt"
secret = "123123"
step = "request"
header
: jwt认证时从header中获取的请求头名(与cookie,query三选一,优先级为header > cookie -> query)cookie
: jwt认证时从cookie中获取的cookie名query
: jwt认证时从query中获取的字段auth_path
: 生成jwt认证信息的路径algorithm
: 认证使用的算法secret
: 认证使用的密钥
CombinedAuth
基于应用id+密钥,并使用时间戳生成摘要的组合式认证方式
[plugins.appAuth]
category = "combined_auth"
step = "request"
[[plugins.appAuth.authorizations]]
app_id = "pingap"
deviation = 10
ip_list = [
"192.168.1.1/24",
"127.0.0.1",
]
secret = "123123"
app_id
: 鉴权使用的iddeviation
: 客户端参数的时间戳与服务器的偏差时间ip_list
: 允许的ip列表secret
: 密钥
请求的query参数如下:app_id=pingap&ts=1727582506&digest=85c623c389177a69860adfd572212507ef98c197ba5105677919e0663eeae091
,digest
通过sha256计算密钥与时间戳的hash值得出。
let mut hasher = Sha256::new();
hasher.update(format!("{}:{ts}", auth_param.secret).as_bytes());
let hash256 = hasher.finalize();
if digest.to_lowercase() != hash256.encode_hex::<String>() {
return Err(Error::Invalid {
category: category.to_string(),
message: "digest is invalid".to_string(),
});
}
Limit
可基于cookie、请求头或query参数来限制并发访问,支持inflight
(并发)与rate
(访问频率)两种限制类型,若配置的字段获取到的值为空,则不限制,支持inflight
与rate
两种限制类型。
根据cookie的bigtree
限制并发数为10
:
[plugins.cookieBigTreeLimit]
category = "limit"
interval = "1m"
key = "bigtree"
max = 10
step = "request"
tag = "cookie"
type = "inflight"
根据请求头的X-App
参数限制并发数10
:
[plugins.headerAppLimit]
category = "limit"
key = "X-App"
max = 10
tag = "header"
type = "inflight"
根据query中的app
参数限制1秒钟仅能访问10
次:
[plugins.queryAppLimit]
category = "limit"
interval = "1s""
key = "app"
max = 10
tag = "query"
type = "rate"
根据ip限制1分钟最多访问10
次(ip获取的顺序为X-Forwarded-For --> X-Real-Ip --> Remote Addr):
[plugins.ipLimit]
category = "limit"
interval = "1m"
max = 10
tag = "ip"
type = "rate"
type
: 限制的类型,有inflight
并发限制与rate
速率限制tag
: 限流的key的获取类型,有cookie
,header
,query
与ip
key
: 限制使用的key,对于ip
类型无需指定max
: 限流最大值interval
: 限流间隔,用于rate
类型
界面配置如图所示,主要是配置限制条件以及对应的最大并发访问量:
IpRestriction
Ip限制分为两种模式,允许或禁止,ip可支持配置为单ip或ip组,配置如下:
[plugins.ipDeny]
category = "ip_restriction"
ip_list = [
"192.168.1.1",
"1.1.1.0/24",
]
message = "禁止该IP访问"
step = "request"
type = "deny"
type
: 类型,是允许还是禁止ip_list
: IP或IP网段列表message
: 拦截时的出错信息
界面配置如图所示,配置IP列表后,填写是允许还是禁止即可:
RefererRestriction
Referer限制分为两种模式,允许或禁止,配置时可使用*前缀匹配,配置如下:
[plugins.referer]
category = "referer_restriction"
message = "禁止访问"
referer_list = ["*.github.com"]
step = "request"
type = "allow"
type
: 类型,是允许还是禁止referer_list
: referer列表message
: 拦截时的出错信息
界面配置如图所示,配置Referer列表 后,填写是允许还是禁止即可:
Csrf
Csrf校验,校验请求时的cookie与请求头的是否一致,若不一致则返回出错。获取令牌的路径则会生成对应的cookie,并设置为非http的模式允许浏览器获取。
[plugins.csrf]
category = "csrf"
key = "WjrXUG47wu"
name = "x-csrf-token"
token_path = "/csrf-token"
ttl = "1h"
key
: 生成csrf信息时使用的keyname
: csrf的名称token_path
: 生成token的目录ttl
: 有效期
Cors
Cors插件,用于设置跨域请求相关配置。
[plugins.cors]
allow_credentials = true
allow_headers = "Content-Type, X-User-Id"
allow_methods = "GET, POST, OPTIONS"
allow_origin = "$http_origin"
category = "cors"
expose_headers = "Content-Type, X-Device"
max_age = "1h"
path = "^/api"
step = "request"
allow_credentials
: 是否允许携带认证信息allow_headers
: 允许的请求头allow_methods
: 允许的http方法allow_origin
: 允许的origin,若设置为$http_origin
则表示按来源设置允许,不建议使用此形式,建议按需设置max_age
: 设置有效期path
: 设置允许cors的路径,可为正则表达式expose_headers
: 暴露给浏览器访问的响应头
ResponseHeaders
响应头的插件主要是设置、添加以及删除请求头。若响应头的值设置为$hostname
表示获取机器的hostname,若以$
开头的则表示从环境变量中获取对应的值。
[plugins.commonResponseHeaders]
add_headers = ["X-Server:pingap"]
category = "response_headers"
remove_headers = ["X-User"]
set_headers = ["X-Response-Id:123"]
step = "response"
add_headers
: 需要添加的响应头set_headers
: 需要设置的响应头,会覆盖原有值remove_headers
: 需要删除的响应头
执行顺序为add_headers --> remove_headers --> set_headers
。界面配置如图所示,按需要配置要设置、添加或删除的响应头,若不需要则不设置即可: