187 lines
5.8 KiB
Markdown
187 lines
5.8 KiB
Markdown
|
## Key Takeaways
|
||
|
|
||
|
* Organisations should focus on defendable infrastructure
|
||
|
to enable efficient security monitoring. This requires
|
||
|
using reverse proxies, software-defined networking, a
|
||
|
well-defined and flexible architecture.
|
||
|
|
||
|
* We can add the LimaCharlie Adapter in file-mode
|
||
|
to stream JSON logs to limacharlie.io for
|
||
|
centralized and actionable logging (free to test).
|
||
|
|
||
|
* Infrastructure as Config/Code enables templating so
|
||
|
that the setup can be re-used across applications and
|
||
|
saves a lot of engineering time.
|
||
|
|
||
|
|
||
|
## Background
|
||
|
|
||
|
Yesterday I published a post about how I'm moving from
|
||
|
OpenBSD to Docker for my Matrix Dendrite setup. At the same
|
||
|
time I reshaped the infrastructure delivering Dendrite
|
||
|
to take advantage of the reverse proxying capabilities
|
||
|
in Traefik.
|
||
|
|
||
|
One of the major advantages of moving to Docker is that
|
||
|
it facilitates security monitoring in a better way than
|
||
|
OpenBSD. This mostly has to do with vendor support and
|
||
|
the fact that OpenBSD does things differently.
|
||
|
|
||
|
Within security monitoring, centralized logging with
|
||
|
[LimaCharlie Adapters](https://doc.limacharlie.io/docs/documentation/73a613e8e43ed-lima-charlie-adapter) is a good option, which is what I wanted to share a
|
||
|
simple example of here.
|
||
|
|
||
|
Please note that this is simplistic in design, since the
|
||
|
rig I'm building for doesn't have much activity. If
|
||
|
I were building the monitoring setup for e.g. Kubernetes
|
||
|
I would need to take into consideration that there will
|
||
|
be multiple containers moving around, distributed file
|
||
|
storage etc.
|
||
|
|
||
|
I'm separating containers by duty. It will probably bring
|
||
|
a little extra overhead over using centralized storage or
|
||
|
an embedded adapter with stdout-streaming per container.
|
||
|
|
||
|
Let's keep the rest of this article short and sweet.
|
||
|
|
||
|
## The Configuration
|
||
|
|
||
|
First we need to add the following [`accessLog` labels to traefik](https://doc.traefik.io/traefik/observability/access-logs/)
|
||
|
in our Docker Compose:
|
||
|
|
||
|
```
|
||
|
[...]
|
||
|
reverse-proxy:
|
||
|
[...]
|
||
|
command:
|
||
|
[...]
|
||
|
- --accessLog.filePath=/var/log/traefik/access.log
|
||
|
- --accessLog.format=json
|
||
|
volumes:
|
||
|
[...]
|
||
|
- ./data/traefik:/var/log/traefik
|
||
|
[...]
|
||
|
[...]
|
||
|
```
|
||
|
|
||
|
This will write logs to `./data/traefik` outside Docker. We'll
|
||
|
share the same directory with the LimaCharlie Adapter. For this
|
||
|
purpose we'll configure the adapter to run in `file-mode`. In a
|
||
|
more demanding configuration I would consider using `stdin-mode`.
|
||
|
|
||
|
There's an example in the LimaCharlie docs of how to use the
|
||
|
[adapter in Docker with syslog](https://doc.limacharlie.io/docs/documentation/73a613e8e43ed-lima-charlie-adapter#syslog). We can also run the
|
||
|
following to get the file configuration options:
|
||
|
|
||
|
```sh
|
||
|
$ docker run --rm -it -p 4444:4444 refractionpoint/lc-adapter:latest
|
||
|
|
||
|
For file
|
||
|
----------------------------------
|
||
|
client_options.identity.oid
|
||
|
client_options.identity.installation_key
|
||
|
client_options.hostname
|
||
|
client_options.platform
|
||
|
client_options.architecture
|
||
|
client_options.mapping.parsing_re
|
||
|
client_options.mapping.sensor_key_path
|
||
|
client_options.mapping.sensor_hostname_path
|
||
|
client_options.mapping.event_type_path
|
||
|
client_options.mapping.event_time_path
|
||
|
client_options.mapping.rename_only
|
||
|
client_options.mapping.mappings
|
||
|
client_options.mappings
|
||
|
client_options.indexing
|
||
|
client_options.is_compressed
|
||
|
client_options.sensor_seed_key
|
||
|
client_options.dest_url
|
||
|
write_timeout_sec
|
||
|
file_path
|
||
|
no_follow
|
||
|
```
|
||
|
|
||
|
Having a look at `data/traefik/access.log`, we find that Traefik
|
||
|
has the following pattern:
|
||
|
|
||
|
```json
|
||
|
{
|
||
|
"ClientAddr": "[IP]:[PORT]",
|
||
|
"ClientHost": "[IP]",
|
||
|
"ClientPort": "[PORT]",
|
||
|
"ClientUsername": "-",
|
||
|
"DownstreamContentSize": 105,
|
||
|
"DownstreamStatus": 200,
|
||
|
"Duration": 30012385827,
|
||
|
"OriginContentSize": 105,
|
||
|
"OriginDuration": 30012309833,
|
||
|
"OriginStatus": 200,
|
||
|
"Overhead": 75994,
|
||
|
"RequestAddr": "matrix.{{tailnet-id}}.ts.net",
|
||
|
"RequestContentSize": 0,
|
||
|
"RequestCount": 5,
|
||
|
"RequestHost": "matrix.{{tailnet-id}}.ts.net",
|
||
|
"RequestMethod": "GET",
|
||
|
"RequestPath": "/_matrix/client/r0/sync?[...]",
|
||
|
"RequestPort": "-",
|
||
|
"RequestProtocol": "HTTP/2.0",
|
||
|
"RequestScheme": "https",
|
||
|
"RetryAttempts": 0,
|
||
|
"RouterName": "web@docker",
|
||
|
"ServiceAddr": "[IP]:[PORT]",
|
||
|
"ServiceName": "dendrite-docker-matrix@docker",
|
||
|
"ServiceURL": "http://[IP]:[PORT]",
|
||
|
"StartLocal": "2023-04-25T08:06:12.121010341Z",
|
||
|
"StartUTC": "2023-04-25T08:06:12.121010341Z",
|
||
|
"TLSCipher": "TLS_AES_128_GCM_SHA256",
|
||
|
"TLSVersion": "1.3",
|
||
|
"entryPointName": "client",
|
||
|
"level": "info",
|
||
|
"msg": "",
|
||
|
"time": "2023-04-25T08:06:42Z"
|
||
|
}```
|
||
|
|
||
|
This won't matter for getting basic logging up and running as
|
||
|
we can use the adapter in JSON mode as follows:
|
||
|
|
||
|
```sh
|
||
|
docker run --rm \
|
||
|
-v ./data/traefik/access.log:/logs/traefik/access.log \
|
||
|
refractionpoint/lc-adapter:latest file \
|
||
|
file_path=/logs/traefik/access.log \
|
||
|
client_options.identity.installation_key={{ADAPTER INSTALL. KEY}} \
|
||
|
client_options.identity.oid={{limacharlie.io ORG ID}} \
|
||
|
client_options.hostname=traefik-ts-dendrite \
|
||
|
client_options.sensor_seed_key=traefik-ts-dendrite \
|
||
|
client_options.platform=json
|
||
|
```
|
||
|
|
||
|
And voilà, our adapter is streaming to our organisation
|
||
|
(synonym to "tenant") at limacharlie.io.
|
||
|
|
||
|
To make it permanent, we convert the `docker` command to
|
||
|
`docker-compose` and end up with:
|
||
|
|
||
|
```docker-compose
|
||
|
version: '3.3'
|
||
|
services:
|
||
|
traefik-lc-adapter:
|
||
|
image: refractionpoint/lc-adapter:latest
|
||
|
command: file file_path=/logs/traefik/access.log client_options.identity.installation_key=$LC_INSTALLATION_KEY client_options.identity.oid=$LC_ORG_ID client_options.hostname=$LC_HOSTNAME client_options.sensor_seed_key=$LC_HOSTNAME client_options.platform=json
|
||
|
restart: unless-stopped
|
||
|
environment:
|
||
|
TZ: UTC
|
||
|
env_file:
|
||
|
- ./config/limacharlie/.limacharlie_env
|
||
|
volumes:
|
||
|
- './data/traefik/access.log:/logs/traefik/access.log'
|
||
|
```
|
||
|
|
||
|
Add keys to environment file in `.env`:
|
||
|
|
||
|
```sh
|
||
|
LC_INSTALLATION_KEY={{ADAPTER INSTALLATION KEY}}
|
||
|
LC_ORG_ID={{limacharlie.io ORG ID}}
|
||
|
LC_HOSTNAME=traefik-ts-dendrite
|
||
|
```
|
||
|
|
||
|
Easy as that.
|