Add some basic docs

- unify http_mock and dns_mock in config styles
- panic if pattern is not a valid regexp in http_mock
- add basic doc structure
This commit is contained in:
Peter 2020-04-07 00:17:07 +02:00
parent 63ba6da810
commit c3e362c8e5
Signed by: prskr
GPG key ID: C1DB5D2E8DB512F9
17 changed files with 374 additions and 36 deletions

View file

@ -6,19 +6,19 @@ endpoints:
options:
rules:
- pattern: ".*\\.(?i)exe"
target: ./assets/fakeFiles/sample.exe
response: ./assets/fakeFiles/sample.exe
- pattern: ".*\\.(?i)(jpg|jpeg)"
target: ./assets/fakeFiles/default.jpg
response: ./assets/fakeFiles/default.jpg
- pattern: ".*\\.(?i)png"
target: ./assets/fakeFiles/default.png
response: ./assets/fakeFiles/default.png
- pattern: ".*\\.(?i)gif"
target: ./assets/fakeFiles/default.gif
response: ./assets/fakeFiles/default.gif
- pattern: ".*\\.(?i)ico"
target: ./assets/fakeFiles/default.ico
response: ./assets/fakeFiles/default.ico
- pattern: ".*\\.(?i)txt"
target: ./assets/fakeFiles/default.txt
response: ./assets/fakeFiles/default.txt
- pattern: ".*"
target: ./assets/fakeFiles/default.html
response: ./assets/fakeFiles/default.html
httpsDowngrade:
handler: tls_interceptor
listenAddress: 0.0.0.0
@ -44,15 +44,15 @@ endpoints:
listenAddress: 0.0.0.0
port: 53
options:
fallback:
strategy: incremental
args:
startIP: 10.0.0.0
rules:
- pattern: ".*\\.google\\.com"
response: 1.1.1.1
- pattern: ".*\\.reddit\\.com"
response: 2.2.2.2
fallback:
strategy: incremental
args:
startIP: 10.0.0.0
dnsOverTlsDowngrade:
handler: tls_interceptor
listenAddress: 0.0.0.0

1
docs/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
book

10
docs/book.toml Normal file
View file

@ -0,0 +1,10 @@
[book]
authors = ["Peter Kurfer"]
language = "en"
multilingual = false
src = "src"
title = "INetMock docs"
[build]
build-dir = "book"
create-missing = true

13
docs/src/SUMMARY.md Normal file
View file

@ -0,0 +1,13 @@
# Summary
- [Building & Installation](build_install.md)
- [Configuration](config.md)
- [`config.yaml`](config/yaml-config.md)
- [`http_mock`](config/http_mock.md)
- [`dns_mock`](config/dns_mock.md)
- [`tls_interceptor`](config/tls_interceptor.md)
- [Deployment](deploy.md)
- [API](api.md)
- [Custom handler](dev/custom_handler.md)
- [Plugin command](dev/plugin_command.md)
- [Logging](dev/logging.md)

1
docs/src/api.md Normal file
View file

@ -0,0 +1 @@
# API

29
docs/src/build_install.md Normal file
View file

@ -0,0 +1,29 @@
# Installation
## Building from source
### Requirements
* go 1.14
* make
* gcc
### Binary
To get the binary and all plugins just run
```bash
make
```
and you'll get a `inetmock` binary and a `plugins` directory containing all default plugins.
The default plugins are:
* `http_mock`
* `dns_mock`
* `tls_interceptor`
## Docker/Podman
## Getting a pre-built binary (coming soon)

19
docs/src/config.md Normal file
View file

@ -0,0 +1,19 @@
# Configuration
## Plugins & handlers
_INetMock_ is based on plugins that ship one or more __protocol handlers__.
Examples for protocol handlers are HTTP or DNS but also TLS.
The application ships with the following handlers:
* `http_mock`
* `dns_mock`
* `tls_interceptor`
The configuration of an so called endpoint always specifies which handler should be used, which IP address and port it should listen on and some handler specific `options`.
This way the whole system is very flexible and can be configured for various individual scenarios.
## Commands
Beside of __protocol handlers__ a plugin can also ship custom commands e.g. the `tls_interceptor` ships a `generate-ca` command to bootstrap a certificate authority key-pair that can be reused for multiple instances.

140
docs/src/config/dns_mock.md Normal file
View file

