Merge pull request #1 from BBVA/server-as-subcommand
"kapow route" and "kapow server" in PoC
This commit is contained in:
@@ -3,6 +3,5 @@ RUN apk update && apk add bash curl coreutils file
|
|||||||
RUN pip install pipenv
|
RUN pip install pipenv
|
||||||
COPY Pipfile Pipfile.lock /tmp/
|
COPY Pipfile Pipfile.lock /tmp/
|
||||||
RUN cd /tmp && pipenv install --system --deploy
|
RUN cd /tmp && pipenv install --system --deploy
|
||||||
COPY kapow /usr/bin
|
|
||||||
COPY bin/* /usr/bin/
|
COPY bin/* /usr/bin/
|
||||||
ENTRYPOINT ["/usr/bin/kapow"]
|
ENTRYPOINT ["/usr/bin/kapow"]
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ import shlex
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from aiohttp import web, StreamReader
|
from aiohttp import web, StreamReader
|
||||||
|
import click
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger('kapow')
|
log = logging.getLogger('kapow')
|
||||||
|
|
||||||
@@ -281,22 +284,21 @@ async def delete_route(request):
|
|||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
|
|
||||||
async def run_init_script(app):
|
async def run_init_script(app, scripts):
|
||||||
"""
|
"""
|
||||||
Run the init script if given, then wait for the shell to finish.
|
Run the init script if given, then wait for the shell to finish.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if len(sys.argv) == 1:
|
if not scripts:
|
||||||
# No script given
|
# No script given
|
||||||
cmd = "/bin/bash"
|
cmd = "/bin/bash"
|
||||||
elif len(sys.argv) == 2:
|
|
||||||
cmd = f"/bin/bash --init-file {sys.argv[1]}"
|
|
||||||
else:
|
else:
|
||||||
print(f"Usage: {sys.argv[0]} <init-script>")
|
filenames = " ".join(shlex.quote(f) for f in scripts)
|
||||||
os._exit(1)
|
cmd = f"/bin/bash --init-file <(cat {filenames})"
|
||||||
|
|
||||||
shell_task = await asyncio.create_subprocess_shell(
|
shell_task = await asyncio.create_subprocess_shell(
|
||||||
cmd,
|
cmd,
|
||||||
|
executable="/bin/bash",
|
||||||
env={**os.environ,
|
env={**os.environ,
|
||||||
"KAPOW_URL": "http://localhost:8080"
|
"KAPOW_URL": "http://localhost:8080"
|
||||||
})
|
})
|
||||||
@@ -308,11 +310,10 @@ async def run_init_script(app):
|
|||||||
|
|
||||||
async def start_background_tasks(app):
|
async def start_background_tasks(app):
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
app["debug_tasks"] = loop.create_task(run_init_script(app))
|
app["debug_tasks"] = loop.create_task(run_init_script(app, app["scripts"]))
|
||||||
|
|
||||||
|
|
||||||
def kapow():
|
def start_kapow_server(scripts):
|
||||||
"""Start aiohttp app."""
|
|
||||||
app = web.Application(client_max_size=1024**3)
|
app = web.Application(client_max_size=1024**3)
|
||||||
app.add_routes([
|
app.add_routes([
|
||||||
# Control API
|
# Control API
|
||||||
@@ -325,9 +326,72 @@ def kapow():
|
|||||||
web.get('/handlers/{id}/{field:.*}', get_field),
|
web.get('/handlers/{id}/{field:.*}', get_field),
|
||||||
web.put('/handlers/{id}/{field:.*}', set_field),
|
web.put('/handlers/{id}/{field:.*}', set_field),
|
||||||
])
|
])
|
||||||
|
app["scripts"] = scripts
|
||||||
app.on_startup.append(start_background_tasks)
|
app.on_startup.append(start_background_tasks)
|
||||||
web.run_app(app)
|
web.run_app(app)
|
||||||
|
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Command Lineagement #
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
@click.pass_context
|
||||||
|
def kapow(ctx):
|
||||||
|
"""Start aiohttp app."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@kapow.command()
|
||||||
|
@click.argument("scripts", nargs=-1)
|
||||||
|
def server(scripts):
|
||||||
|
start_kapow_server(scripts)
|
||||||
|
|
||||||
|
@kapow.group()
|
||||||
|
def route():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@route.command()
|
||||||
|
@click.option("-c", "--command", nargs=1)
|
||||||
|
@click.option("-e", "--entrypoint", default="/bin/sh -c")
|
||||||
|
@click.option("-X", "--method", default="GET")
|
||||||
|
@click.option("--url", envvar='KAPOW_URL')
|
||||||
|
@click.argument("url_pattern", nargs=1)
|
||||||
|
@click.argument("command_file", required=False)
|
||||||
|
def add(url_pattern, entrypoint, command, method, url, command_file):
|
||||||
|
if command:
|
||||||
|
# Command is given inline
|
||||||
|
source = command
|
||||||
|
elif command_file is None:
|
||||||
|
# No command
|
||||||
|
source = ""
|
||||||
|
elif command_file == '-':
|
||||||
|
# Read commands from stdin
|
||||||
|
source = sys.stdin.read()
|
||||||
|
else:
|
||||||
|
# Read commands from a file
|
||||||
|
with open(command_file, 'r', encoding='utf-8') as handler:
|
||||||
|
source = handler.read()
|
||||||
|
|
||||||
|
response = requests.post(f"{url}/routes",
|
||||||
|
json={"method": method,
|
||||||
|
"url_pattern": url_pattern,
|
||||||
|
"entrypoint": entrypoint,
|
||||||
|
"command": source})
|
||||||
|
response.raise_for_status()
|
||||||
|
print(response.json())
|
||||||
|
|
||||||
|
|
||||||
|
@route.command()
|
||||||
|
@click.option("--url", envvar='KAPOW_URL')
|
||||||
|
@click.argument("route-id")
|
||||||
|
def remove(route_id, url):
|
||||||
|
response = requests.delete(f"{url}/routes/{route_id}")
|
||||||
|
response.raise_for_status()
|
||||||
|
print(response.json())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
kapow()
|
kapow()
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copyright 2019 Banco Bilbao Vizcaya Argentaria, S.A.
|
|
||||||
#
|
|
||||||
# Licensed 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 sys
|
|
||||||
|
|
||||||
import click
|
|
||||||
import requests
|
|
||||||
|
|
||||||
@click.group()
|
|
||||||
def kroute():
|
|
||||||
pass
|
|
||||||
|
|
||||||
@kroute.command()
|
|
||||||
@click.option("-c", "--command", nargs=1)
|
|
||||||
@click.option("-e", "--entrypoint", default="/bin/sh -c")
|
|
||||||
@click.option("-X", "--method", default="GET")
|
|
||||||
@click.option("--url", envvar='KAPOW_URL')
|
|
||||||
@click.argument("url_pattern", nargs=1)
|
|
||||||
@click.argument("command_file", required=False)
|
|
||||||
def add(url_pattern, entrypoint, command, method, url, command_file):
|
|
||||||
if command:
|
|
||||||
# Command is given inline
|
|
||||||
source = command
|
|
||||||
elif command_file is None:
|
|
||||||
# No command
|
|
||||||
source = ""
|
|
||||||
elif command_file == '-':
|
|
||||||
# Read commands from stdin
|
|
||||||
source = sys.stdin.read()
|
|
||||||
else:
|
|
||||||
# Read commands from a file
|
|
||||||
with open(command_file, 'r', encoding='utf-8') as handler:
|
|
||||||
source = handler.read()
|
|
||||||
|
|
||||||
response = requests.post(f"{url}/routes",
|
|
||||||
json={"method": method,
|
|
||||||
"url_pattern": url_pattern,
|
|
||||||
"entrypoint": entrypoint,
|
|
||||||
"command": source})
|
|
||||||
response.raise_for_status()
|
|
||||||
print(response.json())
|
|
||||||
|
|
||||||
|
|
||||||
@kroute.command()
|
|
||||||
@click.option("--url", envvar='KAPOW_URL')
|
|
||||||
@click.argument("route-id")
|
|
||||||
def remove(route_id, url):
|
|
||||||
response = requests.delete(f"{url}/routes/{route_id}")
|
|
||||||
response.raise_for_status()
|
|
||||||
print(response.json())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
kroute()
|
|
||||||
@@ -16,4 +16,4 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
kroute add -X POST '/eval' -c '$($(request /body) | response /stream)'
|
kapow route add -X POST '/eval' -c '$($(request /body) | response /stream)'
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ RUN apk update && apk add nmap
|
|||||||
COPY nmap.pow /tmp/
|
COPY nmap.pow /tmp/
|
||||||
RUN cd /tmp && pipenv install --system --deploy
|
RUN cd /tmp && pipenv install --system --deploy
|
||||||
ENTRYPOINT ["/usr/bin/kapow"]
|
ENTRYPOINT ["/usr/bin/kapow"]
|
||||||
CMD ["/tmp/nmap.pow"]
|
CMD ["server", "/tmp/nmap.pow"]
|
||||||
|
|||||||
@@ -16,4 +16,4 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
kroute add -X GET '/list/{ip}' -c 'nmap -sL $(request /matches/ip) | response /body'
|
kapow route add -X GET '/list/{ip}' -c 'nmap -sL $(request /matches/ip) | response /body'
|
||||||
|
|||||||
@@ -16,26 +16,26 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
kroute add /list/files -c 'ls -la $(request /params/path) | response /body'
|
kapow route add /list/files -c 'ls -la $(request /params/path) | response /body'
|
||||||
|
|
||||||
kroute add /list/processes -c 'ps -aux | response /body'
|
kapow route add /list/processes -c 'ps -aux | response /body'
|
||||||
|
|
||||||
kroute add /show/cpuinfo -c 'response /body < /proc/cpuinfo'
|
kapow route add /show/cpuinfo -c 'response /body < /proc/cpuinfo'
|
||||||
|
|
||||||
kroute add /show/memory -c 'free -m | response /body'
|
kapow route add /show/memory -c 'free -m | response /body'
|
||||||
|
|
||||||
kroute add /show/disk -c 'df -h | response /body'
|
kapow route add /show/disk -c 'df -h | response /body'
|
||||||
|
|
||||||
kroute add /show/connections -c 'ss -pluton | response /body'
|
kapow route add /show/connections -c 'ss -pluton | response /body'
|
||||||
|
|
||||||
kroute add /show/mounts -c 'mount | response /body'
|
kapow route add /show/mounts -c 'mount | response /body'
|
||||||
|
|
||||||
kroute add /tail/dmesg - <<-'EOF'
|
kapow route add /tail/dmesg - <<-'EOF'
|
||||||
response /headers/Content-Type text/plain
|
response /headers/Content-Type text/plain
|
||||||
dmesg -w | response /stream
|
dmesg -w | response /stream
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
kroute add /tail/journal - <<-'EOF'
|
kapow route add /tail/journal - <<-'EOF'
|
||||||
response /headers/Content-Type text/plain
|
response /headers/Content-Type text/plain
|
||||||
journalctl -f | response /stream
|
journalctl -f | response /stream
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -16,11 +16,11 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
kroute add -X POST --entrypoint '/bin/zsh -c' '/convert/{from}/{to}' - <<-'EOF'
|
kapow route add -X POST --entrypoint '/bin/zsh -c' '/convert/{from}/{to}' - <<-'EOF'
|
||||||
pandoc --from=$(request /matches/from) \
|
pandoc --from=$(request /matches/from) \
|
||||||
--to=$(request /matches/to) \
|
--to=$(request /matches/to) \
|
||||||
--output=>(response /body) \
|
--output=>(response /body) \
|
||||||
=(request /body)
|
=(request /body)
|
||||||
EOF
|
EOF
|
||||||
kroute add -X GET '/formats/input' -c 'pandoc --list-input-formats | response /body'
|
kapow route add -X GET '/formats/input' -c 'pandoc --list-input-formats | response /body'
|
||||||
kroute add -X GET '/formats/output' -c 'pandoc --list-output-formats | grep -v pdf | response /body'
|
kapow route add -X GET '/formats/output' -c 'pandoc --list-output-formats | grep -v pdf | response /body'
|
||||||
|
|||||||
@@ -16,5 +16,5 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
kroute add -X POST --entrypoint ./topdf '/editor/pdf'
|
kapow route add -X POST --entrypoint ./topdf '/editor/pdf'
|
||||||
kroute add / -c 'response /headers/Content-Type text/html && response /body < pdfeditor.html'
|
kapow route add / -c 'response /headers/Content-Type text/html && response /body < pdfeditor.html'
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
kroute add / - <<-'EOF'
|
kapow route add / - <<-'EOF'
|
||||||
response /headers/Content-Type text/html
|
response /headers/Content-Type text/html
|
||||||
response /body <<-HTML
|
response /body <<-HTML
|
||||||
<html>
|
<html>
|
||||||
@@ -27,7 +27,7 @@ kroute add / - <<-'EOF'
|
|||||||
HTML
|
HTML
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
kroute add /save/magnet -e '/bin/bash -c' - <<-'EOF'
|
kapow route add /save/magnet -e '/bin/bash -c' - <<-'EOF'
|
||||||
link=$(request /params/link)
|
link=$(request /params/link)
|
||||||
[ -z $link ] && response /status 400 && exit 0
|
[ -z $link ] && response /status 400 && exit 0
|
||||||
|
|
||||||
@@ -40,4 +40,4 @@ kroute add /save/magnet -e '/bin/bash -c' - <<-'EOF'
|
|||||||
response /headers/Location /torrent/list
|
response /headers/Location /torrent/list
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
kroute add /torrent/list -c 'response /body "Not Implemented Yet"'
|
kapow route add /torrent/list -c 'response /body "Not Implemented Yet"'
|
||||||
|
|||||||
+1
-1
@@ -537,7 +537,7 @@ You can run it by ...
|
|||||||
Any compliant implementation of Kapow! must provide these commands:
|
Any compliant implementation of Kapow! must provide these commands:
|
||||||
|
|
||||||
|
|
||||||
### `kapow`
|
### `kapow server`
|
||||||
|
|
||||||
This is the master command, that shows the help if invoked without args, and
|
This is the master command, that shows the help if invoked without args, and
|
||||||
runs the sub-commands when provided to it.
|
runs the sub-commands when provided to it.
|
||||||
|
|||||||
Reference in New Issue
Block a user