feat(apisix): add Cloudron package
- Implements Apache APISIX packaging for Cloudron platform. - Includes Dockerfile, CloudronManifest.json, and start.sh. - Configured to use Cloudron's etcd addon. 🤖 Generated with Gemini CLI Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
74
CloudronPackages/APISIX/apisix-source/t/fuzzing/client_abort.py
Executable file
74
CloudronPackages/APISIX/apisix-source/t/fuzzing/client_abort.py
Executable file
@@ -0,0 +1,74 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import http.client
|
||||
import subprocess
|
||||
import time
|
||||
import threading
|
||||
from public import check_leak, run_test
|
||||
import yaml
|
||||
|
||||
def get_admin_key_from_yaml(yaml_file_path):
|
||||
with open(yaml_file_path, 'r') as file:
|
||||
yaml_data = yaml.safe_load(file)
|
||||
try:
|
||||
admin_key = yaml_data['deployment']['admin']['admin_key'][0]['key']
|
||||
return admin_key
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def create_route():
|
||||
key = get_admin_key_from_yaml('conf/config.yaml')
|
||||
if key is None:
|
||||
print("Key not found in the YAML file.")
|
||||
return
|
||||
command = '''curl -i http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY:{key}" -X PUT -d '
|
||||
{
|
||||
"uri": "/client_abort",
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:6666": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
}
|
||||
}'
|
||||
'''
|
||||
subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||
|
||||
def req():
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port=9080)
|
||||
conn.request("GET", "/client_abort?seconds=0.01")
|
||||
time.sleep(0.001)
|
||||
conn.close()
|
||||
|
||||
def run_in_thread():
|
||||
for i in range(50):
|
||||
req()
|
||||
|
||||
@check_leak
|
||||
def run():
|
||||
th = [threading.Thread(target=run_in_thread) for i in range(10)]
|
||||
for t in th:
|
||||
t.start()
|
||||
for t in th:
|
||||
t.join()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_test(create_route,run)
|
100
CloudronPackages/APISIX/apisix-source/t/fuzzing/http_upstream.py
Executable file
100
CloudronPackages/APISIX/apisix-source/t/fuzzing/http_upstream.py
Executable file
@@ -0,0 +1,100 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# This file provides a fuzzing test with different upstreams
|
||||
import http.client
|
||||
import json
|
||||
import random
|
||||
import threading
|
||||
from public import check_leak, run_test, connect_admin
|
||||
import yaml
|
||||
|
||||
REQ_PER_THREAD = 50
|
||||
THREADS_NUM = 4
|
||||
TOTOL_ROUTES = 10
|
||||
|
||||
def get_admin_key_from_yaml(yaml_file_path):
|
||||
with open(yaml_file_path, 'r') as file:
|
||||
yaml_data = yaml.safe_load(file)
|
||||
try:
|
||||
admin_key = yaml_data['deployment']['admin']['admin_key'][0]['key']
|
||||
return admin_key
|
||||
except KeyError:
|
||||
return None
|
||||
def create_route():
|
||||
key = get_admin_key_from_yaml('conf/config.yaml')
|
||||
if key is None:
|
||||
print("Key not found in the YAML file.")
|
||||
return
|
||||
for i in range(TOTOL_ROUTES):
|
||||
conn = connect_admin()
|
||||
scheme = "http" if i % 2 == 0 else "https"
|
||||
port = ":6666" if i % 2 == 0 else ":6667"
|
||||
suffix = str(i + 1)
|
||||
i = str(i)
|
||||
conf = json.dumps({
|
||||
"uri": "/*",
|
||||
"host": "test" + i + ".com",
|
||||
"plugins": {
|
||||
},
|
||||
"upstream": {
|
||||
"scheme": scheme,
|
||||
"nodes": {
|
||||
"127.0.0." + suffix + port: 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
})
|
||||
|
||||
conn.request("PUT", "/apisix/admin/routes/" + i, conf,
|
||||
headers={
|
||||
"X-API-KEY":key,
|
||||
})
|
||||
response = conn.getresponse()
|
||||
assert response.status <= 300, response.read()
|
||||
|
||||
def req():
|
||||
route_id = random.randrange(TOTOL_ROUTES)
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port=9080)
|
||||
conn.request("GET", "/server_addr",
|
||||
headers={
|
||||
"Host":"test" + str(route_id) + ".com",
|
||||
})
|
||||
response = conn.getresponse()
|
||||
assert response.status == 200, response.read()
|
||||
ip = response.read().rstrip().decode()
|
||||
suffix = str(route_id + 1)
|
||||
assert "127.0.0." + suffix == ip, f"expect: 127.0.0.{suffix}, actual: {ip}"
|
||||
|
||||
def run_in_thread():
|
||||
for i in range(REQ_PER_THREAD):
|
||||
req()
|
||||
|
||||
@check_leak
|
||||
def run():
|
||||
th = [threading.Thread(target=run_in_thread) for i in range(THREADS_NUM)]
|
||||
for t in th:
|
||||
t.start()
|
||||
for t in th:
|
||||
t.join()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_test(create_route, run)
|
||||
|
138
CloudronPackages/APISIX/apisix-source/t/fuzzing/public.py
Normal file
138
CloudronPackages/APISIX/apisix-source/t/fuzzing/public.py
Normal file
@@ -0,0 +1,138 @@
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import http.client
|
||||
import subprocess
|
||||
import os
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
import psutil
|
||||
from boofuzz import FuzzLoggerText, Session, TCPSocketConnection, Target
|
||||
|
||||
def cur_dir():
|
||||
return os.path.split(os.path.realpath(__file__))[0]
|
||||
|
||||
def apisix_pwd():
|
||||
return os.environ.get("APISIX_FUZZING_PWD") or \
|
||||
(str(Path.home()) + "/work/apisix/apisix")
|
||||
|
||||
def connect_admin():
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port=9180)
|
||||
return conn
|
||||
|
||||
def check_log():
|
||||
boofuzz_log = cur_dir() + "/test.log"
|
||||
apisix_errorlog = apisix_pwd() + "/logs/error.log"
|
||||
apisix_accesslog = apisix_pwd() + "/logs/access.log"
|
||||
|
||||
cmds = ['cat %s | grep -a "error" | grep -v "invalid request body"'%apisix_errorlog, 'cat %s | grep -a " 500 "'%apisix_accesslog]
|
||||
if os.path.exists(boofuzz_log):
|
||||
cmds.append('cat %s | grep -a "fail"'%boofuzz_log)
|
||||
for cmd in cmds:
|
||||
r = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
|
||||
err = r.stdout.read().strip()
|
||||
print("Error in log: ", err)
|
||||
assert err == b""
|
||||
|
||||
def check_process():
|
||||
with open(apisix_pwd() + "/logs/nginx.pid") as f:
|
||||
pid = int(f.read().strip())
|
||||
parent = psutil.Process(pid)
|
||||
children = parent.children(recursive=True)
|
||||
process = {p.pid for p in children if "cache loader process" not in p.cmdline()[0]}
|
||||
process.add(parent.pid)
|
||||
return process
|
||||
|
||||
def initfuzz():
|
||||
fw = open(cur_dir() + "/test.log",'w')
|
||||
fuzz_loggers = [FuzzLoggerText(file_handle=fw)]
|
||||
session = Session(
|
||||
target=Target(
|
||||
connection=TCPSocketConnection("127.0.0.1", 9080, send_timeout=5.0, recv_timeout=5.0, server=False)
|
||||
),
|
||||
fuzz_loggers=fuzz_loggers,
|
||||
keep_web_open=False,
|
||||
)
|
||||
return session
|
||||
|
||||
def sum_memory():
|
||||
pmap = {}
|
||||
for p in check_process():
|
||||
proc = psutil.Process(p)
|
||||
pmap[proc] = proc.memory_full_info()
|
||||
return sum(m.rss for m in pmap.values())
|
||||
|
||||
def get_linear_regression_sloped(samples):
|
||||
n = len(samples)
|
||||
avg_x = (n + 1) / 2
|
||||
avg_y = sum(samples) / n
|
||||
avg_xy = sum([(i + 1) * v for i, v in enumerate(samples)]) / n
|
||||
avg_x2 = sum([i * i for i in range(1, n + 1)]) / n
|
||||
denom = avg_x2 - avg_x * avg_x
|
||||
if denom == 0:
|
||||
return None
|
||||
return (avg_xy - avg_x * avg_y) / denom
|
||||
|
||||
def gc():
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port=9090)
|
||||
conn.request("POST", "/v1/gc")
|
||||
conn.close()
|
||||
|
||||
def leak_count():
|
||||
return int(os.environ.get("APISIX_FUZZING_LEAK_COUNT") or 100)
|
||||
|
||||
LEAK_COUNT = leak_count()
|
||||
|
||||
def check_leak(f):
|
||||
@wraps(f)
|
||||
def wrapper(*args, **kwds):
|
||||
global LEAK_COUNT
|
||||
|
||||
samples = []
|
||||
for i in range(LEAK_COUNT):
|
||||
f(*args, **kwds)
|
||||
gc()
|
||||
samples.append(sum_memory())
|
||||
count = 0
|
||||
for i in range(1, LEAK_COUNT):
|
||||
if samples[i - 1] < samples[i]:
|
||||
count += 1
|
||||
print(samples)
|
||||
sloped = get_linear_regression_sloped(samples)
|
||||
print(sloped)
|
||||
print(count / LEAK_COUNT)
|
||||
|
||||
if os.environ.get("CI"): # CI is not stable
|
||||
return
|
||||
|
||||
# the threshold is chosen so that we can find leaking a table per request
|
||||
if sloped > 10000 and (count / LEAK_COUNT) > 0.2:
|
||||
raise AssertionError("memory leak")
|
||||
|
||||
return wrapper
|
||||
|
||||
def run_test(create_route, run):
|
||||
# before test
|
||||
create_route()
|
||||
r1 = check_process()
|
||||
run()
|
||||
# after test
|
||||
check_log()
|
||||
r2 = check_process()
|
||||
if r2 != r1:
|
||||
print("before test, nginx's process list:%s,\nafter test, nginx's process list:%s"%(r1,r2))
|
||||
raise AssertionError
|
@@ -0,0 +1,4 @@
|
||||
psutil==5.8.0
|
||||
typing==3.7.4.3
|
||||
boofuzz==0.4.0
|
||||
PyYAML==5.4.1
|
@@ -0,0 +1,106 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import subprocess
|
||||
from public import initfuzz, run_test
|
||||
from boofuzz import s_block, s_delim, s_get, s_group, s_initialize, s_size, s_static, s_string
|
||||
import yaml
|
||||
|
||||
def get_admin_key_from_yaml(yaml_file_path):
|
||||
with open(yaml_file_path, 'r') as file:
|
||||
yaml_data = yaml.safe_load(file)
|
||||
try:
|
||||
admin_key = yaml_data['deployment']['admin']['admin_key'][0]['key']
|
||||
return admin_key
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def create_route():
|
||||
key = get_admin_key_from_yaml('conf/config.yaml')
|
||||
if key is None:
|
||||
print("Key not found in the YAML file.")
|
||||
return
|
||||
command = '''curl -i http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: {key}" -X PUT -d '
|
||||
{
|
||||
"uri": "/post*",
|
||||
"methods": ["POST"],
|
||||
"plugins": {
|
||||
"serverless-post-function": {
|
||||
"functions": ["return function()\n local core = require(\"apisix.core\")\n ngx.req.read_body()\n local req_body = ngx.req.get_body_data()\n if req_body == \"{\\\"a\\\":\\\"b\\\"}\" then\n return\n else\n ngx.exit(ngx.HTTP_BAD_REQUEST)\n end\n end\n"]
|
||||
}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:6666": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
}
|
||||
}'
|
||||
'''
|
||||
subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||
|
||||
def run():
|
||||
session = initfuzz()
|
||||
|
||||
s_initialize(name="Request")
|
||||
with s_block("Request-Line"):
|
||||
s_group("Method", ["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PURGE"])
|
||||
s_delim(" ", name="space-1")
|
||||
s_string("/post", name="Request-URI")
|
||||
s_delim(" ", name="space-2")
|
||||
s_string("HTTP/1.1", name="HTTP-Version")
|
||||
s_static("\r\n", name="Request-Line-CRLF")
|
||||
s_string("Host:", name="Host-Line")
|
||||
s_delim(" ", name="space-3")
|
||||
s_string("127.0.0.1:9080", name="Host-Line-Value")
|
||||
s_static("\r\n", name="Host-Line-CRLF")
|
||||
s_static('User-Agent', name='User-Agent-Header')
|
||||
s_delim(':', name='User-Agent-Colon-1')
|
||||
s_delim(' ', name='User-Agent-Space-1')
|
||||
s_string('Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3223.8 Safari/537.36', name='User-Agent-Value')
|
||||
s_static('\r\n', name='User-Agent-CRLF'),
|
||||
s_static('Accept', name='Accept-Header')
|
||||
s_delim(':', name='Accept-Colon-1')
|
||||
s_delim(' ', name='Accept-Space-1')
|
||||
s_string('text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', name='Accept-Value')
|
||||
s_static('\r\n', name='Accept-CRLF')
|
||||
s_static("Content-Length:", name="Content-Length-Header")
|
||||
s_delim(" ", name="space-4")
|
||||
s_size("Body-Content", output_format="ascii", name="Content-Length-Value")
|
||||
s_static("\r\n", "Content-Length-CRLF")
|
||||
s_static('Connection', name='Connection-Header')
|
||||
s_delim(':', name='Connection-Colon-1')
|
||||
s_delim(' ', name='Connection-Space-1')
|
||||
s_group('Connection-Type', ['keep-alive', 'close'])
|
||||
s_static('\r\n', 'Connection-CRLF')
|
||||
s_static('Content-Type', name='Content-Type-Header')
|
||||
s_delim(':', name='Content-Type-Colon-1')
|
||||
s_delim(' ', name='Content-Type-Space-1')
|
||||
s_string('application/x-www-form-urlencoded', name='Content-Type-Value')
|
||||
s_static('\r\n', name='Content-Type-CRLF')
|
||||
s_static("\r\n", "Request-CRLF")
|
||||
|
||||
with s_block("Body-Content"):
|
||||
s_string('{"a":"b"}', name="Body-Content-Value")
|
||||
|
||||
session.connect(s_get("Request"))
|
||||
session.fuzz(max_depth=1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_test(create_route,run)
|
134
CloudronPackages/APISIX/apisix-source/t/fuzzing/simple_http.py
Executable file
134
CloudronPackages/APISIX/apisix-source/t/fuzzing/simple_http.py
Executable file
@@ -0,0 +1,134 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# This file provides a fuzzing test with most common plugins via plain HTTP request
|
||||
import http.client
|
||||
import json
|
||||
import random
|
||||
import threading
|
||||
from public import check_leak, LEAK_COUNT, run_test, connect_admin
|
||||
import yaml
|
||||
|
||||
REQ_PER_THREAD = 50
|
||||
THREADS_NUM = 10
|
||||
TOTOL_ROUTES = 50
|
||||
|
||||
def get_admin_key_from_yaml(yaml_file_path):
|
||||
with open(yaml_file_path, 'r') as file:
|
||||
yaml_data = yaml.safe_load(file)
|
||||
try:
|
||||
admin_key = yaml_data['deployment']['admin']['admin_key'][0]['key']
|
||||
return admin_key
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def create_route():
|
||||
conf = json.dumps({
|
||||
"username": "jack",
|
||||
"plugins": {
|
||||
"jwt-auth": {
|
||||
"key": "user-key",
|
||||
"secret": "my-secret-key"
|
||||
}
|
||||
}
|
||||
})
|
||||
conn = connect_admin()
|
||||
key = get_admin_key_from_yaml('conf/config.yaml')
|
||||
if key is None:
|
||||
print("Key not found in the YAML file.")
|
||||
return
|
||||
key = key.replace('"', '')
|
||||
print("the key is", key)
|
||||
headers = {
|
||||
"X-API-KEY": key,
|
||||
}
|
||||
print("Request headers:", headers)
|
||||
conn.request("PUT", "/apisix/admin/consumers", conf,
|
||||
headers=headers)
|
||||
response = conn.getresponse()
|
||||
assert response.status <= 300, response.read()
|
||||
|
||||
for i in range(TOTOL_ROUTES):
|
||||
conn = connect_admin()
|
||||
i = str(i)
|
||||
conf = json.dumps({
|
||||
"uri": "/*",
|
||||
"host": "test" + i + ".com",
|
||||
"plugins": {
|
||||
"limit-count": {
|
||||
"count": LEAK_COUNT * REQ_PER_THREAD * THREADS_NUM,
|
||||
"time_window": 3600,
|
||||
},
|
||||
"jwt-auth": {
|
||||
},
|
||||
"proxy-rewrite": {
|
||||
"uri": "/" + i,
|
||||
"headers": {
|
||||
"X-APISIX-Route": "apisix-" + i
|
||||
}
|
||||
},
|
||||
"response-rewrite": {
|
||||
"headers": {
|
||||
"X-APISIX-Route": "$http_x_apisix_route"
|
||||
}
|
||||
},
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:6666": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
})
|
||||
conn.request("PUT", "/apisix/admin/routes/" + i, conf,
|
||||
headers=headers)
|
||||
response = conn.getresponse()
|
||||
assert response.status <= 300, response.read()
|
||||
|
||||
def req():
|
||||
jwt_token = ("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9."+
|
||||
"eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0."+
|
||||
"fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs")
|
||||
route_id = str(random.randrange(TOTOL_ROUTES))
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port=9080)
|
||||
conn.request("GET", "/",
|
||||
headers={
|
||||
"Host":"test" + route_id + ".com",
|
||||
"Authorization":jwt_token,
|
||||
})
|
||||
response = conn.getresponse()
|
||||
assert response.status == 200, response.read()
|
||||
hdr = response.headers["X-APISIX-Route"]
|
||||
assert hdr == "apisix-" + route_id, hdr
|
||||
|
||||
def run_in_thread():
|
||||
for i in range(REQ_PER_THREAD):
|
||||
req()
|
||||
|
||||
@check_leak
|
||||
def run():
|
||||
th = [threading.Thread(target=run_in_thread) for i in range(THREADS_NUM)]
|
||||
for t in th:
|
||||
t.start()
|
||||
for t in th:
|
||||
t.join()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_test(create_route, run)
|
87
CloudronPackages/APISIX/apisix-source/t/fuzzing/simpleroute_test.py
Executable file
87
CloudronPackages/APISIX/apisix-source/t/fuzzing/simpleroute_test.py
Executable file
@@ -0,0 +1,87 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import subprocess
|
||||
from public import initfuzz, run_test
|
||||
from boofuzz import s_block, s_delim, s_get, s_group, s_initialize, s_static, s_string
|
||||
import yaml
|
||||
|
||||
|
||||
def get_admin_key_from_yaml(yaml_file_path):
|
||||
with open(yaml_file_path, 'r') as file:
|
||||
yaml_data = yaml.safe_load(file)
|
||||
try:
|
||||
admin_key = yaml_data['deployment']['admin']['admin_key'][0]['key']
|
||||
return admin_key
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
def create_route():
|
||||
key = get_admin_key_from_yaml('conf/config.yaml')
|
||||
if key is None:
|
||||
print("Key not found in the YAML file.")
|
||||
return
|
||||
# Construct curl command with the extracted key
|
||||
command = f'''curl http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: {key}" -X PUT -d '
|
||||
{{
|
||||
"uri": "/get*",
|
||||
"methods": ["GET"],
|
||||
"upstream": {{
|
||||
"type": "roundrobin",
|
||||
"nodes": {{
|
||||
"127.0.0.1:6666": 1
|
||||
}}
|
||||
}}
|
||||
}}'
|
||||
'''
|
||||
subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||
|
||||
def run():
|
||||
session = initfuzz()
|
||||
|
||||
s_initialize(name="Request")
|
||||
with s_block("Request-Line"):
|
||||
s_group("Method", ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', "PURGE"])
|
||||
s_delim(" ", name='space-1')
|
||||
s_string("/get", name='Request-URI')
|
||||
s_delim(" ", name='space-2')
|
||||
s_string('HTTP/1.1', name='HTTP-Version')
|
||||
s_static("\r\n", name="Request-Line-CRLF")
|
||||
s_string("Host:", name="Host-Line")
|
||||
s_delim(" ", name="space-3")
|
||||
s_string("example.com", name="Host-Line-Value")
|
||||
s_static("\r\n", name="Host-Line-CRLF")
|
||||
s_string("Connection:", name="Connection-Line")
|
||||
s_delim(" ", name="space-4")
|
||||
s_string("Keep-Alive", name="Connection-Line-Value")
|
||||
s_static("\r\n", name="Connection-Line-CRLF")
|
||||
s_string("User-Agent:", name="User-Agent-Line")
|
||||
s_delim(" ", name="space-5")
|
||||
s_string("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.83 Safari/537.1", name="User-Agent-Line-Value")
|
||||
s_static("\r\n", name="User-Agent-Line-CRLF")
|
||||
|
||||
s_static("\r\n", "Request-CRLF")
|
||||
session.connect(s_get("Request"))
|
||||
session.fuzz(max_depth=1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_test(create_route,run)
|
@@ -0,0 +1,75 @@
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
master_process on;
|
||||
worker_processes 1;
|
||||
worker_cpu_affinity auto;
|
||||
error_log logs/error.log error;
|
||||
pid logs/nginx.pid;
|
||||
worker_rlimit_nofile 20480;
|
||||
|
||||
events {
|
||||
accept_mutex off;
|
||||
worker_connections 10620;
|
||||
}
|
||||
|
||||
worker_shutdown_timeout 1;
|
||||
|
||||
http {
|
||||
lua_socket_log_errors off;
|
||||
|
||||
resolver ipv6=off local=on;
|
||||
|
||||
access_log off;
|
||||
server_tokens off;
|
||||
more_clear_headers Server;
|
||||
keepalive_requests 10000;
|
||||
tcp_nodelay on;
|
||||
|
||||
server {
|
||||
listen 6666 reuseport;
|
||||
location / {
|
||||
content_by_lua_block {
|
||||
ngx.say("cur time: ", ngx.time())
|
||||
}
|
||||
}
|
||||
|
||||
location /client_abort {
|
||||
content_by_lua_block {
|
||||
ngx.sleep(tonumber(ngx.var.arg_seconds or 1))
|
||||
}
|
||||
}
|
||||
|
||||
location /server_addr {
|
||||
content_by_lua_block {
|
||||
ngx.say(ngx.var.server_addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 6667 ssl;
|
||||
ssl_certificate ../../certs/apisix.crt;
|
||||
ssl_certificate_key ../../certs/apisix.key;
|
||||
|
||||
location /server_addr {
|
||||
content_by_lua_block {
|
||||
ngx.say(ngx.var.server_addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import subprocess
|
||||
from public import initfuzz, run_test
|
||||
from boofuzz import s_block, s_delim, s_get, s_group, s_initialize, s_static, s_string
|
||||
import yaml
|
||||
|
||||
def get_admin_key_from_yaml(yaml_file_path):
|
||||
with open(yaml_file_path, 'r') as file:
|
||||
yaml_data = yaml.safe_load(file)
|
||||
try:
|
||||
admin_key = yaml_data['deployment']['admin']['admin_key'][0]['key']
|
||||
return admin_key
|
||||
except KeyError:
|
||||
return None
|
||||
def create_route():
|
||||
key = get_admin_key_from_yaml('conf/config.yaml')
|
||||
if key is None:
|
||||
print("Key not found in the YAML file.")
|
||||
return
|
||||
command = '''curl -i http://127.0.0.1:9180/apisix/admin/routes/1 -H "X-API-KEY: {key}" -X PUT -d '
|
||||
{
|
||||
"uri": "/parameter*",
|
||||
"vars": [
|
||||
["arg_name","==","jack"],
|
||||
["http_token","==","140b543013d988f4767277b6f45ba542"]
|
||||
],
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:6666": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
}
|
||||
}'
|
||||
'''
|
||||
subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||
|
||||
def run():
|
||||
session = initfuzz()
|
||||
|
||||
s_initialize(name="Request")
|
||||
with s_block("Request-Line"):
|
||||
s_group("Method", ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PURGE'])
|
||||
s_delim(" ", name='space-1')
|
||||
s_string("/parameter?name=jack", name='Request-URI')
|
||||
s_delim(" ", name='space-2')
|
||||
s_string('HTTP/1.1', name='HTTP-Version')
|
||||
s_static("\r\n", name="Request-Line-CRLF")
|
||||
s_string("Host:", name="Host-Line")
|
||||
s_delim(" ", name="space-3")
|
||||
s_string("example.com", name="Host-Line-Value")
|
||||
s_static("\r\n", name="Host-Line-CRLF")
|
||||
s_string("Connection:", name="Connection-Line")
|
||||
s_delim(" ", name="space-4")
|
||||
s_string("Keep-Alive", name="Connection-Line-Value")
|
||||
s_static("\r\n", name="Connection-Line-CRLF")
|
||||
s_string("User-Agent:", name="User-Agent-Line")
|
||||
s_delim(" ", name="space-5")
|
||||
s_string("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.83 Safari/537.1", name="User-Agent-Line-Value")
|
||||
s_static("\r\n", name="User-Agent-Line-CRLF")
|
||||
s_string("token:", name="age-Line")
|
||||
s_delim(" ", name="space-6")
|
||||
s_string("140b543013d988f4767277b6f45ba542", name="age-Line-Value")
|
||||
s_static("\r\n", name="age-Line-CRLF")
|
||||
|
||||
s_static("\r\n", "Request-CRLF")
|
||||
session.connect(s_get("Request"))
|
||||
session.fuzz(max_depth=1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_test(create_route,run)
|
Reference in New Issue
Block a user