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:
parent
63ba6da810
commit
c3e362c8e5
17 changed files with 374 additions and 36 deletions
22
config.yaml
22
config.yaml
|
@ -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
1
docs/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
book
|
10
docs/book.toml
Normal file
10
docs/book.toml
Normal 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
13
docs/src/SUMMARY.md
Normal 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
1
docs/src/api.md
Normal file
|
@ -0,0 +1 @@
|
|||
# API
|
29
docs/src/build_install.md
Normal file
29
docs/src/build_install.md
Normal 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
19
docs/src/config.md
Normal 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
140
docs/src/config/dns_mock.md
Normal 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
|
||||
```
|
85
docs/src/config/http_mock.md
Normal file
85
docs/src/config/http_mock.md
Normal 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
|
||||
```
|
1
docs/src/config/tls_interceptor.md
Normal file
1
docs/src/config/tls_interceptor.md
Normal file
|
@ -0,0 +1 @@
|
|||
# `tls_interceptor`
|
30
docs/src/config/yaml-config.md
Normal file
30
docs/src/config/yaml-config.md
Normal 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
1
docs/src/deploy.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Deployment
|
1
docs/src/dev/custom_handler.md
Normal file
1
docs/src/dev/custom_handler.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Custom handler
|
1
docs/src/dev/logging.md
Normal file
1
docs/src/dev/logging.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Logging
|
1
docs/src/dev/plugin_command.md
Normal file
1
docs/src/dev/plugin_command.md
Normal file
|
@ -0,0 +1 @@
|
|||
# Plugin command
|
|
@ -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),
|
||||
)
|
||||
|
||||
|
|
|
@ -1,16 +1,27 @@
|
|||
package main
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
rulesConfigKey = "rules"
|
||||
patternConfigKey = "pattern"
|
||||
targetConfigKey = "target"
|
||||
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{})
|
||||
|
||||
if rulePattern, err := regexp.Compile(innerData[patternConfigKey].(string)); err == nil {
|
||||
options.Rules = append(options.Rules, targetRule{
|
||||
pattern: innerData[patternConfigKey].(string),
|
||||
target: innerData[targetConfigKey].(string),
|
||||
pattern: rulePattern,
|
||||
response: innerData[responseConfigKey].(string),
|
||||
})
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return options
|
||||
|
|
Loading…
Reference in a new issue