├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── daemonset.yaml ├── go.mod ├── go.sum └── main.go /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.13-alpine 2 | 3 | RUN sed -i -e 's/v[[:digit:]]\..*\//edge\//g' /etc/apk/repositories 4 | 5 | RUN apk update 6 | RUN apk add bcc-dev build-base linux-headers 7 | 8 | COPY . /project 9 | WORKDIR /project 10 | RUN go build -o /bin/bpf-program 11 | 12 | RUN rm -rf /project 13 | 14 | ENTRYPOINT ["/bin/bpf-program"] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build push 2 | 3 | DOCKER ?= docker 4 | IMAGE_NAME ?= docker.io/bpftools/prometheus-ebpf-example 5 | 6 | COMMIT_NO := $(shell git rev-parse HEAD 2> /dev/null || true) 7 | GIT_COMMIT := $(if $(shell git status --porcelain --untracked-files=no),${COMMIT_NO}-dirty,${COMMIT_NO}) 8 | GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) 9 | GIT_BRANCH_CLEAN := $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g") 10 | 11 | IMAGE_COMMIT := $(IMAGE_NAME):$(GIT_COMMIT) 12 | IMAGE_BRANCH := $(IMAGE_NAME):$(GIT_BRANCH_CLEAN) 13 | IMAGE_LATEST := $(IMAGE_NAME):latest 14 | 15 | build: 16 | $(DOCKER) build -t $(IMAGE_COMMIT) . 17 | $(DOCKER) tag $(IMAGE_COMMIT) $(IMAGE_BRANCH) 18 | $(DOCKER) tag $(IMAGE_COMMIT) $(IMAGE_LATEST) 19 | 20 | push: 21 | $(DOCKER) push $(IMAGE_COMMIT) 22 | $(DOCKER) push $(IMAGE_BRANCH) 23 | $(DOCKER) push $(IMAGE_LATEST) 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Prometheus eBPF example 2 | 3 | A simple `main.go` containing all the code you need to get metrics from the kernel and expose them trough [Prometheus](https://prometheus.io/). 4 | 5 | 6 | ## Build 7 | 8 | You need [Docker](https://docs.docker.com/install/) to build this using the makefile. 9 | 10 | ```bash 11 | make build 12 | ``` 13 | 14 | If you don't want to use Docker, and the Makefile you can build locally with: 15 | 16 | 17 | ```bash 18 | go build -o bpf-program . 19 | ``` 20 | 21 | In this case, you will need to install bcc-dev first, instructions [here](https://github.com/iovisor/bcc/blob/master/INSTALL.md). 22 | 23 | ## Run 24 | 25 | Remember: you might have libreadline somewhere else. This also can work by passing the bash binary directly. If you pass the bash binary or change 26 | the location you can use the environment variable `URETPROBE_BINARY` to tell the program where to load from. 27 | 28 | ```bash 29 | docker run -e NODENAME=$(hostname) -v /sys:/sys:ro -v /lib/modules:/lib/modules:ro --privileged -v /lib/libreadline.so:/host/lib/libreadline.so:ro -p 8080:8080 -it docker.io/bpftools/prometheus-ebpf-example:latest 30 | ``` 31 | 32 | You can test if this works by opening a `bash` shell and doing some commands, then you can curl 33 | the metrics endpoint and see the results. It will show something like: 34 | 35 | ```bash 36 | # HELP commands_count The number of times a command is invoked via bash 37 | # TYPE commands_count counter 38 | commands_count{command="clear",nodename="gallifrey",pid="1834654"} 3 39 | commands_count{command="curl http://127.0.0.1:8080/metrics",nodename="gallifrey",pid="1847919"} 1 40 | commands_count{command="docker images",nodename="gallifrey",pid="1834654"} 1 41 | commands_count{command="docker ps",nodename="gallifrey",pid="1834654"} 1 42 | commands_count{command="ip a",nodename="gallifrey",pid="1834654"} 1 43 | commands_count{command="ip a",nodename="gallifrey",pid="1847919"} 2 44 | commands_count{command="ls -la",nodename="gallifrey",pid="1834654"} 1 45 | commands_count{command="ls -la",nodename="gallifrey",pid="1847919"} 4 46 | commands_count{command="ps",nodename="gallifrey",pid="1834654"} 1 47 | commands_count{command="ps -fe",nodename="gallifrey",pid="1834654"} 1 48 | commands_count{command="ps -fe | grep evil",nodename="gallifrey",pid="1834654"} 1 49 | commands_count{command="vim",nodename="gallifrey",pid="1834654"} 1 50 | commands_count{command="vim",nodename="gallifrey",pid="1847919"} 2 51 | commands_count{command="whoami",nodename="gallifrey",pid="1834654"} 1 52 | ``` 53 | 54 | Notice how the curl command itself was recorded! 55 | 56 | ## Run on Kubernetes as a Daemonset 57 | 58 | Remember: you might have libreadline somewhere else. This also can work by passing the bash binary directly. If you pass the bash binary or change 59 | the location you can use the environment variable `URETPROBE_BINARY` to tell the program where to load from. 60 | In this case, the `daemonset.yaml` uses the `URETPROBE_BINARY` because in my testing I was using [kind](https://github.com/kubernetes-sigs/kind) and it worked well in that way. 61 | 62 | 63 | ```bash 64 | kubectl apply -f daemonset.yaml 65 | ``` 66 | 67 | This will create: 68 | - A namespace called bpf-stuff 69 | - A daemonset called bpf-program 70 | - A service exposing port 8080 called bpf-program 71 | 72 | 73 | At this point you can port-forward your daemonset or the service or even one of the pods 74 | and curl the endpoint to see if it's collecting anything: 75 | 76 | To do the port forwarding for the daemonset: 77 | 78 | ```bash 79 | kubectl port-forward daemonset/bpf-program -n bpf-stuff 8080:8080 80 | ``` 81 | 82 | 83 | ## Scrape with Prometheus 84 | 85 | TODO: Write this 86 | 87 | ## Licensing 88 | 89 | Since the `main.go` file in this repository uses the original work taken from the [bash_readline.go](https://github.com/iovisor/gobpf/blob/master/examples/bcc/bash_readline/bash_readline.go) 90 | example in the [iovisor/gobpf](https://github.com/iovisor/gobpf) repository, we use the same license as them. 91 | Modifications are stated in the `main.go` file. 92 | -------------------------------------------------------------------------------- /daemonset.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: bpf-stuff 6 | --- 7 | apiVersion: v1 8 | kind: Service 9 | metadata: 10 | name: bpf-program 11 | namespace: bpf-stuff 12 | labels: 13 | app: bpf-program 14 | spec: 15 | ports: 16 | - port: 8080 17 | name: metrics 18 | clusterIP: None 19 | selector: 20 | app: bpf-program 21 | --- 22 | apiVersion: apps/v1 23 | kind: DaemonSet 24 | metadata: 25 | name: bpf-program 26 | namespace: bpf-stuff 27 | labels: 28 | app: bpf-program 29 | spec: 30 | selector: 31 | matchLabels: 32 | name: bpf-program 33 | template: 34 | metadata: 35 | labels: 36 | name: bpf-program 37 | spec: 38 | containers: 39 | - name: bpf-program 40 | image: docker.io/bpftools/prometheus-ebpf-example:latest 41 | imagePullPolicy: Always 42 | env: 43 | - name: MY_NODE_NAME 44 | valueFrom: 45 | fieldRef: 46 | fieldPath: spec.nodeName 47 | - name: URETPROBE_BINARY 48 | value: /host/usr/bin/bash 49 | ports: 50 | - containerPort: 8080 51 | securityContext: 52 | privileged: true 53 | resources: 54 | limits: 55 | memory: 200Mi 56 | requests: 57 | cpu: 100m 58 | memory: 200Mi 59 | volumeMounts: 60 | - name: sys 61 | mountPath: /sys 62 | readOnly: true 63 | - name: headers 64 | mountPath: /usr/src 65 | readOnly: true 66 | - name: modules 67 | mountPath: /lib/modules 68 | readOnly: true 69 | - name: bin 70 | mountPath: /host/usr/bin 71 | readOnly: true 72 | volumes: 73 | - name: sys 74 | hostPath: 75 | path: /sys 76 | - name: headers 77 | hostPath: 78 | path: /usr/src 79 | - name: modules 80 | hostPath: 81 | path: /lib/modules 82 | - name: bin 83 | hostPath: 84 | path: /usr/bin 85 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fntlnz/prometheus-ebpf-example 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 7 | github.com/iovisor/gobpf v0.0.0-20191024162143-7c8f8e040b4b 8 | github.com/prometheus/client_golang v1.2.1 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 2 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 3 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 4 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 5 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 6 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 7 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 8 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 9 | github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= 10 | github.com/cespare/xxhash/v2 v2.1.0/go.mod h1:dgIUBU3pDso/gPgZ1osOZ0iQf77oPR28Tjxl5dIMyVM= 11 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 15 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 16 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 17 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 18 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 19 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 20 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 21 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 22 | github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= 23 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 24 | github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= 25 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 26 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 27 | github.com/iovisor/gobpf v0.0.0-20191024162143-7c8f8e040b4b h1:a9WN1ZdBLogkie3+xxnMC6obhlg9/ehMAg+Wyj4Opgw= 28 | github.com/iovisor/gobpf v0.0.0-20191024162143-7c8f8e040b4b/go.mod h1:+5U5qu5UOu8YJ5oHVLvWKH7/Dr5QNHU7mZ2RfPEeXg8= 29 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 30 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 31 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 32 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 33 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 34 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 35 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 36 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 37 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 38 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 39 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 40 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 41 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 42 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 43 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 44 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 45 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 46 | github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= 47 | github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= 48 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 49 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 50 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= 51 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 52 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 53 | github.com/prometheus/common v0.7.0 h1:L+1lyG48J1zAQXA3RBX/nG/B3gjlHq0zTt2tlbJLyCY= 54 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 55 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 56 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 57 | github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= 58 | github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= 59 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 60 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 61 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 62 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 63 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 64 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 65 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 66 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 67 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 68 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 69 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 70 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 71 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 72 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 73 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 74 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 75 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY= 76 | golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 77 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 78 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 79 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 80 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 81 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 82 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Louis McCormack 2 | // Copyright 2019 Lorenzo Fontana (for the prometheus modifications) 3 | // This file was initially taken from the iovisor/gobpf repository 4 | // https://github.com/iovisor/gobpf/blob/master/examples/bcc/bash_readline/bash_readline.go 5 | // It was modified to expose the extracted metrics as a prometheus endpoint on port 8080 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | package main 20 | 21 | import ( 22 | "bytes" 23 | "encoding/binary" 24 | "log" 25 | "net/http" 26 | "os" 27 | "os/signal" 28 | "strconv" 29 | 30 | bpf "github.com/iovisor/gobpf/bcc" 31 | "github.com/prometheus/client_golang/prometheus" 32 | "github.com/prometheus/client_golang/prometheus/promauto" 33 | "github.com/prometheus/client_golang/prometheus/promhttp" 34 | ) 35 | 36 | const source string = ` 37 | #include 38 | 39 | struct readline_event_t { 40 | u32 pid; 41 | char str[80]; 42 | } __attribute__((packed)); 43 | 44 | BPF_PERF_OUTPUT(readline_events); 45 | 46 | int get_return_value(struct pt_regs *ctx) { 47 | struct readline_event_t event = {}; 48 | u32 pid; 49 | if (!PT_REGS_RC(ctx)) { 50 | return 0; 51 | } 52 | pid = bpf_get_current_pid_tgid(); 53 | event.pid = pid; 54 | bpf_probe_read(&event.str, sizeof(event.str), (void *)PT_REGS_RC(ctx)); 55 | readline_events.perf_submit(ctx, &event, sizeof(event)); 56 | 57 | return 0; 58 | } 59 | ` 60 | 61 | // This is our userspace struct 1:1 with the struct readline_event_t in the eBPF program. 62 | type readlineEvent struct { 63 | Pid uint32 64 | Str [80]byte 65 | } 66 | 67 | var ( 68 | readlineProcessed = promauto.NewCounterVec(prometheus.CounterOpts{ 69 | Name: "commands_count", 70 | Help: "The number of times a command is invoked via bash", 71 | }, []string{"command", "pid", "nodename"}) 72 | ) 73 | 74 | func main() { 75 | // URETPROBE_BINARY is the path of the binary (or library) we want to analyze 76 | binaryName := os.Getenv("URETPROBE_BINARY") 77 | 78 | // Get the current node hostname 79 | nodeName := os.Getenv("NODENAME") 80 | 81 | if len(nodeName) == 0 { 82 | nodeName = os.Getenv("HOSTNAME") 83 | } 84 | 85 | if len(nodeName) == 0 { 86 | nodeName = "unknown" 87 | } 88 | 89 | if len(binaryName) == 0 { 90 | binaryName = "/host/lib/libreadline.so" 91 | } 92 | 93 | // This creates a new module to compile our eBPF code asynchronously 94 | m := bpf.NewModule(source, []string{}) 95 | defer m.Close() 96 | 97 | // This loads the uprobe program and sets the "get_return_value" as entrypoint 98 | readlineUretprobe, err := m.LoadUprobe("get_return_value") 99 | if err != nil { 100 | log.Fatalf("Failed to load get_return_value: %v", err) 101 | } 102 | 103 | // This attaches the uretprobe to the readline function of the passed binary. 104 | // This will consider every process (old and new) since we didn't specify the pid to look for. 105 | err = m.AttachUretprobe(binaryName, "readline", readlineUretprobe, -1) 106 | if err != nil { 107 | log.Fatalf("Failed to attach return_value: %v", err) 108 | } 109 | 110 | // This creates a new perf table "readline_events" to look to, 111 | // this must have the same name as the table defined in the eBPF progrma with BPF_PERF_OUTPUT. 112 | table := bpf.NewTable(m.TableId("readline_events"), m) 113 | 114 | // This channel will contain our results 115 | channel := make(chan []byte) 116 | 117 | // Link our channel with the perf table 118 | perfMap, err := bpf.InitPerfMap(table, channel) 119 | if err != nil { 120 | log.Fatalf("Failed to init perf map: %v", err) 121 | } 122 | 123 | // Defined some handlers ot allow the user to kill the program 124 | sig := make(chan os.Signal, 1) 125 | signal.Notify(sig, os.Interrupt, os.Kill) 126 | 127 | // Goroutine to handle the events 128 | go func() { 129 | var event readlineEvent 130 | for { 131 | 132 | // Get the current element from the channel 133 | data := <-channel 134 | 135 | // Read the data and populate the event struct 136 | err = binary.Read(bytes.NewBuffer(data), binary.LittleEndian, &event) 137 | if err != nil { 138 | log.Printf("failed to decode received data: %s", err) 139 | continue 140 | } 141 | 142 | // Convert the C string to a Go string 143 | comm := string(event.Str[:bytes.IndexByte(event.Str[:], 0)]) 144 | 145 | readlineProcessed.WithLabelValues(comm, strconv.Itoa(int(event.Pid)), nodeName).Inc() 146 | 147 | } 148 | }() 149 | 150 | go func() { 151 | r := prometheus.NewRegistry() 152 | r.MustRegister(readlineProcessed) 153 | handler := promhttp.HandlerFor(r, promhttp.HandlerOpts{}) 154 | http.Handle("/metrics", handler) 155 | err := http.ListenAndServe(":8080", nil) 156 | if err != nil { 157 | log.Fatalf("error starting the webserver: %v", err) 158 | } 159 | }() 160 | 161 | // Start reading 162 | perfMap.Start() 163 | // Wait to stop 164 | <-sig 165 | // Stop reading 166 | perfMap.Stop() 167 | } 168 | --------------------------------------------------------------------------------