@ -0,0 +1,140 @@
# `dns_mock`
## Intro
The `dns_mock` handler expects an array of rules how it should respond to dfferent DNS queries and a fallback strategy.
Currently only queries for __A__ records are supported.
The rules are primarily meant to define some exceptions or well known DNS responses e.g. to return to right Google DNS IP but for everything else it will return dummy IPs.
The rules for the `dns_mock` handler are equivalent to the `http_mock` rules.
Every rule consists of a `pattern` that specifies a query name e.g. a single host, a wildcard domain, a wildcard top-level domain or even a _"match all"_ rule is possible.
These rules are evaluated in the same order they are defined in the `config.yaml`.
The fallback strategy is taken into account whenever a query does not match a rule.
Right now the following fallback strategies are available:
* _random_
* _incremental_
Just like the handler is configured via the `options` object the fallback strategies are configured via an `args` object.
### _random_ fallback
The _random_ fallback strategy is the easier one of the both.
It doesn't take any argument and it just shuffles a random IP address for every request no matter if it was already asked for this IP or not.
### _incremental_ fallback
The _incremental_ fallback is little bit more intelligent.
It takes a `startIP` as an argument which defines from which IP address the strategy starts counting up to respond to DNS queries.
Just like the _incremental_ strategy it is _stateless_ and does not store any already given response for later reuse (at least for now).
## Configuration
### Matching an explicit host
The easiest possible pattern is to match a single host:
```yml
endpoints:
plainDns:
handler: dns_mock
listenAddress: 0.0.0.0
port: 53
options:
rules:
- pattern: "github\\.com"
response: 1.1.1.1
```
### Matching a whole domain
While matching a single host is nice2have it's not very helpful in most cases - except for some edge cases where it might be necesary to specifically return a certain IP address.
But it's also possible to match a whole domain no matter what subdomain or sub-subdomain or whatever is requested like this:
```yml
endpoints:
plainDns:
handler: dns_mock
listenAddress: 0.0.0.0
port: 53
options:
rules:
- pattern: ".*\\.google\\.com"
response: 2.2.2.2
```
### Matching a whole TLD
In some cases it might also be interesting to distinguish between different requested TLDs.
Therefore it might be interesting to define one IP address to resolve to for every TLD that should be distinguishable.
```yml
endpoints:
plainDns:
handler: dns_mock
listenAddress: 0.0.0.0
port: 53
options:
rules:
- pattern: ".*\\.com"
response: 2.2.2.2
```
### Matching any query
Last but not least it is obvously also possible to match any query.
This is comparable to a _"static"_ fallback strategy in cases where different IP addresses are not necessary but the network setup should be as easy as possible.
```yml
endpoints:
plainDns:
handler: dns_mock
listenAddress: 0.0.0.0
port: 53
options:
rules:
- pattern: ".*"
response: 10.0.10.1
```
### Fallback strategies
#### _random_
Like previously mentioned the _random_ strategy is easy as it can be.
It just takes a random unsigned integer of 32 bits, converts it to an IP address and returns this address as response.
Therefore no further configuration is necessary for now.
```yml
endpoints:
plainDns:
handler: dns_mock
listenAddress: 0.0.0.0
port: 53
options:
rules: []
fallback:
strategy: random
```
#### _incremental_
Also like previously mentioned the _incremental_ fallback strategy is fairly easy to setup.
It just takes a `startIP` as argument which is used to count upwards.
It does __not__ check for an interval or something like this right now so a overflow might occur.
```yml
endpoints:
plainDns:
handler: dns_mock
listenAddress: 0.0.0.0
port: 53
options:
rules: []
fallback:
strategy: incremental
args:
startIP: 10.0.0.0
```

View file

@ -0,0 +1,85 @@
# `http_mock`
## Intro
The `http_mock` handler expects an array of rules how it should respond to different request paths.
This allows to e.g. return an image if the request path contains something like _"asdf.jpg"_ but with binary if the request path contains something like _"malicous.exe"_.
A _"catch all"_ rule could return in any case an HTML page or if nothing is provided the handler returns an HTTP 404 status code.
The rules are taken into account in the same order than they are defined in the `config.yaml`.
Every rule consists of a regex `pattern` (__re2__ compatible) and a `response` path to the file it should return.
In the future more advanced rules might be possible e.g. to match not on the request path but on some header values.
## Configuration
### Matching a specific path
The easiest possible pattern is to match a static request path:
```yml
endpoints:
plainHttp:
handler: http_mock
listenAddress: 0.0.0.0
port: 80
options:
rules:
- pattern: "/static/http/path/sample.exe"
response: ./assets/fakeFiles/sample.exe
```
### Matching a file extensions
While matching a static path might be nice as an example it's not very useful.
Returning a given file for all kinds of of request paths based on the requested file extension is way more handy:
```yml
endpoints:
plainHttp:
handler: http_mock
listenAddress: 0.0.0.0
port: 80
options:
rules:
- pattern: ".*\\.png"
response: ./assets/fakeFiles/default.png
```
So this is already way more flexible but we can do even better:
```yml
endpoints:
plainHttp:
handler: http_mock
listenAddress: 0.0.0.0
port: 80
options:
rules:
- pattern: ".*\\.(?i)(jpg|jpeg)"
response: ./assets/fakeFiles/default.jpg
```
This way the extension ignores any case and matches both `.jpg` and `.jpeg` (and of course also e.g. `.JpEg` and so on and so forth).
The default `config.yaml` already ships with some basic rules to handle the most common file extensions.
### Defining a fallback
Last but not least a default case might be necessary to get at least any response but a 404.
This can be achieved with a `.*` pattern that literally matches everything:
```yml
endpoints:
plainHttp:
handler: http_mock
listenAddress: 0.0.0.0
port: 80
options:
rules:
- pattern: ".*"
response: ./assets/fakeFiles/default.html
```

