Merge branch 'feature/exec-pow-files'
Closes: #95 and two stones Co-authored-by: Roberto Abdelkader Martínez Pérez <robertomartinezp@gmail.com>
This commit is contained in:
@@ -48,11 +48,5 @@ acceptance: install
|
||||
deps:
|
||||
@echo "deps here"
|
||||
|
||||
docker: build
|
||||
cp $(BUILD_DIR)/$(BINARY_NAME) $(DOCKER_DIR)/
|
||||
cp $(DOCS_DIR)/*.pow $(DOCKER_DIR)/
|
||||
cd $(DOCKER_DIR) && docker build -t kapow .
|
||||
cd ..
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR) $(OUTPUT_DIR) $(DOCKER_DIR)/*
|
||||
|
||||
@@ -21,19 +21,22 @@ us easily **turn that into an HTTP API**.
|
||||
### Let's see this with an example
|
||||
|
||||
We want to expose **log entries** for files not found on our **Apache Web
|
||||
Server**, as an HTTP API. With *Kapow!* we just need to write this file:
|
||||
Server**, as an HTTP API. With *Kapow!* we just need to write this
|
||||
*executable* script:
|
||||
|
||||
```bash
|
||||
[apache-host]$ cat search-apache-errors.pow
|
||||
``` console
|
||||
[apache-host]$ cat search-apache-errors
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /apache-errors - <<-'EOF'
|
||||
cat /var/log/apache2/access.log | grep 'File does not exist' | kapow set /response/body
|
||||
cat /var/log/apache2/access.log | grep 'File does not exist' | kapow set /response/body
|
||||
EOF
|
||||
[apache-host]$ chmod +x search-apache-errors
|
||||
```
|
||||
|
||||
and then, run it using *Kapow!*
|
||||
|
||||
```bash
|
||||
[apache-host]$ kapow server --bind 0.0.0.0:8080 search-apache-errors.pow
|
||||
[apache-host]$ kapow server --bind 0.0.0.0:8080 search-apache-errors
|
||||
```
|
||||
|
||||
finally, we can read from the just-defined endpoint:
|
||||
|
||||
@@ -11,7 +11,8 @@ In this example we'll be adding the header ``X-Content-Type-Options`` to the res
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat sniff.pow
|
||||
$ cat sniff-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /sec-hello-world - <<-'EOF'
|
||||
kapow set /response/headers/X-Content-Type-Options nosniff
|
||||
kapow set /response/headers/Content-Type text/plain
|
||||
@@ -19,7 +20,7 @@ In this example we'll be adding the header ``X-Content-Type-Options`` to the res
|
||||
echo this will be interpreted as plain text | kapow set /response/body
|
||||
EOF
|
||||
|
||||
$ kapow server nosniff.pow
|
||||
$ kapow server nosniff-route
|
||||
|
||||
Testing with :program:`curl`:
|
||||
|
||||
@@ -67,7 +68,8 @@ Uploading a file using *Kapow!* is very simple:
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat upload.pow
|
||||
$ cat upload-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add -X POST /upload-file - <<-'EOF'
|
||||
kapow get /request/files/data/content | kapow set /response/body
|
||||
EOF
|
||||
@@ -89,7 +91,8 @@ In this example we reply the line count of the file received in the request:
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat count-file-lines.pow
|
||||
$ cat count-file-lines
|
||||
#!/usr/bin/env sh
|
||||
kapow route add -X POST /count-file-lines - <<-'EOF'
|
||||
|
||||
# Get sent file
|
||||
@@ -121,7 +124,8 @@ You can specify custom status code for `HTTP` response:
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat error.pow
|
||||
$ cat error-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /error - <<-'EOF'
|
||||
kapow set /response/status 401
|
||||
echo -n '401 error' | kapow set /response/body
|
||||
@@ -158,7 +162,8 @@ In this example we'll redirect our users to `Google`:
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat redirect.pow
|
||||
$ cat redirect
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /redirect - <<-'EOF'
|
||||
kapow set /response/headers/Location https://google.com
|
||||
kapow set /response/status 301
|
||||
@@ -196,7 +201,8 @@ In the next example we'll set a cookie:
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat cookie.pow
|
||||
$ cat cookie
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /setcookie - <<-'EOF'
|
||||
CURRENT_STATUS=$(kapow get /request/cookies/kapow-status)
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ command line:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server --keyfile path/to/keyfile --certfile path/to/certfile foobar.pow
|
||||
$ kapow server --keyfile path/to/keyfile --certfile path/to/certfile foobar-route
|
||||
|
||||
Now *Kapow!* is listening on its default port (8080) accepting requests over
|
||||
HTTPS. You can test it with the following command:
|
||||
@@ -60,7 +60,7 @@ CA certificate issuing the client certificates we want to accept with the
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server --keyfile path/to/keyfile --certfile path/to/certfile --clientauth=true --clientcafile path/to/clientCAfile foobar.pow
|
||||
$ kapow server --keyfile path/to/keyfile --certfile path/to/certfile --clientauth=true --clientcafile path/to/clientCAfile foobar-route
|
||||
|
||||
With this configuration *Kapow!* will reject connections that do not present a
|
||||
client certificate or one certificate not issued by the specified CA. You can
|
||||
|
||||
@@ -10,7 +10,8 @@ from query params:
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat parallel.pow
|
||||
$ cat parallel-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add '/parallel/{ip1}/{ip2}' - <<-'EOF'
|
||||
ping -c 1 -- "$(kapow get /request/matches/ip1)" | kapow set /response/body &
|
||||
ping -c 1 -- "$(kapow get /request/matches/ip2)" | kapow set /response/body &
|
||||
@@ -30,5 +31,6 @@ Script debugging
|
||||
Bash provides the ``set -x`` builtin command that "After expanding each simple command,
|
||||
for command, case command, select command, or arithmetic for command, display the
|
||||
expanded value of PS4, followed by the command and its expanded arguments or associated
|
||||
word list". This feature can be used to help debugging the `.pow` scripts and, together
|
||||
the ``--debug`` option in the server sub-command, the scripts executed in user requests.
|
||||
word list". This feature can be used to help debugging the init programs and,
|
||||
together the ``--debug`` option in the server sub-command, the scripts executed
|
||||
in user requests.
|
||||
|
||||
@@ -3,7 +3,7 @@ Examples
|
||||
|
||||
.. toctree::
|
||||
|
||||
working_with_pow_files
|
||||
working_with_init_programs
|
||||
managing_routes
|
||||
handling_http_requests
|
||||
using_json
|
||||
|
||||
@@ -16,12 +16,13 @@ Example #1
|
||||
++++++++++
|
||||
|
||||
In this example our *Kapow!* service will receive a `JSON` value with an incorrect
|
||||
date, then our ``pow`` file will fix it and return the correct value to the user.
|
||||
date, then our init program will fix it and return the correct value to the user.
|
||||
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat fix_date.pow
|
||||
$ cat fix_date
|
||||
#!/usr/bin/env sh
|
||||
kapow route add -X POST /fix-date - <<-'EOF'
|
||||
kapow set /response/headers/Content-Type application/json
|
||||
kapow get /request/body | jq --arg newdate "$(date +'%Y-%m-%d_%H-%M-%S')" '.incorrectDate=$newdate' | kapow set /response/body
|
||||
@@ -46,7 +47,8 @@ order to generate a two-attribute `JSON` response.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat echo-attribute.pow
|
||||
$ cat echo-attribute
|
||||
#!/usr/bin/env sh
|
||||
kapow route add -X POST /echo-attribute - <<-'EOF'
|
||||
JSON_WHO=$(kapow get /request/body | jq -r .name)
|
||||
|
||||
|
||||
+35
-45
@@ -1,27 +1,28 @@
|
||||
Working with pow Files
|
||||
======================
|
||||
Working with Init Scripts
|
||||
=========================
|
||||
|
||||
Starting *Kapow!* using a pow file
|
||||
----------------------------------
|
||||
Starting *Kapow!* using an init script
|
||||
--------------------------------------
|
||||
|
||||
A :file:`pow` file is just a :command:`bash` script, where you make calls to the
|
||||
``kapow route`` command.
|
||||
An init program, which can be just a shell script, allows you to make calls to
|
||||
the ``kapow route`` command.
|
||||
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ kapow server example.pow
|
||||
$ kapow server example-init-program
|
||||
|
||||
With the :file:`example.pow`:
|
||||
With the :file:`example-init-program`:
|
||||
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat example.pow
|
||||
$ cat example-init-program
|
||||
#!/usr/bin/env sh
|
||||
#
|
||||
# This is a simple example of a pow file
|
||||
# This is a simple example of an init program
|
||||
#
|
||||
echo '[*] Starting my script'
|
||||
echo '[*] Starting my init program'
|
||||
|
||||
# We add 2 Kapow! routes
|
||||
kapow route add /my/route -c 'echo hello world | kapow set /response/body'
|
||||
@@ -29,32 +30,19 @@ With the :file:`example.pow`:
|
||||
|
||||
.. note::
|
||||
|
||||
*Kapow!* can be fully configured using just :file:`pow` files
|
||||
*Kapow!* can be fully configured using just init scripts
|
||||
|
||||
|
||||
Load More Than One pow File
|
||||
---------------------------
|
||||
Writing Multiline Routes
|
||||
------------------------
|
||||
|
||||
You can load more than one :file:`pow` file at time. This can help you keep
|
||||
your :file:`pow` files tidy.
|
||||
If you need to write more complex actions, you can leverage multiline routes:
|
||||
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ ls pow-files/
|
||||
example-1.pow example-2.pow
|
||||
$ kapow server <(cat pow-files/*.pow)
|
||||
|
||||
|
||||
Writing Multiline pow Files
|
||||
---------------------------
|
||||
|
||||
If you need to write more complex actions, you can leverage multiline commands:
|
||||
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat multiline.pow
|
||||
$ cat multiline-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /log_and_stuff - <<-'EOF'
|
||||
echo this is a quite long sentence and other stuff | tee log.txt | kapow set /response/body
|
||||
cat log.txt | kapow set /response/body
|
||||
@@ -77,43 +65,45 @@ Keeping Things Tidy
|
||||
Sometimes things grow, and keeping things tidy is the only way to mantain the
|
||||
whole thing.
|
||||
|
||||
You can distribute your endpoints in several pow files. And you can keep the
|
||||
whole thing documented in one html file, served with *Kapow!*.
|
||||
You can distribute your endpoints in several init programs. And you can keep
|
||||
the whole thing documented in one html file, served with *Kapow!*.
|
||||
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat index.pow
|
||||
$ cat index-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add / - <<-'EOF'
|
||||
cat howto.html | kapow set /response/body
|
||||
EOF
|
||||
|
||||
source ./info_stuff.pow
|
||||
source ./other_endpoints.pow
|
||||
source ./info_stuff
|
||||
source ./other_endpoints
|
||||
|
||||
As you can see, the `pow` files can be imported into another `pow` file using
|
||||
source. In fact, a `pow` file is just a regular shell script.
|
||||
You can import other shell script libraries with `source`.
|
||||
|
||||
Debugging scripts
|
||||
-----------------
|
||||
|
||||
Since *Kapow!* redirects the standard output and the standard error of the `pow`
|
||||
file given on server startup to its own, you can leverage ``set -x`` to see the
|
||||
commands that are being executed, and use that for debugging.
|
||||
Debugging Init Programs/Scripts
|
||||
-------------------------------
|
||||
|
||||
Since *Kapow!* redirects the standard output and the standard error of the init
|
||||
program given on server startup to its own, you can leverage ``set -x`` to see
|
||||
the commands that are being executed, and use that for debugging.
|
||||
|
||||
To support debugging user request executions, the server subcommand has a
|
||||
``--debug`` option flag that prompts *Kapow!* to redirect both the script's
|
||||
standard output and standard error to *Kapow!*'s standard output, so you can
|
||||
leverage ``set -x`` the same way as with `pow` files.
|
||||
leverage ``set -x`` the same way as with init programs.
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat withdebug.pow
|
||||
$ cat withdebug-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add / - <<-'EOF'
|
||||
set -x
|
||||
echo "This will be seen in the log"
|
||||
echo "Hi HTTP" | kapow set /response/body
|
||||
EOF
|
||||
|
||||
$ kapow server --debug withdebug.pow
|
||||
$ kapow server --debug withdebug-route
|
||||
@@ -75,7 +75,7 @@ Contents
|
||||
:maxdepth: 2
|
||||
:caption: Usage Examples
|
||||
|
||||
examples/working_with_pow_files
|
||||
examples/working_with_init_programs
|
||||
examples/managing_routes
|
||||
examples/handling_http_requests
|
||||
examples/using_json
|
||||
|
||||
@@ -111,7 +111,7 @@ After building the image you can run the container with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ docker run --rm -i -p 8080:8080 -v $(pwd)/whatever.pow:/opt/whatever.pow kapow:latest server /opt/whatever.pow
|
||||
$ docker run --rm -i -p 8080:8080 -v $(pwd)/whatever-route:/opt/whatever-route kapow:latest server /opt/whatever-route
|
||||
|
||||
With the ``-v`` parameter we map a local file into the container's filesystem so
|
||||
we can use it to configure our *Kapow!* server on startup.
|
||||
|
||||
@@ -116,10 +116,10 @@ Install *Kapow!*
|
||||
Follow the :ref:`installation instructions <installation>`.
|
||||
|
||||
|
||||
Write a :file:`ping.pow` File
|
||||
+++++++++++++++++++++++++++++
|
||||
Write an Init Program :file:`ping-route`
|
||||
+++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
*Kapow!* uses plain text files (called `pow` files) where the endpoints you want
|
||||
*Kapow!* uses init programs/scripts where the endpoints you want
|
||||
to expose are defined.
|
||||
|
||||
For each endpoint, you can decide which commands get executed.
|
||||
@@ -128,11 +128,15 @@ For our example we need a file like this:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat ping.pow
|
||||
$ chmod +x ping-route
|
||||
$ cat ping-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /ping -c 'ping -c 1 10.10.10.100 | kapow set /response/body'
|
||||
|
||||
Let's dissect this beast piece by piece:
|
||||
|
||||
#. ``#!/usr/bin/env sh`` - shebang line so that the kernel knows which
|
||||
interpreter to use
|
||||
#. ``kapow route add /ping`` - adds a new `HTTP API` endpoint at ``/ping``
|
||||
path in the *Kapow!* server. You have to use the ``GET`` method to invoke
|
||||
the endpoint.
|
||||
@@ -147,18 +151,19 @@ Let's dissect this beast piece by piece:
|
||||
Launch the Service
|
||||
++++++++++++++++++
|
||||
|
||||
At this point, we only need to launch :program:`kapow` with our :file:`ping.pow`:
|
||||
At this point, we only need to launch :program:`kapow` with our
|
||||
:file:`ping-route`:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server ping.pow
|
||||
$ kapow server ping-route
|
||||
|
||||
*Kapow!* can expose the user interface through HTTPS, to do this provide the
|
||||
corresponding key and certificates chain paths at startup:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server --keyfile path/to/keyfile --certfile path/to/certfile ping.pow
|
||||
$ kapow server --keyfile path/to/keyfile --certfile path/to/certfile ping-route
|
||||
|
||||
|
||||
Consume the Service
|
||||
|
||||
@@ -28,7 +28,8 @@ In this example, an attacker can inject arbitrary parameters to :command:`ls`.
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat command-injection.pow
|
||||
$ cat command-injection
|
||||
#!/usr/bin/env sh
|
||||
kapow route add '/vulnerable/{value}' - <<-'EOF'
|
||||
ls $(kapow get /request/matches/value) | kapow set /response/body
|
||||
EOF
|
||||
@@ -48,7 +49,8 @@ request:
|
||||
.. code-block:: console
|
||||
:linenos:
|
||||
|
||||
$ cat command-injection.pow
|
||||
$ cat command-injection
|
||||
#!/usr/bin/env sh
|
||||
kapow route add '/not-vulnerable/{value}' - <<-'EOF'
|
||||
ls -- "$(kapow get /request/matches/value)" | kapow set /response/body
|
||||
EOF
|
||||
|
||||
@@ -138,22 +138,22 @@ Let's Backup that Database!
|
||||
**Senior**
|
||||
|
||||
Not at all. The creators of *Kapow!* have thought of everything. You can put
|
||||
all your route definitions in a special script file and pass it to the server
|
||||
on startup. They call those files :file:`pow` files and they have
|
||||
:file:`.pow` extension.
|
||||
all your route definitions on init programs, which can be shell scripts, and
|
||||
pass them to the server on startup.
|
||||
|
||||
It should look something like:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat backup.pow
|
||||
$ cat backup-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add -X PUT /db/backup -e ./backup_db.sh
|
||||
|
||||
And then you can start *Kapow!* with it:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server backup.pow
|
||||
$ kapow server backup-route
|
||||
|
||||
**Junior**
|
||||
|
||||
@@ -161,10 +161,10 @@ Let's Backup that Database!
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server backup.pow
|
||||
2019/11/26 11:40:01 Running powfile: "backup.pow"
|
||||
$ kapow server backup-route
|
||||
2019/11/26 11:40:01 Running init program: "backup-route"
|
||||
{"id":"19bb4ac7-1039-11ea-aa00-106530610c4d","method":"PUT","url_pattern":"/db/backup","entrypoint":"./backup_db.sh","command":"","index":0}
|
||||
2019/11/26 11:40:01 Done running powfile: "backup.pow"
|
||||
2019/11/26 11:40:01 Done running init program: "backup-route"
|
||||
|
||||
I understand that this is proof that we have the endpoint available.
|
||||
|
||||
|
||||
@@ -37,10 +37,11 @@ What have we done?
|
||||
|
||||
**Junior**
|
||||
|
||||
Let me try add this to our :file:`pow` file:
|
||||
Let me try add this to our init program:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
#!/usr/bin/env sh
|
||||
kapow route add /db/backup_logs -c 'cat /tmp/backup_db.log | kapow set /response/body'
|
||||
|
||||
**Senior**
|
||||
|
||||
@@ -44,7 +44,9 @@ Securing the server
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server --keyfile /etc/kapow/tls/keyfile --certfile /etc/kapow/tls/certfile /etc/kapow/awesome.pow
|
||||
$ kapow server --keyfile /etc/kapow/tls/keyfile \
|
||||
--certfile /etc/kapow/tls/certfile \
|
||||
/etc/kapow/awesome-route
|
||||
|
||||
It's easy, please copy the private key file and certificate chain to `/etc/kapow/tls` and we can restart.
|
||||
|
||||
@@ -70,7 +72,11 @@ Securing the server
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ kapow server --keyfile /etc/kapow/tls/keyfile --certfile /etc/kapow/tls/certfile --clientauth=true --clientcafile /etc/kapow/tls/clientCAfile /etc/kapow/awesome.pow
|
||||
$ kapow server --keyfile /etc/kapow/tls/keyfile \
|
||||
--certfile /etc/kapow/tls/certfile \
|
||||
--clientauth true \
|
||||
--clientcafile /etc/kapow/tls/clientCAfile \
|
||||
/etc/kapow/awesome-route
|
||||
|
||||
Done!
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ A small web gui for [pandoc](https://pandoc.org) that allows to convert between
|
||||
## How to run it
|
||||
|
||||
```
|
||||
$ kapow server DocumentConverter.pow
|
||||
$ kapow server DocumentConverter
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Run a long network scan in background with support for webhook on completion.
|
||||
## How to run it
|
||||
|
||||
```
|
||||
$ kapow server NetworkScanner.pow
|
||||
$ kapow server NetworkScanner
|
||||
```
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env sh
|
||||
|
||||
kapow route add /sniff -c 'tcpdump -i any -U -s0 -w - "not portrange 8080-8082" | kapow set /response/body'
|
||||
@@ -8,7 +8,7 @@ Provides an HTTP service that allows the user to sniff the network in real time.
|
||||
For the sake of simplicity, run:
|
||||
|
||||
```
|
||||
$ sudo -E kapow server NetworkSniffer.pow
|
||||
$ sudo -E kapow server NetworkSniffer
|
||||
```
|
||||
|
||||
In a production environment, tcpdump should be run with the appropiate
|
||||
|
||||
@@ -6,7 +6,7 @@ A simple "Hello World!" type example.
|
||||
## How to run it
|
||||
|
||||
```
|
||||
$ kapow server HelloWorld.pow
|
||||
$ kapow server HelloWorld
|
||||
```
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/usr/bin/env sh
|
||||
|
||||
kapow route add /apache-errors - <<-'EOF'
|
||||
cat /var/log/apache2/access.log | grep 'File does not exist' | kapow set /response/body
|
||||
@@ -5,7 +5,7 @@ A simple service that exposes log entries for files not found on our Apache Web
|
||||
## How to run it
|
||||
|
||||
```
|
||||
$ kapow server FixLogGrep.pow
|
||||
$ kapow server FixLogGrep
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ A simple service that exposes log entries that matches **with a given text** on
|
||||
## How to run it
|
||||
|
||||
```
|
||||
$ kapow server DynamicLogGrep.pow
|
||||
$ kapow server DynamicLogGrep
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Expose several system properties and logs.
|
||||
## How to run it
|
||||
|
||||
```
|
||||
$ kapow server SystemMonitor.pow
|
||||
$ kapow server SystemMonitor
|
||||
```
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ drwxr-xr-x 15 root root 4096 Jan 27 07:34 ..
|
||||
|
||||
```
|
||||
$ curl -s http://localhost:8080/process
|
||||
nil 46717 0.0 0.0 111224 8196 pts/2 Sl 16:48 0:00 kapow server SystemMonitor.pow
|
||||
nil 46717 0.0 0.0 111224 8196 pts/2 Sl 16:48 0:00 kapow server SystemMonitor
|
||||
root 47405 0.0 0.0 0 0 ? I 16:50 0:00 [kworker/3:1-mm_percpu_wq]
|
||||
root 47406 0.0 0.0 0 0 ? I 16:50 0:00 [kworker/0:1]
|
||||
root 47819 0.0 0.0 0 0 ? I 16:52 0:00 [kworker/7:2-mm_percpu_wq]
|
||||
|
||||
@@ -27,10 +27,14 @@ import (
|
||||
// AddRoute will add a new route in kapow
|
||||
func AddRoute(host, path, method, entrypoint, command string, w io.Writer) error {
|
||||
url := host + "/routes"
|
||||
body, _ := json.Marshal(map[string]string{
|
||||
payload := map[string]string{
|
||||
"method": method,
|
||||
"url_pattern": path,
|
||||
"entrypoint": entrypoint,
|
||||
"command": command})
|
||||
"command": command,
|
||||
}
|
||||
if entrypoint != "" {
|
||||
payload["entrypoint"] = entrypoint
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
return http.Post(url, "application/json", bytes.NewReader(body), w)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ func TestSuccessOnCorrectRoute(t *testing.T) {
|
||||
JSON(map[string]string{
|
||||
"method": "GET",
|
||||
"url_pattern": "/hello",
|
||||
"entrypoint": "",
|
||||
"command": "echo Hello World | kapow set /response/body",
|
||||
}).
|
||||
Reply(http.StatusCreated).
|
||||
|
||||
@@ -80,7 +80,7 @@ func init() {
|
||||
// TODO: Add default values for flags and remove path flag
|
||||
routeAddCmd.Flags().String("control-url", getEnv("KAPOW_CONTROL_URL", "http://localhost:8081"), "Kapow! control interface URL")
|
||||
routeAddCmd.Flags().StringP("method", "X", "GET", "HTTP method to accept")
|
||||
routeAddCmd.Flags().StringP("entrypoint", "e", "/bin/sh -c", "Command to execute")
|
||||
routeAddCmd.Flags().StringP("entrypoint", "e", "", "Command to execute")
|
||||
routeAddCmd.Flags().StringP("command", "c", "", "Command to pass to the shell")
|
||||
|
||||
var routeRemoveCmd = &cobra.Command{
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// +build !windows
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func BuildCmd(path string) *exec.Cmd {
|
||||
return exec.Command(path)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func BuildCmd(path string) *exec.Cmd {
|
||||
return exec.Command("cmd.exe", "/c", path)
|
||||
}
|
||||
+48
-19
@@ -17,9 +17,11 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -29,7 +31,7 @@ import (
|
||||
|
||||
// ServerCmd is the command line interface for kapow server
|
||||
var ServerCmd = &cobra.Command{
|
||||
Use: "server [optional flags] [optional pow file(s)]",
|
||||
Use: "server [optional flags] [optional init program(s)]",
|
||||
Short: "Start a kapow server",
|
||||
Long: `Start a Kapow server with a client interface, a data interface and an
|
||||
admin interface`,
|
||||
@@ -57,23 +59,8 @@ var ServerCmd = &cobra.Command{
|
||||
|
||||
server.StartServer(sConf)
|
||||
|
||||
if len(args) > 0 {
|
||||
powfile := args[0]
|
||||
_, err := os.Stat(powfile)
|
||||
if os.IsNotExist(err) {
|
||||
logger.L.Fatalf("%s does not exist", powfile)
|
||||
}
|
||||
logger.L.Printf("Running powfile: %q\n", powfile)
|
||||
kapowCMD := exec.Command("bash", powfile)
|
||||
kapowCMD.Stdout = os.Stdout
|
||||
kapowCMD.Stderr = os.Stderr
|
||||
kapowCMD.Env = os.Environ()
|
||||
|
||||
err = kapowCMD.Run()
|
||||
if err != nil {
|
||||
logger.L.Fatal(err)
|
||||
}
|
||||
logger.L.Printf("Done running powfile: %q\n", powfile)
|
||||
for _, path := range args {
|
||||
go Run(path, sConf.Debug)
|
||||
}
|
||||
|
||||
select {}
|
||||
@@ -112,3 +99,45 @@ func validateServerCommandArguments(cmd *cobra.Command, args []string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Run(path string, debug bool) {
|
||||
logger.L.Printf("Running init program %+q", path)
|
||||
cmd := BuildCmd(path)
|
||||
cmd.Env = os.Environ()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
if debug {
|
||||
if stdout, err := cmd.StdoutPipe(); err == nil {
|
||||
wg.Add(1)
|
||||
go logPipe(path, "stdout", stdout, &wg)
|
||||
}
|
||||
if stderr, err := cmd.StderrPipe(); err == nil {
|
||||
wg.Add(1)
|
||||
go logPipe(path, "stderr", stderr, &wg)
|
||||
}
|
||||
}
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
logger.L.Fatalf("Unable to run init program %+q: %s", path, err)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
logger.L.Printf("Init program exited with error: %s", err)
|
||||
} else {
|
||||
logger.L.Printf("Init program %+q finished OK", path)
|
||||
}
|
||||
}
|
||||
|
||||
func logPipe(path, name string, pipe io.ReadCloser, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
in := bufio.NewScanner(pipe)
|
||||
|
||||
for in.Scan() {
|
||||
logger.L.Printf("%+q (%s): %s", path, name, in.Text())
|
||||
}
|
||||
if err := in.Err(); err != nil {
|
||||
logger.L.Printf("Error reading from %+q’s %s: %s", path, name, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,10 @@ func addRoute(res http.ResponseWriter, req *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if route.Entrypoint == "" {
|
||||
route.Entrypoint = defaultEntrypoint
|
||||
}
|
||||
|
||||
route.ID = id.String()
|
||||
|
||||
created := funcAdd(route)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// +build !windows
|
||||
|
||||
package control
|
||||
|
||||
var defaultEntrypoint = "/bin/sh -c"
|
||||
@@ -0,0 +1,3 @@
|
||||
package control
|
||||
|
||||
var defaultEntrypoint = "cmd.exe /c"
|
||||
@@ -19,7 +19,7 @@ package model
|
||||
// Route contains the data needed to represent a Kapow! user route.
|
||||
type Route struct {
|
||||
// ID is the unique identifier of the Route.
|
||||
ID string `json:"id"`
|
||||
ID string `json:"id,omitempty"`
|
||||
|
||||
// Method is the HTTP method that will match this Route.
|
||||
Method string `json:"method"`
|
||||
@@ -33,7 +33,7 @@ type Route struct {
|
||||
//
|
||||
// This string will be split according to the shell parsing rules to
|
||||
// be passed as a list to exec.Command.
|
||||
Entrypoint string `json:"entrypoint"`
|
||||
Entrypoint string `json:"entrypoint,omitempty"`
|
||||
|
||||
// Command is the last argument to be passed to exec.Command when
|
||||
// executing the Entrypoint
|
||||
@@ -43,5 +43,5 @@ type Route struct {
|
||||
// It is an output field, its value is ignored as input.
|
||||
Index int `json:"index"`
|
||||
|
||||
Debug bool `json:"debug"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
}
|
||||
|
||||
+43
-41
@@ -219,7 +219,7 @@ field must be an escaped JSON string.
|
||||
}
|
||||
]
|
||||
```
|
||||
* **Sample Call**: `$ curl $KAPOW_URL/routes`
|
||||
* **Sample Call**: `$ curl $KAPOW_DATA_URL/routes`
|
||||
* **Notes**: Currently all routes are returned; in the future, a filter may be
|
||||
accepted.
|
||||
|
||||
@@ -260,7 +260,7 @@ A new id is created for the appended route so it can be referenced later.
|
||||
* **Code**: `422`; **Reason**: `Invalid Route`
|
||||
* **Sample Call**:<br />
|
||||
```sh
|
||||
$ curl -X POST --data-binary @- $KAPOW_URL/routes <<EOF
|
||||
$ curl -X POST --data-binary @- $KAPOW_DATA_URL/routes <<EOF
|
||||
{
|
||||
"method": "GET",
|
||||
"url_pattern": "/hello",
|
||||
@@ -313,7 +313,7 @@ A new id is created for the appended route so it can be referenced later.
|
||||
* **Code**: `422`; Reason: `Invalid Route`
|
||||
* **Sample Call**:<br />
|
||||
```sh
|
||||
$ curl -X PUT --data-binary @- $KAPOW_URL/routes <<EOF`
|
||||
$ curl -X PUT --data-binary @- $KAPOW_DATA_URL/routes <<EOF`
|
||||
{
|
||||
"method": "GET",
|
||||
"url_pattern": "/hello",
|
||||
@@ -346,7 +346,7 @@ Removes the route identified by `{id}`.
|
||||
* **Code**: `404`; Reason: `Route Not Found`
|
||||
* **Sample Call**:<br />
|
||||
```sh
|
||||
$ curl -X DELETE $KAPOW_URL/routes/ROUTE_1f186c92_f906_4506_9788_a1f541b11d0f
|
||||
$ curl -X DELETE $KAPOW_DATA_URL/routes/ROUTE_1f186c92_f906_4506_9788_a1f541b11d0f
|
||||
```
|
||||
* **Notes**:
|
||||
|
||||
@@ -374,7 +374,7 @@ Retrieves the information about the route identified by `{id}`.
|
||||
* **Code**: `404`; Reason: `Route Not Found`
|
||||
* **Sample Call**:<br />
|
||||
```sh
|
||||
$ curl -X GET $KAPOW_URL/routes/ROUTE_1f186c92_f906_4506_9788_a1f541b11d0f
|
||||
$ curl -X GET $KAPOW_DATA_URL/routes/ROUTE_1f186c92_f906_4506_9788_a1f541b11d0f
|
||||
```
|
||||
* **Notes**:
|
||||
|
||||
@@ -610,29 +610,30 @@ Commands:
|
||||
|
||||
This command runs the Kapow! server, which is the core of Kapow!. If
|
||||
run without parameters, it will run an unconfigured server. It can accept a path
|
||||
to a `pow` file, which is a shell script that contains commands to configure
|
||||
the Kapow! server.
|
||||
to an executable file, the init program, which can be a shell script that
|
||||
contains commands to configure the *Kapow!* server.
|
||||
|
||||
The `pow` can leverage the `kapow route` command, which is used to define a route.
|
||||
The `kapow route` command needs a way to reach the Kapow! server, and for that,
|
||||
`kapow` provides the `KAPOW_URL` variable in the environment of the
|
||||
aforementioned shell script.
|
||||
The init program can leverage the `kapow route` command, which is used to define
|
||||
a route. The `kapow route` command needs a way to reach the *Kapow!* server,
|
||||
and for that, `kapow` provides the `KAPOW_DATA_URL` variable in the environment
|
||||
of the aforementioned init program.
|
||||
|
||||
Every time the kapow server receives a request, it will spawn a process to
|
||||
handle it, according to the specified entrypoint, `/bin/sh -c` by default, and then
|
||||
execute the specified command. This command is tasked with processing the
|
||||
incoming request, and can leverage the `request` and `response` commands to
|
||||
easily access the `HTTP Request` and `HTTP Response`, respectively.
|
||||
Every time the *Kapow!* server receives a request, it will spawn a process to
|
||||
handle it, according to the specified entrypoint, `/bin/sh -c` by default in
|
||||
unices, and `cmd.exe /c` in Windows®, and then execute the specified command.
|
||||
This command is tasked with processing the incoming request, and can leverage
|
||||
the `get` and `set` commands to easily access the `HTTP Request` and `HTTP
|
||||
Response`, respectively.
|
||||
|
||||
In order for `request` and `response` to do their job, they require a way to
|
||||
reach the Kapow! server, as well as a way to identify the current request being
|
||||
served. Thus, the Kapow! server adds the `KAPOW_URL` and `KAPOW_HANDLER_ID` to the
|
||||
In order for `get` and `set` to do their job, they require a way to reach the
|
||||
*Kapow!* server, as well as a way to identify the current request being served.
|
||||
Thus, the *Kapow!* server adds the `KAPOW_DATA_URL` and `KAPOW_HANDLER_ID` to the
|
||||
process' environment.
|
||||
|
||||
|
||||
#### Example
|
||||
```sh
|
||||
$ kapow server /path/to/service.pow
|
||||
``` console
|
||||
$ kapow server /path/to/service
|
||||
```
|
||||
|
||||
|
||||
@@ -654,11 +655,11 @@ To deregister a route you must provide a *route_id*.
|
||||
|
||||
|
||||
#### **Environment**
|
||||
- `KAPOW_URL`
|
||||
- `KAPOW_DATA_URL`
|
||||
|
||||
|
||||
#### **Help**
|
||||
```sh
|
||||
``` console
|
||||
$ kapow route --help
|
||||
Usage: kapow route [OPTIONS] COMMAND [ARGS]...
|
||||
|
||||
@@ -669,7 +670,7 @@ Commands:
|
||||
add
|
||||
remove
|
||||
```
|
||||
```sh
|
||||
``` console
|
||||
$ kapow route add --help
|
||||
Usage: kapow route add [OPTIONS] URL_PATTERN [COMMAND_FILE]
|
||||
|
||||
@@ -680,7 +681,7 @@ Options:
|
||||
--url TEXT
|
||||
--help Show this message and exit.
|
||||
```
|
||||
```sh
|
||||
``` console
|
||||
$ kapow route remove --help
|
||||
Usage: kapow route remove [OPTIONS] ROUTE_ID
|
||||
|
||||
@@ -691,8 +692,8 @@ Options:
|
||||
|
||||
|
||||
#### Example
|
||||
```sh
|
||||
kapow route add -X GET '/list/{ip}' -c 'nmap -sL $(kapow get /request/matches/ip) | kapow set /response/body'
|
||||
``` console
|
||||
$ kapow route add -X GET '/list/{ip}' -c 'nmap -sL $(kapow get /request/matches/ip) | kapow set /response/body'
|
||||
```
|
||||
|
||||
### `request`
|
||||
@@ -701,14 +702,14 @@ Exposes the requests' resources.
|
||||
|
||||
|
||||
#### **Environment**
|
||||
- `KAPOW_URL`
|
||||
- `KAPOW_DATA_URL`
|
||||
- `KAPOW_HANDLER_ID`
|
||||
|
||||
|
||||
#### Example
|
||||
```sh
|
||||
# Access the body of the request
|
||||
kapow get /request/body
|
||||
``` console
|
||||
$ # Access the body of the request
|
||||
$ kapow get /request/body
|
||||
```
|
||||
|
||||
|
||||
@@ -718,27 +719,28 @@ Exposes the response's resources.
|
||||
|
||||
|
||||
#### **Environment**
|
||||
- `KAPOW_URL`
|
||||
- `KAPOW_DATA_URL`
|
||||
- `KAPOW_HANDLER_ID`
|
||||
|
||||
|
||||
#### Example
|
||||
```sh
|
||||
# Write to the body of the response
|
||||
echo 'Hello, World!' | kapow set /response/body
|
||||
``` console
|
||||
$ # Write to the body of the response
|
||||
$ echo 'Hello, World!' | kapow set /response/body
|
||||
```
|
||||
|
||||
|
||||
## An End-to-End Example
|
||||
```sh
|
||||
$ cat nmap.kpow
|
||||
``` console
|
||||
$ cat nmap-route
|
||||
#!/usr/bin/env sh
|
||||
kapow route add -X GET '/list/{ip}' -c 'nmap -sL $(kapow get /request/matches/ip) | kapow set /response/body'
|
||||
```
|
||||
```sh
|
||||
$ kapow ./nmap.kapow
|
||||
``` console
|
||||
$ kapow server ./nmap-route
|
||||
```
|
||||
```sh
|
||||
$ curl $KAPOW_URL/list/127.0.0.1
|
||||
``` console
|
||||
$ curl $KAPOW_DATA_URL/list/127.0.0.1
|
||||
Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-30 14:45 CEST
|
||||
Nmap scan report for localhost (127.0.0.1)
|
||||
Host is up (0.00011s latency).
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd := exec.Command("rundll32.exe", "url.dll,FileProtocolHandler", os.Args[1])
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user