├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── Makefile ├── README.md ├── beats.txt ├── bin ├── elastic-version └── pytest ├── build ├── auditbeat │ └── config │ │ └── auditbeat.yml ├── filebeat │ └── config │ │ ├── filebeat.yml │ │ └── prospectors.d │ │ └── default.yml ├── heartbeat │ └── config │ │ └── heartbeat.yml ├── journalbeat │ └── config │ │ └── journalbeat.yml ├── metricbeat │ └── config │ │ └── metricbeat.yml └── packetbeat │ └── config │ └── packetbeat.yml ├── requirements.txt ├── templates ├── Dockerfile.j2 ├── docker-compose.yml.j2 └── docker-entrypoint.j2 ├── tests ├── __init__.py ├── conftest.py ├── fixtures.py ├── helpers.py ├── test_base.py ├── test_config.py ├── test_entrypoint.py ├── test_files.py ├── test_labels.py ├── test_modules.py ├── test_process.py └── test_user.py ├── tox.ini └── version.json /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please post all questions and issues on https://discuss.elastic.co/c/beats 2 | before opening a Github Issue. Your questions will reach a wider audience there, 3 | and if we confirm that there is a bug, then you can open a new issue. 4 | 5 | For security vulnerabilities please only send reports to security@elastic.co. 6 | See https://www.elastic.co/community/security for more information. 7 | 8 | For confirmed bugs, please report: 9 | - Beat Version: 10 | - Operating System: 11 | - Docker Version: 12 | - Steps to Reproduce: 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Does this PR include tests? 2 | 3 | `beats-docker` is developed under a test-driven 4 | workflow, so please refrain from submitting patches without test 5 | coverage. If you are not familiar with testing in Python, please 6 | raise an issue instead. 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | build/*/Dockerfile* 3 | build/*/config/*.sh 4 | build/*/docker-entrypoint 5 | docker-compose*.yml 6 | snapshots 7 | .cache 8 | **/__pycache__ 9 | .pytest_cache 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: ['3.5'] 4 | script: make 5 | 6 | sudo: required 7 | services: ['docker'] 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | ifndef ELASTIC_VERSION 4 | export ELASTIC_VERSION := $(shell ./bin/elastic-version) 5 | endif 6 | 7 | ifdef STAGING_BUILD_NUM 8 | export VERSION_TAG := $(ELASTIC_VERSION)-$(STAGING_BUILD_NUM) 9 | DOWNLOAD_URL_ROOT ?= https://staging.elastic.co/$(VERSION_TAG)/downloads/beats 10 | else 11 | export VERSION_TAG := $(ELASTIC_VERSION) 12 | DOWNLOAD_URL_ROOT ?= https://artifacts.elastic.co/downloads/beats 13 | endif 14 | 15 | BUILD_ARTIFACT_PATH ?= beats/build/distributions 16 | 17 | BEATS := $(shell cat beats.txt) 18 | REGISTRY ?= docker.elastic.co 19 | HTTPD ?= beats-docker-artifact-server 20 | 21 | IMAGE_FLAVORS ?= oss full 22 | FIGLET := pyfiglet -w 160 -f puffy 23 | 24 | # Make sure we run local versions of everything, particularly commands 25 | # installed into our virtualenv with pip eg. `docker-compose`. 26 | export PATH := ./bin:./venv/bin:$(PATH) 27 | 28 | all: venv images docker-compose.yml test 29 | 30 | # Run the tests with testinfra (actually our custom wrapper at ./bin/testinfra) 31 | # REF: http://testinfra.readthedocs.io/en/latest/ 32 | test: lint docker-compose.yml 33 | $(foreach FLAVOR, $(IMAGE_FLAVORS), \ 34 | $(FIGLET) "test: $(FLAVOR)"; \ 35 | ./bin/pytest -v --image-flavor=$(FLAVOR) tests/; \ 36 | ) 37 | .PHONY: test 38 | 39 | # Test a snapshot image, which requires modifying the ELASTIC_VERSION to find the right images. 40 | test-snapshot: 41 | ELASTIC_VERSION=$(ELASTIC_VERSION)-SNAPSHOT make test 42 | 43 | lint: venv 44 | flake8 tests/ 45 | 46 | docker-compose.yml: venv 47 | $(foreach FLAVOR, $(IMAGE_FLAVORS), \ 48 | jinja2 \ 49 | -D beats='$(BEATS)' \ 50 | -D version=$(VERSION_TAG) \ 51 | -D registry=$(REGISTRY) \ 52 | -D image_flavor='$(FLAVOR)' \ 53 | templates/docker-compose.yml.j2 > docker-compose-$(FLAVOR).yml; \ 54 | ) 55 | .PHONY: docker-compose.yml 56 | 57 | # Bring up a full-stack demo with Elasticsearch, Kibana and all the Unix Beats. 58 | # Point a browser at http://localhost:5601 to see the results, and log in to 59 | # to Kibana with "elastic"/"changeme". 60 | demo: all 61 | docker-compose up 62 | 63 | # Build images for all the Beats, generate the Dockerfiles as we go. 64 | images: $(BEATS) 65 | $(BEATS): venv 66 | mkdir -p build/$@/config 67 | touch build/$@/config/$@.yml 68 | jinja2 \ 69 | -D beat=$@ \ 70 | -D elastic_version=$(ELASTIC_VERSION) \ 71 | templates/docker-entrypoint.j2 > build/$@/docker-entrypoint 72 | chmod +x build/$@/docker-entrypoint 73 | 74 | jinja2 \ 75 | -D beat=$@ \ 76 | -D elastic_version=$(ELASTIC_VERSION) \ 77 | -D url=$(DOWNLOAD_URL_ROOT)/$@/$@-$(ELASTIC_VERSION)-linux-x86_64.tar.gz \ 78 | -D image_flavor=full \ 79 | templates/Dockerfile.j2 > build/$@/Dockerfile-full 80 | docker build $(DOCKER_FLAGS) -f build/$@/Dockerfile-full --tag=$(REGISTRY)/beats/$@:$(VERSION_TAG) build/$@ 81 | 82 | jinja2 \ 83 | -D beat=$@ \ 84 | -D elastic_version=$(ELASTIC_VERSION) \ 85 | -D url=$(DOWNLOAD_URL_ROOT)/$@/$@-oss-$(ELASTIC_VERSION)-linux-x86_64.tar.gz \ 86 | -D image_flavor=oss \ 87 | templates/Dockerfile.j2 > build/$@/Dockerfile-oss 88 | docker build $(DOCKER_FLAGS) -f build/$@/Dockerfile-oss --tag=$(REGISTRY)/beats/$@-oss:$(VERSION_TAG) build/$@ 89 | 90 | local-httpd: 91 | docker run --rm -d --name=$(HTTPD) --network=host \ 92 | -v $(ARTIFACTS_DIR):/mnt \ 93 | python:3 bash -c 'cd /mnt && python3 -m http.server' 94 | timeout 120 bash -c 'until curl -s localhost:8000 > /dev/null; do sleep 1; done' 95 | 96 | release-manager-snapshot: local-httpd 97 | ELASTIC_VERSION=$(ELASTIC_VERSION)-SNAPSHOT \ 98 | DOWNLOAD_URL_ROOT=http://localhost:8000/$(BUILD_ARTIFACT_PATH) \ 99 | DOCKER_FLAGS='--network=host' \ 100 | make images || (docker kill $(HTTPD); false) 101 | -docker kill $(HTTPD) 102 | release-manager-release: local-httpd 103 | ELASTIC_VERSION=$(ELASTIC_VERSION) \ 104 | DOWNLOAD_URL_ROOT=http://localhost:8000/$(BUILD_ARTIFACT_PATH) \ 105 | DOCKER_FLAGS='--network=host' \ 106 | make images || (docker kill $(HTTPD); false) 107 | -docker kill $(HTTPD) 108 | 109 | # Build images from the latest snapshots on snapshots.elastic.co 110 | from-snapshot: 111 | rm -rf ./snapshots 112 | for beat in $(BEATS); do \ 113 | mkdir -p snapshots/$(BUILD_ARTIFACT_PATH)/$$beat; \ 114 | (cd snapshots/$(BUILD_ARTIFACT_PATH)/$$beat && \ 115 | wget https://snapshots.elastic.co/downloads/beats/$$beat/$$beat-$(ELASTIC_VERSION)-SNAPSHOT-linux-x86_64.tar.gz && \ 116 | wget https://snapshots.elastic.co/downloads/beats/$$beat/$$beat-oss-$(ELASTIC_VERSION)-SNAPSHOT-linux-x86_64.tar.gz); \ 117 | done 118 | ARTIFACTS_DIR=$$PWD/snapshots make release-manager-snapshot 119 | 120 | # Push the images to the dedicated push endpoint at "push.docker.elastic.co" 121 | push: all 122 | for beat in $(BEATS); do \ 123 | docker tag $(REGISTRY)/beats/$$beat:$(VERSION_TAG) push.$(REGISTRY)/beats/$$beat:$(VERSION_TAG); \ 124 | docker push push.$(REGISTRY)/beats/$$beat:$(VERSION_TAG); \ 125 | docker rmi push.$(REGISTRY)/beats/$$beat:$(VERSION_TAG); \ 126 | done 127 | 128 | # The tests are written in Python. Make a virtualenv to handle the dependencies. 129 | venv: requirements.txt 130 | @if [ -z $$PYTHON3 ]; then\ 131 | PY3_MINOR_VER=`python3 --version 2>&1 | cut -d " " -f 2 | cut -d "." -f 2`;\ 132 | if (( $$PY3_MINOR_VER < 5 )); then\ 133 | echo "Couldn't find python3 in \$PATH that is >=3.5";\ 134 | echo "Please install python3.5 or later or explicity define the python3 executable name with \$PYTHON3";\ 135 | echo "Exiting here";\ 136 | exit 1;\ 137 | else\ 138 | export PYTHON3="python3.$$PY3_MINOR_VER";\ 139 | fi;\ 140 | fi;\ 141 | test -d venv || virtualenv --python=$$PYTHON3 venv;\ 142 | pip install -r requirements.txt;\ 143 | touch venv;\ 144 | 145 | clean: venv 146 | docker-compose down -v || true 147 | rm -f docker-compose.yml build/*/Dockerfile build/*/config/*.sh build/*/docker-entrypoint 148 | rm -rf venv 149 | find . -name __pycache__ | xargs rm -rf 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This repository is no longer used to generate the official [Beats][beats] Docker image from 2 | [Elastic][elastic]. 3 | 4 | To build Beats docker images for pre-6.6 releases, switch branches in this repo to the matching release. 5 | 6 | [beats]: https://www.elastic.co/products/beats 7 | [elastic]: https://www.elastic.co/ 8 | -------------------------------------------------------------------------------- /beats.txt: -------------------------------------------------------------------------------- 1 | auditbeat 2 | filebeat 3 | heartbeat 4 | journalbeat 5 | metricbeat 6 | packetbeat 7 | -------------------------------------------------------------------------------- /bin/elastic-version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Print the Elastic Stack version for the current branch, as defined in 4 | # the 'version.json' file. 5 | # 6 | # The version can also be forced by setting the ELASTIC_VERSION environment variable. 7 | 8 | import json 9 | import os 10 | 11 | 12 | def get_hard_coded_version(): 13 | version_info = json.load(open('version.json')) 14 | return version_info['version'] 15 | 16 | 17 | def qualify(version): 18 | qualifier = os.getenv('VERSION_QUALIFIER') 19 | if qualifier: 20 | # ignore None or '' 21 | return "-".join([version, qualifier]) 22 | return version 23 | 24 | 25 | def get_version(): 26 | version = os.getenv('ELASTIC_VERSION') 27 | if version: 28 | return version 29 | return qualify(get_hard_coded_version()) 30 | 31 | 32 | if __name__ == '__main__': 33 | # Provide a shell compatible interface 34 | print(get_version()) 35 | -------------------------------------------------------------------------------- /bin/pytest: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # A wrapper for `pytest` to handle the appropriate testinfra arguments. 4 | 5 | source venv/bin/activate 6 | pytest --connection=docker --hosts=`cat beats.txt | tr "\n" ',' | head -c -1` $@ 7 | -------------------------------------------------------------------------------- /build/auditbeat/config/auditbeat.yml: -------------------------------------------------------------------------------- 1 | auditbeat.modules: 2 | 3 | - module: auditd 4 | audit_rules: | 5 | -w /etc/passwd -p wa -k identity 6 | -a always,exit -F arch=b32 -S open,creat,truncate,ftruncate,openat,open_by_handle_at -F exit=-EPERM -k access 7 | 8 | - module: file_integrity 9 | paths: 10 | - /bin 11 | - /usr/bin 12 | - /sbin 13 | - /usr/sbin 14 | - /etc 15 | 16 | output.elasticsearch: 17 | hosts: ['elasticsearch:9200'] 18 | username: elastic 19 | password: changeme 20 | -------------------------------------------------------------------------------- /build/filebeat/config/filebeat.yml: -------------------------------------------------------------------------------- 1 | filebeat.config: 2 | prospectors: 3 | path: ${path.config}/prospectors.d/*.yml 4 | reload.enabled: false 5 | modules: 6 | path: ${path.config}/modules.d/*.yml 7 | reload.enabled: false 8 | 9 | processors: 10 | - add_cloud_metadata: 11 | 12 | output.elasticsearch: 13 | hosts: ['elasticsearch:9200'] 14 | username: elastic 15 | password: changeme 16 | -------------------------------------------------------------------------------- /build/filebeat/config/prospectors.d/default.yml: -------------------------------------------------------------------------------- 1 | - type: log 2 | paths: 3 | - /mnt/log/*.log 4 | -------------------------------------------------------------------------------- /build/heartbeat/config/heartbeat.yml: -------------------------------------------------------------------------------- 1 | heartbeat.monitors: 2 | - type: http 3 | schedule: '@every 5s' 4 | urls: 5 | - http://elasticsearch:9200 6 | - http://kibana:5601 7 | 8 | - type: icmp 9 | schedule: '@every 5s' 10 | hosts: 11 | - elasticsearch 12 | - kibana 13 | 14 | processors: 15 | - add_cloud_metadata: 16 | 17 | output.elasticsearch: 18 | hosts: ['elasticsearch:9200'] 19 | username: elastic 20 | password: changeme 21 | -------------------------------------------------------------------------------- /build/journalbeat/config/journalbeat.yml: -------------------------------------------------------------------------------- 1 | journalbeat.inputs: 2 | - paths: [] 3 | 4 | processors: 5 | - add_host_metadata: 6 | - add_cloud_metadata: 7 | 8 | output.elasticsearch: 9 | hosts: ['elasticsearch:9200'] 10 | username: elastic 11 | password: changeme 12 | -------------------------------------------------------------------------------- /build/metricbeat/config/metricbeat.yml: -------------------------------------------------------------------------------- 1 | metricbeat.config.modules: 2 | path: ${path.config}/modules.d/*.yml 3 | reload.enabled: false 4 | 5 | processors: 6 | - add_cloud_metadata: 7 | 8 | output.elasticsearch: 9 | hosts: ['elasticsearch:9200'] 10 | username: elastic 11 | password: changeme 12 | -------------------------------------------------------------------------------- /build/packetbeat/config/packetbeat.yml: -------------------------------------------------------------------------------- 1 | packetbeat.interfaces.device: any 2 | 3 | packetbeat.flows: 4 | timeout: 30s 5 | period: 10s 6 | 7 | packetbeat.protocols.dns: 8 | ports: [53] 9 | include_authorities: true 10 | include_additionals: true 11 | 12 | packetbeat.protocols.http: 13 | ports: [80, 5601, 9200, 8080, 8081, 5000, 8002] 14 | 15 | packetbeat.protocols.memcache: 16 | ports: [11211] 17 | 18 | packetbeat.protocols.mysql: 19 | ports: [3306] 20 | 21 | packetbeat.protocols.pgsql: 22 | ports: [5432] 23 | 24 | packetbeat.protocols.redis: 25 | ports: [6379] 26 | 27 | packetbeat.protocols.thrift: 28 | ports: [9090] 29 | 30 | packetbeat.protocols.mongodb: 31 | ports: [27017] 32 | 33 | packetbeat.protocols.cassandra: 34 | ports: [9042] 35 | 36 | 37 | processors: 38 | - add_cloud_metadata: 39 | 40 | output.elasticsearch: 41 | hosts: ['elasticsearch:9200'] 42 | username: elastic 43 | password: changeme 44 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | docker-compose==1.11.1 2 | flake8==3.3.0 3 | jinja2-cli[yaml]==0.6.0 4 | jinja2==2.9.5 5 | testinfra==1.5.3 6 | pip<10 7 | pyfiglet 8 | -------------------------------------------------------------------------------- /templates/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # This Dockerfile was generated from templates/Dockerfile.j2 2 | {% set beat_home = '/usr/share/%s' % beat -%} 3 | {% set binary_file = '%s/%s' % (beat_home, beat) -%} 4 | 5 | FROM centos:7 6 | 7 | RUN yum update -y && yum install -y curl && yum clean all 8 | 9 | RUN curl -Lso - {{ url }} | \ 10 | tar zxf - -C /tmp && \ 11 | mv /tmp/{{ beat }}-{{ elastic_version }}-linux-x86_64 {{ beat_home }} 12 | 13 | 14 | ENV ELASTIC_CONTAINER true 15 | ENV PATH={{ beat_home }}:$PATH 16 | 17 | COPY config {{ beat_home }} 18 | 19 | # Add entrypoint wrapper script 20 | ADD docker-entrypoint /usr/local/bin 21 | 22 | # Provide a non-root user. 23 | RUN groupadd --gid 1000 {{ beat }} && \ 24 | useradd -M --uid 1000 --gid 1000 --home {{ beat_home }} {{ beat }} 25 | 26 | WORKDIR {{ beat_home }} 27 | RUN mkdir data logs && \ 28 | chown -R root:{{ beat }} . && \ 29 | find {{ beat_home }} -type d -exec chmod 0750 {} \; && \ 30 | find {{ beat_home }} -type f -exec chmod 0640 {} \; && \ 31 | chmod 0750 {{ binary_file }} && \ 32 | {%- if beat == 'filebeat' or beat == 'metricbeat' -%} 33 | chmod 0770 modules.d && \ 34 | {% endif -%} 35 | chmod 0770 data logs 36 | 37 | {%- if beat == 'packetbeat' %} 38 | RUN setcap cap_net_raw,cap_net_admin=eip {{ binary_file }} 39 | {% endif %} 40 | {%- if beat == 'heartbeat' %} 41 | RUN setcap cap_net_raw=eip {{ binary_file }} 42 | {% endif %} 43 | {%- if beat == 'auditbeat' %} 44 | USER root 45 | {% else %} 46 | USER 1000 47 | {% endif %} 48 | 49 | LABEL org.label-schema.schema-version="1.0" \ 50 | org.label-schema.vendor="Elastic" \ 51 | org.label-schema.name="{{ beat }}" \ 52 | org.label-schema.version="{{ elastic_version }}" \ 53 | org.label-schema.url="https://www.elastic.co/products/beats/{{ beat }}" \ 54 | org.label-schema.vcs-url="https://github.com/elastic/beats-docker" \ 55 | {% if image_flavor == 'oss' -%} 56 | license="Apache-2.0" 57 | {% else -%} 58 | license="Elastic License" 59 | {% endif -%} 60 | 61 | ENTRYPOINT ["/usr/local/bin/docker-entrypoint"] 62 | CMD ["-e"] 63 | -------------------------------------------------------------------------------- /templates/docker-compose.yml.j2: -------------------------------------------------------------------------------- 1 | {% if image_flavor == 'oss' -%} 2 | {% set beat_suffix = '-oss' -%} 3 | {% else -%} 4 | {% set beat_suffix = '' -%} 5 | {% endif -%} 6 | 7 | --- 8 | # This file was generated from templates/docker-compose.yml.j2 9 | version: '2.1' 10 | services: 11 | {%- for beat in beats.split() %} 12 | {{ beat }}: 13 | image: {{ registry }}/beats/{{ beat }}{{ beat_suffix }}:{{ version }} 14 | container_name: {{ beat }} 15 | 16 | {%- if beat == 'auditbeat' %} 17 | cap_add: 18 | - AUDIT_CONTROL 19 | pid: host 20 | {%- endif %} 21 | 22 | {%- if beat == 'packetbeat' %} 23 | cap_add: 24 | - NET_RAW 25 | - NET_ADMIN 26 | {%- endif %} 27 | 28 | {% if beat == 'journalbeat' %} 29 | volumes: 30 | - /etc/machine-id:/etc/machine-id 31 | - /etc/hostname:/etc/hostname:ro 32 | {%- endif %} 33 | {% endfor %} 34 | -------------------------------------------------------------------------------- /templates/docker-entrypoint.j2: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -euo pipefail 4 | 5 | # Check if the the user has invoked the image with flags. 6 | # eg. "{{ beat }} -c {{ beat }}.yml" 7 | if [[ -z $1 ]] || [[ ${1:0:1} == '-' ]] ; then 8 | exec {{ beat }} "$@" 9 | else 10 | # They may be looking for a Beat subcommand, like "{{ beat }} setup". 11 | subcommands=$({{ beat }} help \ 12 | | awk 'BEGIN {RS=""; FS="\n"} /Available Commands:/' \ 13 | | awk '/^\s+/ {print $1}') 14 | 15 | # If we _did_ get a subcommand, pass it to {{ beat }}. 16 | for subcommand in $subcommands; do 17 | if [[ $1 == $subcommand ]]; then 18 | exec {{ beat }} "$@" 19 | fi 20 | done 21 | fi 22 | 23 | # If neither of those worked, then they have specified the binary they want, so 24 | # just do exactly as they say. 25 | exec "$@" 26 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/beats-docker/bcc776df86623cc42bb813a49df1e93dc0cfd03d/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | from subprocess import run 2 | 3 | 4 | def get_compose_file(config): 5 | return 'docker-compose-%s.yml' % config.getoption('--image-flavor') 6 | 7 | 8 | def pytest_addoption(parser): 9 | """Customize testinfra with config options via cli args""" 10 | # Let us specify which docker-compose-(image_flavor).yml file to use 11 | parser.addoption('--image-flavor', action='store', 12 | help='Docker image flavor; the suffix used in docker-compose-.yml') 13 | 14 | 15 | def pytest_configure(config): 16 | run(['docker-compose', '-f', get_compose_file(config), 'down']) 17 | run(['docker-compose', '-f', get_compose_file(config), 'up', '--force-recreate', '-d', '--no-deps']) 18 | 19 | 20 | def pytest_unconfigure(config): 21 | run(['docker-compose', '-f', get_compose_file(config), 'down']) 22 | -------------------------------------------------------------------------------- /tests/fixtures.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import pytest 4 | import datetime 5 | from subprocess import run, PIPE 6 | 7 | version = run('./bin/elastic-version', stdout=PIPE).stdout.decode().strip() 8 | 9 | 10 | @pytest.fixture() 11 | def beat(Process, File, TestinfraBackend, Command): 12 | class Beat: 13 | def __init__(self): 14 | # We name the container after the Beat, so asking for the hostname 15 | # lets us know which Beat we are testing. 16 | name = TestinfraBackend.get_hostname() 17 | self.name = name 18 | 19 | # Auditbeat might already be running on this system and in the top-level 20 | # PID namespace too. We can't just assume that the first Auditbeat process 21 | # we see is the one we are interested in. A better assumption is that the 22 | # _newest_ process is the one we are interested in. 23 | processes = {} 24 | for process in Process.filter(comm=name): 25 | start_time = datetime.datetime.strptime(process['lstart'], "%a %b %d %H:%M:%S %Y") 26 | processes[start_time] = process 27 | newest_process = processes[max(processes.keys())] 28 | self.process = newest_process 29 | 30 | home = os.path.join(os.sep, 'usr', 'share', name) 31 | self.home_dir = File(home) 32 | self.data_dir = File(os.path.join(home, 'data')) 33 | self.config_dir = File(home) 34 | self.log_dir = File(os.path.join(home, 'logs')) 35 | self.kibana_dir = File(os.path.join(home, 'kibana')) 36 | self.binary_file = File(os.path.join(home, name)) 37 | self.config_file = File(os.path.join(home, '%s.yml' % name)) 38 | self.version = version.replace('-SNAPSHOT', '') 39 | 40 | # What Linux capabilities does the binary file have? 41 | capability_string = Command.check_output('getcap %s' % self.binary_file.path) 42 | # Like: '/usr/share/packetbeat/packetbeat = cap_net_admin,cap_net_raw+eip' 43 | if capability_string: 44 | self.capabilities = capability_string.split()[-1].split('+')[0].split(',') 45 | # Like: ['cap_net_raw', 'cap_net_admin'] 46 | else: 47 | self.capabilities = [] 48 | 49 | if 'STAGING_BUILD_NUM' in os.environ: 50 | self.tag = '%s-%s' % (version, os.environ['STAGING_BUILD_NUM']) 51 | else: 52 | self.tag = version 53 | 54 | self.flavor = pytest.config.getoption('--image-flavor') 55 | if self.flavor != 'full': 56 | self.image = 'docker.elastic.co/beats/%s-%s:%s' % (self.name, self.flavor, self.tag) 57 | else: 58 | self.image = 'docker.elastic.co/beats/%s:%s' % (self.name, self.tag) 59 | 60 | self.docker_metadata = json.loads( 61 | run(['docker', 'inspect', self.image], stdout=PIPE).stdout.decode())[0] 62 | 63 | return Beat() 64 | -------------------------------------------------------------------------------- /tests/helpers.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | 5 | def run(beat, command): 6 | 7 | caps = '' 8 | if beat.name == 'packetbeat': 9 | caps = '--cap-add net_admin --cap-add net_raw' 10 | 11 | if beat.name == 'heartbeat': 12 | caps = '--cap-add net_raw' 13 | 14 | if beat.name == 'auditbeat': 15 | caps = '--cap-add audit_control --pid=host' 16 | 17 | cli = 'docker run %s --rm --interactive %s %s' % (caps, beat.image, command) 18 | result = subprocess.run(cli, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 19 | result.stdout = result.stdout.rstrip() 20 | return result 21 | -------------------------------------------------------------------------------- /tests/test_base.py: -------------------------------------------------------------------------------- 1 | def test_base_os(SystemInfo): 2 | assert SystemInfo.distribution == 'centos' 3 | assert SystemInfo.release == '7' 4 | -------------------------------------------------------------------------------- /tests/test_config.py: -------------------------------------------------------------------------------- 1 | from .fixtures import beat 2 | 3 | 4 | def test_config_file_passes_config_test(Command, beat): 5 | configtest = '%s -c %s -configtest' % (beat.binary_file.path, beat.config_file.path) 6 | Command.run_expect([0], configtest) 7 | -------------------------------------------------------------------------------- /tests/test_entrypoint.py: -------------------------------------------------------------------------------- 1 | from .fixtures import beat 2 | from .helpers import run 3 | 4 | 5 | def test_entrypoint_with_args(beat): 6 | cmd = run(beat, "-c %s -configtest" % beat.config_file.path) 7 | assert cmd.returncode == 0 8 | 9 | 10 | def test_entrypoint_with_beat_subcommand(beat): 11 | cmd = run(beat, 'help') 12 | assert cmd.returncode == 0 13 | assert 'Usage:' in cmd.stdout.decode() 14 | assert beat.name in cmd.stdout.decode() 15 | 16 | 17 | def test_entrypoint_with_beat_subcommand_and_longopt(beat): 18 | cmd = run(beat, 'setup --help') 19 | assert cmd.returncode == 0 20 | assert b'This command does initial setup' in cmd.stdout 21 | 22 | 23 | def test_entrypoint_with_abitrary_command(beat): 24 | cmd = run(beat, "echo Hello World!") 25 | assert cmd.returncode == 0 26 | assert cmd.stdout == b'Hello World!' 27 | 28 | 29 | def test_entrypoint_with_explicit_beat_binary(beat): 30 | cmd = run(beat, '%s --version' % beat.name) 31 | assert cmd.returncode == 0 32 | assert beat.version in cmd.stdout.decode() 33 | -------------------------------------------------------------------------------- /tests/test_files.py: -------------------------------------------------------------------------------- 1 | from .fixtures import beat 2 | import os 3 | 4 | 5 | def test_binary_file_version(Command, beat): 6 | version_string = '%s version %s (amd64), libbeat %s' \ 7 | % (beat.name, beat.version, beat.version) 8 | command = Command('%s --version' % beat.binary_file.path) 9 | assert command.stdout.strip() == version_string 10 | 11 | 12 | def test_binary_file_permissions(beat): 13 | assert beat.binary_file.user == 'root' 14 | assert beat.binary_file.group == beat.name 15 | assert beat.binary_file.mode == 0o0750 16 | 17 | 18 | def test_net_raw_capability(Command, beat): 19 | if beat.name in ['packetbeat', 'heartbeat']: 20 | assert 'cap_net_raw' in beat.capabilities 21 | else: 22 | assert 'cap_net_raw' not in beat.capabilities 23 | 24 | 25 | def test_net_admin_capability(Command, beat): 26 | if beat.name == 'packetbeat': 27 | assert 'cap_net_admin' in beat.capabilities 28 | else: 29 | assert 'cap_net_admin' not in beat.capabilities 30 | 31 | 32 | def test_config_file_permissions(beat): 33 | assert beat.config_file.user == 'root' 34 | assert beat.config_file.group == beat.name 35 | assert beat.config_file.mode == 0o0640 36 | 37 | 38 | def test_config_dir_permissions(beat): 39 | assert beat.config_dir.user == 'root' 40 | assert beat.config_dir.group == beat.name 41 | assert beat.config_dir.mode == 0o0750 42 | 43 | 44 | def test_data_dir_permissions(beat): 45 | assert beat.data_dir.user == 'root' 46 | assert beat.data_dir.group == beat.name 47 | assert beat.data_dir.mode == 0o0770 48 | 49 | 50 | def test_kibana_dir_permissions(beat): 51 | assert beat.kibana_dir.user == 'root' 52 | assert beat.kibana_dir.group == beat.name 53 | assert beat.kibana_dir.mode == 0o0750 54 | 55 | 56 | def test_log_dir_permissions(beat): 57 | assert beat.log_dir.user == 'root' 58 | assert beat.log_dir.group == beat.name 59 | assert beat.log_dir.mode == 0o0770 60 | -------------------------------------------------------------------------------- /tests/test_labels.py: -------------------------------------------------------------------------------- 1 | from .fixtures import beat 2 | 3 | 4 | def test_labels(beat): 5 | labels = beat.docker_metadata['Config']['Labels'] 6 | assert labels['org.label-schema.name'] == beat.name 7 | assert labels['org.label-schema.schema-version'] == '1.0' 8 | assert labels['org.label-schema.url'] == 'https://www.elastic.co/products/beats/' + beat.name 9 | assert labels['org.label-schema.vcs-url'] == 'https://github.com/elastic/beats-docker' 10 | assert labels['org.label-schema.vendor'] == 'Elastic' 11 | assert labels['org.label-schema.version'] == beat.tag 12 | if beat.flavor == 'oss': 13 | assert labels['license'] == 'Apache-2.0' 14 | else: 15 | assert labels['license'] == 'Elastic License' 16 | -------------------------------------------------------------------------------- /tests/test_modules.py: -------------------------------------------------------------------------------- 1 | from .fixtures import beat 2 | import os 3 | 4 | # Beats supporting modules command: 5 | MODULES_BEATS = ('filebeat', 'metricbeat') 6 | 7 | 8 | def test_list_modules(Command, beat): 9 | if beat.name in MODULES_BEATS: 10 | cmd = Command.run('%s modules list' % beat.name) 11 | 12 | assert cmd.rc == 0 13 | assert 'Enabled' in cmd.stdout 14 | assert 'Disabled' in cmd.stdout 15 | assert 'system' in cmd.stdout 16 | 17 | 18 | def test_enable_module(Command, beat): 19 | if beat.name in MODULES_BEATS: 20 | cmd = Command.run('%s modules enable nginx' % beat.name) 21 | 22 | assert cmd.rc == 0 23 | assert cmd.stdout == 'Enabled nginx\n' 24 | -------------------------------------------------------------------------------- /tests/test_process.py: -------------------------------------------------------------------------------- 1 | from .fixtures import beat 2 | 3 | 4 | def test_process_is_pid_1(beat): 5 | if beat.name == 'auditbeat': 6 | assert beat.process.pid > 1 7 | else: 8 | assert beat.process.pid == 1 9 | 10 | 11 | def test_process_is_running_as_the_correct_user(beat): 12 | if beat.name == 'auditbeat': 13 | assert beat.process.user == 'root' 14 | else: 15 | assert beat.process.user == beat.name 16 | 17 | 18 | def test_process_was_started_with_the_foreground_flag(beat): 19 | assert '-e' in beat.process['args'] 20 | -------------------------------------------------------------------------------- /tests/test_user.py: -------------------------------------------------------------------------------- 1 | from .fixtures import beat 2 | 3 | 4 | def test_group_properties(Group, beat): 5 | group = Group(beat.name) 6 | assert group.exists 7 | assert group.gid == 1000 8 | 9 | 10 | def test_user_properties(User, beat): 11 | user = User(beat.name) 12 | assert user.uid == 1000 13 | assert user.gid == 1000 14 | assert user.group == beat.name 15 | assert user.home == '/usr/share/%s' % beat.name 16 | assert user.shell == '/bin/bash' 17 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # pytest fixtures (which are wonderful) trigger false positives for these 2 | # pyflakes checks. 3 | [flake8] 4 | ignore = F401,F811 5 | max-line-length = 120 6 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | {"version": "7.0.0"} 2 | --------------------------------------------------------------------------------