View file

@ -0,0 +1 @@
# `tls_interceptor`

View file

@ -0,0 +1,30 @@
# `config.yaml`
## Intro
The configuration of _INetMock_ is mostly done in the `config.yaml`.
It defines which endpoints should be started with which handler and a few more things.
Every endpoint has a name that is used for logging and as already mentioned consists of listening IP and port, the handler and its options.
INetMock comes with _"Batteries included"_ and ships with a basic `config.yaml` that defines a basic set of endpoints for:
* HTTP
* HTTPS
* DNS
* DNS-over-TLS
## Sample
```yml
endpoints:
myHttpEndpoint:
handler: http_mock
listenAddress: 127.0.0.1
port: 8080
options:
rules:
- pattern: ".*"
target: ./assets/fakeFiles/default.html
```

1
docs/src/deploy.md Normal file
View file

@ -0,0 +1 @@
# Deployment

View file

@ -0,0 +1 @@
# Custom handler

1
docs/src/dev/logging.md Normal file
View file

@ -0,0 +1 @@
# Logging

View file

@ -0,0 +1 @@
# Plugin command

View file

@ -8,7 +8,6 @@ import (
"go.uber.org/zap"
"net/http"
"path/filepath"
"regexp"
"sync"
)
@ -58,23 +57,13 @@ func (p *httpHandler) startServer() {
}
func (p *httpHandler) setupRoute(rule targetRule) {
var compiled *regexp.Regexp
var err error
if compiled, err = regexp.Compile(rule.pattern); err != nil {
p.logger.Warn(
"failed to parse route - skipping",
zap.String("route", rule.pattern),
zap.Error(err),
)
return
}
p.logger.Info(
"setup routing",
zap.String("route", compiled.String()),
zap.String("target", rule.target),
zap.String("route", rule.Pattern().String()),
zap.String("response", rule.Response()),
)
p.router.Handler(compiled, createHandlerForTarget(p.logger, rule.target))
p.router.Handler(rule.Pattern(), createHandlerForTarget(p.logger, rule.response))
}
func createHandlerForTarget(logger *zap.Logger, targetPath string) http.Handler {
@ -91,7 +80,7 @@ func createHandlerForTarget(logger *zap.Logger, targetPath string) http.Handler
zap.String("method", request.Method),
zap.String("protocol", request.Proto),
zap.String("path", request.RequestURI),
zap.String("target", targetFilePath),
zap.String("response", targetFilePath),
zap.Reflect("headers", request.Header),
)

View file

@ -1,16 +1,27 @@
package main
import "github.com/spf13/viper"
import (
"github.com/spf13/viper"
"regexp"
)
const (
rulesConfigKey = "rules"
patternConfigKey = "pattern"
targetConfigKey = "target"
rulesConfigKey = "rules"
patternConfigKey = "pattern"
responseConfigKey = "response"
)
type targetRule struct {
pattern string
target string
pattern *regexp.Regexp
response string
}
func (tr targetRule) Pattern() *regexp.Regexp {
return tr.pattern
}
func (tr targetRule) Response() string {
return tr.response
}
type httpOptions struct {
@ -23,10 +34,15 @@ func loadFromConfig(config *viper.Viper) httpOptions {
for _, i := range anonRules {
innerData := i.(map[interface{}]interface{})
options.Rules = append(options.Rules, targetRule{
pattern: innerData[patternConfigKey].(string),
target: innerData[targetConfigKey].(string),
})
if rulePattern, err := regexp.Compile(innerData[patternConfigKey].(string)); err == nil {
options.Rules = append(options.Rules, targetRule{
pattern: rulePattern,
response: innerData[responseConfigKey].(string),
})
} else {
panic(err)
}
}
return options